Connect your app to WhaBuzz
Let a WhaBuzz customer connect your app in one click. They approve on a WhaBuzz page; your server gets a scoped API key, a webhook secret and the number.
Connect lets a WhaBuzz customer hand your app access to one of their WhatsApp numbers without copying keys. It works like "Sign in with" flows: you send the user to a WhaBuzz page, they approve, WhaBuzz sends them back to you with a one-time code, and your server swaps the code for credentials.
Connect is available to apps registered with WhaBuzz. Today that is WhaLeads (app=whaleads). To register another app, write to support@whabuzz.com with its name and the host pattern of its callback.
#How it works
- Your app sends the user to
https://app.whabuzz.com/connectwith your app id, your callback URL and a randomstate. - The user signs in to WhaBuzz, picks a workspace and a number. If the workspace has no number yet, they connect one there through Meta (coexistence with the WhatsApp Business app included).
- WhaBuzz shows what your app will be able to do and the host of your callback. The user presses Approve.
- WhaBuzz sends a verify ping to
https://<your host>/api/whabuzz/webhook. If your server does not answer it, nothing is created and the user sees why. - WhaBuzz redirects the browser to your callback with
codeandstate. - Your server posts the code to
https://api.whabuzz.com/connect/tokenand receives the API key, the webhook signing secret and the number, once.
#1. Send the user to WhaBuzz
https://app.whabuzz.com/connect
?app=whaleads
&callback=https://acme.example.com/api/whabuzz/callback
&state=Qm9vcC1iZWVwLTEyMzQ1Njc4OTA
&code_challenge=VAVLIyYOqvtuLg_z2ITRlp_41lQmqEtYFeN-BpcepQ0| Parameter | Required | Meaning |
|---|---|---|
app | yes | Your registered app id. |
callback | yes | https://<your host>/api/whabuzz/callback exactly: HTTPS, that path, no query string or fragment. Public host names only (no IP addresses or localhost). |
state | yes | Up to 200 printable ASCII characters, no spaces. Make it random per attempt, keep it in the user's session, and compare it when the user comes back. |
code_challenge | no | PKCE: base64url (no padding) of the SHA-256 of a random code_verifier of 43 to 128 characters. If you send it, the exchange needs the verifier. Recommended. |
Keep the whole link under about 500 characters: WhaBuzz carries it through sign-in, and a longer one makes the user start again after signing in.
#2. Answer the verify ping
Before anything is issued, WhaBuzz POSTs a signed ping to https://<your host>/api/whabuzz/webhook:
{ "type": "webhook.verify", "challenge": "6f1c0b8e4a7d4f29b3e5a0c2d9f81a47", "endpoint_id": "3c2b…", "created_at": "2026-10-19T09:30:00.000Z" }Answer 2xx within 5 seconds with the challenge echoed, as the plain-text body or as JSON {"challenge":"…"}. You do not have the signing secret yet at this point, so accept the ping without checking X-WhaBuzz-Signature (it is still signed, with the secret you receive in step 4). Check signatures on everything else.
Your webhook receives all events (*) for the whole workspace, starting the moment your server completes the exchange in step 4. See Webhooks for payloads, retries and the signature check.
#3. Handle the callback
On approve, the browser lands on:
https://acme.example.com/api/whabuzz/callback?code=7Hq0…(43 characters)&state=Qm9vcC1iZWVwLTEyMzQ1Njc4OTAIf the user pressed Cancel:
https://acme.example.com/api/whabuzz/callback?error=access_denied&state=Qm9vcC1iZWVwLTEyMzQ1Njc4OTACompare state with the one you stored before doing anything else. The code works once and expires after 5 minutes.
#4. Exchange the code
Server to server, never from the browser:
curl -X POST https://api.whabuzz.com/connect/token \
-H "Content-Type: application/json" \
-d '{"app":"whaleads","code":"7Hq0…","state":"Qm9vcC1iZWVwLTEyMzQ1Njc4OTA","code_verifier":"dBjftJeZ4CVP-mJ92K1RmuIBRHzyl1Ftw7gJA4KiBnI"}'No Authorization header: the code is the credential. Send code_verifier only if you sent a code_challenge.
200 OK, shown once (the response carries Cache-Control: no-store):
{
"api_key": "wb_live_…",
"webhook_secret": "whsec_…",
"phone_number_id": "106540352242922",
"waba_id": "102290129340398",
"tenant_id": "a578246e-b97f-48e6-ae42-939ec3ccf917",
"display_phone_number": "+91 98765 43210"
}Store api_key and webhook_secret encrypted. WhaBuzz keeps only hashes and cannot show them again; if they are lost, the user connects again.
| Status | error.code | Meaning |
|---|---|---|
400 | unknown_app | app is not a registered app. |
400 | invalid_code | The code, state or code_verifier is wrong or malformed. A wrong state or verifier also burns the code. |
410 | code_used | The code was already exchanged. WhaBuzz treats this as a leaked code and revokes the connection it created. Ask the user to connect again. |
410 | code_expired | More than 5 minutes passed. Ask the user to connect again. |
410 | connection_gone | The user revoked or replaced the connection before you exchanged the code. |
409 | connection_unavailable | The number was disconnected or the webhook endpoint deleted since the user approved. |
429 | rate_limited | More than 10 exchanges a minute from your IP. Retry after Retry-After seconds. |
#What the key can do
The key is limited to the one number the user picked and to these scopes:
| Scope | Allows |
|---|---|
messages | POST /v1/messages, GET /v1/messages/… |
templates | /v1/templates…, for the number's WhatsApp Business Account only |
media | /v1/media… |
numbers:read | GET /v1/numbers… (only this number is listed) |
events:read | GET /v1/events…, GET /v1/stream (workspace-wide) |
Anything else answers 403 insufficient_scope: API keys, webhooks, billing, tenant settings, exports, other numbers. A request that names another number answers 404. A connected app's key cannot be rotated (409); connect again instead.
#Revoking and reconnecting
The user sees every connection under Settings, Connected apps in the console and can revoke it. Revoking deletes the key and the webhook endpoint at once: your next API call answers 401, and deliveries stop.
Connecting the same workspace to the same host again replaces the earlier connection: after the new exchange succeeds, the old key and endpoint are revoked. Use the newest credentials.
#Checklist
GET /api/whabuzz/callback: checkstate, handleerror=access_denied, exchange the code server-side within 5 minutes.POST /api/whabuzz/webhook: echo the verifychallenge; verifyX-WhaBuzz-Signatureon every other request withwebhook_secret.- Never log
code,api_keyorwebhook_secret. - Treat
401from the API as "the user revoked access": show a Reconnect button that starts step 1 again.
This page as Markdown: /guides/connect-apps.md. All docs in one file: /llms-full.txt. Status.