> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kapso.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversation routing

> How Kapso behaves when a WhatsApp number is shared with other apps or Meta's AI agent

A WhatsApp number can be connected to Kapso and to other apps at the same time, for example a helpdesk or Meta's AI agent. Meta's [conversation routing](https://developers.facebook.com/documentation/business-messaging/whatsapp/conversation-routing/overview) decides which app handles each conversation. Kapso follows those decisions.

Numbers connected only to Kapso work as before. Nothing on this page applies to them.

## How routing works

Meta tracks one thread per business number and customer. At any time the thread is:

* **Handled by Kapso**: Kapso can send regular (Service) messages.
* **Handled by another app**: Kapso receives copies of the conversation but cannot send regular messages.
* **Idle**: no app handles it. Meta routes the customer's next message using your routing settings.

A thread returns to idle after 24 hours without customer messages. You configure routing in Meta Business Suite, not in Kapso. See [Meta's setup guide](https://developers.facebook.com/documentation/business-messaging/whatsapp/conversation-routing/get-started).

## Setup requirements

Your business must accept the **Meta Business Agents and Platform Terms of Service** to use standby visibility, designate an escalation partner, or use thread control. See [Meta's terms requirement](https://developers.facebook.com/documentation/business-messaging/whatsapp/conversation-routing/get-started/#terms-of-service-requirement).

Basic routing to the configured primary app works without accepting these terms. Until you accept, standby copies and thread control are unavailable; Kapso cannot observe threads owned by another app.

## When Kapso treats a number as shared

Kapso marks a number as shared the first time it sees another app on it: a copy of another app's conversation, a handover from Meta, or a successful thread control call. An admin can also mark a number as shared, or clear the mark, in the number's settings under **Conversation routing (Meta)**. If another app is still connected after the mark is cleared, its next message marks the number as shared again.

Numbers with [Meta Business Agent](/docs/whatsapp/meta-business-agents) enabled are always shared.

## Messages from other apps

Kapso stores the other app's side of the conversation so your team sees the whole thread:

* Customer messages the other app handles, and the other app's replies, are saved with `passive: true`. The other app's replies have `origin: "other_app"`.
* Passive messages never start message-triggered workflows or agents, so Kapso doesn't reply to them.
* Passive messages count toward your Kapso message usage. Status updates for the other app's messages never create Meta charges on your Kapso bill.
* In the Inbox they mark the conversation unread like any other message.

## Sending messages

Templates can always be sent. Regular messages go out only when Kapso handles the thread.

If Kapso doesn't handle the thread, the send fails with `409 Conflict` before reaching Meta:

```json theme={null}
{
  "error": "Another app controls this conversation. Automated Service messages are blocked; templates are still allowed.",
  "code": "thread_not_owner",
  "thread_error": "thread_not_owner"
}
```

| `code` | Meaning |
| - | - |
| `thread_not_owner` | Another app handles the thread, or no app does |
| `thread_ownership_unknown` | Kapso hasn't seen enough to know who handles the thread |
| `meta_business_agent_control` | Same as above on numbers with Meta Business Agent; `thread_error` has the specific reason |

To send anyway and take the thread, add `X-Kapso-Take-Control: true` to a `/{phone_number_id}/messages` request. Meta only accepts this when Kapso is your escalation app.

Meta returns error `131070` when an app that is neither the thread owner nor the designated escalation partner sends a Service message. This also applies to idle threads: no owner does not mean any app can reply.

The proxy returns Meta's error unchanged. If Kapso had recorded itself as the owner, this rejection clears that claim to `unknown` and blocks further automated Service sends, unless a newer ownership event has already arrived. It does not identify the current owner.

Don't automatically retry the send. Have the current owner pass control to Kapso, or configure Kapso as the escalation partner and explicitly take over. An idle thread can also be routed by the customer's next message. Marketing, utility, and authentication templates do not require ownership; their other sending requirements still apply.

## Thread control

Pass, release or take a thread through the proxy. The body is forwarded to Meta unchanged, and Meta's response comes back unchanged.

```bash theme={null}
curl -X POST https://api.kapso.ai/meta/whatsapp/v24.0/{phone_number_id}/thread_control \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "messaging_product": "whatsapp",
    "to": "15551234567",
    "action": "pass",
    "control_pass": { "target_role": "escalation" },
    "metadata": "Customer asked for a human"
  }'
```

* `action`: `take`, `release` or `pass`.
* Identify the customer with `to` (phone number) or `recipient` (business-scoped user ID).
* `control_pass.target_role` (pass only): `escalation`, `ai_agent`, `customer_service`, `marketing`, `utility` or `ctwa`.
* `metadata` (optional, up to 2,000 characters) reaches the app that receives the thread.

Only your escalation app can `take`. When Meta refuses, you get Meta's error, for example code `2494191` when Kapso isn't allowed to take the thread.

Meta enables each action per WhatsApp account. When an action isn't enabled, Meta answers with code `100`:

* `(#100) Pass action is not supported.` — the account can't pass threads. Use `release` instead, or ask Meta to enable passing.
* `(#100) Take action is not supported.` — the account can't take threads.
* A `not supported` error naming the target role — that `control_pass.target_role` isn't available on the account.

`release` returns the thread to Meta's routing, which is the fallback when passing isn't enabled.

Kapso records every call and updates the conversation's state. It returns `409` while another action for the same thread is still in progress, and `502` when the result is unknown because Meta didn't answer. Don't retry automatically after a `502`: the action may have gone through.

## Webhooks

On shared numbers, subscribe to these events on the number's webhook:

* [`whatsapp.thread.ownership_changed`](/docs/platform/webhooks/message-events#whatsapp-thread-ownership_changed): the app handling a thread changed.
* [`whatsapp.thread.standby`](/docs/platform/webhooks/message-events#whatsapp-thread-standby): a passive message arrived, another app sent a message, or a status update arrived for one of the other app's messages.

`whatsapp.message.*` events never fire for passive messages, so reply bots don't answer conversations another app handles. Numbers with Meta Business Agent keep sending them, marked `passive: true`.

Conversation payloads on shared numbers include a `thread` object in `conversation.kapso.thread` (`conversation.thread` in v1 payloads):

```json theme={null}
"kapso": {
  "thread": {
    "ownership": "other_app",
    "owner_role": "customer_service",
    "passive": true
  }
}
```

`ownership` is `this_app`, `other_app`, `idle` or `unknown`. `passive` is `true` while the conversation only has passive messages.

## Workflows

The `whatsapp.thread.control_received` [event trigger](/docs/flows/triggers#whatsapp-event-trigger) starts a workflow when another app passes a thread to Kapso. The workflow can reply right away.

Conversation triggers such as `whatsapp.conversation.created` also fire for conversations that start as passive. Skip them when `{{system.event.conversation.kapso.thread.ownership}}` is `other_app`.

## Inbox

* When another app, or no app, handles a conversation, the message box is replaced by a status bar with **Take over** and **Send a template**. Your draft is kept.
* When Kapso handles a shared conversation, the toolbar menu offers **Transfer to** and **Release**. Transfer lists the escalation app and Meta's AI agent first; the entry-point roles (customer service, marketing, utility, click-to-WhatsApp ads) sit under **Other roles**.
* When Meta rejects a transfer or take over because the action isn't enabled on the account, the Inbox says so and points to Release. Other errors show Meta's own message.
* Handovers show Meta's summary when the other app provides one.
* The **Routing** tab in the conversation sidebar lists handovers and control actions.

If Meta refuses a take over, the status bar says so. Take over stays available so you can retry after changing your Meta settings.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.