Skip to main content

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).

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 to into 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_iduses exactly that number (ignores everything)
pool_idrotates within the pool
nothing (default)sticky: reuses the conversation's number; if none, rotates
sticky: falseforces 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):

  1. The inbound tells you the number. Every received message fires the message.received webhook (and the SSE) with the instance_id that received it.
  2. You store the link conversation → instance_id in your database when you open the ticket.
  3. Every reply goes with the instance_id of 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_id always beats rotation: the selector returns that number directly, with no rotation/warming check at all.

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_id and the chat_jid (canonical JID).

In short

  • Want simplicity? Don't send instance_idsticky handles it.
  • Want control? Capture the instance_id from the inbound webhook and use it on replies.
  • Broadcast? Send sticky: false or use a pool_id.