WABridges
← Docs API REFERENCE
Quickstarts openapi.yml llms-full.txt
Reference

API Reference

Plain HTTP and JSON. Manage bridges under /api/instances, and talk to WhatsApp through each bridge's proxy. Every example below is a curl call you can paste into a terminal.

Authentication

Every request takes your API key as a Bearer token. Keep it in an environment variable, never in a script or a browser.

Create an account to get an API key.

The ?apikey= query parameter still authenticates but is deprecated and answers with a Deprecation: true header. Query strings end up in access logs; use the header.

Shell
export WA_API_KEY="sk_..."
Header
Authorization: Bearer $WA_API_KEY

Base URL

Bridge management lives at https://wabridges.com/api/instances. Everything that talks to WhatsApp goes through a bridge's proxy, addressed by the customer_ref you gave it:

https://wabridges.com/api/instances/{customer_ref}/proxy

default always means the account's first bridge, so a single-number integration never carries a name in the URL. sandbox is the simulated bridge every account has.

The examples below call $BASE; set it once.

Shell
export BASE="https://wabridges.com/api/instances/default/proxy"
Request
curl "$BASE/status" \
  -H "Authorization: Bearer $WA_API_KEY"

Conventions

Phone numbers
E.164 digits, no +, no spaces: 15550001234. A number without its country code is refused with 400 invalid_chat.
JIDs
WhatsApp's own addresses. A person is 15550001234@s.whatsapp.net, a group 120363000000000001@g.us. Every chat field takes a bare number or a full JID.
Timestamps
Unix epoch seconds, as integers.
Request IDs
Every proxied response carries X-Request-Id. Include it when you write to support.

Errors

Errors come back as JSON with a stable code to branch on; error is the human message. A few platform errors (401, 429, 503) carry only error, which then holds the code.

StatusCodeWhat to do
400validation_failed invalid_chat invalid_sender media_fetch_failedFix the request. Retrying won't help.
401unauthorizedMissing or unknown API key.
402no_subscription payment_requiredCreating a bridge needs an active subscription.
404contact_not_found media_not_found avatar_not_foundAlso returned for an unknown customer_ref.
409not_connectedThe WhatsApp session is down. Re-pair the phone; retrying won't help.
409already_paired idempotency_conflictThe bridge is already linked, or the same Idempotency-Key is still in flight.
429 Rate limited. Wait Retry-After seconds.
501not_available_in_sandboxThe sandbox doesn't simulate this endpoint.
502upstream_errorWhatsApp-side failure. Safe to retry.
503bridge_startingAn idle bridge was woken by this call. Wait Retry-After seconds and retry.
504qr_timeoutNo QR code was ready in time. Ask again.
500internal_errorOur fault. Retry, and send us the X-Request-Id if it keeps happening.
Response409
{
  "error": "not connected",
  "code": "not_connected"
}
Response503
HTTP/1.1 503 Service Unavailable
Retry-After: 5
Content-Type: application/json

{
  "error": "bridge_starting",
  "message": "Bridge was idle and is starting. Retry in a few seconds."
}

Rate limits

Limits are per API key over a 60-second window. Going over returns 429 Too Many Requests with a Retry-After header saying how many seconds until the window resets. Back off exponentially and honour Retry-After.

