
# API de Ligação de Canais

Este guia mostra como ligar canais de mensagens a uma conta utilizando a API. Foi escrito para um programador que esteja a criar uma integração ou um wrapper, pelo que se foca nos pedidos exatos, na ordem em que devem ser efetuados e nas respostas obtidas.

Existe um padrão que precisa de compreender desde o início, porque se aplica a quase todos os canais aqui presentes.

## O padrão de ligar e consultar (connect-then-poll)

A maioria dos canais não pode ser ligada com uma única chamada de API. Ligar o WhatsApp, o Instagram ou o Messenger significa que o titular da conta tem de iniciar sessão na sua própria conta de fornecedor e aprovar o acesso. **Não existe um caminho headless (totalmente automatizado)** para essa aprovação - uma pessoa real tem de abrir um URL num navegador ou ler um código QR com o seu telemóvel.

Portanto, o fluxo é sempre:

1. **Inicie a ligação** com um `POST`. A resposta fornece-lhe um URL para abrir ou um código QR para apresentar.
2. **Entregue isso ao utilizador final** - abra o URL no seu navegador ou apresente o código QR no ecrã para que este o possa ler.
3. **Consulte o endpoint de estado** com `GET` num curto intervalo (a cada poucos segundos) até que o estado atinja um estado de ligado.

O trabalho da sua integração é conduzir esse ciclo: mostrar o URL ou o QR e, em seguida, consultar até estar concluído. Planeie a sua interface de utilizador em torno da consulta - um indicador de carregamento com uma mensagem do tipo "a aguardar que termine no seu navegador" funciona bem.

::: note
**Nota:** Antes de começar, certifique-se de que o acesso à API está ativado no plano e que possui uma chave de API. Consulte [Acesso à API](../integrations/api-access.md) para saber como gerar uma. Todos os pedidos abaixo utilizam o URL base `https://api.youraiconnector.com/v1` e deve autenticar cada pedido. Consulte [Autenticação](authentication.md) para as quatro formas aceites - os exemplos aqui utilizam o cabeçalho `X-API-Key`, com um exemplo cURL por página a mostrar a forma de consulta `?apiKey=` mais simples.
:::


---

## Instagram + Messenger (Meta)

O Instagram e o Messenger são ligados em conjunto num único fluxo, porque ambos funcionam numa Página de Facebook. O titular da conta autoriza através do Facebook, você obtém a lista de Páginas que gere e escolhe qual a Página a ligar.

### Passo 1 - Iniciar a ligação Instagram + Messenger

```
POST /channels/meta/connect
```

