> For the complete documentation index, see [llms.txt](https://docs.otima.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otima.io/whatsapp/recursos-de-api/orquestracao-de-conversas.md).

# Orquestração de conversas

Configure o roteamento, controle a propriedade das conversas e processe webhooks do WhatsApp Business.

A Orquestração de Conversas permite que várias integrações atendam o mesmo número do WhatsApp.

Ela define qual integração controla cada conversa e pode responder ao usuário.

Use-a para transferir ou liberar o controle e acompanhar os eventos de atendimento.

A Ótima encaminha sua integração à Meta. Atualize o estado atual após o evento de confirmação.

{% hint style="success" %}
Responda apenas quando sua integração possuir o controle(**`control_passed`**) da conversa. \
\
Eventos **`standby`** servem apenas para observar a conversa e manter seus registros atualizados.
{% endhint %}

### Pré-requisitos

Você precisa de um número WhatsApp Business ativo e de um token válido.

Consulte [Autenticação](/provedor/canais-de-comunicacao/whatsapp-business/recursos-de-api/autenticacao.md) para gerar as credenciais.

A URL base é:

```http
https://sync-core-api.otima.io/whatsapp/provider
```

Use estes headers em todas as requisições:

```http
Authorization: Bearer TOKEN_DA_INTEGRACAO
x-workspace-uuid: UUID_DO_WORKSPACE
X-API-Version: 2.0.0
Accept: application/json
Content-Type: application/json
```

Use telefone com DDI, com ou sem pontuação, ou o `wa_id` do número como `{entity_id}`.

```
5511999999999
+55 11 99999-9999
1200149673189278
```

O número deve pertencer ao workspace informado. A API sempre encaminha o Phone Number ID oficial à Meta.

### Conceitos principais

<table><thead><tr><th width="252.8177490234375">Conceito</th><th>Comportamento</th></tr></thead><tbody><tr><td><code>idle</code></td><td>Nenhuma integração possui a conversa. A configuração de entrada define o próximo handler.</td></tr><tr><td><code>owned</code></td><td>Uma integração possui a conversa e pode enviar mensagens de serviço.</td></tr><tr><td><code>standby</code></td><td>A integração observa a conversa. Não responda ao evento.</td></tr><tr><td><code>handover</code></td><td>A Meta transfere o controle entre integrações e confirma por webhook.</td></tr><tr><td><code>escalation</code></td><td>Uma integração autorizada pode assumir a conversa segundo a configuração Meta.</td></tr></tbody></table>

A primeira mensagem de serviço em uma thread `idle` concede a propriedade ao handler. A thread volta a `idle` após liberação ou inatividade definida pela Meta.

Templates de Marketing, Utility e Authentication não alteram a propriedade. Mensagens de serviço dependem da propriedade atual.

### Fluxo de integração

{% stepper %}
{% step %}

#### Configure o webhook

Configure um endpoint HTTPS no número WhatsApp. Ele recebe todos os eventos necessários.
{% endstep %}

{% step %}

#### Habilite o roteamento

Envie `PUT /{entity_id}/conversation-routing` com os três campos obrigatórios de webhook.
{% endstep %}

{% step %}

#### Confirme a configuração

Use `GET /{entity_id}/conversation-routing` para validar settings e endpoint.
{% endstep %}

{% step %}

#### Processe os eventos

Percorra todos os itens de `entry[]` e `changes[]`. Trate `messages`, `messaging_handovers` e `standby` pelo valor de `field`.
{% endstep %}

{% step %}

#### Aguarde a confirmação

Após `thread_control`, aguarde o webhook antes de atualizar a propriedade.
{% endstep %}
{% endstepper %}

### Configuração do roteamento

#### Consultar configuração

```http
GET https://sync-core-api.otima.io/whatsapp/provider/{entity_id}/conversation-routing
```

Retorna configurações, endpoint, threads rastreadas e eventos recentes. Esta rota não recebe corpo JSON.

```bash
curl --request GET \
  'https://sync-core-api.otima.io/whatsapp/provider/5511999999999/conversation-routing' \
  --header 'Authorization: Bearer TOKEN_DA_INTEGRACAO' \
  --header 'x-workspace-uuid: UUID_DO_WORKSPACE' \
  --header 'X-API-Version: 2.0.0' \
  --header 'Accept: application/json'
```

**Resposta — HTTP 200**

```json
{
  "data": {
    "phone_number_id": "1200149673189278",
    "whatsapp_number_id": "UUID_INTERNO_DO_NUMERO",
    "settings": {
      "enabled": true,
      "role": "primary",
      "standby_enabled": true,
      "webhook_fields": [
        "messages",
        "messaging_handovers",
        "standby"
      ]
    },
    "webhook": {
      "endpoint": "https://customer.example/webhooks/whatsapp",
      "source": "whatsapp_numbers.webhook",
      "configured": true,
      "required_fields": [
        "messages",
        "messaging_handovers",
        "standby"
      ]
    },
    "threads": [],
    "events": []
  }
}
```

Quando não houver webhook, `endpoint` será `null` e `configured` será `false`.

#### Habilitar ou atualizar o roteamento

```http
PUT https://sync-core-api.otima.io/whatsapp/provider/{entity_id}/conversation-routing
```

```json
{
  "enabled": true,
  "role": "primary",
  "standby_enabled": true,
  "webhook_fields": [
    "messages",
    "messaging_handovers",
    "standby"
  ]
}
```

| Valor de `role` | Uso                                          |
| --------------- | -------------------------------------------- |
| `primary`       | Recebe normalmente o primeiro atendimento.   |
| `service`       | Recebe o controle em etapas especializadas.  |
| `escalation`    | Assume escalonamentos autorizados pela Meta. |

Mantenha os três valores em `webhook_fields`. Uma lista incompleta retorna HTTP `422`.

```json
{
  "message": "Conversation Routing exige a assinatura dos três campos da Meta.",
  "errors": {
    "webhook_fields": [
      "Ausentes: messaging_handovers, standby"
    ]
  }
}
```

### Controle de thread

#### Rota oficial da Meta

```http
POST https://sync-core-api.otima.io/whatsapp/provider/business/whatsapp/phone_numbers/{entity_id}/thread_control
```

Esta rota preserva o corpo recebido. Use `X-API-Version: 1.0.0`.

Use `release` para devolver a thread a `idle`. Use `pass` para transferir ao próximo participante elegível. A Meta define esse participante.

**Liberar conversa**

```json
{
  "messaging_product": "whatsapp",
  "action": "release",
  "to": "+5511988888888"
}
```

**Transferir conversa**

```json
{
  "messaging_product": "whatsapp",
  "action": "pass",
  "to": "+5511988888888",
  "metadata": "O cliente precisa de atendimento especializado"
}
```

Campos nativos adicionais também são preservados:

```json
{
  "messaging_product": "whatsapp",
  "action": "pass",
  "to": "+5511988888888",
  "recipient": "BUSINESS_SCOPED_USER_ID",
  "metadata": "Retornar o atendimento ao agente da Meta",
  "control_pass": {
    "target_role": "ai_agent"
  }
}
```

Uma resposta HTTP `200` confirma o aceite. Aguarde o webhook para atualizar o estado local.

#### Caminhos

```http
POST https://sync-core-api.otima.io/whatsapp/provider/{entity_id}/thread_control
POST https://sync-core-api.otima.io/whatsapp/provider/{entity_id}/conversation-routing/thread-control
```

### Webhooks

A funcionalidade usa o webhook já configurado no número. Não cadastre outra URL.

O envelope e os campos Meta permanecem inalterados. O endpoint deve aceitar `POST`, retornar `2xx` rapidamente e processar reentregas com idempotência.

```json
{
  "object": "whatsapp_business_account",
  "entry": [
    {
      "id": "WABA_ID",
      "time": 1788000000,
      "changes": [
        {
          "field": "messages",
          "value": {}
        }
      ]
    }
  ]
}
```

| `change.field`        | Papel                         | Ação                                             |
| --------------------- | ----------------------------- | ------------------------------------------------ |
| `messages`            | Handler ativo                 | Processe e responda conforme a regra de negócio. |
| `messaging_handovers` | Participante da transferência | Atualize a propriedade e processe o contexto.    |
| `standby`             | Observador passivo            | Sincronize registros. Não responda.              |

#### Mensagem recebida

Em `messages`, use `value.messages[]` para identificar mensagens recebidas. `conversation_context` é opcional e fica ao lado de `messages`.

```json
{
  "field": "messages",
  "value": {
    "metadata": {
      "phone_number_id": "1200149673189278"
    },
    "contacts": [
      {
        "wa_id": "5511988888888",
        "profile": {
          "name": "Maria"
        }
      }
    ],
    "messages": [
      {
        "from": "5511988888888",
        "id": "wamid.MESSAGE_ID",
        "timestamp": "1788000010",
        "type": "text",
        "text": {
          "body": "Preciso de ajuda com meu pedido"
        }
      }
    ],
    "conversation_context": {
      "type": "summary",
      "summary": {
        "text": "A cliente precisa de ajuda com o pedido 123."
      }
    }
  }
}
```

Use `messages[].id` como chave idempotente. Status em `value.statuses[]` não são novas mensagens e não devem iniciar uma resposta.

#### Controle recebido

`control_passed` confirma que sua integração recebeu o controle.

```json
{
  "field": "messaging_handovers",
  "value": {
    "sender": {
      "phone_number": "5511988888888"
    },
    "recipient": {
      "phone_number_id": "1200149673189278"
    },
    "timestamp": "1788000030",
    "type": "control_passed",
    "control_passed": {
      "previous_owner_app_id": "PREVIOUS_APP_ID",
      "previous_owner_app_role": "META_AI",
      "metadata": "Escalonamento solicitado pelo cliente",
      "conversation_context": {
        "type": "summary",
        "summary": {
          "text": "A cliente solicitou atendimento sobre o pedido 123."
        }
      }
    }
  }
}
```

Quando receber `control_taken`, interrompa mensagens automáticas. Permaneça passivo até um novo evento acionável.

#### Eventos standby

Eventos `standby` mantêm o histórico sincronizado. Eles podem conter `messages[]`, `message_echoes[]` e `statuses[]` dentro de `value.standby`.

```json
{
  "field": "standby",
  "value": {
    "metadata": {
      "phone_number_id": "1200149673189278"
    },
    "standby": {
      "messages": [
        {
          "from": "5511988888888",
          "id": "wamid.STANDBY_MESSAGE_ID",
          "timestamp": "1788000050",
          "type": "text",
          "text": {
            "body": "Mensagem observada em standby"
          }
        }
      ]
    }
  }
}
```

Em um echo de template, `message.template` contém os valores enviados. O objeto irmão `template` contém a definição e os placeholders. Combine ambos para reconstruir o texto.

### Contexto da conversa

O contexto pode existir nestes caminhos:

```
messages:             change.value.conversation_context
messaging_handovers:  change.value.control_passed.conversation_context
```

A propriedade é omitida quando não houver contexto.

#### Summary

```json
{
  "type": "summary",
  "summary": {
    "text": "A cliente informou o pedido 123 e precisa alterar o endereço."
  }
}
```

Disponibilize `summary.text` ao atendimento. O resumo não substitui um histórico auditável.

#### History

```json
{
  "type": "history",
  "history": {
    "items": [
      {
        "sender_type": "user",
        "id": "wamid.HISTORY_USER_ID",
        "timestamp": "1787999900",
        "message": {
          "type": "text",
          "text": {
            "body": "Qual é o status do pedido 123?"
          }
        }
      },
      {
        "sender_type": "business",
        "id": "wamid.HISTORY_BUSINESS_ID",
        "timestamp": "1787999950",
        "message_echo": {
          "message": {
            "type": "text",
            "text": {
              "body": "O pedido 123 já foi enviado."
            }
          }
        }
      }
    ]
  }
}
```

Percorra `history.items[]` na ordem recebida. Use `item.message` para `user` e `item.message_echo.message` para `business`.

### Exemplo de handler

```javascript
app.post('/webhooks/whatsapp', async (request, response) => {
  response.sendStatus(200);

  for (const entry of request.body.entry ?? []) {
    for (const change of entry.changes ?? []) {
      if (change.field === 'messages') {
        await processMessagesOrStatuses(change.value);
      } else if (change.field === 'messaging_handovers') {
        await processHandover(change.value);
      } else if (change.field === 'standby') {
        await observeWithoutReplying(change.value);
      }
    }
  }
});
```

### Checklist de homologação

| Cenário                                     | Resultado esperado                                 |
| ------------------------------------------- | -------------------------------------------------- |
| `standby` com mensagem, echo ou status      | Sincronizar o evento sem responder.                |
| `control_passed` sem contexto               | Assumir o atendimento normalmente.                 |
| `control_passed` com `summary` ou `history` | Disponibilizar e percorrer o contexto.             |
| `messages` com ou sem contexto              | Processar a mensagem normalmente.                  |
| `thread_control` com `pass` ou `release`    | Preservar o corpo e aguardar o webhook.            |
| Webhook duplicado ou fora de ordem          | Não duplicar respostas ou reverter estado recente. |

### Respostas e erros

| Código         | Situação                                           |
| -------------- | -------------------------------------------------- |
| `400`          | Workspace ausente ou requisição inválida.          |
| `401`          | Credencial ausente, inválida ou expirada.          |
| `404`          | Número não encontrado no workspace.                |
| `422`          | Configuração local inválida.                       |
| Status da Meta | `thread_control` devolve status e corpo originais. |

Em falhas de Thread Control, trate o corpo Meta como fonte principal de diagnóstico. Não converta recusas de permissão em sucesso.

### Referências oficiais

* [Meta Business Agent — Getting started](https://developers.facebook.com/documentation/meta-business-agent/get-started)
* [Meta Business Agent — Thread Control Cloud API](https://developers.facebook.com/documentation/meta-business-agent/reference/operate/thread-control-cloud-api)
* [WhatsApp Business Platform — webhook `standby`](https://developers.facebook.com/documentation/business-messaging/whatsapp/webhooks/reference/standby)
