API Reference
Get Presigned URLs (S3 Upload)
Overview
Get short-lived S3 presigned URLs so your chat package / client can upload files directly to storage — without sending file bytes through the RealtimeX API server.
For each file you request, the API returns an uploadUrl (PUT the file here) and a fileUrl (the permanent URL to use in messages, profiles, or group images after upload succeeds).
When to use this endpoint?
Use Get Presigned URLs when:
- A chat user attaches an image, video, audio, or document in the composer.
- They update a profile avatar or group image from the chat SDK.
- You need to upload several files in one batch (request multiple entries in
files). - You want direct-to-S3 uploads for better performance and smaller API payload size.
Part of the Chat Uploads flow for the chat package / SDK. Requires a chat user Access Token and tenant headers. Presigned URLs expire quickly (see X-Amz-Expires in the URL, often ~300 seconds) — upload promptly after you receive them.
Upload Workflow
Follow these steps in your chat client every time a user sends or attaches a file. Skipping the PUT step will leave you with a fileUrl that has no file behind it.
- 1
Pick the file(s)
In your chat or profile UI, let the user choose one or more files (image, document, or other supported types). Read each file’s name and MIME type.
- 2
Request presigned URLs
Call this endpoint with a files array of { filename, fileType }. Use the chat user Access Token and tenant headers.
- 3
Upload bytes to uploadUrl
For each item in data, PUT the raw file body to uploadUrl. Do not send the file through your API server — upload goes directly to S3.
- 4
Keep the permanent fileUrl
After a successful PUT, store fileUrl. This is the public/stable URL you attach to messages, avatars, or group images.
- 5
Send or save with fileUrl
Include fileUrl in your chat message payload (or profile update). The chat UI can then render the media from that URL.
- 6
Confirm in the UI
Show a success state (thumbnail, attachment chip, or avatar). If upload or send fails, let the user retry without creating orphan messages.
End-User Guidelines
Guidance for product and SDK implementers so uploads feel reliable for people using the chat app:
- Show progress — while PUT is in flight, show an upload indicator on the attachment bubble or composer. Do not mark the message as sent until both S3 upload and your message API succeed.
- Validate before requesting URLs — check file size, type, and count in the UI first. Avoid calling this endpoint for empty selections or unsupported MIME types.
- Match Content-Type — the
fileTypeyou send in the request and theContent-Typeheader on the PUT must match the real file MIME type, or S3 may reject the upload. - Upload before sending the chat message — put
fileUrlinto the message only after PUT returns success. Otherwise peers open a broken attachment link. - Handle expiry — if the user stays on a slow network and the presigned URL expires, request new URLs and retry the PUT. Do not reuse an expired
uploadUrl. - Allow cancel / retry — if the user cancels mid-upload, discard the pending message draft. On failure, offer Retry without creating duplicate messages.
- Keep order —
dataarray items align with your requestfilesarray order. Map each response pair back to the correct local file.
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
| files | array | Required | Non-empty list of files to upload. Each item needs filename and fileType. |
| files[].filename | string | Required | Original file name (for example profile-photo.png). Used when generating the storage key. |
| files[].fileType | string | Required | MIME type (for example image/png, application/pdf). Must match the PUT Content-Type. |
{baseUrl}/api/{apiVersion}/aws/upload/get-presigned-urlsAuthentication
Required (Bearer token)
Tenant-scoped
Yes (tenant DB — requires x-client-id)
Request Headers
| Header | Value | Description |
|---|---|---|
| Authorization | Bearer <access_token> | Chat user Access Token (Bearer) for the signed-in messaging participant. |
| is-tenant | true | Targets the tenant DB ("true", needs x-client-id). |
| x-client-id | {{clientId}} | Tenant (client) id. Required when is-tenant=true. |
| Content-Type | application/json | JSON body for this endpoint (not the file bytes). |
Request Payload
Example requesting presigned URLs for an image and a PDF in one call:
{
"files": [
{
"filename": "profile-photo.png",
"fileType": "image/png"
},
{
"filename": "report.pdf",
"fileType": "application/pdf"
}
]
}Success Response (HTTP 200 OK)
data is an array aligned with your files input. Each entry has:
| Field | Description |
|---|---|
| uploadUrl | Temporary signed URL. PUT the file bytes here. Expires after a short window — upload immediately. |
| fileUrl | Permanent URL of the object. Use this in chat messages, avatars, or group images after a successful PUT. |
{
"success": true,
"message": "Presigned URLs generated successfully",
"data": [
{
"uploadUrl": "https://example.amazonaws.com/uploads/images/avatar-7f8c9d12.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE123456789%2F20260719%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20260719T134500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=9f8a7b6c5d4e3f210987654321abcdef1234567890abcdef1234567890abcdef",
"fileUrl": "https://example.com/avatar-7f8c9d12.png"
},
{
"uploadUrl": "https://example.amazonaws.com/uploads/files/report-7f8c9d12.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAEXAMPLE123456789%2F20260719%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20260719T134500Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=9f8a7b6c5d4e3f210987654321abcdef1234567890abcdef1234567890abcdef",
"fileUrl": "https://example.com/report-7f8c9d12.pdf"
}
],
"error": null
}Upload the File to S3
This API does not accept file bytes. After you receive uploadUrl, upload directly to S3 with PUT:
// After you receive uploadUrl + fileUrl from this API:
const file = selectedFile; // File from <input type="file"> or picker
const putResponse = await fetch(uploadUrl, {
method: "PUT",
headers: {
"Content-Type": file.type, // must match fileType you sent above
},
body: file,
});
if (!putResponse.ok) {
throw new Error("Upload to storage failed");
}
// Use permanent fileUrl in your chat message / profile payload:
// e.g. { type: "image", attachmentUrl: fileUrl }
Do not attach Authorization headers from RealtimeX when calling uploadUrl — authentication is already embedded in the query string signature. Only send the file body and the correct Content-Type.
Common Errors
This endpoint fails when the token is missing or the files array is empty. S3 PUT errors happen separately after you receive the URLs.
HTTP 400 Bad Request — No files provided
{
"success": false,
"message": "No files provided",
"data": null,
"error": "No files provided"
}HTTP 401 Unauthorized — No token
{
"success": false,
"message": "No token, authorization denied",
"data": null,
"error": "Unauthorized"
}| Code | Reason |
|---|---|
| 400 Bad Request | files is missing or empty. |
| 401 Unauthorized | Access Token is missing, invalid, or expired. |
| 500 Internal Server Error | An unexpected error occurred while generating presigned URLs. |
Best Practices
- Request URLs only for files the user has confirmed — then PUT immediately while the signature is valid.
- Never log full
uploadUrlvalues in production (they are temporary credentials in URL form). - Prefer batching a small number of attachments in one
filesrequest when the user multi- selects media. - After upload, persist
fileUrlonly — not the expireduploadUrl. - Always send tenant headers with the chat user Bearer token over HTTPS when calling this API.
Ready to upload
Once PUT succeeds, attach fileUrl to your chat message or profile update. For media already in a thread, browse them later with Conversation → Attachments.