Quesday REST API

Expo mobile API reference

These endpoints are the stable REST contract exposed by the Next.js backend for the Expo app. The web client should keep using Server Actions for in-app workflows.

Base URL

/api

Auth

JWT bearer

Response format

JSON

Mobile Integration Rules

Register or login, then store token from { token, user } in Expo SecureStore.

Send Authorization: Bearer <token> on authenticated calls. On any 401, clear the token and route to login.

Load communities with GET /api/communities, then GET /api/communities/[slug] for detail tabs.

Use GET /api/communities/[slug]/questions for archives and GET /api/communities/[slug]/questions/[id] before answering.

Submit answers with POST /api/communities/[slug]/questions/[id]/answers, then show result, explanation, and unlocked comments.

Use cursor pagination for comments and broadcasts; use offset pagination for communities and questions.

Headers

Content-Type: application/json
Authorization: Bearer <token>

Endpoints

Dynamic path segments use square brackets to match the Next.js route names. All timestamps are ISO 8601 strings.

POST/api/auth/register

Create a member account and receive a JWT for mobile storage.

Public
Body
{ email, username, password }
Returns
201 { token, user }
Errors
400 invalid JSON; 409 email or username taken; 422 validation
POST/api/auth/login

Authenticate by email and password.

Public
Body
{ email, password }
Returns
200 { token, user }
Errors
400 invalid JSON; 401 invalid credentials; 422 validation
GET/api/auth/me

Resolve the current JWT into the signed-in user.

Bearer required
Returns
200 { user }
Errors
401 missing, invalid, or stale token
GET/api/communities

List discoverable communities with viewer membership flags when signed in.

Optional bearer
Query
limit default 24; offset default 0
Returns
200 { items: Community[], pagination: { limit, offset } }
Errors
Standard 5xx for unexpected server errors
POST/api/communities

Create a community. This is exposed in REST, though creator-heavy flows live on web.

Bearer required
Body
{ name, description, emoji, cadence }
Returns
201 Community
Errors
400 invalid JSON; 401 unauthenticated; 403 suspended; 409 duplicate name; 422 validation
GET/api/communities/[slug]

Fetch one community by slug.

Optional bearer
Returns
200 Community
Errors
404 community not found
POST/api/communities/[slug]/join

Join a community as a member.

Bearer required
Returns
200 Community
Errors
401 unauthenticated; 403 suspended; 404 community not found
DELETE/api/communities/[slug]/join

Leave a joined community.

Bearer required
Returns
200 Community
Errors
401 unauthenticated; 403 suspended; 404 community not found; 409 membership rule conflict
GET/api/communities/[slug]/questions

List scheduled and published questions for a community with viewer answer summaries.

Optional bearer
Query
limit default 20; offset default 0
Returns
200 { items: QuestionSummary[], pagination: { limit, offset } }
Errors
Standard 5xx for unexpected server errors
POST/api/communities/[slug]/questions

Create a question as a community creator.

Bearer required
Body
{ prompt, explanation, imageUrl?, scheduledFor, choices }
Returns
201 QuestionSummary
Errors
400 invalid JSON; 401 unauthenticated; 403 not creator or suspended; 422 validation
GET/api/communities/[slug]/questions/[id]

Fetch answer-state-aware question detail for the signed-in member.

Bearer required
Returns
200 QuestionDetail
Errors
401 unauthenticated; 403 not allowed; 404 question not found
POST/api/communities/[slug]/questions/[id]/answers

Submit one multiple-choice answer and receive grading plus explanation.

Bearer required
Body
{ choiceId }
Returns
201 { questionId, canAnswer, isClosed, isScheduled, result, explanation, choices }
Errors
400 invalid JSON; 401 unauthenticated; 403 not allowed or suspended; 404 question not found; 409 already answered or unavailable; 422 validation
GET/api/communities/[slug]/questions/[id]/comments

List unlocked question comments with one reply level.

Bearer required
Query
limit normalized by server; cursor optional
Returns
200 { items: Comment[], pagination: { limit, nextCursor } }
Errors
400 invalid cursor; 401 unauthenticated; 403 locked or not allowed; 404 question not found
POST/api/communities/[slug]/questions/[id]/comments

Post a top-level comment or one-level reply after answering.

