Skip to main content
WhatsApp is where customers write first, and where a single thread can last for months. This guide connects a WhatsApp agent to Niadra: which identifiers to send, how to make webhook retries harmless, how a months-long thread becomes sessions, how an OTP on the same channel raises the verification level and how voice notes and attachments travel by reference. The example is Marina Souza’s message at 2:02 pm: “The technician never showed up. I am calling you.” Five minutes later she calls, and the voice agent already knows what she wrote.

Identifiers on WhatsApp

The WhatsApp Cloud API reports several ids for the same person, and Niadra keeps each one as its own handle type. Send every id you receive in the same event: handles that arrive together on the same event are linked when they are strong, unblocked and consistent. With WhatsApp usernames, the sender may no longer be a phone number: it becomes a BSUID. The link between a BSUID and a phone is never assumed. It needs an explicit assertion, such as the customer typing the number, an OTP or your own records. A username is a display attribute and never identifies anyone.
A BSUID is only unique inside one business account. Always send it with the account in scope (the SDK helpers take it as an argument).

Steps

1. Record each inbound message with its WhatsApp id

Use the provider message id (wamid) as the idempotency_key. The WhatsApp Cloud API retries webhooks for days, and Niadra deduplicates by that key in the database: a repeated delivery is counted as a duplicate, never stored twice. Events are ordered by occurred_at, the time the message was sent, never by arrival, so a resync that replays old messages lands them in the right place.
track() returns at once. The SDK queues the event and sends it in batches of up to 15 events or every second. The raw event is written before the API answers, and the message is readable in the live layer in under a second: when Marina calls at 2:07 pm, the voice agent receives it in live. The voice agent looks Marina up by phone_e164; your CRM knows her by its own id. When you know those belong to the same person, say so with identify(). It goes out right away, so the next context() sees the linked profile.
Every link is an assertion with its method and evidence, so a wrong one can be retracted and the memory follows each handle back to its origin. See Identity and verification.

3. Treat the thread as a conversation and let Niadra split sessions

Your conversation_id is the thread, and a WhatsApp thread may last months. Niadra splits it into sessions: a session closes after about 20 minutes of inactivity on WhatsApp (configurable per channel). Each session is extracted into memory on its own, and billing counts a conversation once per window of activity, not once per message. Read the context before every answer. Inside a session the pack is pinned, so the same bytes come back and the model provider keeps the prompt prefix cached; the SDK also caches it per conversation.

4. Raise the verification level with an OTP on WhatsApp

A message from a WhatsApp number is plausible by channel. Account details, documents and payments usually need more. Send a one-time code through WhatsApp; when the customer types it back correctly, record a verify with method otp_whatsapp and level V3. The level applies to this conversation only, and an OTP on WhatsApp also proves possession of that number.
verify is sent immediately and drops the cached pack of that conversation. The next read comes back with the items the policy released at V3; before that, the pack said how many were withheld in withheld, so the agent knows verifying is worth asking for. A level above the ceiling of your source comes back as the item error verification_not_allowed.

5. Send voice notes and attachments by reference

Media never travels inside an event. Reserve an upload with the subject it belongs to, PUT the bytes to the presigned URL with exactly the upload_headers of the answer (the store refuses anything else), then send the event with media_ref and the SHA-256. The SDKs do the first two steps in upload_media() and uploadMedia(). For voice notes, send your own transcript in transcript, with stt_confidence.

6. Record your agent’s answers and the handoffs

Record outbound messages with conversation.agent() (the WhatsApp message id as idempotency_key when you have it), attendant messages with human_agent() in Python or human() in TypeScript, and internal notes with visibility="internal": the customer never saw them, and the pack marks them as such. When the conversation moves to a person, record the handoff so the measurement can tell whether the destination read the context.

Next steps

Events and the batch

idempotency, ordering and per-item errors.

Identity and verification

assertions, merges and the levels.

Voice agents

the call at 2:07 pm.

Send a batch

the full request and response.