Isto devolve um URL de consentimento. Não são enviadas credenciais neste pedido - a ligação é autorizada inteiramente no navegador.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/meta/connect?apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/connect", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Open data.oauth_url in the end user's browser.
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/channels/meta/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Open data["oauth_url"] in the end user's browser.
```

**Resposta**

```json
{
  "success": true,
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "connect_url": "https://api.youraiconnector.com/v1/channels/meta/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000,
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Abra o `oauth_url` no navegador do utilizador final para que este possa iniciar sessão no Facebook e aprovar o acesso. A tentativa de ligação expira em `expires_at` (cerca de 30 minutos) - se expirar, comece de novo. Trate o `state_token` como um segredo de curta duração e não o registe nos logs.

### Opção mais fácil para Instagram + Messenger: entregar `connect_url`

A resposta também inclui um `connect_url` pronto a usar: uma página alojada que executa todo o fluxo para o titular da conta. Eles abrem-na, iniciam sessão no Facebook e, quando têm mais do que uma Página, é apresentada a lista que lhes permite escolher qual ligar - depois, a página comunica o sucesso por si própria. Forneça esta ligação ao titular da conta em vez de abrir o `oauth_url` por si próprio, criar um seletor de Páginas e efetuar consultas. A ligação funciona durante cerca de 30 minutos (`connect_url_expires_at`); se expirar, inicie uma nova ligação. Os passos manuais abaixo destinam-se a integrações que pretendem conduzir o fluxo e apresentar o seletor de Páginas por conta própria.

### Passo 2 - Consultar o estado até as páginas carregarem

```
GET /channels/meta/status
```

Após o utilizador concluir o início de sessão no Facebook, consulte este endpoint a cada poucos segundos. O campo `status` percorre estes passos:

| `status` | Significado |
|---|---|
| `pending` | Consentimento ainda não concluído. Continue a aguardar. |
| `token_received` | Autorizado, mas a lista de Páginas ainda está a carregar. |
| `pages_loaded` | As Páginas estão disponíveis - avance para o passo 3. |
| `connected` | Uma Página foi selecionada e o canal está ativo. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/status", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
// Poll until data.status === "pages_loaded".
```

**Python**

```python
res = requests.get(
    "https://api.youraiconnector.com/v1/channels/meta/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "pages_loaded".
```

**Resposta (assim que as páginas tiverem carregado)**

```json
{
  "success": true,
  "status": "pages_loaded",
  "pages": [
    {
      "id": "1234567890",
      "name": "My Business Page",
      "category": "Local business",
      "instagram_business_account": {
        "id": "17890000000000000",
        "username": "mybusiness"
      }
    }
  ],
  "selected_page": null
}
```

### Passo 3 - Listar as páginas (opcional)

Se preferir obter a lista de Páginas separadamente (por exemplo, para renderizar um seletor), utilize:

```
GET /channels/meta/pages
```

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/pages" \
  -H "X-API-Key: YOUR_API_KEY"
```

Retorna a mesma matriz `pages` que o endpoint de estado. (O endpoint `status` já inclui as páginas, pelo que esta chamada é apenas uma conveniência.)

### Passo 4 - Selecionar a página a ligar

```
POST /channels/meta/select-page
```

Envie o `page_id` da Página que o utilizador escolheu. A conta de Instagram associada a essa Página é ligada automaticamente; apenas necessita do objeto `instagram` se pretender substituir a conta de Instagram a utilizar.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/meta/select-page" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "page_id": "1234567890" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/meta/select-page", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ page_id: "1234567890" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/meta/select-page",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"page_id": "1234567890"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "page_id": "1234567890",
  "instagram_business_account_id": "17890000000000000"
}
```

O canal está agora ligado. Um `GET /channels/meta/status` de seguimento reportará `status: "connected"`.

### Listar as publicações da página ligada

```
GET /channels/meta/posts?platform=instagram
```

Devolve as publicações recentes da página que ligou - conteúdos do Instagram ou publicações do Facebook. É a partir daqui que cria um seletor quando configura um Ponto de Entrada que reage a comentários numa publicação específica.

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `platform` | Sim | `instagram` ou `facebook`. Qualquer outro valor devolve um `400`. |
| `limit` | Não | Quantas publicações devolver, `1`-`50`. O padrão é `25`. |
| `after` | Não | Cursor para a página seguinte - passe o valor `nextCursor` da resposta anterior. |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/meta/posts?platform=instagram&limit=25" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "connected": true,
  "platform": "instagram",
  "posts": [
    {
      "id": "17900000000000000",
      "caption": "New spring menu is live",
      "thumbnailUrl": "https://scontent.cdninstagram.com/...",
      "permalink": "https://www.instagram.com/p/Cxxxxxxxxxx/",
      "createdAt": "2026-05-02T09:12:00.000Z",
      "mediaType": "REELS"
    }
  ],
  "nextCursor": "QVFIUkxxxxxxxx"
}
```

`mediaType` é a etiqueta própria do Instagram (`REELS`, `FEED`, `STORY`, ou o formato - `IMAGE`, `VIDEO`, `CAROUSEL_ALBUM`); para o Facebook é sempre `POST`. `nextCursor` é `null` na última página.

Se não for possível listar nada, a chamada devolve na mesma `200` com `connected: false` e uma matriz `posts` vazia, além de um `reason` a indicar o motivo:

| `reason` | O que fazer |
|---|---|
| _(ausente)_ | Ainda não existe nenhuma página ligada - execute primeiro o fluxo de ligação. |
| `no_instagram_account` | Uma Página do Facebook está ligada, mas não existe nenhuma conta profissional do Instagram associada à mesma. As publicações do Facebook são listadas normalmente. |
| `token_expired` | A credencial da página guardada já não funciona - volte a ligar o canal. |

### Desligar Instagram + Messenger

```
DELETE /channels/meta
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/meta" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "disconnected": true }
```

Isto interrompe o encaminhamento de entrada tanto para o Instagram como para o Messenger. É idempotente - chamá-lo quando nada está ligado continua a ser bem-sucedido.

---

## WhatsApp Business

Isto liga um número oficial do WhatsApp Business. O número já deve existir na conta antes de solicitar a ligação. Tal como na Meta, o titular da conta autoriza no seu navegador e, em seguida, deve consultar o estado até que o número reporte `ONLINE`.

### Passo 1 - Iniciar a ligação WhatsApp Business

```
POST /channels/whatsapp/connect
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/whatsapp/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+14155551234" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/whatsapp/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+14155551234" }),
});
const data = await res.json();
// Open data.oauth_url in the account holder's browser.
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/whatsapp/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+14155551234"},
)
data = res.json()
# Open data["oauth_url"] in the account holder's browser.
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phone_number` | Sim | O número a ligar, no formato E.164 (por exemplo, `+14155551234`). |
| `only_waba_sharing` | Não | Restringe a autorização à partilha de uma Conta WhatsApp Business existente, ignorando a configuração de um novo remetente. O valor predefinido é `false`. |
| `retry` | Não | Executa novamente a autorização para um número cuja tentativa anterior não foi concluída. O valor predefinido é `false`. |
| `business_name` | Não | Substituição estética para o nome da empresa apresentado apenas no ecrã de consentimento (máx. 256 caracteres). Não é guardado. |
| `description` | Não | Substituição estética para a descrição da empresa apresentada apenas no ecrã de consentimento (máx. 256 caracteres). Não é guardado. |

**Resposta**

```json
{
  "success": true,
  "status": "pending",
  "oauth_url": "https://www.facebook.com/v21.0/dialog/oauth?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Abra `oauth_url` no navegador do titular da conta para autorizar. Assim que este aprovar, o registo é concluído em segundo plano.

### Passo 2 - Consultar o estado até ONLINE

```
GET /channels/whatsapp/connect/{phoneNumber}/status
```

Consulte este estado até que `status` seja `ONLINE`.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp/connect/+14155551234/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155551234");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp/connect/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "ONLINE".
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155551234")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp/connect/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "ONLINE".
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "status": "ONLINE",
  "status_reason": null,
  "live": true
}
```

O campo `status` pode ser:

| `status` | Significado |
|---|---|
| `PENDING` | Autorizado, aprovação ainda em curso. Continue a consultar. |
| `ONLINE` | Ligado e pronto a enviar. |
| `RATE_LIMITED` | Demasiadas tentativas - aguarde antes de tentar novamente. |
| `REGISTRATION_FAILED` | Não foi possível concluir a configuração. |
| `DELETED` | O registo já não existe. |

`live: true` significa que o estado foi verificado junto do fornecedor em tempo real; `false` significa que provém do último estado em cache.

### Desligar um número WhatsApp Business

```
DELETE /channels/whatsapp/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/whatsapp/+14155551234" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "phone_number": "+14155551234", "disconnected": true }
```

O número em si permanece na conta, pelo que pode voltar a ligá-lo mais tarde.

---

## WhatsApp Web

O WhatsApp Web associa um número de WhatsApp normal através da leitura de um código QR, tal como associar um dispositivo na aplicação WhatsApp. O fluxo é: iniciar a sessão, obter o código QR e mostrá-lo, e depois verificar o estado até que seja `connected`.

### Passo 1 - Iniciar uma sessão de emparelhamento WhatsApp Web

```
POST /channels/whatsapp-web/connections
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+15551230000" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/whatsapp-web/connections", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+15551230000" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+15551230000"},
)
data = res.json()
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phone_number` | Sim | O número de WhatsApp a ligar, no formato E.164. |
| `proxy_country` | Não | Código de país ISO 3166-1 alpha-2 para a região de encaminhamento. Detetado automaticamente a partir do número quando omitido. |
| `force_new` | Não | Descartar qualquer sessão existente e iniciar um novo emparelhamento. Predefinição: `false`. |
| `import_contacts` | Não | Importar os contactos existentes do dispositivo na primeira ligação. Predefinição: `false`. |
| `pause_ai_for_imported_contacts` | Não | Ao importar contactos, manter as respostas automáticas em pausa para os mesmos. Predefinição: `true`. |
| `import_existing_chats` | Não | Importar o histórico de conversas existente (requer `import_contacts: true`). Predefinição: `false`. |

**Resposta**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "session_id": "session-id",
  "status": "qr_pending",
  "connect_url": "https://api.youraiconnector.com/v1/channels/whatsapp-web/connect?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000,
  "poll_qr_path": "/v1/channels/whatsapp-web/connections/%2B15551230000/qr",
  "poll_status_path": "/v1/channels/whatsapp-web/connections/%2B15551230000/status"
}
```

### Opção mais fácil para WhatsApp Web: entregar `connect_url`

A resposta inclui um `connect_url` pronto a usar: uma página alojada que apresenta o código QR, atualiza-o automaticamente à medida que este roda e muda para uma mensagem de sucesso no momento em que o número é associado. Basta fornecer esta ligação ao titular da conta (abri-la num navegador, enviá-la ou mostrá-la como um QR/botão) e pedir-lhe que a digitalize com o WhatsApp - não precisa de obter o QR nem de consultar o estado manualmente. A ligação funciona durante cerca de 30 minutos (`connect_url_expires_at`); se expirar antes de terminarem, inicie uma nova ligação para obter uma nova.

Este é o caminho recomendado quando uma pessoa pode abrir uma ligação. Os passos manuais abaixo (obter o QR por si próprio, consultar o estado) destinam-se a integrações que pretendem renderizar o QR dentro da sua própria interface.

A resposta também lhe fornece o `poll_qr_path` e o `poll_status_path` exatos a utilizar, para que não tenha de os criar manualmente.

### Passo 2 - Obter o código QR e mostrá-lo

```
GET /channels/whatsapp-web/connections/{phoneNumber}/qr
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000/qr" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+15551230000");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/${phone}/qr`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Render data.qr_data_url as an <img src> for the user to scan.
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+15551230000")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/{phone}/qr",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Render data["qr_data_url"] for the user to scan.
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "status": "qr_pending",
  "qr_code": "2@raw-qr-payload-string...",
  "qr_data_url": "data:image/png;base64,iVBORw0KGgo...",
  "expires_at": "2026-06-10T12:05:00.000Z"
}
```

Apresente o QR para o utilizador ler com o seu telemóvel (WhatsApp > Dispositivos associados > Associar um dispositivo):

- `qr_data_url` é uma imagem pronta a usar - coloque-a diretamente numa `<img src>`.
- `qr_code` é o payload bruto se preferir gerar a imagem você mesmo.

O QR tem uma duração curta. Se chamar isto logo após iniciar a sessão, poderá obter um `404` com "QR code not available yet" - aguarde um momento e tente novamente. Se obtiver um `410` ("QR code expired"), reinicie a ligação para obter um novo código.

### Passo 3 - Verificar o estado até estar ligado

```
GET /channels/whatsapp-web/connections/{phoneNumber}/status
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+15551230000");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "connected" (or "open").
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+15551230000")
res = requests.get(
    f"https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "connected" (or "open").
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+15551230000",
  "status": "connected",
  "has_qr": false,
  "qr_expires_at": null,
  "last_activity": null,
  "message_count": null,
  "proxy": null,
  "live": true
}
```

| `status` | Significado |
|---|---|
| `not_initialized` | Ainda sem sessão (falha terminal). |
| `qr_pending` | À espera que o QR seja lido. |
| `connecting` | Lido, a concluir a configuração. |
| `connected` / `open` | Associado e ativo - isto é um sucesso. |
| `disconnected` | Sessão terminada (falha terminal). |

### Desligar uma sessão WhatsApp Web

```
DELETE /channels/whatsapp-web/connections/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/whatsapp-web/connections/+15551230000" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "phone_number": "+15551230000", "status": "removed" }
```

