Skip to main content
A message carries attachments as media. Media has an id, not a URL. The bytes live in private storage, and the API gives you a short-lived signed URL each time you read the media.

Upload

Upload in three steps.
1

Reserve the media

Send the mime type. The response holds a Media and an upload.
2

Send the bytes

PUT the raw bytes to upload.url. Do not send an Authorization header. The URL is signed and it carries its own token.
The URL expires in 15 minutes. If it expires, reserve a new media and start again.
3

Attach it to a message

Send the media id in mediaIds.
The server confirms the upload when you send the message, so step 3 needs no extra call. Media that never reached storage is dropped from the message. The send does not fail.

Read

GET /api/media/{id} returns the media with a fresh signed URL. It also confirms a pending upload, so it answers the question “did my upload land?”.
Every message read gives you the same fields inside media, so a normal chat load needs no extra request.
url expires. It is valid for one hour by default. Do not cache it, and do not store it in a database. Read the media or the message again for a new one.

Fields

Limits

  • 25 MB for one media. A larger sizeBytes returns 400 with {"error":"media too large"}.
  • 10 media on one message.
  • Lala reads at most 4 images from one message. The rest stay on the message for the student.

What Lala sees

Lala reads image attachments. It does not read audio, video, or other files today. A message that has attachments and no text reaches the model as a placeholder, such as [görsel]. The placeholder exists only in the history of the model. On the wire, content for such a message is the empty string. An empty content means that the student wrote no caption, so there is nothing to show next to the attachment.
Media belongs to the student who created it. A media id from another account is unknown to your account, and the send drops it.