Skip to main content
POST /api/chat/send is the only streaming endpoint. It accepts one student message and responds with text/event-stream. OpenAPI cannot describe the event framing, so this page is the contract.

Request

Send message, image, or both. A request with an empty message and no image returns 400 with {"error":"empty message"}. The 400 arrives before the stream, as normal JSON.
The image travels inline as base64 in the JSON body. Compress and resize the photo on the device before you encode it. Base64 adds about 33 percent to the byte count.

Response events

The response status is 200 and the content type is text/event-stream. Each event has a name and a JSON data payload.
ChatChunkEvent
One text delta. Repeats many times.
ChatDoneEvent
Terminal. Holds the complete answer text and the reactions.
ChatErrorEvent
Terminal. The turn failed.
The stream emits exactly one terminal event: done or error. A model failure is an error event, not an HTTP status. The HTTP status stays 200 in both cases.

Rules for the client

  1. Append each chunk payload to the text on screen.
  2. On done, replace the text on screen with the text field. This corrects any lost delta.
  3. On done, show the emoji in reactions. The array is usually empty. Lala reacts rarely.
  4. On error, show a retry control. Do not show the partial text as a finished answer.
  5. If the socket closes with no terminal event, treat the turn as failed.
The server persists the student message before the model runs. It persists the answer after the model runs. A turn that fails mid-stream can leave the student message in the history with no answer. Call GET /api/chat/history to resynchronize after an error.

No resume

The stream has no message identifier and no cursor. You cannot reconnect to a running turn. If the application goes to the background and the connection drops, the server still completes the turn and writes the answer to the history. Read the history back when the screen returns.

Timing

The server aborts the model call after 60 seconds. One turn can take several model steps, because Lala can call tools. Allow at least 90 seconds of client timeout on this request. Do not set an idle timeout below the gap between two chunks.

Client code

Do not use the browser EventSource API. EventSource sends only GET requests and cannot set the Authorization header. Use fetch with a stream reader, as shown above.

Concurrency

Send one turn at a time for one student. The server does not reject a second concurrent request, but both turns read and write the same conversation history. Disable the send control until a terminal event arrives.