Isto desassocia o dispositivo e remove a ligação. Limpa sempre o estado local, pelo que é idempotente mesmo que a sessão subjacente já tenha desaparecido.

---

## Telegram

> **Disponibilidade:** O Telegram liga-se como qualquer outro canal e está aberto a todas as contas — não precisa de o ter ativado para si. Os endpoints do Telegram abaixo ainda podem devolver `403` se o Telegram não estiver incluído no plano da conta, caso em que o erro diz `"This channel is not included in your current plan. Upgrade to unlock it."`.

O Telegram liga uma conta pessoal através do número de telefone e de um código de início de sessão único (e uma palavra-passe de dois fatores, se a conta tiver uma definida). O fluxo é: iniciar a sessão, submeter o código, opcionalmente submeter a palavra-passe e, em seguida, confirmar através do estado.

### Passo 1 - Iniciar uma sessão de ligação Telegram

```
POST /channels/telegram/connect
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "phone_number": "+14155550100" }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/telegram/connect", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ phone_number: "+14155550100" }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/telegram/connect",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"phone_number": "+14155550100"},
)
data = res.json()
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phone_number` | Sim | O número de telefone da conta a ligar, no formato E.164. |
| `mode` | Não | `code` (predefinição) envia um código de início de sessão único para a conta; `qr` devolve um token de início de sessão e um URL QR para apresentar. |
| `proxy_country` | Não | Código de país ISO 3166-1 alpha-2 para a rota de rede de saída. |
| `force_new` | Não | Quando `true`, descarta qualquer sessão existente e começa do zero. |

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "code_required",
  "session_id": "session-id",
  "connect_url": "https://api.youraiconnector.com/v1/channels/telegram/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000
}
```

No modo `code`, a conta recebe um código de início de sessão no Telegram e `status` é `code_required`. (No modo `qr`, a resposta também inclui `login_token` e `qr_url` para apresentar para leitura, e `status` é `qr_required`.)

### Opção mais fácil para Telegram: entregar `connect_url`

A resposta inclui um `connect_url` pronto a usar: uma página alojada que conclui a ligação por si própria. No modo `code`, o titular da conta introduz o código de início de sessão - e uma palavra-passe de verificação em dois passos, caso a sua conta a tenha. No modo `qr`, a página apresenta um QR que se atualiza automaticamente para que o utilizador o possa digitalizar a partir da aplicação Telegram. Em qualquer dos casos, a página comunica o sucesso automaticamente, pelo que pode simplesmente fornecer esta ligação ao titular da conta em vez de criar a sua própria interface e efetuar o polling. A ligação funciona durante cerca de 30 minutos (`connect_url_expires_at`); se expirar, inicie uma nova ligação para obter uma nova.

Os passos manuais abaixo (recolher o código por si próprio, submetê-lo, verificar o estado; ou renderizar `qr_url` e verificar) destinam-se a integrações que pretendem renderizar a interface por si próprias.

### Passo 2 - Submeter o código de início de sessão

```
POST /channels/telegram/connect/{phoneNumber}/verify-code
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/verify-code" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "12345" }'
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155550100");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/telegram/connect/${phone}/verify-code`,
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ code: "12345" }),
  }
);
const data = await res.json();
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155550100")
res = requests.post(
    f"https://api.youraiconnector.com/v1/channels/telegram/connect/{phone}/verify-code",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"code": "12345"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "username": "myhandle"
}
```

Se `status` for `connected`, terminou. Se a conta tiver a autenticação de dois fatores ativada, `status` será `password_required` - avance para o passo 3.

### Passo 3 - Submeter a palavra-passe de dois fatores (apenas se necessário)

```
POST /channels/telegram/connect/{phoneNumber}/verify-password
```

Apenas chame isto quando o passo 2 devolver `password_required`.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/verify-password" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "password": "the-2fa-password" }'
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155550100");
const res = await fetch(
  `https://api.youraiconnector.com/v1/channels/telegram/connect/${phone}/verify-password`,
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ password: "the-2fa-password" }),
  }
);
const data = await res.json();
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155550100")
res = requests.post(
    f"https://api.youraiconnector.com/v1/channels/telegram/connect/{phone}/verify-password",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"password": "the-2fa-password"},
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "username": "myhandle"
}
```

### Verificar o estado do Telegram

```
GET /channels/telegram/connect/{phoneNumber}/status
```

```bash
curl "https://api.youraiconnector.com/v1/channels/telegram/connect/+14155550100/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155550100",
  "status": "connected",
  "telegram_user_id": "100000001",
  "live": true
}
```

`status` pode ser `connected`, `code_required`, `password_required`, `initializing`, `disconnected`, `not_initialized` ou `error`.

### Desligar Telegram

```
DELETE /channels/telegram/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/telegram/+14155550100" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "phone_number": "+14155550100", "status": "removed" }
```

Idempotente - chamadas repetidas são bem-sucedidas.

---

## Instagram (conta pessoal)

> Versão beta de disponibilidade limitada, ativada por conta. Isto liga uma conta pessoal do Instagram através do início de sessão com o respetivo nome de utilizador e palavra-passe (não a API oficial para Empresas). Se a conta não estiver ativada para a versão beta, a chamada de ligação devolve um erro de permissão.

Como isto requer o início de sessão do próprio titular da conta no Instagram, o caminho mais simples é fornecer-lhe o `connect_url` alojado e deixar que introduza as suas credenciais aí - a sua integração nunca processa a palavra-passe.

### Passo 1 - Iniciar uma ligação Instagram (pessoal)

```
POST /channels/instagram-private/connect
```

Envie o `username` e o `password` do Instagram.

**Resposta**

```json
{
  "success": true,
  "status": "connected",
  "connect_url": "https://api.youraiconnector.com/v1/channels/instagram-private/connect/page?token=eyJhbGciOi...",
  "connect_url_expires_at": 1717000000000
}
```

Se a conta tiver autenticação de dois fatores ou o Instagram apresentar um ponto de controlo, `status` é devolvido como `two_factor_required` ou `challenge_required` - submeta o código para `/connect/{id}/verify-2fa` ou `/connect/{id}/verify-challenge` abaixo, e depois consulte `/connect/{id}/status` até `connected`. `{id}` é o nome de utilizador normalizado do Instagram devolvido como `account_id`/`username` na resposta acima - utilize-o em cada passo abaixo.

### Passo 2 - Submeter o código de dois fatores (se solicitado)

```
POST /channels/instagram-private/connect/{id}/verify-2fa
```

Chame isto apenas quando o passo 1 (ou o passo 3) devolver `two_factor_required`.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/verify-2fa" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'
```

**Resposta**

```json
{
  "success": true,
  "account_id": "yourbrand",
  "status": "connected",
  "ig_user_id": "17890000000000000",
  "username": "yourbrand"
}
```

`status` pode devolver `connected` (concluído), `two_factor_required` (código incorreto, tente novamente), ou `challenge_required` (o Instagram também quer um código de ponto de controlo - vá para o passo 3).

### Passo 3 - Submeter o código de confirmação do ponto de controlo (se solicitado)

```
POST /channels/instagram-private/connect/{id}/verify-challenge
```

