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

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

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