Customer support: keeping the conversation on the same number
A classic scenario: you have several numbers rotating and a customer starts a conversation. In your system (CRM/helpdesk) a ticket opens. How do you ensure every interaction happens on the same number that started the conversation?
There are two ways — pick whichever you prefer (or use both).
1. Automatic affinity (sticky) — recommended
By default, when you send without instance_id and without pool_id, bZapper
automatically reuses the number that already talks to that recipient. In other
words: reply to whoever messaged you → it goes through the same number, every time.
# Replying to a customer: you don't even have to name the number.
curl -X POST https://api.bzapper.com.br/messages/text \
-H "Authorization: Bearer bz_live_..." -H "Content-Type: application/json" \
-d '{"to":"+5511988887777","body":"Hi! How can I help?"}'
# → goes through the same number that already talks to +5511988887777
How it works under the hood:
- bZapper resolves the
tointo WhatsApp's canonical JID (fixing the Brazilian 9th digit and@lid). - It looks for the existing conversation with that contact and uses that number, if it's connected.
- If there's no prior conversation, it falls into the normal rotation (and from then on that number becomes "stuck" to that contact).
To force rotation (e.g. a broadcast, where you want to distribute):
{ "to": "+5511988887777", "body": "Promo!", "sticky": false }
| You send with… | What happens |
|---|---|
instance_id | uses exactly that number (ignores everything) |
pool_id | rotates within the pool |
| nothing (default) | sticky: reuses the conversation's number; if none, rotates |
sticky: false | forces rotation (broadcast) |
2. Pin it yourself (full control)
If you'd rather control it in your own system, the contract is simple — and it's what the whole industry does (just like Twilio):
- The inbound tells you the number. Every received message fires the
message.receivedwebhook (and the SSE) with theinstance_idthat received it. - You store the link
conversation → instance_idin your database when you open the ticket. - Every reply goes with the
instance_idof that ticket — the number never changes.
// webhook message.received (summary)
{
"type": "message.received",
"instance_id": "492e6b46-0c5f-4727-96e3-6f9f5e4f93e8", // ← store this
"payload": { "from": "[email protected]", "body": "Hello" }
}
# Reply pinning the number:
curl -X POST https://api.bzapper.com.br/messages/text \
-H "Authorization: Bearer bz_live_..." -H "Content-Type: application/json" \
-d '{"to":"+5511988887777","instance_id":"492e6b46-...","body":"Reply"}'
Passing
instance_idalways beats rotation: the selector returns that number directly, with no rotation/warming check at all.
Rebuilding the link
You can recover which number talks to whom at any time:
GET /conversations?instance_id=<id>— lists each number's conversations.- Each message stores the
instance_idand thechat_jid(canonical JID).
In short
- Want simplicity? Don't send
instance_id— sticky handles it. - Want control? Capture the
instance_idfrom the inbound webhook and use it on replies. - Broadcast? Send
sticky: falseor use apool_id.