Chame isto apenas quando um passo anterior devolver `challenge_required`. A estrutura do pedido e da resposta é a mesma que no passo 2 acima.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/verify-challenge" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'
```

### Verificar o estado do Instagram (pessoal)

```
GET /channels/instagram-private/connect/{id}/status
```

Consulte isto até `status` ser `connected`, ou até reportar uma falha terminal.

```bash
curl "https://api.youraiconnector.com/v1/channels/instagram-private/connect/yourbrand/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "account_id": "yourbrand",
  "status": "connected",
  "ig_user_id": "17890000000000000",
  "username": "yourbrand",
  "live": true
}
```

`status` pode ser `connected`, `two_factor_required`, `challenge_required`, `initializing`, `disconnected`, `not_initialized`, ou `error`. `live: true` significa que isto foi lido em tempo real a partir do trabalhador de ligação em vez de ser um valor em cache.

### Opção mais fácil para Instagram (pessoal): entregar `connect_url`

A resposta inclui um `connect_url`: uma página alojada onde o titular da conta introduz o seu nome de utilizador e palavra-passe do Instagram (e um código de 2FA ou de ponto de controlo, se o Instagram o solicitar), e que comunica o sucesso por si própria. As credenciais vão diretamente para o Instagram e não são armazenadas. Forneça esta ligação ao titular da conta em vez de recolher a palavra-passe na sua própria interface. A ligação funciona durante cerca de 30 minutos (`connect_url_expires_at`).

### Desligar Instagram (pessoal)

```
DELETE /channels/instagram-private/{id}
```

Idempotente - chamadas repetidas são bem-sucedidas.

### Sincronizar seguidores

```
POST /channels/instagram-private/{id}/sync-followers
```

Aciona manualmente uma sincronização de seguidores para uma conta ligada - o mesmo trabalho que é executado automaticamente em segundo plano, aqui exposto para uma ação de "Atualizar seguidores" a pedido. Obtém a lista atual de seguidores da conta, regista qualquer novo seguidor e (quando uma campanha em direto tem a divulgação a seguidores ativada) envia aos novos seguidores uma mensagem direta de abertura, até um limite diário.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/instagram-private/yourbrand/sync-followers" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "accountId": "yourbrand",
  "totalFollowers": 1204,
  "newFollowers": 6,
  "dmsSent": 6,
  "isBaselineSeed": false
}
```

> Estes cinco campos são o único local nesta página que devolve `camelCase` em vez de `snake_case` - é assim que este endpoint está configurado atualmente, não é um erro ortográfico. `isBaselineSeed: true` significa que esta foi a primeira sincronização após a ligação, que apenas regista a lista inicial de seguidores e nunca envia mensagens diretas de divulgação (por isso, `dmsSent` é sempre `0` nessa execução).

A primeira chamada para uma conta pode demorar algum tempo (a percorrer toda a lista de seguidores); as chamadas posteriores são mais rápidas, uma vez que apenas os novos seguidores são comparados. `404` significa que a conta não está ligada; `412` significa que a ligação ainda não terminou a inicialização - aguarde e tente novamente.

---

## LINE

O LINE é o canal mais simples de ligar, uma vez que não existe redirecionamento de navegador nem polling. O cliente cria um canal Messaging API na consola LINE Developers, copia dois valores e submete-os numa única chamada. De seguida, fornece-lhe um URL de webhook para colar na consola.

### Passo 1 - Ligar com as credenciais do canal

```
POST /channels/line
```

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/line?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel_access_token": "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
    "channel_secret": "CHANNEL_SECRET"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/line", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel_access_token: "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
    channel_secret: "CHANNEL_SECRET",
  }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/channels/line",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "channel_access_token": "LONG_LIVED_CHANNEL_ACCESS_TOKEN",
        "channel_secret": "CHANNEL_SECRET",
    },
)
data = res.json()
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `channel_access_token` | Sim | O token de acesso ao canal Messaging API de longa duração da Conta Oficial. Utilizado para enviar e receber mensagens. |
| `channel_secret` | Sim | O segredo do canal Messaging API, utilizado para verificar assinaturas de eventos recebidos. |
| `channel_id` | Não | O ID numérico do canal. Apenas informativo. |

**Resposta**

```json
{
  "success": true,
  "status": "connected",
  "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "basic_id": "@mybusiness",
  "display_name": "My Business",
  "picture_url": "https://...",
  "chat_mode": "bot",
  "chat_mode_ok": true,
  "webhook_url": "https://api.youraiconnector.com/line/webhook/..."
}
```

Dois campos são importantes para o que fará a seguir:

- **`webhook_url`** - o cliente deve colar isto no campo **Webhook URL** do seu canal LINE na consola LINE Developers (e ativar "Use webhook"). Até que o façam, não chegam mensagens recebidas. Mostre isto de forma proeminente.
- **`chat_mode_ok`** - quando `false`, a Conta Oficial está no modo "chat" e não receberá nem enviará mensagens até que seja mudada para o modo "bot" no LINE Official Account Manager. Bloqueie a sua integração com base neste sinalizador e diga ao cliente para mudar o modo.

> O `channel_access_token` e o `channel_secret` nunca são devolvidos por nenhum endpoint. Guarde-os do seu lado se precisar deles novamente; caso contrário, volte a colá-los a partir da consola LINE.

O `bot_user_id` devolvido aqui é o identificador de ligação que utiliza nas chamadas de estado, verificação e desligação abaixo.

### Passo 2 - Reverificar após a configuração do webhook

```
POST /channels/line/{botUserId}/verify-webhook
```

Após o cliente terminar de configurar o URL do webhook e mudar para o modo de bot, chame isto para revalidar o token armazenado e atualizar o modo de chat em cache.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx.../verify-webhook" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "token_valid": true,
  "chat_mode": "bot",
  "chat_mode_ok": true,
  "webhook_url": "https://api.youraiconnector.com/line/webhook/..."
}
```

Se `token_valid` for `false`, o token de acesso armazenado já não autentica - peça ao cliente para o reemitir na consola e chame `POST /channels/line` novamente com o novo token.

### Verificar o estado do LINE

```
GET /channels/line/{botUserId}/status
```

```bash
curl "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx.../status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "channel": "line",
  "status": "connected",
  "basic_id": "@mybusiness",
  "display_name": "My Business",
  "picture_url": "https://...",
  "chat_mode": "bot",
  "is_active": true,
  "live": false
}
```

O LINE não tem um feed de estado em tempo real, por isso `live` é sempre `false` aqui - os valores refletem o estado capturado no momento da ligação (ou da última verificação).

### Desligar LINE

```
DELETE /channels/line/{botUserId}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/line/Uxxxxxxxx..." \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "status": "removed", "bot_user_id": "Uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
```

---

## Viber

O Viber liga-se da mesma forma que o LINE - cole o token de autenticação do bot a partir do Painel de Administração do Viber numa chamada - com uma diferença que vale a pena conhecer: a ligação também REGISTA o nosso webhook no seu bot nesse momento, pelo que não existe um passo de consola separado posteriormente. Isso também significa que uma tentativa de ligação pode falhar se o nosso ingresso não conseguir responder à verificação síncrona do webhook do Viber, e não apenas se o próprio token estiver incorreto.

### Passo 1 - Ligar com o token de autenticação do bot

```
POST /channels/viber
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `auth_token` | Sim | O token de autenticação do bot, a partir do Painel de Administração do Viber (Definições do Meu Bot). |

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/viber?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "auth_token": "444d5555e6666f7777a8888b9999c000" }'
```

**Resposta**

```json
{
  "success": true,
  "status": "connected",
  "bot_id": "botIdFromViber",
  "bot_name": "My Business Bot",
  "bot_avatar": "https://...",
  "bot_uri": "mybusinessbot",
  "subscribers_count": 0,
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "event_types": ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"]
}
```

O token de autenticação nunca é devolvido por nenhum endpoint - guarde-o do seu lado se precisar de o voltar a colar. `bot_id` é o identificador de ligação utilizado pelas chamadas de estado, verificação e desligação abaixo.

### Verificar o estado do Viber

```
GET /channels/viber/{botId}/status
```

Reporta o estado da ligação guardado. Adicione `?live=true` para também verificar novamente o bot junto do Viber e atualizar o registo do webhook em cache - útil antes de assumir que um bot silencioso está realmente avariado.

```bash
curl "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber/status?live=true" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "bot_id": "botIdFromViber",
  "channel": "viber",
  "status": "connected",
  "bot_name": "My Business Bot",
  "bot_avatar": "https://...",
  "bot_uri": "mybusinessbot",
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "registered_webhook": "https://api.youraiconnector.com/v1/incoming-viber-message/...",
  "webhook_ok": true,
  "subscribers_count": 128,
  "is_active": true,
  "live": true
}
```

`webhook_ok: false` significa que o webhook do bot já não aponta para nós - as mensagens recebidas estão perdidas. Isto significa normalmente que outra ferramenta ligou o mesmo bot posteriormente (o registo de webhook do Viber funciona com base na última escrita). Corrija-o com a chamada de re-verificação abaixo, não é necessário pedir ao cliente para voltar a colar o seu token. `live` é `false` quando a resposta é o último estado em cache em vez de uma verificação recente junto do Viber.

### Registar novamente o webhook

```
POST /channels/viber/{botId}/verify-webhook
```

A ação de reparação para `webhook_ok: false` - regista novamente o nosso webhook no bot utilizando o token de autenticação já guardado.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber/verify-webhook" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "token_valid": true, "webhook_ok": true, "webhook_url": "https://api.youraiconnector.com/v1/incoming-viber-message/...", "event_types": ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"] }
```