RouteLimit
POST /api/instances10 / min
/api/instances/:customer_ref/proxy/*120 / min
/api/*, everything else120 / min
Any route, per IP300 / min
Response429
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{"error":"rate limit exceeded"}

Idempotency

Endpoints that produce a message (the /send/* family, edit and revoke) take an optional Idempotency-Key header that makes them safe to retry: if a request times out, send it again with the same key and the message goes out at most once. They're marked Accepts Idempotency-Key below.

First use of a keyThe message is sent; the response is stored for 24 hours.
Retry, same keyThe stored response is replayed with X-Idempotency-Replayed: true. Nothing is sent again.
Concurrent duplicate409 idempotency_conflict while the first request is in flight.
Failed sendDoesn't use up the key; a retry with it sends normally.

Use one value per logical message, such as a UUID or your own order or job id, up to 255 characters. Without the header nothing changes.

Request
curl -X POST "$BASE/send/text" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8812-confirmation" \
  -d '{"chat": "15550001234", "body": "Your order shipped!"}'

Sandbox

Every account has a simulated bridge at customer_ref = sandbox. It needs no phone and is never billed. Its one contact, Alice (15550001234), replies a few seconds after every message.

It answers /status and the messaging endpoints (/send/text, /send/media, /send/reaction, /send/poll, /send/location, /send/typing, /messages/:id/read) with production-shaped responses; anything else returns 501 not_available_in_sandbox. Its events show on the dashboard rather than being posted to a webhook.

Request
curl -X POST "https://wabridges.com/api/instances/sandbox/proxy/send/text" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"chat": "15550001234", "body": "hi Alice"}'

Bridges

Create, list, inspect and re-point bridges. These routes sit under /api/instances, not the proxy.

Create a bridge

POST /api/instances

Provision a new bridge on your subscription. If customer_ref already exists, the existing record comes back. The subscription itself (and the 3-day free trial, card on file) starts when you connect your first number in the dashboard; the API adds bridges to it.

Parameters
customer_ref string required
Your own identifier for this bridge (a user ID, an order number). Letters, digits, dashes and underscores.
webhook_url string
URL that receives every inbound WhatsApp event as a POST. Omit it to use the hosted inbox (events show on the dashboard); change it later with PATCH.

Errors. 400 invalid_customer_ref or invalid_webhook_url. 402 no_subscription: the account has no subscription yet, connect a number in the dashboard first (the body carries dashboard_url and billing_url); or payment_required when payment is past due. 403 the account's bridge limit is reached (default 10, contact support to raise it).

Request
curl -X POST "https://wabridges.com/api/instances" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_ref": "user-123",
    "webhook_url": "https://yourbackend.com/hook"
  }'
Response 201
{
  "id": "7d09aa9c-2f4e-4b6a-9c1d-3e5f7a9b1c2d",
  "customer_ref": "user-123",
  "state": "running",
  "created_at": 1777180605,
  "webhook_secret": "whsec_...",
  "webhook_url": "https://yourbackend.com/hook"
}

List bridges

GET /api/instances

Every bridge on the account.

Request
curl "https://wabridges.com/api/instances" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
[
  {
    "id": "7d09aa9c-2f4e-4b6a-9c1d-3e5f7a9b1c2d",
    "customer_ref": "user-123",
    "state": "running",
    "created_at": 1777180605
  }
]

Get a bridge

GET /api/instances/:customer_ref

One bridge, including live container state from the orchestrator. state is running, paused or suspended.

Parameters
customer_ref string path required
The customer_ref you chose when creating the bridge, or default for the account's first bridge.

webhook_secret is sent as Authorization: Bearer <webhook_secret> on every outbound webhook. Verify it before trusting a payload.

Request
curl "https://wabridges.com/api/instances/user-123" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "id": "7d09aa9c-2f4e-4b6a-9c1d-3e5f7a9b1c2d",
  "customer_ref": "user-123",
  "state": "running",
  "created_at": 1777180605,
  "webhook_url": "https://yourbackend.com/hook",
  "webhook_secret": "whsec_...",
  "manager": {
    "health": "healthy",
    "started_at": "2025-06-17T10:00:00Z",
    "fail_count": 0
  }
}

Change the webhook URL

PATCH /api/instances/:customer_ref

Change where the bridge delivers webhooks. The bridge restarts to apply it (a few seconds; the WhatsApp session is kept). Works for sandbox too and returns its webhook_secret.

Parameters
customer_ref string path required
The customer_ref you chose when creating the bridge, or default for the account's first bridge.
webhook_url string | null required
The new endpoint. Send "" or null to switch to the hosted inbox.

Errors. 400 invalid_webhook_url. 404 unknown customer_ref. 409 bridge_paused or bridge_suspended (an idle auto-paused bridge is woken instead). 502 the orchestrator could not apply the change.

Request
curl -X PATCH "https://wabridges.com/api/instances/user-123" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhook_url": "https://yourbackend.com/hook"}'
Response 200
{
  "id": "7d09aa9c-2f4e-4b6a-9c1d-3e5f7a9b1c2d",
  "customer_ref": "user-123",
  "state": "running",
  "webhook_url": "https://yourbackend.com/hook",
  "webhook_secret": "whsec_...",
  "inbox": false
}

Stream bridge logs

GET /api/instances/:customer_ref/logs

The bridge's logs as server-sent events.

Parameters
customer_ref string path required
The customer_ref you chose when creating the bridge, or default for the account's first bridge.
tail integer query
How many trailing lines to start from. Default 100, max 10000.
Request
curl -N "https://wabridges.com/api/instances/user-123/logs?tail=50" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200 text/event-stream
data: 2025-06-17T10:00:01Z INF websocket connected
data: 2025-06-17T10:00:02Z INF webhook delivered event=message status=200

Session

Pair a phone, read the connection state and see recent webhook deliveries. Paths are relative to $BASE.

Connection status

GET /status
Request
curl "$BASE/status" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "jid": "",
  "lid": "",
  "name": "",
  "phone": "",
  "status": "unpaired"
}
Request
curl "$BASE/qr" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -o qr.png
Response 200
Binary file · image/png

Pair via link code sent to phone

POST /pair
Parameters
phone string required
Phone number (digits, optional + prefix).
Errors 400409500502
Request
curl -X POST "$BASE/pair" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "15550001234"
  }'
Response 200
{
  "code": "ABCD-EFGH"
}

Recent webhook delivery attempts

GET /webhook-logs

Returns up to 200 most recent outbound webhook delivery attempts, newest first. Ring buffer — oldest entries are evicted once full.

Request
curl "$BASE/webhook-logs" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
[
  {
    "attempt": 0,
    "error": "",
    "event": "",
    "event_id": "",
    "latency_ms": 0,
    "status_code": 0,
    "success": false,
    "timestamp": ""
  }
]

Messaging

Send, edit, revoke and mark messages read, and download media you received. Paths are relative to $BASE.

Send a text message

POST /send/text
Parameters
body string required
chat string required
Phone or full JID.
Request
curl -X POST "$BASE/send/text" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "hello",
    "chat": "15550001234@s.whatsapp.net"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a media message (image, video, audio, document, sticker)

POST /send/media

Media type is inferred from the Content-Type of the URL response. Set as_sticker to send image/webp media as a WhatsApp sticker.

Parameters
chat string required
Phone or full JID.
url string required
HTTP(S) URL to fetch. Media type inferred from response Content-Type.
as_sticker boolean
Send as a WhatsApp sticker. Media must be image/webp; caption is ignored.
caption string
Request
curl -X POST "$BASE/send/media" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234",
    "url": "https://example.com/voice.ogg"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

React to a message with an emoji

POST /send/reaction
Parameters
chat string required
Chat to send the reaction in (DM peer or group JID).
message_id string required
emoji string
Empty string removes the reaction.
from_me boolean
Set true when reacting to a message you sent. Ignored if 'sender' is provided.
sender string
For group reactions, the original sender's JID. Optional in DMs.
Request
curl -X POST "$BASE/send/reaction" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234",
    "emoji": "👍",
    "message_id": "ACE41E988162AB19A4A7BB7A9E663693"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a poll

POST /send/poll
Parameters
chat string required
options string[] required
question string required
max_answers integer
1 = single choice. Higher = multiple choice.
Request
curl -X POST "$BASE/send/poll" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234",
    "max_answers": 3,
    "options": [
      "Mon",
      "Tue",
      "Wed",
      "Thu",
      "Fri"
    ],
    "question": "Which days work for you?"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a location message

POST /send/location
Parameters
chat string required
lat number required
lng number required
address string
name string
Request
curl -X POST "$BASE/send/location" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "Plaza de la Constitución, Mexico City",
    "chat": "15550001234",
    "lat": 19.432608,
    "lng": -99.133209,
    "name": "Zócalo"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a calendar event

POST /send/event

Sends a WA EventMessage. The bridge embeds a fresh MessageSecret in MessageContextInfo so future EncEventResponseMessage RSVPs can be decrypted and delivered as event_response webhooks.

Parameters
chat string required
Phone or full JID.
name string required
Event title. Surfaced as body on the resulting message webhook.
start_time integer required
Unix epoch seconds.
description string
end_time integer
extra_guests_allowed boolean
has_reminder boolean
is_schedule_call boolean
True for a scheduled WA voice/video call.
join_link string
location object
location.address string
location.lat number
location.lng number
location.name string
reminder_offset_sec integer
Request
curl -X POST "$BASE/send/event" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "120363000000000001@g.us",
    "description": "weekly status",
    "end_time": 1777338000,
    "has_reminder": true,
    "is_schedule_call": true,
    "join_link": "https://meet.example.com/team-sync",
    "name": "Team sync",
    "reminder_offset_sec": 900,
    "start_time": 1777334400
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a typing indicator

POST /send/typing
Parameters
chat string required
mode string
textvoice
state string
composingpaused
Request
curl -X POST "$BASE/send/typing" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234",
    "state": "composing"
  }'
Response 200
{
  "ok": true
}

Revoke a sent message for everyone

DELETE /messages/:id
Parameters
id string path required
A message_id returned from a send call or seen on a webhook.
chat string query required
JID or phone identifying the chat the message was sent to.
Request
curl -X DELETE "$BASE/messages/{id}" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Edit a sent text message

PATCH /messages/:id/edit
Parameters
id string path required
A message_id returned from a send call or seen on a webhook.
body string required
chat string required
Request
curl -X PATCH "$BASE/messages/{id}/edit" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "corrected text",
    "chat": "15550001234@s.whatsapp.net"
  }'
Response 200
{
  "message_id": "ACE41E988162AB19A4A7BB7A9E663693",
  "timestamp": 1777180605
}

Send a read receipt

POST /messages/:id/read
Parameters
id string path required
A message_id returned from a send call or seen on a webhook.
chat string required
sender string
Original sender's JID for group messages.
Request
curl -X POST "$BASE/messages/{id}/read" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234@s.whatsapp.net"
  }'
Response 200
{
  "ok": true
}

Download received media by message_id (cached 3h)

GET /messages/:id/media
Parameters
id string path required
A message_id returned from a send call or seen on a webhook.
Errors 404500502
Request
curl "$BASE/messages/{id}/media" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -o media
Response 200
Binary file · audio/ogg, image/jpeg, video/mp4

Calls

Act on a ringing call announced by the call_incoming webhook. Paths are relative to $BASE.

Reject a ringing incoming call

POST /calls/:id/reject

Declines the call identified by a call_incoming webhook while it is still ringing. The caller sees the call declined immediately instead of ringing out. Rejecting a call that already ended is not an error WhatsApp reports; the request still returns ok.

Parameters
id string path required
The call_id from the call_incoming webhook.
contact_id string required
Caller's contact_id as delivered on the call_incoming webhook.
Request
curl -X POST "$BASE/calls/{id}/reject" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "15550001234@s.whatsapp.net"
  }'
Response 200
{
  "ok": true
}

Contacts

Look up contacts and groups, and check which numbers are on WhatsApp. Paths are relative to $BASE.

List all contacts the bridge knows about

GET /contacts

Returns a plain JSON array of contacts sorted by JID ascending. Without parameters the full list is returned (back-compat). To page, pass limit and use the last JID of the previous page as after on the next request; a page shorter than limit means you have reached the end.

Parameters
limit integer query
Maximum number of contacts to return. Values above 1000 are clamped to 1000. 0, absent, or negative returns everything (unlimited).
after string query
JID cursor: only contacts with a JID strictly greater than this value (in ascending JID order) are returned. Pass the last JID of the previous page to fetch the next one.
Errors 500502
Request
curl "$BASE/contacts" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
[
  {
    "about": "",
    "business_address": "",
    "business_email": "",
    "business_name": "",
    "first_name": "",
    "full_name": "",
    "is_business": false,
    "jid": "",
    "name": "",
    "phone": "",
    "redacted_phone": "",
    "registered": false,
    "verified_name": ""
  }
]

Check which phone numbers are on WhatsApp

POST /contacts/check
Parameters
phones string[] required
Errors 400500502
Request
curl -X POST "$BASE/contacts/check" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phones": [
      "15550001234",
      "00000000000"
    ]
  }'
Response 200
[
  {
    "is_business": false,
    "is_on_whatsapp": false,
    "jid": "",
    "phone": "",
    "registered": false,
    "verified_name": ""
  }
]

Get info for a single contact

GET /contacts/:jid
Parameters
jid string path required
Full JID or phone (e.g. 15550001234, 15550001234@s.whatsapp.net).
Errors 400404500
Request
curl "$BASE/contacts/{jid}" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "about": "",
  "business_address": "",
  "business_email": "",
  "business_name": "",
  "first_name": "",
  "full_name": "",
  "is_business": false,
  "jid": "",
  "name": "",
  "phone": "",
  "redacted_phone": "",
  "registered": false,
  "verified_name": ""
}

Download a contact's avatar

GET /contacts/:jid/avatar
Parameters
jid string path required
Full JID or phone.
Errors 404500
Request
curl "$BASE/contacts/{jid}/avatar" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -o avatar.jpg
Response 200
Binary file · image/jpeg

Profile

Read and change the linked number's own profile. Paths are relative to $BASE.

Your own profile

GET /profile
Errors 409503
Request
curl "$BASE/profile" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "about": "",
  "jid": "",
  "name": "",
  "phone": ""
}

Set your About status text

PATCH /profile/status
Parameters
status string required
Request
curl -X PATCH "$BASE/profile/status" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "Hey there!"
  }'
Response 200
{
  "ok": true
}

Download your own avatar

GET /profile/avatar
Errors 404409503
Request
curl "$BASE/profile/avatar" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -o avatar.jpg
Response 200
Binary file · image/jpeg

Set your profile picture from a URL

PUT /profile/avatar

Server fetches the URL (must return JPEG) and uploads it as the account profile picture.

Parameters
url string required
HTTP(S) URL to a JPEG image. Server fetches it and uploads as the profile picture.
Request
curl -X PUT "$BASE/profile/avatar" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/me.jpg"
  }'
Response 200
{
  "picture_id": ""
}

Personal QR link to share your contact

GET /profile/qr
Parameters
revoke boolean query
When true, revoke the current code and issue a new one.
Errors 500502
Request
curl "$BASE/profile/qr" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "link": "https://wa.me/qr/ABCDEFGHIJKL"
}

Privacy

Who can see the linked number's status updates. Paths are relative to $BASE.

Presence

Set your own presence and subscribe to a contact's typing and online events. Paths are relative to $BASE.

Set your online/offline presence

POST /presence
Parameters
state string required
availableunavailable
Request
curl -X POST "$BASE/presence" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "unavailable"
  }'
Response 200
{
  "ok": true
}

Subscribe to a contact's presence so typing/online events are delivered

POST /presence/subscribe

WhatsApp only pushes ChatPresence (typing/recording) and Presence (online/offline) for contacts the client has subscribed to. Subscriptions decay after a few minutes; refresh periodically. Requires global presence to be available first.

Parameters
chat string required
Contact JID to subscribe presence for.
Request
curl -X POST "$BASE/presence/subscribe" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chat": "15550001234"
  }'
Response 200
{
  "ok": true
}

Debug

Liveness and runtime numbers for the bridge process. Paths are relative to $BASE.

Process liveness probe (unauthenticated)

GET /healthz

Used by the container HEALTHCHECK. Always returns 200 while the process is up. Does NOT check WhatsApp connection — use /status for that.

Request
curl "$BASE/healthz" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "ok": true
}
Request
curl "$BASE/debug/stats" \
  -H "Authorization: Bearer $WA_API_KEY"
Response 200
{
  "gc_cycles": 0,
  "goroutines": 0,
  "heap_alloc_mb": 0,
  "heap_objects": 0,
  "heap_sys_mb": 0,
  "media_cache_size": 0,
  "next_gc_mb": 0,
  "total_alloc_mb": 0
}

Webhook events

Every inbound event is a POST to your webhook_url. Switch on the event field. Answer 200 right away and do the work afterwards: the timeout is 10 seconds. The webhook guide covers retries, signature verification and handlers in six languages.

message

Inbound or outbound message

Fields
body string
chat_id string
contact_id string
event string
message
from_me boolean
is_group boolean
media_type string
Set only when GET /messages/{id}/media can return the file; empty for text, locations, contacts, link previews and other non-downloadable messages. GIFs and video notes report video. imagevideoaudiovoicedocumentsticker
message_id string
name string
Push name from the sender's device.
phone string
Digits-only phone number, or empty for LID-only senders.
timestamp integer
type string
textmediareactionpolllocationevent
caption string optional
Caption for image/video/document.
edit string optional
Present only when the message is an edit/revoke/pin. editrevokepin
event_details object optional
Calendar/scheduled-event payload. Present only when type=event.
forwarded boolean optional
Present and true only when forwarded.
is_broadcast boolean optional
Present and true only when sent via a broadcast list.
location object optional
Shared pin or live-location snapshot. Present only when type=location.
max_answers integer optional
Number of choices a voter may pick. 1 = single choice. Present only when type=poll.
mentions string[] optional
Canonicalized mentioned contact JIDs.
offline boolean optional
Present and true when delivered as part of post-reconnect offline catch-up. Bracketed by offline_sync_preview / offline_sync_completed webhooks.
options string[] optional
Poll choices, in display order. Present only when type=poll.
quoted object optional
reaction object optional
target_id string optional
Message ID of the edited/revoked/pinned message. Present only alongside edit. For edits, body/caption carry the new content.
verified_business boolean optional
Present and true only when the sender is a verified business.
Payload
{
  "body": "Team sync",
  "chat_id": "15550009999@g.us",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "message",
  "event_details": {
    "description": "weekly status",
    "end_time": 1777338000,
    "join_link": "https://meet.example.com/team-sync",
    "name": "Team sync",
    "start_time": 1777334400
  },
  "from_me": false,
  "is_group": true,
  "media_type": "",
  "message_id": "ACEVENT0001",
  "name": "John Doe",
  "phone": "15550001234",
  "timestamp": 1777330600,
  "type": "event"
}

connected

WhatsApp session connected

Fields
event string
connected
phone string
Payload
{
  "event": "connected",
  "phone": "15550001234"
}

disconnected

WhatsApp session disconnected

Fields
event string
disconnected
detail string optional
Underlying protocol reason string, for diagnostics. Present only with reason=logged_out.
reason string optional
Present when the disconnect is terminal and the bridge will NOT reconnect on its own: logged_out (device was unpaired from the phone or server side) or stream_replaced (another client took over the session). Absent for transient disconnects, which reconnect automatically. logged_outstream_replaced
Payload
{
  "detail": "401: logged out",
  "event": "disconnected",
  "reason": "logged_out"
}

typing

Contact started or stopped typing

Fields
chat_id string
contact_id string
event string
typing
mode string
textvoice
state string
composingpaused
Payload
{
  "chat_id": "15550001234@s.whatsapp.net",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "typing",
  "mode": "text",
  "state": "composing"
}

presence

Contact came online or went offline

Fields
contact_id string
event string
presence
unavailable boolean
True when the contact just went offline, false when they came online.
last_seen integer optional
Unix seconds of last-online time. Omitted when the contact hides last-seen.
Payload
{
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "presence",
  "last_seen": 1777180605,
  "unavailable": true
}

poll_vote

Contact voted on a poll

Fields
chat_id string
contact_id string
event string
poll_vote
from_me boolean
message_id string
message_id of the vote update itself.
name string
phone string
poll_id string
message_id of the original poll being voted on.
selected_options string[]
Chosen option names. Empty array clears the vote, or means the original poll was not cached.
timestamp integer
offline boolean optional
Present and true when delivered as part of post-reconnect offline catch-up.
Payload
{
  "chat_id": "15550001234@s.whatsapp.net",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "poll_vote",
  "from_me": false,
  "message_id": "ACVOTE0099",
  "name": "John Doe",
  "offline": true,
  "phone": "15550001234",
  "poll_id": "ACPOLL0001",
  "selected_options": [
    "red"
  ],
  "timestamp": 1777180800
}

event_response

Contact RSVP'd to a calendar event

Fields
chat_id string
contact_id string
event string
event_response
event_id string
message_id of the original EventMessage being RSVP'd to.
extra_guest_count integer
from_me boolean
message_id string
message_id of the response itself.
name string
phone string
response string
goingnot_goingmaybeunknown
response_time_ms integer
Sender-supplied response timestamp in milliseconds.
timestamp integer
offline boolean optional
Present and true when delivered as part of post-reconnect offline catch-up.
Payload
{
  "chat_id": "15550009999@g.us",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "event_response",
  "event_id": "ACEVENT0001",
  "extra_guest_count": 1,
  "from_me": false,
  "message_id": "ACEVRESP0001",
  "name": "John Doe",
  "phone": "15550001234",
  "response": "going",
  "response_time_ms": 1777330700123,
  "timestamp": 1777330700
}

call_incoming

Incoming call

Fields
call_id string
contact_id string
event string
call_incoming
platform string
timestamp integer
Payload
{
  "call_id": "ABCDEF123456",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "call_incoming",
  "platform": "android",
  "timestamp": 1777180605
}

call_terminated

Call ended (missed or hung up)

Fields
call_id string
contact_id string
event string
call_terminated
reason string
timeouthangupdeclinebusyaccept_elsewhere
timestamp integer
Payload
{
  "call_id": "ABCDEF123456",
  "contact_id": "15550001234@s.whatsapp.net",
  "event": "call_terminated",
  "reason": "hangup",
  "timestamp": 1777180720
}

offline_sync_preview

Post-reconnect catch-up is starting

Fields
app_data_changes integer
event string
offline_sync_preview
messages integer
notifications integer
receipts integer
total integer
Payload
{
  "app_data_changes": 0,
  "event": "offline_sync_preview",
  "messages": 120,
  "notifications": 8,
  "receipts": 14,
  "total": 142
}

offline_sync_completed

Post-reconnect catch-up has finished

Fields
count integer
event string
offline_sync_completed
Payload
{
  "count": 142,
  "event": "offline_sync_completed"
}

profile_picture_updated

Own profile picture changed or removed

Fields
event string
profile_picture_updated
remove boolean
True when the profile picture was removed.
timestamp integer
picture_id string optional
New picture ID assigned by WhatsApp. Absent when remove is true.
Payload
{
  "event": "profile_picture_updated",
  "remove": true,
  "timestamp": 1777180800
}