Skip to main content

Example: Building a Mobile App

Everything a consumer mobile or web app needs sits on top of the same REST + real-time surface. This is a worked example of stitching those APIs together into a client — auth, a chat turn, files, and notifications — in the order you'd typically build them.

Treat it as an illustration, not a spec: adapt the flow to whatever platform or framework you're on.

Authentication

Client apps send two credentials on every request:

HeaderValueIdentifies
X-API-Keyyour publishable key (pk_…)your app / tenant
AuthorizationBearer <Firebase ID token>the signed-in end user

The publishable key is safe to ship in the app binary — it can only act on behalf of an authenticated user and cannot be used for backend/admin operations (see Publishable Key Restrictions). The Firebase ID token is what actually carries user identity: the platform derives user_id, memory scope, and per-user data from it. You do not put a user ID in request bodies.

curl -X POST https://api.yocaso.dev/api/v1/llm/gateway/list-threads \
-H "X-API-Key: pk_your_publishable_key" \
-H "Authorization: Bearer <firebase_id_token>" \
-H "Content-Type: application/json" \
-d '{}'

Signing in the user

Users sign in with Firebase Authentication (email/password, Sign in with Apple, etc.) using the Firebase config for your tenant. After sign-in, get an ID token from the Firebase SDK:

let token = try await Auth.auth().currentUser?.getIDToken()

The same token authenticates both REST calls and the real-time stream.

Refreshing the token

Firebase ID tokens expire after ~1 hour. Cache the token and refresh it before it expires rather than on every call:

  • Parse exp from the token payload and treat it as expired ~5 minutes early to absorb clock skew and in-flight requests.
  • When within that buffer, force a refresh (getIDToken(forcingRefresh: true)), and coalesce concurrent refreshes so a burst of requests triggers one refresh, not many.

Handling 401 and 429

StatusWhat it meansWhat to do
401Token expired or invalidForce-refresh the Firebase token once and retry the request. If it still fails, treat the session as invalid and send the user back to sign-in.
429Rate limitedBack off using the Retry-After / X-RateLimit-Reset response headers, then retry. See Rate Limiting.

A Chat Turn

The core loop is: create a thread once, then send messages and stream replies.

  1. Create a threadcreate-thread returns a threadId. Reuse it as conversation_key for the whole conversation.
  2. Send a messagesend-message returns a runId immediately; generation runs in the background.
  3. Show the reply in real time — subscribe to the streaming node for live tokens, or poll conversation-state if you don't use Firebase.
  4. Track busy state — watch the conversation status node to enable/disable the composer.

See the Conversations guide for message shape, generation config, and tool calling.

Synchronous & Structured Generation

When you need the answer in the HTTP response itself — structured output (a JSON plan, a form, a classification) rather than a streamed chat bubble — use send-message-sync. It blocks until generation finishes and returns the messages inline. Send an Idempotency-Key so a retried request doesn't run generation twice.

Files

Attach a file with a single call and reference it by ID afterward:

  1. POST /api/v1/storage/gateway/upload-file with the base64 content → returns a fileId. See Inline Upload. Use pre-signed upload for large files.
  2. Keep only the fileId. Reference it in message content — the platform resolves a fresh signed download URL just-in-time, so you never store or expire URLs client-side.

Notifications

  • Register for push on login: POST /api/v1/notifications/register-push-device with the device's FCM token and platform: "PLATFORM_IOS" (or Android). Unregister on logout. See Push Device Registration.
  • Real-time in-app inbox: call get-inbox-session for a WebSocket URL + token, connect for live badge/feed updates, and fall back to polling the unseen count if the socket drops.

Checklist

A minimal client covers:

  • Firebase Auth sign-in → ID token, with 5-minute-early refresh
  • Publishable key + Bearer token on every request; 401→refresh→retry, 429→back off
  • Create thread → send message → stream (or poll) the reply
  • File upload by fileId
  • Push registration + real-time inbox