POST /api/chat/send 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, mediaIds, image, or a combination. A request with none of them returns 400
with {"error":"empty message"}. The 400 arrives before the stream, as normal JSON.
POST /api/media first. Read Media. The image field
still works and puts the bytes inline as base64, but it costs about 33 percent more bytes and
holds the request open while the server stores them.
Lala answers in one message or several
Lala writes the way a person texts. Most turns are one message. Some are two or three short ones. Each message becomes its own row with its own id, and the stream announces each one. Theindex field is the position of the message in the turn. It starts at 0 for every turn.
Response events
The response status is200 and the content type is text/event-stream. Each event has a name
and a JSON data payload.
ChatMessageStartEvent
Lala started a new message. Open a new bubble.
ChatChunkEvent
One text delta of the message at
index. Repeats many times.ChatMessageEndEvent
The message at
index is complete. It has no id yet.ChatRestartEvent
Rare. The turn starts again. Discard every message from this turn. The next
index is 0.ChatToolEvent
Lala started or finished a tool. Cosmetic. Use it to say what the wait is for.
ChatSyncEvent
A tool wrote data that the application shows. Reload those screens.
ChatDoneEvent
Terminal. Holds the persisted messages, with their ids, the reactions, and every resource that
the turn wrote.
ChatErrorEvent
Terminal. The turn failed.
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
- On
message_start, open a new bubble for thatindex. - Append each
chunkpayload to the bubble named by itsindex. - On
message_end, mark that bubble complete. - On
restart, delete every bubble of this turn and wait for the nextmessage_start. - On
done, replace your bubbles with themessagesarray. Each entry carries the realid. - On
done, show the emoji inreactions. The array is usually empty. Lala reacts rarely. - On
error, show a retry control. Do not show the partial text as a finished answer. - If the socket closes with no terminal event, treat the turn as failed.
- On
sync, reload the named screens. Do not wait fordone. - On
tool,restart,done, orerror, update the activity indicator. Astartwith noendfollows a failed turn, so a terminal event must clear the indicator.
Tools change data while Lala writes
A turn can write to the planner, the deneme history, and the student profile. Lala does this with tools, in the middle of the answer. Thesync event names the data that changed:
Each resource fires one
sync event per turn, at the moment of the write. The write is complete
when the event arrives, so a read after it returns the new data. The changed array on done
repeats the full set. A client that missed an event still reloads the correct screens.
The tool event carries the name of the tool. The application maps the name to its own copy, for
example plan_study to “programa yazıyor”. A turn can call several tools, one after the other.
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. Read
GET /api/chat/messages to resynchronize after an error.Reactions
Lala’s reaction lands on the student message it answers, withactor set to lala. The student
reacts through POST /api/chat/messages/{id}/reactions.
DELETE /api/chat/messages/{id}/reactions/{emoji} with the emoji
percent-encoded. Both calls return the whole message with its current reactions. Both are
idempotent.