Skip to main content
This page is written for an agent that writes client code. Every fact here is also somewhere else on this site. Nothing here contradicts the OpenAPI document. When they disagree, the OpenAPI document wins.
Machine-readable spec: https://client-api.lala.ist/openapi.json. Fetch it before you generate code.

Hard facts

  • Base URL, production: https://client-api.lala.ist
  • Base URL, local: http://localhost:3000
  • Auth: Authorization: Bearer <supabase_access_token>. No API key exists.
  • Only GET /health is unauthenticated.
  • Content type for requests with a body: application/json.
  • Timestamps are UTC, ISO 8601. Lala’s own day runs on Europe/Istanbul.
  • All student-visible text is Turkish. All identifiers and error strings are English.
  • There is no rate limit today. Growing lists (chat messages, denemeler) page with opaque cursors; date-range reads are bounded by the range instead.

Endpoint table

The SSE contract

POST /api/chat/send responds 200 with text/event-stream. Lala answers in one message or several. Each message has an index inside the turn. GET /api/chat/stream responds 200 with text/event-stream and stays open. Exactly one terminal event per stream. A model failure is an error event, with HTTP status 200. Do not map a failed turn to an HTTP status.

Mistakes to avoid

EventSource sends GET and cannot set the Authorization header. Use fetch, or an HTTP client that exposes the response body as a byte stream, and parse the SSE lines yourself.
The request body holds only message and an optional image. The server owns the history. Sending a message array is not supported and is silently dropped by schema validation.
The student message is persisted before the model runs. An automatic retry duplicates it. Retry only on an explicit student action.
The auth provider owns email and phone; Lala owns preferredName. Schema validation drops all three. To change an address, run the Supabase auth flow that verifies it. The new value reaches the profile on the next request.
Lala updates the profile itself during a conversation. Re-read GET /api/me instead of writing back a cached object.
PATCH /api/profile accepts communicationStyle, but no response returns it. It is stored with the conversation context, not the profile.
ChatMessage.id is optional and is not present on every message. Rebuild the list from the server response instead.
Route errors are {"error": string}. Schema validation errors are {"success": false, "data": ..., "error": [issues]}. Both use status 400 or 401.
Lala writes the plan too. A chat turn can add a block, move one, or mark one done. The sync event on the send stream names what changed. Read GET /api/plan again when it says plan, instead of writing back a cached day.
A block has a local Istanbul date (YYYY-MM-DD) and a wall-clock startTime (HH:MM). There is no instant, and there is no timezone field. Both fields can be absent: a date with no startTime is a real block, not an incomplete one.
An overlap comes back in conflicts and the blocks are still written. Show the overlap. Do not resend or roll back.
There is no upload endpoint. The image goes inline as base64 in the chat body, with a mimeType.

Client checklist

1

Read the token from the Supabase session for each request

Do not cache the string. The library refreshes it.
2

Call GET /api/me after sign-in

It creates the student row, copies the identity out of the token, and tells you whether onboarding is complete.
3

Register the FCM token with POST /api/devices

Do it after sign-in, and again on every token refresh. Without a registered token, no notification reaches the device.
4

Set the chat request timeout to 90 seconds or more

The server aborts the model after 60 seconds, and a turn can run several tool steps.
5

Handle exactly one terminal SSE event, and a dropped socket

A dropped socket with no terminal event is a failed turn on the client. The server still finishes it.
6

Refetch history on foreground, after a push, and after an error

The history endpoint is the source of truth.

Do not generate

  • Do not generate a stream reader from the OpenAPI document. The SSE framing is not in it.
  • Do not hand-edit openapi.json or a generated client package. Run npm run openapi:emit.
  • Do not invent endpoints. Version 1 has the five routes in the table above, and nothing else.
  • Do not translate or rewrite Turkish text from the API. Render it as it arrives.