WABridges
LIMITS BRIDGES SESSION MESSAGING CONTACTS PROFILE PRESENCE PRIVACY WEBHOOKS DEBUG

API REFERENCE

AUTHENTICATION

All requests require your API key as a Bearer token. Store it as an environment variable. Never hardcode it in scripts.

SHELL
export WA_API_KEY="sk_..."
HTTP HEADER
Authorization: Bearer $WA_API_KEY

Sign in to get your API key.

RATE LIMITS

All limits are per API key. Exceeded requests return 429 Too Many Requests with a Retry-After header indicating seconds until the window resets.

EndpointLimitWindow
POST /api/instances (provision)10 reqper minute
/api/instances/:id/proxy/*120 reqper minute
/api/* (all other endpoints)120 reqper minute
All endpoints (per IP, global floor)300 reqper minute
429 RESPONSE
HTTP/1.1 429 Too Many Requests
Retry-After: 42
Content-Type: application/json

{"error":"rate limit exceeded"}

Implement exponential backoff. Check Retry-After before retrying.

QUICK START

1. Provision a bridge

CURL
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 200
{"id":42,"customer_ref":"user-123","state":"running"}

2. Pair the phone

CURL
curl -X POST https://wabridges.com/api/instances/{customer_ref}/proxy/pair \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"15550001234"}'
# → {"code":"ABCD-EFGH"}   Enter this on the phone in WhatsApp → Linked Devices

3. Check status

CURL
curl https://wabridges.com/api/instances/{customer_ref}/proxy/status \
  -H "Authorization: Bearer $WA_API_KEY"
# → {"status":"connected","phone":"15550001234","jid":"15550001234@s.whatsapp.net","name":"Alice"}

4. Send a message

CURL
curl -X POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/text \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"chat":"15559876543","body":"Hello from the API"}'
# → {"message_id":"ACE41E...","timestamp":1777180605}

BRIDGES

Manage bridge instances. All routes require Authorization: Bearer $WA_API_KEY.

POST https://wabridges.com/api/instances

Provision a new bridge. If customer_ref already exists, returns the existing record.

customer_refstring, requiredYour internal identifier for this bridge (e.g. user ID). Alphanumeric + dashes.
webhook_urlstring, requiredURL that receives all inbound WhatsApp events as POST requests.
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 200
{
  "id": 42,
  "customer_ref": "user-123",
  "state": "running"
}
GET https://wabridges.com/api/instances

List all bridges for this account.

REQUEST
curl https://wabridges.com/api/instances \
  -H "Authorization: Bearer $WA_API_KEY"
RESPONSE 200
[
  {
    "id": 42,
    "customer_ref": "user-123",
    "state": "running",
    "created_at": 1777180605
  }
]
GET https://wabridges.com/api/instances/:id

Get a single bridge including live container state from the manager.

REQUEST
curl https://wabridges.com/api/instances/{customer_ref} \
  -H "Authorization: Bearer $WA_API_KEY"
RESPONSE 200
{
  "id": 42,
  "customer_ref": "user-123",
  "state": "running",
  "created_at": 1777180605,
  "manager": {
    "health": "healthy",
    "started_at": "2025-06-17T10:00:00Z",
    "fail_count": 0
  }
}
DELETE https://wabridges.com/api/instances/:id

Permanently delete a bridge. Stops the container and removes all data. Cannot be undone.

REQUEST
curl -X DELETE https://wabridges.com/api/instances/{customer_ref} \
  -H "Authorization: Bearer $WA_API_KEY"
RESPONSE
204 No Content

SESSION

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

Connection state. When connected, includes phone, jid, and name.

RESPONSE
{"status":"connected","phone":"15550001234","jid":"15550001234@s.whatsapp.net","name":"Alice"}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/qr

QR code PNG, open in browser (unpaired state only)

RESPONSE
(image/png)
POST https://wabridges.com/api/instances/{customer_ref}/proxy/pair

Link-code pairing. WhatsApp shows 8-digit code on phone

REQUEST BODY
{"phone":"15550001234"}
RESPONSE
{"code":"ABCD-EFGH"}
DELETE https://wabridges.com/api/instances/{customer_ref}/proxy/logout

Unpair and destroy session. Requires re-pair after.

RESPONSE
{"ok":true}

MESSAGING

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

Send a text message. chat = phone number or full JID

REQUEST BODY
{"chat":"15550001234","body":"Hello!"}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/media

Send image, video, audio, or document. Media type inferred from Content-Type of URL response

REQUEST BODY
{"chat":"15550001234","url":"https://example.com/photo.jpg","caption":"check this out"}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/reaction

React to a message with an emoji. Empty string removes the reaction

REQUEST BODY
{"chat":"15550001234","message_id":"ACE41E...","emoji":"👍"}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/poll

Send a poll. max_answers=1 is single choice, higher = multiple choice

REQUEST BODY
{"chat":"15550001234","question":"Favorite color?","options":["Red","Green","Blue"],"max_answers":1}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/location

Send a location pin with optional name and address

REQUEST BODY
{"chat":"15550001234","lat":19.432608,"lng":-99.133209,"name":"Zócalo","address":"Plaza de la Constitución"}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/event

Send a calendar event (WhatsApp EventMessage). Contacts can RSVP and trigger event_response webhooks.

REQUEST BODY
{"chat":"15550001234","name":"Team sync","start_time":1777334400,"end_time":1777338000}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/send/typing

Send typing indicator. state=composing|paused, mode=text|voice

REQUEST BODY
{"chat":"15550001234","state":"composing","mode":"text"}
RESPONSE
{"ok":true}
DELETE https://wabridges.com/api/instances/{customer_ref}/proxy/messages/:id?chat=15550001234

Revoke a sent message for everyone

RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
PATCH https://wabridges.com/api/instances/{customer_ref}/proxy/messages/:id/edit

Edit a sent text message

REQUEST BODY
{"chat":"15550001234@s.whatsapp.net","body":"corrected text"}
RESPONSE
{"message_id":"ACE41E...","timestamp":1777180605}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/messages/:id/read

Send a read receipt. Include sender for group messages

REQUEST BODY
{"chat":"15550001234@s.whatsapp.net"}
RESPONSE
{"ok":true}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/messages/:id/media

Download inbound media by message_id. Cached 3 hours from receipt

RESPONSE
(raw bytes: image/jpeg, video/mp4, audio/ogg, etc.)

CONTACTS

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

List all contacts the bridge knows about

RESPONSE
[{"jid":"15550001234@s.whatsapp.net","phone":"15550001234","name":"Alice","is_business":false}]
GET https://wabridges.com/api/instances/{customer_ref}/proxy/contacts/:jid

Get info for a single contact. Pass phone number or full JID

RESPONSE
{"jid":"...","phone":"15550001234","name":"Alice","about":"Hey!","is_business":false}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/contacts/:jid/avatar

Download contact avatar as JPEG

RESPONSE
(image/jpeg)
POST https://wabridges.com/api/instances/{customer_ref}/proxy/contacts/check

Check which phone numbers are registered on WhatsApp

REQUEST BODY
{"phones":["15550001234","00000000000"]}
RESPONSE
[{"phone":"15550001234","registered":true,"is_business":false},{"phone":"00000000000","registered":false}]

PROFILE

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

Your own profile

RESPONSE
{"jid":"15550001234@s.whatsapp.net","phone":"15550001234","name":"My Name","about":"Hey there!"}
PATCH https://wabridges.com/api/instances/{customer_ref}/proxy/profile/status

Set your About status text

REQUEST BODY
{"status":"Hey there! I am using WA Bridges."}
RESPONSE
{"ok":true}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/profile/avatar

Download your own avatar as JPEG

RESPONSE
(image/jpeg)
PUT https://wabridges.com/api/instances/{customer_ref}/proxy/profile/avatar

Set your profile picture from a URL (must return JPEG)

REQUEST BODY
{"url":"https://example.com/me.jpg"}
RESPONSE
{"picture_id":"12345678901"}
DELETE https://wabridges.com/api/instances/{customer_ref}/proxy/profile/avatar

Remove your profile picture

RESPONSE
{"ok":true}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/profile/qr

Get your shareable wa.me/qr link

RESPONSE
{"link":"https://wa.me/qr/ABCDEFGHIJKL"}

PRESENCE

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

Set your online/offline presence

REQUEST BODY
{"state":"available"}
RESPONSE
{"ok":true}
POST https://wabridges.com/api/instances/{customer_ref}/proxy/presence/subscribe

Subscribe to a contact's typing/online state. Requires global presence to be available. Refresh every few minutes.

REQUEST BODY
{"chat":"15550001234"}
RESPONSE
{"ok":true}

PRIVACY

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

Get who can see your status updates

RESPONSE
[{"type":"contacts","list":[],"is_default":true}]

DEBUG

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

Process liveness probe (unauthenticated). Returns 200 while the process is up. Does not check WA connection; use /status for that.

RESPONSE
{"ok":true}
GET https://wabridges.com/api/instances/{customer_ref}/proxy/debug/stats

Runtime stats: goroutines, heap usage, GC cycles, media cache size

RESPONSE
{"goroutines":12,"heap_alloc_mb":14,"heap_sys_mb":32,"heap_objects":84201,"total_alloc_mb":50,"gc_cycles":3,"next_gc_mb":28,"media_cache_size":4}

WEBHOOKS

Every inbound event fires a POST to your webhook_url.

Respond 200 quickly. Delivery is async, single-attempt. Process asynchronously.

WEBHOOK message

Fields: event, message_id, contact_id, phone, chat_id, name, body, type, media_type, is_group, from_me, timestamp

EXAMPLE PAYLOAD
{"event":"message","message_id":"ACE41E...","contact_id":"15550001234@s.whatsapp.net","phone":"15550001234","chat_id":"15550001234@s.whatsapp.net","name":"Alice","body":"Hello!","type":"text","media_type":"","is_group":false,"from_me":false,"timestamp":1777180605}
WEBHOOK connected

Fields: event, phone

EXAMPLE PAYLOAD
{"event":"connected","phone":"15550001234"}
WEBHOOK disconnected

Fields: event

EXAMPLE PAYLOAD
{"event":"disconnected"}
WEBHOOK typing

Fields: event, contact_id, chat_id, state (composing|paused), mode (text|voice)

EXAMPLE PAYLOAD
{"event":"typing","contact_id":"15550001234@s.whatsapp.net","chat_id":"15550001234@s.whatsapp.net","state":"composing","mode":"text"}
WEBHOOK poll_vote

Fields: event, contact_id, phone, chat_id, poll_id, message_id, name, from_me, timestamp, selected_options

EXAMPLE PAYLOAD
{"event":"poll_vote","contact_id":"15550001234@s.whatsapp.net","phone":"15550001234","chat_id":"15550001234@s.whatsapp.net","poll_id":"ACPOLL0001","message_id":"ACVOTE0001","name":"Alice","from_me":false,"timestamp":1777330500,"selected_options":["Red"]}
WEBHOOK event_response

Fields: event, contact_id, phone, chat_id, event_id, message_id, name, from_me, timestamp, response (going|not_going|maybe), extra_guest_count

EXAMPLE PAYLOAD
{"event":"event_response","contact_id":"15550001234@s.whatsapp.net","phone":"15550001234","chat_id":"15550009999@g.us","event_id":"ACEVENT0001","message_id":"ACEVRESP0001","name":"Alice","from_me":false,"timestamp":1777330700,"response":"going","extra_guest_count":0}
WEBHOOK call_incoming

Fields: event, call_id, contact_id, platform, timestamp

EXAMPLE PAYLOAD
{"event":"call_incoming","call_id":"ABCDEF123456","contact_id":"15550001234@s.whatsapp.net","platform":"android","timestamp":1777180605}
WEBHOOK call_terminated

Fields: event, call_id, contact_id, reason (timeout|hangup|decline|busy), timestamp

EXAMPLE PAYLOAD
{"event":"call_terminated","call_id":"ABCDEF123456","contact_id":"15550001234@s.whatsapp.net","reason":"hangup","timestamp":1777180720}
WEBHOOK offline_sync_preview

Fields: event, total, messages, notifications, receipts, app_data_changes

EXAMPLE PAYLOAD
{"event":"offline_sync_preview","total":142,"messages":120,"notifications":8,"receipts":14,"app_data_changes":0}
WEBHOOK offline_sync_completed

Fields: event, count

EXAMPLE PAYLOAD
{"event":"offline_sync_completed","count":142}
WEBHOOK profile_picture_updated

Fields: event, remove (bool), picture_id?, timestamp

EXAMPLE PAYLOAD
{"event":"profile_picture_updated","remove":false,"picture_id":"12345678901","timestamp":1777180605}

GUIDES

Step-by-step guides for common integration patterns.

Webhook Guide
All 11 event types, payloads, delivery behavior, and handler examples.
Node.js Quickstart
Install, provision a bridge, pair a phone, and send a message in 5 steps.
Python Quickstart
End-to-end integration with requests and Flask for incoming events.
PHP Quickstart
Send and receive WhatsApp messages with plain PHP and curl.
Ruby Quickstart
Net::HTTP client with Sinatra webhook receiver. Zero extra gems for the client.
Go Quickstart
Standard library only - net/http and encoding/json. No external dependencies.
Rust Quickstart
reqwest for the HTTP client, axum for the webhook server.