Coexistence
Keep using the WhatsApp Business app on a number the inbox also works: Meta's seven limits, echoes, agent pause and history import.
Coexistence lets a business keep the WhatsApp Business app on its phone while the same number works through the Cloud API, so the owner answers from the phone and the team (and the AI) answers from your inbox. Sendo stores what the owner sends from the phone, pauses the AI where the owner stepped in and, if you want, imports the phone’s chat history.
Meta’s requirements#
- The number is onboarded through Embedded Signup with the WhatsApp Business app flavour (
featureType: "whatsapp_business_app_onboarding",sessionInfoVersion: "3"or later). The oldercoexistencevalue is deprecated, and Embedded Signup v2 is retired around October 15, 2026. - You are a Meta Solution Partner or Tech Provider, with Embedded Signup session logging on (see Embedded Signup).
- The business uses a recent WhatsApp Business app (the minimum version is the first limit).
- Your app subscribes to three extra webhook fields:
smb_message_echoes,historyandsmb_app_state_sync(next tomessages). - Sendo doesn’t call
registerfor a coexistence number (the phone app keeps it registered), and the account is stored withcoexistence = true.
The seven limits#
Each limit is marked pending confirmation against Meta’s own docs until it is verified.
- WhatsApp Business app 2.24.17 or newer. Pending confirmation against Meta’s own docs.
- The phone app must be opened at least every 13 days or the account is deactivated. This is the one that bites: if the admin forgets during a vacation, the number goes down and your app takes the blame. Put a reminder on someone’s calendar. Pending confirmation against Meta’s own docs.
- Up to 6 months of history syncs. Attachments older than 14 days before onboarding may arrive without a usable file. Pending confirmation against Meta’s own docs.
- Groups don’t sync and aren’t supported through the Cloud API in this flow. Pending confirmation against Meta’s own docs.
- Disappearing, view-once and live-location messages are disabled in individual chats after onboarding. Pending confirmation against Meta’s own docs.
- No verified badge with coexistence. Pending confirmation against Meta’s own docs.
- 20 messages per second combined between the app and the API, a real limit for broadcasts. Pending confirmation against Meta’s own docs.
Other things to know#
- 24-hour window: only messages received after onboarding open it. Messages sent from the phone app don’t open or extend the window.
- History sync: contacts and history can each be synced once, within 24 hours of onboarding. Media in history arrives after the messages.
- Not available through the Cloud API either: broadcast lists (existing ones become read-only), voice and video calls, catalogs/orders/status, quick replies, labels, greeting and away messages, business profile settings and Channels.
- Companion devices: up to four; onboarding unlinks the existing ones (WhatsApp for Windows and Wear OS aren’t supported).
- The number can’t be deregistered through the API while it is shared; the business disconnects it from the app.
What Sendo does#
| Webhook field | Sendo |
|---|---|
smb_message_echoes | Each message the owner sent from the phone is stored as an outbound message: sender human, senderRef: "business_app", metadata.origin: "business_app", status sent, its Meta id. The inbox shows it as "Desde el teléfono". A repeated echo is stored once. |
history | With importHistory, each message is stored with metadata.imported = true (the owner’s with senderRef: "business_app"). Imported messages never count as unread, never open the window and never start an AI turn. Without it, the webhook is acknowledged and ignored. |
smb_app_state_sync | Acknowledged (contact sync isn’t stored yet). Subscribe anyway: Meta requires it for coexistence. |
An echo updates the conversation’s last activity and clears “awaiting reply”, but never last_inbound_at or the window. It creates the contact and conversation if the owner started the chat from the phone. It doesn’t fire the message event (that one is for inbound messages).
The AI steps aside#
When the owner answers from the phone in a conversation the AI is handling (status agent), Sendo pauses the AI on that conversation so the owner and the AI never double-answer. It emits conversation.agent_paused with by: "business_app". Later inbound messages are stored, count as unread and keep the window open, but the AI runs no turns until the conversation is resumed from the inbox. Turn it off with pauseAgentOnPhoneReply: false.
A reply the AI was already writing when the echo arrived may still go out. This is a known limit: the pause stops the next turn, not one in flight.
sendo.on("conversation.agent_paused", ({ conversation, paused, by }) => {
if (by === "business_app" && paused) {
console.log(`The owner answered ${conversation.id} from the phone; the AI is paused there.`);
}
});Config#
| Option | Default | Notes |
|---|---|---|
coexistence.pauseAgentOnPhoneReply | true | Pause the AI on a conversation when the owner replies from the phone app. |
coexistence.importHistory | false | Store history webhooks as imported messages. |
export const sendo = createSendo({
adapter: memoryAdapter(),
appSecret: process.env.SENDO_META_APP_SECRET ?? "",
verifyToken: process.env.SENDO_VERIFY_TOKEN ?? "",
resolveToken: (account) => process.env[account.tokenRef],
coexistence: {
// An owner's reply from the phone pauses the AI on that conversation (default true).
pauseAgentOnPhoneReply: true,
// Store the phone app's chat history when Meta sends it (default false).
importHistory: true,
},
});Known gaps#
- Imported messages keep Meta’s timestamp in
sent_at(incoming) but are ordered in the thread by when Sendo stored them. - Media inside echoes is downloaded like inbound media; media inside history is not (Meta sends it separately and only for 14 days).
- Edits and revokes made from the phone aren’t applied to stored messages yet.