Send messages
Send media
Send images, PDFs, videos, or audio with an optional caption. First upload the file with a short-lived URL, then send the returned storageKey through the message API.
3-step flow
- POST /v1/messages/upload - send file metadata to request an uploadUrl + storageKey.
- PUT <uploadUrl> - upload the raw file bytes with the same Content-Type.
- POST /v1/messages/send with media: { storageKey, caption }.
Step 1: request an upload URL
POST
/v1/messages/uploadmimestringrequired- File MIME type. Must match the whitelist below.
sizeinteger (bytes)required- File size in bytes. Default cap: 16,777,216 bytes (16 MB).
filenamestring- Optional, used for document-type messages and download labels.
curl -X POST https://api.shiftwa.dev/v1/messages/upload \
-H "X-API-Key: $SHIFTWA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mime": "image/jpeg",
"size": 245678,
"filename": "invoice-100.jpg"
}'{
"uploadUrl": "https://...r2.cloudflarestorage.com/...?X-Amz-Signature=...",
"storageKey": "users/<userId>/media/2026-06-01/01HXX....jpg",
"expiresAt": "2026-06-02T12:34:56.000Z"
}Step 2: upload the bytes
PUT the file bytes to uploadUrl. The Content-Type header must match the mime you sent to /messages/upload.
curl -X PUT "<uploadUrl>" \
-H "Content-Type: image/jpeg" \
--upload-file ./invoice-100.jpgimport { readFile } from "node:fs/promises";
const bytes = await readFile("./invoice-100.jpg");
await fetch(uploadUrl, {
method: "PUT",
headers: { "Content-Type": "image/jpeg" },
body: bytes,
});Step 3: send the message
POST
/v1/messages/send{
"to": "+6281234567890",
"media": {
"storageKey": "users/<userId>/media/2026-06-01/01HXX....jpg",
"caption": "Invoice #100"
},
"sender": { "label": "cs-jakarta" }
}The API returns 202 Accepted with the queued message, selected sender, quota snapshot, and a media block confirming storageKey, mimeType, filename, and sizeBytes.
{
"messageId": "01HXXXXXXXXXXXXXXXXXXXXXXX",
"status": "queued",
"queueState": "enqueued",
"kind": "image",
"targetKind": "individual",
"numberId": "num-uuid",
"sender": {
"numberId": "num-uuid",
"phoneNumber": "+6285138741668",
"label": "cs-jakarta",
"source": "hint_label"
},
"quota": {
"used": 1248,
"limit": 10000,
"remaining": 8752,
"resetAt": "2026-06-01T00:00:00.000Z"
},
"media": {
"storageKey": "users/<userId>/media/2026-06-01/01HXX....jpg",
"mimeType": "image/jpeg",
"filename": "invoice-100.jpg",
"sizeBytes": 245678
}
}MIME whitelist
| Category | MIME |
|---|---|
| Image | image/jpeg, image/png, image/webp |
| Document | application/pdf |
| Video | video/mp4 |
| Audio | audio/mpeg, audio/ogg, audio/aac |