Chat File Sharing API
These two endpoints back file attachments in the Video SDK's native
in-meeting chat. Sharing a file is a two-step flow: **upload** the bytes to get a file reference, then send a chat message that points at it with `meeting.sendChatFileMessage(...)`.Authentication
Both endpoints use the chat access token returned by meeting.getChatAccessToken(), sent in the
Authorization: Bearer <token> header.
The chat access token is accepted in the Authorization header only. It is not read from the query
string.
Upload a file
https://<appname>.metered.live/api/v1/chat/upload
<appname> - replace it with the name of your app.
Description
Upload a file and get back a reference you pass to meeting.sendChatFileMessage(...).
Request
- Auth:
Authorization: Bearer <chatAccessToken> - Content-Type:
multipart/form-data - Body: a single field named
file.
Constraints
| Constraint | Value |
|---|---|
| Max size | 10 MB |
| Rate limit | 10 uploads per minute |
| MIME types | must be on the allowlist below |
Allowed MIME types:
image/pngimage/jpegimage/gifimage/webpapplication/pdftext/plaintext/csvapplication/zipapplication/x-zip-compressedapplication/mswordapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentapplication/vnd.ms-excelapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Request Samples
- cURL
- NodeJs
curl --request POST \
--url 'https://appname.metered.live/api/v1/chat/upload' \
--header 'Authorization: Bearer <chatAccessToken>' \
--form 'file=@/path/to/photo.png'
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
async function uploadChatFile(chatAccessToken) {
const form = new FormData();
form.append('file', fs.createReadStream('/path/to/photo.png'));
const response = await axios.post(
'https://appname.metered.live/api/v1/chat/upload',
form,
{
headers: {
...form.getHeaders(),
Authorization: `Bearer ${chatAccessToken}`,
},
}
);
// { fileS3Key, fileName, fileMimeType, fileSizeBytes }
return response.data;
}
Responses
{
"fileS3Key": "<opaque file reference>",
"fileName": "photo.png",
"fileMimeType": "image/png",
"fileSizeBytes": 12345
}
Pass those four fields, in order, to meeting.sendChatFileMessage(fileS3Key, fileName, fileMimeType, fileSizeBytes).
All errors use the same shape: { "error": "<message>" }.
{
"error": "No file uploaded"
}
Also "File type not allowed" when the MIME type is not on the allowlist, or "Invalid upload" for a malformed multipart body (for example extra form fields).
{
"error": "chat access token is required"
}
Also "unauthorized" when the token is present but invalid, expired, or the participant is no longer in the meeting.
{
"error": "Chat is disabled for this participant"
}
Also "Chat is not available for this meeting" when the room has chat turned off.
{
"error": "File size exceeds 10MB limit"
}
Returned when the file is over 10 MB.
{
"error": "Too many uploads, please wait a moment"
}
Returned when the per-participant limit of 10 uploads per minute is exceeded. A separate per-IP limit of 120 requests per minute applies to both chat endpoints and returns { "error": "Too many requests" }.
Download a file
https://<appname>.metered.live/api/v1/chat/file/{messageId}
<appname> - replace it with the name of your app.
{messageId} - the _id of the file chat message.
Description
Download the attachment of a file chat message.
Authentication options
Choose one:
- Download token (query string):
?dl=<downloadToken>— the per-message token delivered in thechatMessageReceived/chatHistorypayload. Use this for links you render in your UI; it is the option the built-in UI uses. - Chat access token (header):
Authorization: Bearer <chatAccessToken>— for SDK callers. This is subject to the same visibility rules as history: you can only fetch a file you were allowed to see (a broadcast file, or a directed file you sent or received). When the room stores history, it is also scoped to the retention window — a message older than the window is no longer reachable this way. The?dl=download token is not affected by the window and stays valid for the life of the attachment.
Response
302 redirect to a short-lived (1 hour) signed URL. The file is served as an attachment.
Request Samples
- cURL (download token)
- cURL (chat token)
curl -L \
--url 'https://appname.metered.live/api/v1/chat/file/<messageId>?dl=<downloadToken>' \
--output attachment
curl -L \
--url 'https://appname.metered.live/api/v1/chat/file/<messageId>' \
--header 'Authorization: Bearer <chatAccessToken>' \
--output attachment
-L tells curl to follow the 302 redirect to the signed URL.
Responses
All errors use the same shape: { "error": "<message>" }.
{
"error": "unauthorized"
}
Returned when neither a valid download token nor a valid chat access token is provided.
{
"error": "Chat is disabled for this participant"
}
Returned on the chat-access-token path when chat is disabled for the requesting participant.
{
"error": "File not found"
}
Returned when the message does not exist, or the attachment is not visible to you.
{
"error": "File has expired"
}
Returned when the attachment has expired — file attachments are deleted after 24 hours.
{
"error": "Too many downloads, please wait a moment"
}
Returned on the chat-access-token path when the per-participant limit of 60 downloads per minute is exceeded. The shared per-IP limit of 120 requests per minute also applies and returns { "error": "Too many requests" }.
Full flow example
// 1. Upload the file (sender)
const form = new FormData();
form.append("file", file); // a File / Blob
const res = await fetch("https://<appname>.metered.live/api/v1/chat/upload", {
method: "POST",
headers: { Authorization: `Bearer ${meeting.getChatAccessToken()}` },
body: form,
});
const { fileS3Key, fileName, fileMimeType, fileSizeBytes } = await res.json();
// 2. Send the file message (sender)
meeting.sendChatFileMessage(fileS3Key, fileName, fileMimeType, fileSizeBytes);
// 3. Download the attachment (recipient)
meeting.on("chatMessageReceived", (message) => {
if (message.type === "file" || message.type === "image") {
const url =
`https://<appname>.metered.live/api/v1/chat/file/${message._id}` +
`?dl=${message.downloadToken}`;
// e.g. render <a href={url}>{message.fileName}</a>
}
});