WASync · Developers

Pairing code & QR

The QR rotates every 20 seconds. Ship both paths, because a one-device onboarding cannot photograph its own screen.

Pairing applies to qr connections. A cloud_api number never scans anything and answers 409 not_supported_for_provider.

The QR path

POST /connections returns an initial QR as a data-URI PNG.

Re-render the QR on every poll

WhatsApp rotates the pairing code roughly every 20 seconds. The qr in the create response is stale within seconds of being displayed, and this fails silently: the phone never pairs and nothing on screen explains why.

Poll GET /connections/{id} every 3–5 s and re-render the qr from every response until status is "connected". Do not cache it, and do not hide the refresh behind a button the user has to press.

The same rule applies to an idempotent replay: when POST /connections returns 200 with Idempotency-Replayed: true, the qr in that response has been re-fetched fresh rather than replayed from storage — precisely because a stored code would no longer scan.

The pairing-code path

A QR needs two devices: a screen showing it and a phone photographing it. A large share of real onboardings happen on one device — the customer finishes your flow on the same phone that runs WhatsApp, and cannot photograph its own screen. For them the QR path is not awkward, it is impossible.

POST /connections/{id}/pairing-code
curl -X POST https://developers.wasync.app/api/v1/connections/conn_8f3a21/pairing-code \
  -H "Authorization: Bearer $WASYNC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phoneNumber":"40740267964"}'
200 response
{
  "code": "7S59-1KZP"
}

The customer opens WhatsApp → Settings → Linked devices → Link with phone number and types the code.

phoneNumber is E.164 without the leading + — digits only. Anything else is rejected with 400 { "error": "invalid_phone_number" } rather than cleaned up for you: silently rewriting the number you sent is how a customer ends up pairing an account nobody asked for. Uses whatsapp.manage, same as creating the connection.

A pairing code is not always available — keep the QR as a fallback

The engine can decline to issue one. That is an ordinary outcome, not an outage, and it answers 502 { "error": "pairing_code_unavailable" } instead of a generic 500 precisely so you can tell the two apart and send the user back to the QR. An integration that ships only the pairing-code path will, sooner or later, show a customer a screen with no way forward. Render both, side by side.

Asking for another code

You can request another code on the same connection: it returns a new code and does not rebuild the session, so a "send me another code" button is safe.

It is capped at 5 per connection per 15 minutes; past that you get 429 { "error": "rate_limited" } with a Retry-After header to honour. The cap is not bureaucracy — repeatedly requesting codes for one number is exactly what WhatsApp's anti-abuse systems act on, and the number at risk is your customer's.

Nothing extra is consumed

Pairing by code uses the same paid slot and starts the same 7-day free trial as scanning a QR: the licence is claimed when the number actually pairs, whichever way it paired.

Watching for success is unchanged too. There is no "code accepted" event, so poll GET /connections/{id} until status is "connected" and/or listen for the connection.connected webhook.

Two refusals are worth coding for:

  • 409 already_connected — the session is live, there is nothing to pair.
  • 409 not_supported_for_provider — a cloud_api number. Pairing codes are a qr concept and never apply, so do not retry.

Re-pairing later

A qr session can break on its own. connection.disconnected tells you, and needs_reconnect is the field to drive your UI from. The QR is deliberately not in that payload — it is a scan-to-login credential — so fetch it from GET /connections/{id} and show it to the number's owner exactly as during first setup. See Webhooks.

On this page