/v1/conversations/{conversation_id}/filesbetaSend a file in a conversation
- Needs
- Send WhatsApp messages; manage templates, quick messages and WhatsApp short links
(messaging:write) - Plan
- Any plan with API access
- Limits
- 60 a minute per key · 20 an hour · 100 a day · 3 per contact a day
- Dry run
- Yes — a full preview with ?dry_run=true
- Undo
- Cannot be undone: the customer receives the file.
Uploads a file and sends it to the customer in a conversation, in one request: a PDF, an Office document, a photo, a voice note or other audio, a video or a text file, with an optional caption. The customer receives it and it cannot be undone. Nothing is kept on our side after the answer, so there is no upload step and no upload id: to send the same file again, send it again (or host it and use Send a message in a conversation with media.url).
Send the file as multipart/form-data with a file part (recommended; any HTTP client or curl -F builds it):
curl https://mcp.wa-api.cloud/v1/conversations/77410/files \
-H "Authorization: Bearer ${env_var_name}" \
-H "Idempotency-Key: 5f1c2d9e-invoice-1001" \
-F file=@invoice-1001.pdf \
-F caption="Your invoice"or as JSON with the file in content_base64 (with filename), which suits small files: a JSON body is about a third larger than the file.
What can be sent. The file's content decides its type, whatever its name or declared type says; a declared type that does not match the content is refused (reason: content_mismatch). Accepted: PDF, JPEG, PNG, WebP, MP4, 3GP, MP3, OGG, AAC, AMR, DOCX, XLSX, PPTX, plain text and CSV. Executables, scripts, HTML, SVG, XML and other archives are refused (reason: unsupported_type). A file is at most 10 MB (the answer names this server's limit). Images go as images up to 5 MB; a larger image, or any file with as_document: true, goes as a document. Audio has no caption. A video may go as a document on channels that cannot take a video file yet (the answer's note says so).
Like any free-form message it needs an open reply window where the channel has one (on WhatsApp, 24 hours after the customer's last message): otherwise 409 with reason: window_closed (send an approved template instead). The key's staff member must be allowed to reply in the conversation. Files count against the workspace send limits and the workspace's file upload limits (files per hour and per day, and megabytes per day: 429 with reason: upload_cap or upload_bytes_cap). Try it with ?dry_run=true (or a test key): the file is read and every check runs, and nothing is sent or kept.
Try it
Protects against doing it twice if you retry: a retry with the same key gets the first answer back instead of running again.
Code and response
curl -X POST 'https://mcp.wa-api.cloud/v1/conversations/77410/files?dry_run=true' \ -H "Authorization: Bearer $API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -F 'file=@invoice-1001.pdf;type=application/pdf' \ --form-string 'caption=Your order summary'
The code reads your key from $API_KEY.
Parameters
| Field | Type | What it is |
|---|---|---|
| conversation_idrequired | string · path | The conversation id.pattern ^\d{1,19}$Signed in? Pick one from your data with “My data”. |
Body
| Field | Type | What it is |
|---|---|---|
| content_base64required | string | JSON body: the file itself, base64-encoded (standard or URL-safe alphabet). With multipart/form-data send the file part instead.0–140000000 characters |
| filenamerequired | string | The file name the customer sees, e.g. invoice-1001.pdf (up to 200 characters; any path is removed and the extension is set from the content).1–200 characters |
| content_type | string | The file's type as you know it, e.g. application/pdf. Optional: the content decides, and a type that does not match it is refused.0–120 characters |
| caption | string | Text shown with the file (up to 1024 characters; not for audio).0–1024 characters |
| as_document | boolean | true sends the file as a document whatever its type (e.g. a photo in full quality, or an image over 5 MB). |
Headers
| Field | Type | What it is |
|---|---|---|
| Authorizationrequired | header | Bearer $API_KEY — your API key. |
| Api-Version | header | The API version to use, e.g. 2026-10-01. Default: the version your key is pinned to.one of: 2026-10-01 · pattern ^\d{4}-\d{2}-\d{2}$ |
| Idempotency-Keyrequired | header | Required here. Any unique string (8–128 characters), e.g. your order id plus the step. Kept 24 hours. Not needed with dry_run=true.pattern ^[A-Za-z0-9._:-]+$ · 8–128 characters |
Response 202
Accepted for delivery (or the dry-run check): how the file went (`sent_as`: `image`, `video`, `audio` or `document`) and the file as it was sent. Delivery and read receipts show as the message's `status` in the message list.
| Field | Type | What it is |
|---|---|---|
| message_idrequired | string or null | null for a dry run. |
| statusrequired | string | queued, or dry_run. |
| conversation_idrequired | string | |
| sent_asrequired | string | image, video, audio or document. |
| filerequired | object | |
| namerequired | string | |
| content_typerequired | string | |
| size_bytesrequired | number | |
| channelrequired | object | |
| idrequired | string | |
| typerequired | string | |
| windowrequired | object | |
| appliesrequired | boolean | false for channels without a window (web chat, custom) and for system messages. |
| openrequired | boolean or null | null when the window does not apply. |
| kindrequired | string or null | service (24 h), free_entry (72 h after an ad), standard, tiktok (48 h), web. |
| closes_atrequired | string or null | Open window: when it closes. |
| closed_atrequired | string or null | Closed window: when it closed (null = the customer never wrote on this channel, or unknown). |
| noterequired | string or null | |
| dry_run | boolean | true when this was a dry run: every check ran and nothing changed. |
Errors
Errors are application/problem+json. Branch on code.
| Status | Code | When |
|---|---|---|
| 400 | invalid_input | A field is missing or has the wrong format. |
| 401 | unauthenticated | The Authorization header is missing, the key is unknown, expired or revoked. |
| 403 | entitlement_required | The workspace's plan does not include API access ( |
| 403 | forbidden | The key's staff member may not reply here: the conversation is closed, or assigned to someone else ( |
| 403 | insufficient_scope | The key does not have the permission this operation needs. |
| 404 | not_found | No conversation with this id, or your key cannot see it. |
| 409 | conflict | The customer's reply window is closed ( |
| 429 | rate_limited | The key or workspace went over its rate limit. Wait for |
| 503 | upstream_unavailable | A service behind the API is briefly unavailable. Safe to retry with backoff. |
| 504 | timeout | The change did not finish in time. Retry with the same Idempotency-Key: it never runs twice. |
Examples
Send a small text file as JSON (base64)
Request body
{
"content_base64": "T3JkZXIgIzEwMDE6IDIgaXRlbXMsIHRvdGFsICQ0MC4wMAo=",
"filename": "order-1001.txt",
"content_type": "text/plain",
"caption": "Your order summary"
}Response 202
{
"message_id": "5501240",
"status": "queued",
"conversation_id": "77410",
"sent_as": "document",
"file": {
"name": "order-1001.txt",
"content_type": "text/plain",
"size_bytes": 35
},
"channel": {
"id": "301",
"type": "whatsapp"
},
"window": {
"applies": true,
"open": true,
"kind": "service",
"closes_at": "2026-09-25T09:12:00.000Z",
"closed_at": null
},
"note": null
}Operation path
The same operation is also at POST /v1/ops/inbox_send_file, with every field in the JSON body.