`token_valid: false` significa que o token guardado já não funciona - ligue novamente com `POST /channels/viber` e um novo token.

### Desligar o Viber

```
DELETE /channels/viber/{botId}
```

Anula o registo do nosso webhook no lado do Viber (best-effort) e remove a ligação.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/viber/botIdFromViber" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "status": "removed", "bot_id": "botIdFromViber", "webhook_removed": true }
```

---

## TikTok

> **Disponibilidade:** Beta de disponibilidade limitada, ativado por conta. A ligação ao TikTok devolve um erro de permissão até que a conta seja ativada para o efeito.

O TikTok Business Messaging é um canal OAuth completo, tal como o Meta, mas mais simples no que toca à consulta (polling): não existe um passo dedicado de consulta de estado para implementar, uma vez que a conta ligada aparece por si só assim que o TikTok redireciona de volta e a ligação é escrita. O endpoint de estado abaixo existe para confirmar o estado a pedido (ferramentas de suporte, verificações de integridade), e não como algo em que precise de criar um ciclo durante a ligação.

### Passo 1 - Iniciar a ligação ao TikTok

```
POST /channels/tiktok/connect
```

Não requer credenciais - o titular da conta autoriza tudo no seu próprio navegador.

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/tiktok/connect?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "status": "pending_authorization",
  "oauth_url": "https://www.tiktok.com/v2/auth/authorize?client_key=...&state=...",
  "state_token": "opaque-one-time-token",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Abra `oauth_url` no navegador do titular da conta para que este possa iniciar sessão no TikTok e aprovar o acesso. O estado expira em `expires_at` (cerca de 30 minutos) - se expirar, comece de novo. Não existe nenhum atalho de página alojada `connect_url` para o TikTok; abrir `oauth_url` manualmente é o único caminho.

### Verificar o estado do TikTok

```
GET /channels/tiktok/{openId}/status
```

`openId` é o open_id da Conta Business do TikTok, conhecido assim que o callback OAuth tiver sido executado.

```bash
curl "https://api.youraiconnector.com/v1/channels/tiktok/openIdFromTikTok/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "open_id": "openIdFromTikTok",
  "channel": "tiktok",
  "status": "connected",
  "business_id": "openIdFromTikTok",
  "username": "mybusiness",
  "display_name": "My Business",
  "avatar_url": "https://...",
  "status_reason": null,
  "is_active": true,
  "live": false
}
```

O TikTok não tem uma verificação de integridade em tempo real económica, por isso `live` é sempre `false` aqui - os campos refletem o que a ligação (ou a última atualização de token) escreveu. `status: "reauth_required"` com `status_reason` definido significa que a conta precisa de passar pelo processo de ligação novamente; os tokens do TikTok são atualizados automaticamente numa rotação anual, e é isto que aparece se essa rotação falhar.

### Desligar o TikTok

```
DELETE /channels/tiktok/{openId}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/tiktok/openIdFromTikTok" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "status": "removed", "open_id": "openIdFromTikTok" }
```

---

## GoHighLevel

O GoHighLevel (GHL) é uma integração de CRM, não um canal de mensagens - ligá-lo não consome um espaço de canal no plano, porque utiliza os canais já existentes da conta em vez de adicionar um novo. É também a única integração nesta página que pode manter **mais do que uma ligação ao mesmo tempo**: cada subconta GHL ("localização") na qual o cliente instala a aplicação obtém a sua própria entrada.

### Passo 1 - Iniciar a ligação ao GHL

```
POST /channels/ghl/connect
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `brand` | Não | Qual listagem do marketplace GHL utilizar para autorizar. A predefinição é a listagem padrão - apenas relevante se a sua implementação tiver mais do que uma aplicação de marketplace configurada. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/ghl/connect?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "status": "pending_authorization",
  "oauth_url": "https://marketplace.gohighlevel.com/oauth/chooselocation?client_id=...&state=...",
  "state_token": "opaque-one-time-token",
  "brand": "dmchamp",
  "expires_at": "2026-06-10T12:30:00.000Z"
}
```

Abra o `oauth_url` no navegador do titular da conta para que este possa escolher uma localização GHL e aprovar o acesso. O estado expira em `expires_at` (cerca de 30 minutos).

### Listar ligações GHL

```
GET /channels/ghl/status
```

Ao contrário de outros canais, este não é o estado de uma única ligação - lista todas as localizações que a conta ligou.

```bash
curl "https://api.youraiconnector.com/v1/channels/ghl/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "connections": [
    {
      "location_id": "abc123location",
      "company_id": "xyz789company",
      "brand": "dmchamp",
      "status": "connected",
      "status_reason": null,
      "scopes": ["conversations.readonly", "conversations.write", "conversations/message.write"],
      "connected_at": "2026-06-01T10:00:00.000Z",
      "conversation_provider_id": "provider-id-in-ghl",
      "trigger_subscriptions": [
        { "id": "sub_1", "key": "InboundMessage", "workflow_id": "wf_123" }
      ]
    }
  ]
}
```

### Desligar uma localização GHL

```
DELETE /channels/ghl/{locationId}
```

Elimina a ligação aqui, o que interrompe todas as sincronizações e acionadores para essa localização. Isto não desinstala a aplicação do lado do GHL - o cliente remove-a das suas instalações do marketplace GHL se também desejar isso.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/ghl/abc123location" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "status": "disconnected", "location_id": "abc123location" }
```

---

## Números de telefone (comprar e libertar)

Em vez de ligar um número existente, pode comprar diretamente um novo número compatível com WhatsApp. Procure números disponíveis, compre um e, em seguida, faça sondagens até que o aprovisionamento esteja concluído.

::: note
**Nota:** Os números comprados aqui são compatíveis com o WhatsApp. O registo do remetente do WhatsApp é executado em segundo plano após a compra, pelo que deve verificar o estado até que este atinja `ONLINE` antes de enviar. Os créditos são deduzidos no momento da compra e **não** são reembolsados quando liberta o número.
:::


### Passo 1 - Pesquisar números disponíveis