Bearer required
Body
{ body, parentCommentId? }
Returns
201 { comment }
Errors
401 unauthenticated; 403 locked, not allowed, or suspended; 404 question/comment not found; 422 invalid JSON or validation
DELETE/api/communities/[slug]/questions/[id]/comments/[commentId]

Soft-delete a comment. Authors and same-community creators can delete.

Bearer required
Returns
204 empty response
Errors
401 unauthenticated; 403 not allowed or suspended; 404 question/comment not found
GET/api/communities/[slug]/broadcasts

List community broadcast posts. Some communities may require membership.

Optional bearer
Query
limit default 20 after normalization; cursor optional
Returns
200 { items: Broadcast[], pagination: { limit, nextCursor } }
Errors
400 invalid cursor; 401 auth required by policy; 403 membership required; 404 community not found
POST/api/communities/[slug]/broadcasts

Create a creator broadcast post.

Bearer required
Body
{ body, imageUrl? }
Returns
201 { post }
Errors
400 invalid JSON; 401 unauthenticated; 403 not creator or suspended; 404 community not found; 422 validation
GET/api/communities/[slug]/broadcasts/[postId]

Fetch one broadcast post.

Optional bearer
Returns
200 { post }
Errors
401 auth required by policy; 403 membership required; 404 broadcast not found
PATCH/api/communities/[slug]/broadcasts/[postId]

Edit an existing broadcast post as its author or an allowed creator.

Bearer required
Body
{ body, imageUrl? }
Returns
200 { post }
Errors
400 invalid JSON; 401 unauthenticated; 403 not allowed or suspended; 404 broadcast not found; 422 validation
DELETE/api/communities/[slug]/broadcasts/[postId]

Soft-delete a broadcast post.

Bearer required
Returns
204 empty response
Errors
401 unauthenticated; 403 not allowed or suspended; 404 broadcast not found
GET/api/communities/[slug]/leaderboard

Fetch the community leaderboard and signed-in viewer rank when available.

Optional bearer
Query
window one of 7d, 30d, all; defaults by server normalization
Returns
200 { community, window, entries, viewerEntry }
Errors
404 community not found
GET/api/users/[username]

Fetch a public user profile for mobile profile screens.

Public
Returns
200 { user, stats, communities, streak }
Errors
404 user not found
POST/api/uploads/presign

Create a signed R2 upload target for images and attachments.

Bearer required
Body
{ scope, communityId?, contentType, sizeBytes }
Returns
200 { uploadUrl, method, headers, publicUrl, key }
Errors
400 invalid JSON; 401 unauthenticated; 422 validation

Resource Shapes

User

{ id, email, username, role: 'member' | 'admin', status: 'active' | 'suspended', createdAt }

Community

{ id, slug, name, description, emoji, coverImageUrl, cadence, status, creatorUserId, category, isFeatured, featuredRank, directoryRank, memberCount, liveQuestionCount, unansweredQuestionCount, newBroadcastCount, currentUserRole, createdAt, updatedAt }

QuestionChoice

{ id, label, imageUrl, position, isCorrect }. isCorrect may be hidden as null until the solution is visible.

QuestionSummary

{ id, communityId, creatorUserId, prompt, explanation, imageUrl, scheduledFor, publishedAt, closesAt, timeZone, points, choiceCount, choices, viewerAnswer, createdAt, updatedAt }

QuestionDetail

QuestionSummary plus { currentUserRole, canAnswer, canSeeSolution, isClosed, isScheduled, result }.

QuestionResult

{ id, questionId, selectedChoiceId, correctChoiceId, isCorrect, isLate, pointsAwarded, answeredAt, selectedChoice, correctChoice }

Comment

{ id, questionId, parentCommentId, author, body, deletedAt, createdAt, updatedAt, canDelete, replies }

Broadcast

{ id, communityId, author, body, imageUrl, publishedAt, createdAt, updatedAt, canEdit, canDelete }

LeaderboardEntry

{ rank, userId, username, points, lastScoringAnswerAt }

PublicUserProfile

{ user: { id, username, joinedAt }, stats: { totalPoints, communityCount }, communities: [{ id, slug, name, role, joinedAt }] }

Errors and CORS

Error responses use JSON with an errorstring. Validation responses may also include fieldErrors keyed by field name.

REST routes support OPTIONS and return CORS headers through the shared API utility, so Expo web export and native clients can call the backend from configured origins.

{
  "error": "Validation failed.",
  "fieldErrors": {
    "email": "Enter a valid email address."
  }
}