# WA BRIDGES API Reference WhatsApp-as-a-service. One bridge per phone number. ## Authentication All requests require: `Authorization: Bearer $WA_API_KEY` ## Rate Limits All limits per API key. Exceeded requests → `429` with `Retry-After` header. | Endpoint | Limit | Window | |----------|-------|--------| | `POST /api/instances` (provision) | 10 req | per minute | | `/api/instances/:id/proxy/*` | 120 req | per minute | | `/api/*` (all other endpoints) | 120 req | per minute | | All endpoints (per IP) | 300 req | per minute | ## Bridges Manage bridge instances. Base URL: `https://wabridges.com/api/instances/{customer_ref}/proxy` ### POST /api/instances Provision a new bridge. **Body:** `{"customer_ref": "user-123", "webhook_url": "https://yourbackend.com/hook"}` **Response 200:** `{"id": 42, "customer_ref": "user-123", "state": "running"}` If customer_ref already exists returns existing record. ### GET /api/instances List all bridges. Response: array of `{id, customer_ref, state, created_at}`. ### GET /api/instances/:id Get single bridge with live manager state. Includes `manager.health`, `manager.started_at`, `manager.fail_count`. ### DELETE /api/instances/:id Permanently delete bridge. Stops container, removes all data. Returns 204. ## 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`. Events POST to the webhook_url configured on the bridge. ### webhook: message **Fields:** event, message_id, contact_id, phone, chat_id, name, body, type, media_type, is_group, from_me, timestamp **Example payload:** ```json {"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:** ```json {"event":"connected","phone":"15550001234"} ``` ### webhook: disconnected **Fields:** event **Example payload:** ```json {"event":"disconnected"} ``` ### webhook: typing **Fields:** event, contact_id, chat_id, state (composing|paused), mode (text|voice) **Example payload:** ```json {"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:** ```json {"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:** ```json {"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:** ```json {"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:** ```json {"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:** ```json {"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:** ```json {"event":"offline_sync_completed","count":142} ``` ### webhook: profile_picture_updated **Fields:** event, remove (bool), picture_id?, timestamp **Example payload:** ```json {"event":"profile_picture_updated","remove":false,"picture_id":"12345678901","timestamp":1777180605} ```