```
GET /phone-numbers/available?country_code=ISO2
```

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/phone-numbers/available?country_code=US&apiKey=YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/phone-numbers/available?country_code=US",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
```

**Python**

```python
res = requests.get(
    "https://api.youraiconnector.com/v1/phone-numbers/available",
    params={"country_code": "US"},
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
```

| Parâmetro de consulta | Obrigatório | Descrição |
|---|---|---|
| `country_code` | Sim | Código de país ISO 3166-1 alpha-2 para pesquisar (por exemplo, `US`, `GB`, `NL`). |
| `type` | Não | Classe de número preferencial, `local` ou `mobile`. Ambas as classes podem ser devolvidas. |

**Resposta**

```json
{
  "success": true,
  "phone_numbers": [
    {
      "phone_number": "+14155551234",
      "purchase_credits": 50,
      "monthly_credits": 50,
      "cost_usd": 1.15
    }
  ]
}
```

Cada resultado mostra o `purchase_credits` único e o `monthly_credits` recorrente. Um número fornecido pela plataforma custa, no mínimo, 50 créditos por mês, aumentando de acordo com o preço mensal da operadora, cobrado no momento da compra e em cada renovação. Utilize o `purchase_credits` / `monthly_credits` que a pesquisa devolve; nunca calcule o preço por conta própria. A primeira pesquisa numa conta nova provisiona alguns recursos subjacentes, pelo que pode ser um pouco mais lenta do que as pesquisas subsequentes.

### Passo 2 - Comprar um número

```
POST /phone-numbers
```

Utilize um `phone_number` dos resultados da pesquisa.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155551234",
    "country_code": "US",
    "display_name": "Support line"
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/phone-numbers", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+14155551234",
    country_code: "US",
    display_name: "Support line",
  }),
});
const data = await res.json();
```

**Python**

```python
res = requests.post(
    "https://api.youraiconnector.com/v1/phone-numbers",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phone_number": "+14155551234",
        "country_code": "US",
        "display_name": "Support line",
    },
)
data = res.json()
```

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phone_number` | Sim | Um número devolvido pela pesquisa de números disponíveis, no formato E.164. |
| `country_code` | Sim | Código de país ISO 3166-1 alpha-2 (por exemplo, `US`). |
| `display_name` | Não | Uma etiqueta amigável. Por predefinição, utiliza o número de telefone. |
| `category` | Não | Etiqueta de categoria opcional. |

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "whatsapp_status": "PURCHASED",
  "outgoing_status": "PURCHASED",
  "status": "PURCHASED",
  "purchase_credits": 50,
  "monthly_credits": 50
}
```

O número começa no estado `PURCHASED`. O registo no WhatsApp prossegue então em segundo plano: `PURCHASED` -> `PENDING` -> `ONLINE`.

> Se a compra falhar porque falta um endereço comercial ou outro detalhe obrigatório não está definido, receberá um `400` com uma `error` descritiva. Configure o detalhe em falta e tente novamente.

### Passo 3 - Consultar até estar ONLINE

```
GET /phone-numbers/{phoneNumber}/status
```

Este é o endpoint partilhado de estado do número de telefone - funciona tanto para números WhatsApp comprados como para os seus outros números ligados.

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/status" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const phone = encodeURIComponent("+14155551234");
const res = await fetch(
  `https://api.youraiconnector.com/v1/phone-numbers/${phone}/status`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
// Poll until data.status === "ONLINE".
```

**Python**

```python
import urllib.parse

phone = urllib.parse.quote("+14155551234")
res = requests.get(
    f"https://api.youraiconnector.com/v1/phone-numbers/{phone}/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
# Poll until data["status"] == "ONLINE".
```

**Resposta**

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "status": "ONLINE",
  "status_reason": null,
  "live": true
}
```

### Passo 4 - Libertar um número

```
DELETE /phone-numbers/{phoneNumber}
```

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/phone-numbers/+14155551234" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "phone_number": "+14155551234", "released": true }
```

O que isto faz depende de a quem pertence o número.

Para um número **alugado através da plataforma**, trata-se de uma libertação real: o remetente do WhatsApp é cancelado, o número é devolvido à operadora e removido da conta, é aplicado um período de arrefecimento de 7 dias durante o qual o número não pode ser recomprado por ninguém, e não são reembolsados créditos.

Para um número **que a conta trouxe consigo** (a sua própria conta Twilio, a sua própria aplicação Meta ou Conta WhatsApp Business, ou um gateway SMS Android), a mesma chamada apenas o remove da conta. Nada é libertado no fornecedor a montante e nenhum período de arrefecimento é registado, pelo que o número pode ser ligado novamente de imediato. O seu registo de remetente WhatsApp, se existisse, pode ou não sobreviver: o processo de desativação tenta eliminar o remetente utilizando as credenciais Twilio geridas pela plataforma da conta. Numa conta que ainda utiliza a configuração gerida, essas credenciais são válidas e o remetente é eliminado, pelo que voltar a ligar significa registá-lo novamente. Numa conta que mudou para a sua própria Twilio, a eliminação não pode ser autenticada e o remetente permanece registado nessa conta — voltar a ligar é, então, apenas voltar a associar o remetente existente.

### Adicionar um número que já possui (BYO)

```
POST /phone-numbers/byo
```

Ignora completamente o fluxo de pesquisa e compra acima. Utilize isto quando a conta traz o seu próprio número (o seu próprio Twilio, a sua própria conta Meta WhatsApp Business, ou um gateway SMS Android) em vez de alugar um através da plataforma. Isto apenas regista o número - não são cobrados créditos e nada é provisionado com um fornecedor aqui. O número permanece inativo até que o titular da conta conclua o OAuth do WhatsApp para registar um Remetente no mesmo (o mesmo fluxo que o botão "Trazer o seu próprio número" do painel inicia).

| Campo | Obrigatório | Descrição |
|---|---|---|
| `phone_number` | Sim | O número a adicionar, no formato E.164 (por exemplo, `+14155551234`). |
| `country_code` | Sim | Código de país ISO 3166-1 alpha-2 (por exemplo, `US`). |
| `display_name` | Não | Uma etiqueta amigável. A predefinição é o número de telefone. |
| `category` | Não | Etiqueta de categoria opcional. |

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers/byo?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155551234",
    "country_code": "US",
    "display_name": "Support line"
  }'
```

**Resposta** (`201 Created`):

```json
{
  "success": true,
  "phone_number": "+14155551234",
  "channel": "whatsapp",
  "type": "BYO",
  "whatsapp_status": "ADDED",
  "outgoing_status": "ADDED",
  "is_active": false
}
```

Um `phone_number` que não seja um número E.164 real (ou que pareça o número de teste do WhatsApp da Meta, que nunca pode enviar mensagens a clientes reais) devolve `400`. Adicionar um número que já existe na conta - mesmo que escrito de forma ligeiramente diferente, como as formas `+52` vs `+521` do México - devolve `409` em vez de criar uma linha duplicada.

### Definir um número como principal

```
POST /phone-numbers/{phoneNumber}/set-primary
```

Altera um número para `is_active: true` e todos os outros números na conta para `is_active: false`, de forma atómica - a conta nunca fica com dois números ativos, ou nenhum, a meio do pedido. `is_active` não pode ser definido através do endpoint de atualização geral propositadamente; esta chamada dedicada é a única forma de alterar qual o número que é o principal.

```bash
curl -X POST "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/set-primary" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "phone_number": {
    "id": "+14155551234",
    "phone_number": "+14155551234",
    "display_name": "Support line",
    "channel": "whatsapp",
    "is_active": true,
    "whatsapp_status": "ONLINE"
  }
}
```

`phone_number` aqui é o objeto de número completo (a mesma forma que `GET /phone-numbers` devolve), não apenas a string. Um `phoneNumber` que não esteja na conta devolve `404`.

### Remover o registo de um número (sem o libertar)

```
DELETE /phone-numbers/{phoneNumber}/record
```

Uma eliminação simples do registo do número nesta conta - sem libertação ou desregisto do lado do fornecedor, e sem o período de arrefecimento de 7 dias como se aplica no passo de libertação acima. Utilize isto para limpar registos BYO, WhatsApp Web, Telegram ou LINE, ou uma entrada obsoleta, sem passar pelo fluxo de libertação gerida. Ao contrário de uma libertação, eliminar um número que não está na conta é um `404`, não um sucesso silencioso.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/phone-numbers/+14155551234/record" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Resposta**

```json
{ "success": true, "phone_number": "+14155551234", "deleted": true }
```

---

## Direcionar um canal para uma campanha

Ligar um canal permite a entrada de mensagens **na** conta. Não decide **qual Agente de IA lhes responde**.

O encaminhamento é gerido por **Pontos de Entrada** num Agente de IA, não por campanhas. Cada canal tem um Ponto de Entrada predefinido que nomeia o Agente que responde a contactos novos e desconhecidos nesse canal:

| O que pretende fazer | Chamada |
|---|---|
| Apontar um canal para o Agente que deve responder ao mesmo | `PUT /entry-points/channel-defaults` com o corpo `{ "channel": "instagram", "agent_id": "AGENT_ID" }` |
| Verificar se a hierarquia de Pontos de Entrada está ativa para a conta | `GET /entry-points/routing-status`, que devolve `{ "success": true, "cutover_enabled": true }` assim que os Pontos de Entrada decidem o encaminhamento dessa conta |
| Deixar um canal sem nenhum Agente a responder-lhe | `DELETE /entry-points/channel-defaults?channel=instagram` |

Até que um canal tenha um Ponto de Entrada, uma primeira mensagem de alguém com quem nunca falou continua a ser armazenada, mas nada a recolhe e nenhum assistente responde. Este é o passo que a maioria das integrações falha: ligar o Instagram e criar um Agente não é suficiente por si só — também tem de apontar o canal para o Agente. O conjunto completo de chamadas — incluindo um Agente por número de WhatsApp, palavras-chave e regras de comentários — encontra-se na [API de Pontos de Entrada](entry-points.md).

`POST /channels/campaign` ainda escreve o mapa de encaminhamento de campanhas legado por canal, documentado abaixo, mas esse mapa já não é consultado para encaminhamento de entrada em nenhuma conta; é mantido apenas para reversão. Não desenvolva com base nele.

### Encaminhar um ou mais canais (mapa de encaminhamento de campanhas legado)

`POST /channels/campaign`

**Campos do pedido**

| Campo | Obrigatório | Descrição |
|---|---|---|
| `campaign_id` | Sim | A campanha que deve responder a novos contactos nestes canais. Tem de pertencer à conta. |
| `channels` | Sim | Uma matriz não vazia de canais a encaminhar. Permitidos: `whatsapp`, `whatsapp_web`, `telegram`, `instagram`, `messenger`, `chat_widget`, `custom_channel`, `sms`, `email`. |

O espaço de encaminhamento e a lista `enabled_channels` da campanha são atualizados em conjunto numa única operação atómica, pelo que nunca podem divergir. Um canal já encaminhado para uma campanha diferente é simplesmente redirecionado para esta.

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/channels/campaign?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
    "channels": ["instagram", "messenger"]
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/channels/campaign", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "NBCXrhqGPSFsd6MV7pRo",
    channels: ["instagram", "messenger"],
  }),
});
const data = await res.json();
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/channels/campaign",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
        "channels": ["instagram", "messenger"],
    },
)
data = res.json()
```

**Resposta**

```json
{
  "success": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo",
  "channels": ["instagram", "messenger"]
}
```

### O que tem de ser verdade para o encaminhamento ser efetivado

Numa conta que ainda lê o mapa de encaminhamento de campanhas legado, o encaminhamento é bem-sucedido como uma chamada de API, mas três aspetos na campanha decidem se uma mensagem de entrada real é respondida. Verifique os três quando um canal encaminhado permanece em silêncio.

| Requisito | O que acontece caso contrário |
|---|---|
| `type` é `Incoming from Unknown Contacts` ou `Combined` | O pedido é rejeitado com `400`. As campanhas de saída e de Palavras-chave não podem ocupar um espaço de encaminhamento. |
| `status` é `Live` | O encaminhamento é guardado, mas nunca recolhe nada. Uma campanha `Draft` é a causa mais comum para "encaminhei-o e nada acontece". |
| `ai_mode` é `true` | O contacto é criado e a mensagem guardada, mas o assistente nunca responde. |

A correspondência de palavras-chave reside agora nos Pontos de Entrada — crie um Ponto de Entrada do tipo `keyword` no Agente de IA que deve responder.

### Uma campanha por canal

Cada canal detém exatamente um espaço de encaminhamento legado. Encaminhar uma segunda campanha para o mesmo canal reatribui silenciosamente o espaço e devolve `200` — não existe erro de conflito. A campanha anterior continua a tratar dos contactos que já possui; apenas deixa de receber novos.

### Limpar o encaminhamento de um canal

`DELETE /channels/campaign/{channel}`

Remove o encaminhamento de um único canal, independentemente da campanha para a qual aponta atualmente, e retira o canal do `enabled_channels` dessa campanha. Novos contactos desconhecidos no canal deixam de ser captados por qualquer campanha. Os contactos que já se encontram na campanha continuam como antes.

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/channels/campaign/instagram?apiKey=YOUR_API_KEY"
```

