Post an image or video status
POSThttps://api.wapito.com/v1/stories/media
Send a channel token on every request: Authorization: Bearer wpt_YOUR_TOKEN
Posts an image or video status with an optional caption, visible for twenty-four hours. Video statuses are capped by WhatsApp at thirty seconds and are re-encoded on the recipient device, so keep them short and legible on a phone screen. Supply the file as a URL, base64 bytes or a Wapito media id.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
caption | string | Optional | Caption shown over the image or video. |
contacts | array of string | Optional | Contacts allowed to see the status, at most 256, people only (a group or Channel id is `invalid_recipient`). Defaults to every known contact.max 256 items |
media | string | object | Required | Where the file comes from. Pass a string to use an HTTPS URL, a `data:` URI or a Wapito media id returned by `POST /media`, or pass a `MediaInput` object when you need to override the media type or file name. |
data | string | Optional | Base64-encoded file bytes, without a data URL prefix. |
filename | string | Optional | File name shown to the recipient. |
mimetype | string | Optional | IANA media type; required with `data`, inferred from the response headers with `url`. |
url | string | Optional | Publicly reachable HTTPS URL Wapito downloads before sending.uri |
{
"caption": "New colours, same price.",
"contacts": [
"+15551234567"
],
"media": "https://acme.example/status/new-colours.jpg"
}Responses
201
The status that was posted.
| Field | Type | Required | Description |
|---|---|---|---|
caption | string | null | Required | Caption or body text. |
contacts | array of string | Required | The audience as submitted, normalised to digits. Empty means every contact. |
id | string | Required | Status message id. |
link | string | null | Required | Signed Wapito media link when the status was posted from a media id returned by `POST /media`, the one case where Wapito holds the file. `null` for a text status and for a file that came as a URL or a data URI.uri |
timestamp | integer | Required | Unix epoch milliseconds the status was posted. |
type | string | Required | Kind of status posted.one of text, image, video, audio |
{
"caption": "New colours, same price.",
"contacts": [
"15551234567",
"15559876543"
],
"id": "true_status@broadcast_A71D3F9C0B2E5468",
"link": "https://api.wapito.com/v1/media/med_01JRQ9AAB2C3D4E5F6G7H8J9K0?exp=1790064500&sig=1b7e4c02da96f358",
"timestamp": 1789459500000,
"type": "image"
}400
The request body or query string is malformed, or the recipient cannot be parsed into a WhatsApp id.
Show 2 example bodies
invalid_recipient — `to` is not a dialable number or valid WhatsApp id
{
"error": {
"code": "invalid_recipient",
"details": {
"to": "+1555"
},
"message": "The recipient is not a valid WhatsApp address.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}invalid_request — schema validation failed
{
"error": {
"code": "invalid_request",
"details": {
"issues": [
{
"message": "Array must contain at least 2 element(s)",
"path": "body.options"
}
]
},
"message": "The request payload failed validation.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}401
The channel token is missing, malformed, revoked or belongs to a deleted channel.
Show 2 example bodies
token_revoked — the token was rotated in the dashboard
{
"error": {
"code": "token_revoked",
"message": "This channel token has been revoked.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}unauthorized — no or unusable Bearer token
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid channel token.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}403
The token is valid but the channel may not perform this action right now.
Show 2 example bodies
channel_locked — billing lapsed or the channel was locked by an operator
{
"error": {
"code": "channel_locked",
"details": {
"reason": "plan_required"
},
"message": "This channel is locked.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}forbidden — the channel does not own the target object
{
"error": {
"code": "forbidden",
"message": "You are not allowed to perform this action.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}409
The channel is in the wrong state for this action, or the account type does not support it.
Show 3 example bodies
business_account_required — labels need WhatsApp Business
{
"error": {
"code": "business_account_required",
"message": "This action requires a WhatsApp Business account.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}channel_not_connected — link the number first
{
"error": {
"code": "channel_not_connected",
"details": {
"status": "qr"
},
"message": "The channel is not connected.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}channel_not_in_qr_state — no QR available while the session boots
{
"error": {
"code": "channel_not_in_qr_state",
"details": {
"status": "created"
},
"message": "The channel is not waiting for a QR scan.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}413
The upload exceeds the media size cap for the plan.
Show the example body
payload_too_large — 16 MB on sandbox, 64 MB on premium
{
"error": {
"code": "payload_too_large",
"details": {
"max_bytes": 16777216
},
"message": "The request payload is too large.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}415
The file type cannot be sent as the requested message kind.
Show the example body
unsupported_media_type — wrong media type for the endpoint
{
"error": {
"code": "unsupported_media_type",
"details": {
"mimetype": "application/x-msdownload"
},
"message": "This media type is not supported.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}429
A rate limit, a plan quota or one of the anti-ban guards stopped the request. Every one of these is safe to retry later; read `Retry-After` when present.
Show 5 example bodies
cold_send_limit — too many first messages to new recipients this hour
{
"error": {
"code": "cold_send_limit",
"details": {
"resets_at": "2026-09-15T09:00:00.000Z",
"window_cap": 20
},
"message": "The hourly limit for messages to new recipients is reached.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}quota_exceeded — plan quota for the day or month
{
"error": {
"code": "quota_exceeded",
"details": {
"quota": "sent",
"resets_at": "2026-09-16T00:00:00.000Z",
"used": 150
},
"message": "The plan quota for this resource is exhausted.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}rate_limited — per-minute API rate limit
{
"error": {
"code": "rate_limited",
"details": {
"retry_after": 12
},
"message": "Too many requests.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}send_rate_limited — the send queue did not drain within 20 s
{
"error": {
"code": "send_rate_limited",
"details": {
"retry_after": 5
},
"message": "The send queue for this channel is saturated.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}warmup_limit — the warm-up ladder cap for today
{
"error": {
"code": "warmup_limit",
"details": {
"cap": 200,
"day": 2
},
"message": "The warm-up limit for this channel is reached.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}500
Something went wrong inside Wapito. Quote `request_id` when reporting it.
Show the example body
internal_error — unexpected failure
{
"error": {
"code": "internal_error",
"message": "Something went wrong on our side.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}502
The engine answered with an error Wapito could not translate into a more specific code.
Show the example body
engine_error — unexpected engine failure
{
"error": {
"code": "engine_error",
"details": {
"engine": "gows",
"status": 500
},
"message": "The WhatsApp engine returned an error.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}503
The engine session is not reachable. Retry with backoff.
Show the example body
engine_unavailable — engine down or restarting
{
"error": {
"code": "engine_unavailable",
"details": {
"engine": "gows"
},
"message": "The WhatsApp engine is unavailable.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}504
The engine did not answer before the upstream timeout.
Show the example body
engine_timeout — no answer within the deadline
{
"error": {
"code": "engine_timeout",
"details": {
"timeout_ms": 20000
},
"message": "The WhatsApp engine did not respond in time.",
"request_id": "req_01JRQ8F4X9N2K7YB3C5V6W8H0T"
}
}Webhook events
- messagesMessage sent or received
onMessages() - messages.statusDelivery receipt for a sent message
onMessagesStatus()
Errors
Code examples
import requests
url = "https://api.wapito.com/v1/stories/media"
payload = {
"media": "https://acme.example/status/new-colours.jpg",
"caption": "New colours, same price.",
"contacts": ["+15551234567"]
}
headers = {
"Authorization": "Bearer wpt_YOUR_TOKEN",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())Used in
Related
Other endpoints in this group
Glossary
Reference
Try it on your own number
Create a channel, link a WhatsApp number by QR or pairing code, and call the API in a couple of minutes. The Sandbox plan is free and needs no card.