Omnichannel Messaging
Connect WhatsApp, Facebook Messenger, and Instagram to Sonzai agents, observe live threads, and hand conversations to human operators when needed.
Omnichannel messaging lets a Sonzai agent speak through Meta channels while keeping the same personality, memory, and conversation history you use in web or mobile chat. You connect the channel once, map it to an agent persona, and then use the Conversations API to inspect threads, subscribe to events, or take over a live exchange.
Connect a Meta channel
There are two setup paths:
- Bring Your Own App — primary path. You create and own the Meta app, business assets, webhook verify token, and access token. Sonzai stores the channel credentials you provide and delivers messages through that app.
- Embedded Signup — a simpler one-click flow from the Sonzai console. It is currently rolling out; use it when it appears for your tenant and you do not need to manage the Meta app yourself.
Bring Your Own App
Use this flow for WhatsApp, Facebook Messenger, or Instagram.
- In Meta for Developers, create or open the app you want to use for this channel.
- Add the product for the channel: WhatsApp, Messenger, or Instagram.
- Connect the required business asset:
- WhatsApp: a WhatsApp Business Account and phone number.
- Messenger: the Facebook Page that will receive messages.
- Instagram: an Instagram professional account connected to a Facebook Page.
- In the Meta product's webhook settings, set the callback URL to
https://api.sonz.ai/webhooks/channels/meta. - Create a verify token value. Use a random, tenant-owned string and store it securely. Paste the same value into Meta and the Sonzai console.
- Subscribe the webhook to message delivery fields for the selected product.
- Generate an access token with permissions for the selected asset. Use a long-lived system user or page token where Meta supports it.
- In the Sonzai console, open Channels, choose Add channel, then select the Meta channel and Bring Your Own App.
- Paste the values requested by the console:
- App ID
- App Secret
- Phone Number ID for WhatsApp, Page ID for Messenger, or Instagram Account ID for Instagram
- Access Token
- Verify Token
- Choose the agent routing target, save the channel, and send a test message from the connected account.
Credential handling
Treat the App Secret, Access Token, and Verify Token as credentials. Do not commit them to source control or send them to browser clients. Rotate the token in Meta and update the Sonzai channel if it is exposed.
Channel routing
Each channel maps to an agent persona. You can route all messages from one channel to a single default agent, or split traffic by tier when your console has tiers enabled. For example, a WhatsApp support channel can route free-tier users to a triage persona and paid-tier users to a concierge persona.
If a message does not match a configured channel or tier, Sonzai uses the channel's default agent. If no default agent is configured, the conversation is left unrouted and a conversation.unrouted webhook event is emitted so your backend can assign a route or alert an operator.
Conversations and observability
Use the Conversations API to list, search, inspect, and stream threads across WhatsApp, Messenger, Instagram, and any other channel connected to the tenant.
List and search threads
REST:
curl -H "Authorization: Bearer $SONZAI_API_KEY" \
"https://api.sonz.ai/conversations?channel=whatsapp&status=open&q=refund&limit=25"SDK:
const conversations = await client.conversations.list({
channel: "whatsapp",
status: "open",
query: "refund",
agentId: "agent-id",
limit: 25,
});
for (const thread of conversations.items) {
console.log(thread.id, thread.channel, thread.agentId, thread.lastMessageAt);
}View a thread
REST:
curl -H "Authorization: Bearer $SONZAI_API_KEY" \
"https://api.sonz.ai/conversations/conversation-id"
curl -H "Authorization: Bearer $SONZAI_API_KEY" \
"https://api.sonz.ai/conversations/conversation-id/messages?limit=50"SDK:
const thread = await client.conversations.get("conversation-id");
const messages = await client.conversations.messages("conversation-id", {
limit: 50,
});
console.log(thread.participant, messages.items.at(-1)?.text);Stream a live conversation
Use stream when your console, CRM, or support desk needs live updates without polling.
for await (const event of client.conversations.stream("conversation-id")) {
console.log(event.type, event.message?.text);
}Webhook events
Subscribe to conversation events when your backend needs durable observability or workflow automation. Verify Sonzai webhook signatures before processing events; the signing model is the same as the Webhooks page.
| Event | Fires when |
|---|---|
conversation.started | A new channel conversation is created and routed to an agent |
conversation.message | A user, agent, or operator message is added to the thread |
conversation.takeover.started | A human operator takes over the conversation |
conversation.takeover.released | The operator releases the conversation back to the agent |
conversation.message.failed | A message could not be delivered to the external channel |
conversation.unrouted | Sonzai received a message that did not match a configured channel, tier, or default agent |
Event payloads include the conversation ID, channel, agent ID when routed, participant identifiers, and the message or failure details relevant to the event. Do not rely on webhook delivery as the only source of truth; use the Conversations API to re-read the thread after receiving an event.
Human takeover
Human takeover pauses autonomous agent replies for a live conversation while an operator responds through the same agent persona. The end user still sees messages from the channel identity, and the agent keeps the transcript in memory. When takeover is released, the agent remembers what the user and operator said during the takeover window.
REST flow
# 1. Take over the live conversation
curl -X POST "https://api.sonz.ai/conversations/conversation-id/takeover" \
-H "Authorization: Bearer $SONZAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"operator_id":"op-123","reason":"billing escalation"}'
# 2. Send a reply as the agent persona
curl -X POST "https://api.sonz.ai/conversations/conversation-id/messages" \
-H "Authorization: Bearer $SONZAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"operator_id":"op-123","text":"I can help check that invoice now."}'
# 3. Release back to autonomous agent handling
curl -X DELETE "https://api.sonz.ai/conversations/conversation-id/takeover" \
-H "Authorization: Bearer $SONZAI_API_KEY"SDK flow
await client.conversations.takeOver("conversation-id", {
operatorId: "op-123",
reason: "billing escalation",
});
await client.conversations.sendAsAgent("conversation-id", {
operatorId: "op-123",
text: "I can help check that invoice now.",
});
await client.conversations.release("conversation-id", {
operatorId: "op-123",
});Operator voice
Operator replies are sent as the agent's persona, not as a separate human identity. Keep your support desk copy aligned with the agent's tone, because the transcript becomes part of the agent's future context.