**Resposta**

```json
{
  "success": true,
  "channel": "instagram",
  "cleared": true,
  "campaign_id": "NBCXrhqGPSFsd6MV7pRo"
}
```

É idempotente: limpar um canal que nunca foi encaminhado também devolve `200`, com `cleared: false` e `campaign_id: null`. Este endpoint requer a funcionalidade **campanhas de entrada** no plano; sem ela, obterá um `403`.


---

## Utilize a sua própria aplicação Meta (Instagram + Messenger)

Por predefinição, a ligação Instagram + Messenger é executada através da aplicação Meta da plataforma, pelo que o nome dessa aplicação é o que o titular da conta vê no ecrã de consentimento do Facebook. Se pretender que o ecrã de consentimento apresente a **sua** marca, pode registar a sua própria aplicação Meta e encaminhar todo o fluxo através dela. Uma vez configurada, aplica-se à sua conta — nada muda nas chamadas de ligação acima, exceto a marca.

> **Isto abrange apenas o Instagram + Messenger.** As ligações ao WhatsApp, WhatsApp Web, Telegram e LINE não são afetadas por uma aplicação Meta personalizada.

### O que a sua aplicação precisa primeiro

Esta é a parte que demora algum tempo e ocorre inteiramente do lado da Meta:

1. **Uma aplicação** do tipo Business, com os produtos Messenger e Instagram adicionados.
2. **Acesso Avançado** (via Meta App Review) para: `pages_show_list`, `pages_messaging`, `pages_manage_metadata`, `pages_read_engagement`, `instagram_basic`, `instagram_manage_messages`. Sem Acesso Avançado, apenas as pessoas que possuem uma função na sua aplicação podem concluir a ligação — as ligações dos seus clientes falharão. A App Review demora normalmente algumas semanas e requer a Verificação da Empresa.
3. **Uma configuração de Facebook Login for Business** criada dentro da sua aplicação, concedendo as mesmas permissões. O seu ID de configuração numérico é por aplicação, pelo que tem de criar o seu próprio.

Se à sua aplicação faltar alguma das permissões necessárias, a ligação falha no momento da ligação com um erro claro que indica o que falta (visível na sondagem `/status` como `byo_app_missing_permissions`) — em vez de parecer funcionar e falhar na primeira mensagem.

### Passo 1 - Guardar a sua aplicação

`PUT /account-config/meta-app`

| Campo | Obrigatório | Descrição |
|---|---|---|
| `app_id` | Sim | O seu ID de Aplicação Meta (Definições → Básico). |
| `app_secret` | Sim | O seu Segredo de Aplicação Meta. Verificado junto da Meta antes de ser armazenado e, em seguida, encriptado. Nunca devolvido por qualquer endpoint. |
| `config_id` | Sim | O ID numérico da configuração de Facebook Login for Business dentro da sua aplicação. |

Os três são necessários para o fluxo de Início de Sessão no Facebook. Se apenas executar a via de envio de token de Início de Sessão no Instagram descrita mais abaixo, pode omiti-los completamente.

```bash
curl -X PUT "https://api.youraiconnector.com/v1/account-config/meta-app?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "app_id": "1234567890123456",
    "app_secret": "your-app-secret",
    "config_id": "9876543210987654"
  }'
```

**Resposta**

```json
{
  "success": true,
  "app_id": "1234567890123456",
  "config_id": "9876543210987654",
  "verify_token": "1f4c…a9",
  "webhook_urls": {
    "instagram": "https://api.youraiconnector.com/v1/incoming-instagram-message/byo/YOUR_ACCOUNT_ID",
    "messenger": "https://api.youraiconnector.com/v1/incoming-messenger-message/byo/YOUR_ACCOUNT_ID"
  }
}
```

### Passo 2 - Configurar a sua aplicação para comunicar connosco

No painel de controlo da sua aplicação Meta:

1. **Webhooks** - para os produtos Instagram e Messenger, defina o URL de Callback para o valor `webhook_urls` correspondente da resposta e o token de Verificação para `verify_token`. Subscreva os campos `messages`, `messaging_postbacks` e `comments`.
2. **URIs de Redirecionamento OAuth Válidos** - adicione `https://api.youraiconnector.com/v1/auth-meta-callback-handler` para que o fluxo de consentimento possa regressar.

`GET /account-config/meta-app` devolve o mesmo material de configuração a qualquer momento; `DELETE /account-config/meta-app` remove a aplicação (as ligações futuras revertem para a aplicação da plataforma — remova também a subscrição do webhook dentro da sua aplicação).

