Create a bridge, link a WhatsApp number to it, and confirm it is connected - the three calls that come before everything else.
A bridge is one WhatsApp number under API control. You create it, link a phone to it once, and from then on it is a base path you send HTTP requests to. It runs as its own container, so one noisy number never affects another.
You address a bridge by the customer_ref you chose when you created it - your own identifier, not ours. If you only ever run one bridge, the alias default works everywhere a customer_ref does, so a single-number integration never carries a name in its URLs.
Billing note: the subscription and its 7-day free trial start when you connect your first number in the dashboard, card on file. The API adds bridges to an existing subscription rather than opening one.
One POST. Give it your customer_ref and the URL that should receive events. Omit webhook_url and events land in the hosted inbox on the dashboard instead, which is the easier way to start - you can point it at your own endpoint later with PATCH.
The response carries the webhook_secret you will need to verify deliveries. Store it now; it is shown again on the bridge page but never in a webhook.
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"
}'
{
"id": "7d09aa9c-2f4e-4b6a-9c1d-3e5f7a9b1c2d",
"customer_ref": "user-123",
"state": "running",
"created_at": 1777180605,
"webhook_secret": "whsec_...",
"webhook_url": "https://yourbackend.com/hook"
}
Creating bridges is rate limited to 10 requests per minute, separately from the 120 per minute everything else gets. Provisioning is expensive on our side; sending is not.
A fresh bridge is running but not yet linked to a number. There are two ways to link one and they end in the same place.
A pairing code is the one you can do without leaving the terminal. POST the phone number, then on the handset open Linked devices → Link with phone number instead and type the eight characters that come back.
curl -X POST https://wabridges.com/api/instances/user-123/proxy/pair \
-H "Authorization: Bearer $WA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone": "15550001234"}'
{
"code": "ABCD-EFGH"
}
The link is a WhatsApp linked device, the same mechanism as WhatsApp Web. The phone stays the owner of the account and keeps receiving messages normally.
A QR code is the other way, and the one to use if the phone is in your hand. GET /qr returns the pairing QR as a PNG. Save it, open it, and scan it from Linked devices → Link a device on the phone.
curl https://wabridges.com/api/instances/user-123/proxy/qr \ -H "Authorization: Bearer $WA_API_KEY" \ --output qr.png
The endpoint takes your API key, so it is a server-side call - the URL cannot be dropped into a browser or an <img> tag as-is. Once a phone is linked it returns 409, which is a useful way to ask "is this one already paired?".
Pairing is not instant - the phone has to scan or type, and the session has to come up. Poll this after showing a code until status is connected, and use the same call as a health check afterwards.
status is one of three values. unpaired means no phone has ever linked, connected means it is live, disconnected means it was linked and the link dropped - re-pairing fixes the last one and keeps the same API key, endpoints and webhook config.
curl https://wabridges.com/api/instances/user-123/proxy/status \ -H "Authorization: Bearer $WA_API_KEY"
{
"status": "connected",
"jid": "15550001234@s.whatsapp.net",
"phone": "15550001234",
"name": "Acme Support"
}
A 503 from any other endpoint means the same thing as status disconnected, and carries a Retry-After header in seconds so a client can back off on its own. Your webhook also gets a disconnected event the moment a link drops, which beats polling - you find out before your users do.
The bridge is up and linked, so the next two pages are the whole loop: send a message and receive a message. If you do not have a spare number yet, start on the sandbox instead - every account has one, and it answers the real API with no phone and no billing.
Something here not matching what you see? Write to us - a person answers.