← Home

Refocus API for mobile apps

Last updated September 21, 2026


AI / automated clients — fetch the catalog (do not paste docs):

The JSON includes every mobile-relevant route, body fields, enums, status codes, error strings, cookies, Ably channels, and call-window constants. Prefer it over this HTML page when generating a client.

Build a React Native (or other native) client against the Refocus product API. Canonical source: docs/mobile-api.json on the product (test-dash) branch — GitHub.

No Bearer tokens for end users. Auth is NextAuth JWT in httpOnly cookies — use a cookie jar.

Overview

  • Base URL: https://dashboard.refocus.co.in (never the marketing apex for /api/*)
  • JSON: application/json
  • NextAuth login/signout: application/x-www-form-urlencoded

Minimum path

  1. Register → verify email → login (cookies)
  2. GET /api/users/me → require emailVerified
  3. List / create / join sessions
  4. Call window → Daily token → Daily RN SDK
  5. Ably token → friend chat
  6. Sign out

There is no in-session text-chat API beyond Daily A/V. In-call extras are tasks + cheer alerts over Ably.

Authentication

Cookies (production)

  • __Secure-next-auth.session-token — session JWT (Domain=.refocus.co.in)
  • __Host-next-auth.csrf-token — CSRF (host-only on dashboard)

Persist Set-Cookie. Do not forge Origin/Referer (403 cross-origin). No CORS — native HTTP only.

Register

POST /api/auth/register — email, password required → { "id": "<userId>" }. Does not set a session.

Login (exact)

  1. GET /api/auth/csrf → { "csrfToken": "…" }
  2. POST /api/auth/callback/credentials as x-www-form-urlencoded with csrfToken, callbackUrl, json=true, and email+password or firebaseIdToken

Success: { "url": "<callbackUrl>" } + session Set-Cookie. Failure often 401 with error=CredentialsSignin in the url. Without json=true you get a 302 — avoid that in RN.

Probe: GET /api/auth/session. Sign out: POST /api/auth/signout (same form style + json=true).

Email verification

GET /api/auth/verify-email?token=… redirects (not JSON). Handle via deep link / in-app browser, then re-check GET /api/users/me. Resend: POST /api/auth/resend-verification.

Booking & access gates

403 email:

{
  "error": "Verify your email to use this feature. You can browse until then.",
  "code": "EMAIL_NOT_VERIFIED"
}

403 first session (zero attendance → only one upcoming booking/request):

{
  "error": "Attend your first session before booking another. Finish the one you already have, then you can schedule more.",
  "code": "FIRST_SESSION_REQUIRED"
}

Call window: join allowed 10 minutes before start through 10 minutes after end (WRAP_UP token padding: 5 min).

Current user

  • GET /api/users/me — profile + emailVerified + attendance
  • PATCH /api/users/me — profile fields (verified)
  • GET /api/users/username?q= — availability
  • GET/PATCH /api/users/preferences
  • POST /api/users/me/avatar — multipart field avatar (≤5 MB)

Sessions

Durations 25 | 50 | 75. Types focus | deep-work | learning. Starts on 30-minute marks. Horizon 90 days.

  • GET /api/sessions?from=&to= or ?mineUpcoming=1 — list includes durationMin, sessionType, status, participants, occupied chips
  • POST /api/sessions — { "start", "durationMin", "sessionType", "quietOwner?" } → { "id" }
  • GET /api/sessions/[id] — sparse: { "id", "owner_id", "start", "end", "participants": [{ "user_id", "joined_at", "quiet?" }], "youQuiet", "partner": { "userId", "name", "username", "avatarUrl" } }
  • POST …/join — { "quiet?" } → { "ok": true } (400 if started/ended; 409 if full/overlap)
  • POST …/leave — non-owner; optional message
  • DELETE …/[id] — owner cancel; optional message
  • PATCH …/[id] — name?, color?

Daily video

POST /api/sessions/[id]/daily/token inside the call window:

{
  "token": "<daily-meeting-token>",
  "roomName": "…",
  "domain": "….daily.co"
}

Use the Daily React Native SDK with that token and domain. Also: /tasks, /alert, /attendance.

Friends & session requests

  • GET /api/friends — { friends, nextCursor, total }
  • POST /api/friends/requests — { "to_user_id" }
  • GET /api/friends/requests?type=incoming|outgoing
  • POST /api/friends/requests/[id] — { "action": "accept" | "decline" }
  • POST /api/session-requests — { "to_user_id", "start", "durationMin", "message?" }
  • GET /api/session-requests?type=&status=
  • POST /api/session-requests/[id] — { "action": "accept" | "decline", "message?" } → { "ok": true, "sessionId": "<id>|null" }
  • DELETE /api/session-requests/[id] — cancel pending

Chat & Ably

Friend REST: GET/POST /api/chat/[friendId] — { "type": "text", "content" } or { "type": "session-request", "start", "durationMin", "message?" }. Also unread-counts and read markers.

Global: GET/POST /api/global-chat — { "content" }.

GET /api/ably/token → Ably TokenRequest (send cookies). Channels:

  • DM: chat:{sortedUserA}:{sortedUserB}
  • Inbox: user:{userId}:chat
  • Global: chat:global
  • Calendar: sessions:updates
  • Tasks/alerts: session:{sessionId}:tasks / session:{sessionId}:alerts

Community

  • GET /api/community/posts
  • POST /api/community/posts — { "content" }
  • Like + comments under /posts/[postId]/…

Public profile

GET /api/profile/[username] — no session for public profiles; 404 if private/missing.

Safety

POST /api/reports:

{
  "targetType": "friend_message",
  "targetId": "…",
  "reason": "harassment",
  "details?": "…",
  "reportedUserId?"
}

Block: POST /api/users/blocks / DELETE /api/users/blocks/[userId].

Errors

  • 401 — no session
  • 403 — origin, email, first-session, ban, call window
  • 404 / 409 — not found / conflict
  • 429 — { "error": "Too many requests", "retryAfter" } + Retry-After

Rough limits: auth 5/min · api 100/min · chat 30/min.

RN stack checklist

  • Cookie-aware HTTP client targeting dashboard host only
  • Daily React Native SDK for video
  • Ably client for chat / session events
  • Optional Firebase Auth if using Google → firebaseIdToken
  • Deep links for email verification

Out of scope

  • Admin / cron / ops endpoints
  • Server-only secrets
  • Marketing host JSON API
  • Mobile Bearer auth (not implemented yet)