### Passo 3 - Ligue-se como habitualmente

Nada mais muda. O `POST /channels/meta/connect` (e a página `connect_url` alojada) utiliza automaticamente a sua aplicação para a sua conta; o `uses_byo_meta_app: true` da resposta confirma que aplicação o ecrã de consentimento irá apresentar. O envio de mensagens, a seleção de páginas e as desconexões funcionam de forma idêntica.

## Traga a sua própria aplicação de Início de Sessão do Instagram (push de token)

A secção acima aborda o fluxo de Início de Sessão do Facebook, onde a conta se liga através de uma Página do Facebook. A Meta também oferece a **API do Instagram com Início de Sessão do Instagram** (Início de Sessão Empresarial para Instagram): o titular da conta autentica-se no próprio Instagram, sem necessidade de uma conta ou Página do Facebook.

Se a sua plataforma já utiliza a sua própria aplicação Meta com esse produto, não precisa de qualquer fluxo OAuth da nossa parte. Os seus clientes autorizam a **sua** aplicação e o utilizador envia-nos a credencial finalizada por conta:

1. Guarda as credenciais da sua aplicação Instagram uma vez (para que possamos verificar os seus webhooks).
2. Por conta, envia o ID da conta profissional do Instagram + o token de utilizador do Instagram de longa duração que a sua aplicação obteve.
3. Aponta o webhook de mensagens do Instagram da sua aplicação para nós. Os eventos para contas que nunca enviou são reconhecidos e ignorados.
4. É responsável pelo ciclo de vida do token: atualize os tokens no seu próprio sistema e envie cada token atualizado com a mesma chamada. Nós nunca atualizamos um token enviado.

### O que a sua aplicação precisa primeiro

- O produto **Instagram** ("Configuração da API com início de sessão do Instagram") adicionado à sua aplicação Meta. Esse produto tem o seu **próprio par de ID de Aplicação e Segredo de Aplicação**, separado do ID/Segredo da Aplicação do Facebook — encontre-os no painel de configuração do produto.
- **Acesso Avançado** (através da Revisão da Aplicação Meta) para `instagram_business_basic` e `instagram_business_manage_messages` (adicione `instagram_business_manage_comments` se utilizar automatizações de comentários). Sem isto, apenas as pessoas com uma função na sua aplicação a podem autorizar.

### Passo 1 - Guardar as credenciais da sua aplicação Instagram

O mesmo endpoint que o anterior — envie o par do Instagram para `PUT /account-config/meta-app`. Os campos do Facebook não são necessários para esta via: envie o par sozinho se o Início de Sessão no Instagram for tudo o que executa, ou em conjunto com os campos do Facebook se executar ambos. Uma gravação descreve sempre a definição completa, pelo que qualquer conjunto que omita será removido.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `instagram_app_id` | Em conjunto | O ID de Aplicação numérico do próprio produto Instagram (não o ID de Aplicação do Facebook). |
| `instagram_app_secret` | Em conjunto | O Segredo de Aplicação do próprio produto Instagram. Encriptado em repouso, nunca devolvido. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/account-config/meta-app?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instagram_app_id": "1122334455667788",
    "instagram_app_secret": "your-instagram-app-secret"
  }'
```

**Resposta** — contém o URL do webhook de Início de Sessão no Instagram (os URLs `instagram` e `messenger` só aparecem quando os campos do Facebook também estão armazenados):

```json
{
  "success": true,
  "instagram_app_id": "1122334455667788",
  "verify_token": "1f4c…a9",
  "webhook_urls": {
    "instagram_login": "https://api.youraiconnector.com/v1/incoming-instagram-login-message/byo/YOUR_ACCOUNT_ID"
  }
}
```

No painel de **Webhooks** da sua aplicação para o produto Instagram, defina o URL de Callback para `webhook_urls.instagram_login`, o token de Verificação para `verify_token` e subscreva os campos `messages` e `comments`.

### Passo 2 - Enviar um token por conta

`PUT /channels/instagram-login/token`

Funciona com `sub_account_id` como qualquer outra rota, pelo que uma chave de agência pode provisionar toda a sua frota.

| Campo | Obrigatório | Descrição |
|---|---|---|
| `ig_user_id` | Sim | O **ID da conta profissional do Instagram** — o campo `user_id` de `GET https://graph.instagram.com/v21.0/me?fields=user_id,username`. Este é o mesmo ID que os webhooks do Instagram transportam como `entry.id`. ⚠️ **Não** é o campo `id` de `/me` — esse é limitado à aplicação e difere consoante a aplicação Meta. O envio do ID limitado à aplicação devolve um `400` a indicar o erro. |
| `access_token` | Sim | O token de utilizador do Instagram de longa duração que a sua aplicação obteve para essa conta. Validado em tempo real junto do Instagram antes de ser armazenado: o token tem de funcionar e tem de pertencer a `ig_user_id`. |
| `expires_at` | Não | Expiração ISO-8601 do token. Em alternativa, envie `expires_in` (segundos). O padrão é 60 dias. |
| `username` | Não | O @handle da conta; lemo-lo do Instagram de qualquer forma. |

```bash
curl -X PUT "https://api.youraiconnector.com/v1/channels/instagram-login/token?apiKey=YOUR_AGENCY_KEY&sub_account_id=CLIENT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "ig_user_id": "17841400000000000",
    "access_token": "IGAAR…",
    "expires_at": "2026-11-01T00:00:00Z"
  }'
```

**Resposta**

```json
{
  "success": true,
  "ig_user_id": "17841400000000000",
  "username": "acme.studio",
  "expires_at": "2026-11-01T00:00:00.000Z",
  "webhook_url": "https://api.youraiconnector.com/v1/incoming-instagram-login-message/byo/YOUR_ACCOUNT_ID"
}
```

Como parte do envio, subscrevemos a sua aplicação aos webhooks dessa conta (`subscribed_apps` com o token enviado), para que as mensagens comecem a fluir sem qualquer chamada adicional da sua parte.

**Atualização** - envie o token atualizado para o mesmo endpoint com o mesmo `ig_user_id`; isto atualiza o token armazenado e a validade no local.

**Conflitos** - uma conta do Instagram nunca está ativa em duas ligações. Se a conta já estiver ligada noutro local, ou nesta mesma conta através do fluxo da Página de Facebook, o push devolve um `409` indicando qual a ligação a desligar primeiro. Uma ligação de fluxo do Facebook nunca é substituída automaticamente, porque pode também estar a servir o Messenger.

### Passo 3 - Desligar quando um cliente sai

`DELETE /channels/instagram-login/token` (mesma autenticação e `sub_account_id`) cancela a subscrição dos webhooks da melhor forma possível e remove a credencial armazenada. É sempre bem-sucedido, mesmo quando o token já expirou — e uma vez que a credencial é removida, os eventos de webhook dessa conta são ignorados.

---

## Dicas para criar um wrapper fiável

- **Faça sondagens com moderação.** Bastam alguns segundos. Pare assim que atingir um estado terminal (`connected` / `ONLINE`, ou um estado de falha) e defina um tempo limite global sensato para o ciclo (os passos do navegador/QR expiram, veja cada `expires_at`).
- **Codifique números de telefone em URL no caminho.** O `+` inicial deve ser enviado como `%2B`. Os endpoints também recuperam dígitos simples, mas a codificação é a opção segura por defeito.
- **Nunca espere receber segredos de volta.** Os tokens de acesso, segredos de canal e tokens de página são aceites ou guardados, mas nunca são devolvidos em nenhuma resposta.
- **Lide com o bloqueio de autenticação.** Um `403` significa que o acesso à API não está incluído no plano, ou que o canal que está a ligar não está incluído no plano da conta. Consulte [Acesso à API](../integrations/api-access.md).
- **Tenha em atenção o limite de taxa.** Os pedidos autenticados estão limitados a 300 por minuto; um `429` significa que deve aguardar e tentar novamente. Consulte [Autenticação](authentication.md).

## Próximos passos

- [Autenticação](authentication.md) - as quatro formas de autenticação aceites e o formato de erro.
- [Acesso à API](../integrations/api-access.md) - geração e gestão da sua chave de API.
