
# API de conexión de canales

Esta guía muestra cómo conectar canales de mensajería a una cuenta mediante la API. Está escrita para un desarrollador que crea una integración o un contenedor, por lo que se centra en las solicitudes exactas, el orden en que deben realizarse y las respuestas que se obtienen.

Hay un patrón que debe comprender desde el principio, ya que se aplica a casi todos los canales aquí.

## El patrón de conectar y consultar (poll)

La mayoría de los canales no se pueden conectar con una sola llamada a la API. Conectar WhatsApp, Instagram o Messenger significa que el titular de la cuenta debe iniciar sesión en su propia cuenta de proveedor y aprobar el acceso. **No existe una ruta sin interfaz (totalmente automatizada)** para esa aprobación: una persona real debe abrir una URL en un navegador o escanear un código QR con su teléfono.

Por lo tanto, el flujo es siempre:

1. **Inicie la conexión** con una `POST`. La respuesta le proporciona una URL para abrir o un código QR para mostrar.
2. **Entrégueselo al usuario final**: abra la URL en su navegador o renderice el código QR en la pantalla para que lo escanee.
3. **Consulte el endpoint de estado** con `GET` en un intervalo corto (cada pocos segundos) hasta que el estado llegue a un estado conectado.

El trabajo de su integración es impulsar ese bucle: mostrar la URL o el QR, luego consultar hasta que termine. Planifique su interfaz de usuario en torno a la consulta: un indicador de carga con un mensaje de "esperando a que termine en su navegador" funciona bien.

::: note
**Nota:** Antes de comenzar, asegúrese de que el acceso a la API esté habilitado en el plan y de que tenga una clave de API. Consulte [Acceso a la API](../integrations/api-access.md) para saber cómo generar una. Todas las solicitudes a continuación utilizan la URL base `https://api.youraiconnector.com/v1` y debe autenticar cada solicitud. Consulte [Autenticación](authentication.md) para conocer las cuatro formas aceptadas; los ejemplos aquí utilizan el encabezado `X-API-Key`, con un ejemplo de cURL por página que muestra la forma de consulta `?apiKey=` más sencilla.
:::


---

## Instagram + Messenger (Meta)

Instagram y Messenger se conectan juntos en un solo flujo, porque ambos funcionan en una página de Facebook. El titular de la cuenta autoriza a través de Facebook, usted obtiene la lista de páginas que administra y elige qué página conectar.

### Paso 1 - Iniciar la conexión de Instagram + Messenger

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

Esto devuelve una URL de consentimiento. No se envían credenciales en esta solicitud; la conexión se autoriza completamente en el 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.
```

**Respuesta**

```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 `oauth_url` en el navegador del usuario final para que pueda iniciar sesión en Facebook y aprobar el acceso. El intento de conexión caduca en `expires_at` (aproximadamente 30 minutos); si caduca, comience de nuevo. Trate `state_token` como un secreto de corta duración y no lo registre.

### La opción más sencilla para Instagram + Messenger: entregar `connect_url`

La respuesta también incluye una `connect_url` lista para usar: una página alojada que ejecuta todo el flujo para el titular de la cuenta. La abren, inician sesión en Facebook y, cuando tienen más de una página, se muestra la lista y les permite elegir cuál conectar; luego, informa del éxito por sí misma. Proporcione este enlace al titular de la cuenta en lugar de abrir `oauth_url` usted mismo, crear un selector de páginas y realizar sondeos. El enlace funciona durante unos 30 minutos (`connect_url_expires_at`); si caduca, inicie una nueva conexión. Los pasos manuales a continuación son para integraciones que desean controlar el flujo y renderizar el selector de páginas por sí mismas.

### Paso 2 - Consultar el estado hasta que se carguen las páginas

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

Después de que el usuario termine de iniciar sesión en Facebook, consulta este endpoint cada pocos segundos. El campo `status` recorre estos pasos:

| `status` | Significado |
|---|---|
| `pending` | El consentimiento aún no se ha completado. Sigue esperando. |
| `token_received` | Autorizado, pero la lista de páginas aún se está cargando. |
| `pages_loaded` | Las páginas están disponibles: pasa al paso 3. |
| `connected` | Se ha seleccionado una página y el canal está activo. |

**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".
```

**Respuesta (una vez que las páginas se hayan cargado)**

```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
}
```

### Paso 3 - Listar las páginas (opcional)

Si prefieres obtener la lista de páginas por separado (por ejemplo, para renderizar un selector), utiliza:

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

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

Devuelve la misma matriz `pages` que el endpoint de estado. (El endpoint `status` ya incluye las páginas, por lo que esta llamada es solo por conveniencia.)

### Paso 4 - Seleccionar la página para conectar

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

Envía el `page_id` de la página que eligió el usuario. La cuenta de Instagram vinculada a esa página se conecta automáticamente; solo necesitas el objeto `instagram` si deseas anular qué cuenta de Instagram 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()
```

**Respuesta**

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

El canal ya está conectado. Un `GET /channels/meta/status` de seguimiento informará `status: "connected"`.

### Listar las publicaciones de la página conectada

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

Devuelve las publicaciones recientes de la página que conectaste: contenido de Instagram o publicaciones de Facebook. Esto es lo que utilizas para renderizar un selector cuando configuras un Punto de Entrada que reacciona a los comentarios en una publicación específica.

| Parámetro de consulta | Requerido | Descripción |
|---|---|---|
| `platform` | Sí | `instagram` o `facebook`. Cualquier otra cosa devuelve un `400`. |
| `limit` | No | Cuántas publicaciones devolver, `1`-`50`. El valor predeterminado es `25`. |
| `after` | No | Cursor para la página siguiente: pasa el valor `nextCursor` de la respuesta anterior. |

**cURL**

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

**Respuesta**

```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` es la etiqueta propia de Instagram (`REELS`, `FEED`, `STORY`, o el formato - `IMAGE`, `VIDEO`, `CAROUSEL_ALBUM`); para Facebook siempre es `POST`. `nextCursor` es `null` en la última página.

Si no se puede listar nada, la llamada aún devuelve `200` con `connected: false` y una matriz `posts` vacía, además de un `reason` que indica el motivo:

| `reason` | Qué hacer |
|---|---|
| _(ausente)_ | Aún no hay ninguna página conectada: ejecuta primero el flujo de conexión. |
| `no_instagram_account` | Hay una página de Facebook conectada, pero no hay ninguna cuenta comercial de Instagram vinculada a ella. Las publicaciones de Facebook se listan correctamente. |
| `token_expired` | La credencial de página almacenada ya no funciona: vuelve a conectar el canal. |

### Desconectar Instagram + Messenger

```
DELETE /channels/meta
```

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

**Respuesta**

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

Esto detiene el enrutamiento entrante tanto para Instagram como para Messenger. Es idempotente: llamarlo cuando no hay nada conectado sigue teniendo éxito.

---

## WhatsApp Business

Esto conecta un número oficial de WhatsApp Business. El número debe existir ya en la cuenta antes de llamar a la conexión. Al igual que con Meta, el titular de la cuenta autoriza en su navegador, y luego usted realiza sondeos hasta que el número informe `ONLINE`.

### Paso 1 - Iniciar la conexión de 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 | Obligatorio | Descripción |
|---|---|---|
| `phone_number` | Sí | El número a conectar, en formato E.164 (p. ej., `+14155551234`). |
| `only_waba_sharing` | No | Restringe la autorización a compartir una cuenta de WhatsApp Business existente, omitiendo la configuración de un nuevo remitente. El valor predeterminado es `false`. |
| `retry` | No | Vuelve a ejecutar la autorización para un número cuyo intento anterior no se completó. El valor predeterminado es `false`. |
| `business_name` | No | Sustitución cosmética para el nombre de la empresa que se muestra solo en la pantalla de consentimiento (máx. 256 caracteres). No se almacena. |
| `description` | No | Sustitución cosmética para la descripción de la empresa que se muestra solo en la pantalla de consentimiento (máx. 256 caracteres). No se almacena. |

**Respuesta**

```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` en el navegador del titular de la cuenta para autorizar. Una vez que aprueben, el registro se completa en segundo plano.

### Paso 2 - Sondear el estado hasta que esté ONLINE

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

Realice sondeos hasta que `status` sea `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".
```

**Respuesta**

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

El campo `status` puede ser:

| `status` | Significado |
|---|---|
| `PENDING` | Autorizado, la aprobación aún está en curso. Siga realizando sondeos. |
| `ONLINE` | Conectado y listo para enviar. |
| `RATE_LIMITED` | Demasiados intentos: espere antes de volver a intentarlo. |
| `REGISTRATION_FAILED` | No se pudo completar la configuración. |
| `DELETED` | El registro ya no existe. |

`live: true` significa que el estado se verificó con el proveedor en tiempo real; `false` significa que provino del último estado almacenado en caché.

### Desconectar un número de 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"
```

**Respuesta**

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

El número en sí permanece en la cuenta, por lo que puede volver a conectarlo más tarde.

---

## WhatsApp Web

WhatsApp Web vincula un número de WhatsApp normal escaneando un código QR, igual que al vincular un dispositivo en la aplicación de WhatsApp. El flujo es: iniciar la sesión, obtener el código QR y mostrarlo, y luego realizar sondeos hasta que el estado sea `connected`.

### Paso 1 - Iniciar una sesión de emparejamiento de 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 | Requerido | Descripción |
|---|---|---|
| `phone_number` | Sí | El número de WhatsApp a conectar, en formato E.164. |
| `proxy_country` | No | Código de país ISO 3166-1 alpha-2 para la región de enrutamiento. Se detecta automáticamente desde el número si se omite. |
| `force_new` | No | Descartar cualquier sesión existente e iniciar un emparejamiento nuevo. El valor predeterminado es `false`. |
| `import_contacts` | No | Importar los contactos existentes del dispositivo en la primera conexión. El valor predeterminado es `false`. |
| `pause_ai_for_imported_contacts` | No | Al importar contactos, mantener las respuestas automáticas pausadas para ellos. El valor predeterminado es `true`. |
| `import_existing_chats` | No | Importar el historial de chat existente (requiere `import_contacts: true`). El valor predeterminado es `false`. |

**Respuesta**

```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"
}
```

### La opción más sencilla para WhatsApp Web: entregar `connect_url`

La respuesta incluye un `connect_url` listo para usar: una página alojada que muestra el código QR, lo actualiza automáticamente a medida que rota y cambia a un mensaje de éxito en el momento en que se vincula el número. Simplemente proporcione este enlace al titular de la cuenta (ábralo en un navegador, envíeselo o muéstrelo como un QR o botón) y pídale que lo escanee con WhatsApp; no necesita obtener el QR ni realizar sondeos usted mismo. El enlace funciona durante unos 30 minutos (`connect_url_expires_at`); si caduca antes de que terminen, inicie una nueva conexión para obtener uno nuevo.

Esta es la ruta recomendada cuando una persona puede abrir un enlace. Los pasos manuales a continuación (obtener el QR usted mismo, consultar el estado) son para integraciones que desean renderizar el QR dentro de su propia interfaz.

La respuesta también le proporciona el `poll_qr_path` y el `poll_status_path` exactos que debe utilizar, para que no tenga que crearlos usted mismo.

### Paso 2 - Obtener el código QR y mostrarlo

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

**Respuesta**

```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"
}
```

Renderice el código QR para que el usuario lo escanee con su teléfono (WhatsApp > Dispositivos vinculados > Vincular un dispositivo):

- `qr_data_url` es una imagen lista para usar: colóquela directamente en una etiqueta `<img src>`.
- `qr_code` es la carga útil sin procesar si prefiere generar la imagen usted mismo.

El código QR tiene una duración breve. Si llama a esto justo después de iniciar la sesión, es posible que obtenga un `404` con "QR code not available yet" (código QR aún no disponible); simplemente espere un momento y vuelva a intentarlo. Si obtiene un `410` ("QR code expired" - código QR caducado), reinicie la conexión para obtener un código nuevo.

### Paso 3 - Sondear el estado hasta que se conecte

```
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").
```

**Respuesta**

```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` | Aún no hay sesión (error terminal). |
| `qr_pending` | Esperando a que se escanee el QR. |
| `connecting` | Escaneado, finalizando la configuración. |
| `connected` / `open` | Vinculado y activo: esto es un éxito. |
| `disconnected` | Sesión finalizada (error terminal). |

### Desconectar una sesión de 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"
```

**Respuesta**

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

Esto desvincula el dispositivo y elimina la conexión. Siempre limpia el estado local, por lo que es idempotente incluso si la sesión subyacente ya había desaparecido.

---

## Telegram

> **Disponibilidad:** Telegram se conecta como cualquier otro canal y está abierto para todas las cuentas; no es necesario que esté activado para usted. Los endpoints de Telegram a continuación aún pueden devolver `403` si Telegram no está incluido en el plan de la cuenta, en cuyo caso el error dice `"This channel is not included in your current plan. Upgrade to unlock it."`.

Telegram conecta una cuenta personal mediante un número de teléfono y un código de inicio de sesión de un solo uso (y una contraseña de dos factores, si la cuenta tiene una configurada). El flujo es: iniciar la sesión, enviar el código, enviar opcionalmente la contraseña y luego confirmar mediante el estado.

### Paso 1 - Iniciar una sesión de conexión de 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 | Requerido | Descripción |
|---|---|---|
| `phone_number` | Sí | El número de teléfono de la cuenta para conectar, en formato E.164. |
| `mode` | No | `code` (predeterminado) envía un código de inicio de sesión de un solo uso a la cuenta; `qr` devuelve un token de inicio de sesión y una URL de código QR para mostrar. |
| `proxy_country` | No | Código de país ISO 3166-1 alpha-2 para la ruta de red saliente. |
| `force_new` | No | Cuando es `true`, descarta cualquier sesión existente y comienza desde cero. |

**Respuesta**

```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
}
```

En el modo `code`, la cuenta recibe un código de inicio de sesión en Telegram y `status` es `code_required`. (En el modo `qr`, la respuesta también incluye `login_token` y `qr_url` para mostrar para el escaneo, y `status` es `qr_required`.)

### La opción más sencilla para Telegram: entregar `connect_url`

La respuesta incluye un `connect_url` listo para usar: una página alojada que finaliza la conexión por sí misma. En el modo `code`, el titular de la cuenta introduce el código de inicio de sesión y, si su cuenta lo tiene, una contraseña de verificación en dos pasos. En el modo `qr`, la página muestra un código QR que se actualiza automáticamente para que lo escaneen desde la aplicación de Telegram. En cualquier caso, informa del éxito por sí misma, por lo que puedes simplemente proporcionar este enlace al titular de la cuenta en lugar de crear tu propia interfaz de usuario y realizar sondeos. El enlace funciona durante unos 30 minutos (`connect_url_expires_at`); si caduca, inicia una nueva conexión para obtener uno nuevo.

Los pasos manuales a continuación (recopilar el código tú mismo, enviarlo, consultar el estado; o renderizar `qr_url` y consultar) son para integraciones que desean renderizar la interfaz de usuario por sí mismas.

### Paso 2 - Enviar el código de inicio de sesión

```
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()
```

**Respuesta**

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

Si `status` es `connected`, has terminado. Si la cuenta tiene habilitada la autenticación de dos factores, `status` será `password_required` en su lugar; ve al paso 3.

### Paso 3 - Enviar la contraseña de dos factores (solo si es necesario)

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

Llama a esto solo cuando el paso 2 haya devuelto `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()
```

**Respuesta**

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

### Comprobar el estado de 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"
```

**Respuesta**

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

`status` puede ser `connected`, `code_required`, `password_required`, `initializing`, `disconnected`, `not_initialized` o `error`.

### Desconectar Telegram

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

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

**Respuesta**

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

Idempotente: las llamadas repetidas tienen éxito.

---

## Instagram (cuenta personal)

> Versión beta de disponibilidad limitada, habilitada por cuenta. Esto conecta una cuenta personal de Instagram iniciando sesión con su nombre de usuario y contraseña (no la API oficial de Business). Si la cuenta no está habilitada para la versión beta, la llamada de conexión devuelve un error de permiso.

Debido a que esto requiere el inicio de sesión de Instagram del propio titular de la cuenta, la ruta más sencilla es entregarle la `connect_url` alojada y dejar que introduzca sus credenciales allí; su integración nunca maneja la contraseña.

### Paso 1 - Iniciar una conexión de Instagram (personal)

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

Envía el `username` y `password` de Instagram.

**Respuesta**

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

Si la cuenta tiene autenticación de dos factores o Instagram presenta un punto de control, `status` regresa como `two_factor_required` o `challenge_required`: envía el código a `/connect/{id}/verify-2fa` o `/connect/{id}/verify-challenge` a continuación, luego consulta `/connect/{id}/status` hasta que `connected`. `{id}` es el nombre de usuario de Instagram normalizado devuelto como `account_id`/`username` en la respuesta anterior: úsalo en cada paso a continuación.

### Paso 2 - Enviar el código de dos factores (si se solicita)

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

Llama a esto solo cuando el paso 1 (o el paso 3) devuelva `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" }'
```

**Respuesta**

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

`status` puede regresar como `connected` (hecho), `two_factor_required` (código incorrecto, intenta de nuevo) o `challenge_required` (Instagram también solicita un código de punto de control: ve al paso 3).

### Paso 3 - Enviar el código de confirmación del punto de control (si se solicita)

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

Llama a esto solo cuando un paso anterior haya devuelto `challenge_required`. Tiene la misma forma de solicitud y respuesta que el paso 2 anterior.

```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" }'
```

### Comprobar el estado de Instagram (personal)

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

Consulta esto hasta que `status` sea `connected`, o hasta que informe un error terminal.

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

**Respuesta**

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

`status` puede ser `connected`, `two_factor_required`, `challenge_required`, `initializing`, `disconnected`, `not_initialized` o `error`. `live: true` significa que esto se leyó en vivo desde el trabajador de conexión en lugar de ser un valor almacenado en caché.

### La opción más sencilla para Instagram (personal): entregar `connect_url`

La respuesta incluye un `connect_url`: una página alojada donde el titular de la cuenta introduce su nombre de usuario y contraseña de Instagram (y un código de 2FA o de punto de control si Instagram lo solicita), y que informa del éxito por sí misma. Las credenciales van directamente a Instagram y no se almacenan. Proporcione este enlace al titular de la cuenta en lugar de recopilar su contraseña en su propia interfaz de usuario. El enlace funciona durante unos 30 minutos (`connect_url_expires_at`).

### Desconectar Instagram (personal)

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

Idempotente: las llamadas repetidas tienen éxito.

### Sincronizar seguidores

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

Activa manualmente una sincronización de seguidores para una cuenta conectada: es el mismo trabajo que se ejecuta automáticamente en segundo plano, expuesto aquí para una acción de "Actualizar seguidores" bajo demanda. Obtiene la lista actual de seguidores de la cuenta, registra a los nuevos y (cuando una campaña en vivo tiene activada la captación de seguidores) envía a los nuevos seguidores un mensaje directo de apertura, hasta un límite diario.

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

**Respuesta**

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

> Estos cinco campos son el único lugar en esta página que devuelve `camelCase` en lugar de `snake_case`; así es como está configurado este endpoint actualmente, no es un error tipográfico. `isBaselineSeed: true` significa que esta fue la primera sincronización después de conectarse, la cual solo registra la lista inicial de seguidores y nunca envía mensajes directos de captación (por lo que `dmsSent` siempre es `0` en esa ejecución).

La primera llamada para una cuenta puede tardar un poco (recorrer toda la lista de seguidores); las llamadas posteriores son más rápidas ya que solo se comparan los nuevos seguidores. `404` significa que la cuenta no está conectada; `412` significa que la conexión aún no ha terminado de inicializarse; espere y vuelva a intentarlo.

---

## LINE

LINE es el canal más sencillo de conectar porque no requiere redirecciones de navegador ni sondeos. El cliente crea un canal de Messaging API en la consola de desarrolladores de LINE, copia dos valores y usted los envía en una sola llamada. Luego, usted les proporciona una URL de webhook para que la peguen en la consola.

### Paso 1: Conectar con las credenciales del 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 | Obligatorio | Descripción |
|---|---|---|
| `channel_access_token` | Sí | El token de acceso al canal de Messaging API de larga duración de la Cuenta Oficial. Se utiliza para enviar y recibir mensajes. |
| `channel_secret` | Sí | El secreto del canal de Messaging API, utilizado para verificar las firmas de los eventos entrantes. |
| `channel_id` | No | El ID numérico del canal. Solo informativo. |

**Respuesta**

```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/..."
}
```

Dos campos son importantes para lo que debe hacer a continuación:

- **`webhook_url`**: el cliente debe pegar esto en el campo **Webhook URL** de su canal de LINE en la consola de desarrolladores de LINE (y habilitar "Use webhook"). Hasta que lo hagan, no llegarán mensajes entrantes. Muéstrelo de forma destacada.
- **`chat_mode_ok`**: cuando `false`, la Cuenta Oficial está en modo "chat" y no recibirá ni enviará mensajes hasta que se cambie al modo "bot" en el LINE Official Account Manager. Condicione su proceso de incorporación a este indicador y dígale al cliente que cambie el modo.

> El `channel_access_token` y el `channel_secret` nunca son devueltos por ningún endpoint. Guárdelos de su lado si los necesita de nuevo; de lo contrario, vuelva a pegarlos desde la consola de LINE.

El `bot_user_id` devuelto aquí es el identificador de conexión que utiliza en las llamadas de estado, verificación y desconexión a continuación.

### Paso 2: Volver a verificar después de la configuración del webhook

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

Después de que el cliente termine de configurar la URL del webhook y cambie al modo bot, llame a esto para volver a validar el token almacenado y actualizar el modo de chat en caché.

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

**Respuesta**

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

Si `token_valid` es `false`, el token de acceso almacenado ya no autentica; pídale al cliente que lo vuelva a emitir en la consola y llame a `POST /channels/line` nuevamente con el nuevo token.

### Comprobar el estado de LINE

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

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

**Respuesta**

```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
}
```

LINE no tiene una fuente de estado en vivo, por lo que `live` siempre es `false` aquí; los valores reflejan el estado capturado en el momento de la conexión (o la última verificación).

### Desconectar LINE

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

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

**Respuesta**

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

---

## Viber

Viber se conecta de la misma manera que LINE: pegue el token de autenticación del bot desde el Panel de administración de Viber en una llamada, con una diferencia que vale la pena conocer: conectarse también REGISTRA nuestro webhook en su bot en ese mismo momento, por lo que no hay un paso de consola separado después. Eso también significa que un intento de conexión puede fallar si nuestra entrada no puede responder a la verificación de webhook síncrona de Viber, no solo si el token en sí es incorrecto.

### Paso 1 - Conectar con el token de autenticación del bot

```
POST /channels/viber
```

| Campo | Obligatorio | Descripción |
|---|---|---|
| `auth_token` | Sí | El token de autenticación del bot, desde el Panel de administración de Viber (Configuración de mi 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" }'
```

**Respuesta**

```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"]
}
```

El token de autenticación nunca es devuelto por ningún endpoint; guárdelo de su lado si necesita volver a pegarlo. `bot_id` es el identificador de conexión utilizado por las llamadas de estado, verificación y desconexión a continuación.

### Comprobar el estado de Viber

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

Informa del estado de conexión almacenado. Añada `?live=true` para volver a comprobar el bot con Viber y actualizar el registro del webhook en caché; es útil antes de asumir que un bot silencioso está realmente roto.

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

**Respuesta**

```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 el webhook del bot ya no apunta a nosotros; los mensajes entrantes no llegan. Esto generalmente significa que otra herramienta conectó el mismo bot después (el registro de webhook de Viber funciona con el último que escribe). Soluciónelo con la llamada de verificación a continuación, no es necesario pedirle al cliente que vuelva a pegar su token. `live` es `false` cuando la respuesta es el último estado almacenado en caché en lugar de una verificación nueva con Viber.

### Volver a registrar el webhook

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

La acción de reparación para `webhook_ok: false`: vuelve a registrar nuestro webhook en el bot utilizando el token de autenticación ya almacenado.

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

**Respuesta**

```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 el token almacenado ya no funciona; vuelva a conectarse con `POST /channels/viber` y un token nuevo.

### Desconectar Viber

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

Anula el registro de nuestro webhook en el lado de Viber (mejor esfuerzo) y elimina la conexión.

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

**Respuesta**

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

---

## TikTok

> **Disponibilidad:** Beta de disponibilidad limitada, habilitada por cuenta. Conectar TikTok devuelve un error de permiso hasta que la cuenta esté habilitada para ello.

TikTok Business Messaging es un canal OAuth completo como Meta, pero más sencillo en cuanto al sondeo: no hay un paso de sondeo de estado dedicado que construir, ya que la cuenta conectada aparece por sí sola una vez que TikTok redirige de vuelta y se escribe la conexión. El punto final de estado a continuación existe para confirmar el estado bajo demanda (herramientas de soporte, comprobaciones de estado), no como algo en lo que necesite iterar durante la conexión.

### Paso 1 - Iniciar la conexión de TikTok

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

No requiere credenciales: el titular de la cuenta autoriza completamente desde su navegador.

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

**Respuesta**

```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` en el navegador del titular de la cuenta para que pueda iniciar sesión en TikTok y aprobar el acceso. El estado caduca en `expires_at` (unos 30 minutos); si caduca, empiece de nuevo. No hay un atajo de página alojada `connect_url` para TikTok; abrir `oauth_url` usted mismo es la única vía.

### Comprobar el estado de TikTok

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

`openId` es el open_id de la cuenta de TikTok Business, conocido una vez que se ha ejecutado la devolución de llamada OAuth.

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

**Respuesta**

```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
}
```

TikTok no tiene una comprobación de estado en vivo económica, por lo que `live` siempre es `false` aquí: los campos reflejan lo que escribió la conexión (o la última actualización de token). `status: "reauth_required"` con `status_reason` establecido significa que la cuenta necesita pasar por la conexión de nuevo; los tokens de TikTok se actualizan automáticamente en una rotación anual, y esto es lo que aparece si esa rotación falla alguna vez.

### Desconectar TikTok

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

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

**Respuesta**

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

---

## GoHighLevel

GoHighLevel (GHL) es una integración de CRM, no un canal de mensajería; conectarlo no consume un espacio de canal en el plan, ya que utiliza los canales existentes de la cuenta en lugar de añadir uno nuevo. También es la única integración en esta página que puede mantener **más de una conexión a la vez**: cada subcuenta de GHL ("ubicación") en la que el cliente instala la aplicación obtiene su propia entrada.

### Paso 1 - Iniciar la conexión de GHL

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

| Campo | Obligatorio | Descripción |
|---|---|---|
| `brand` | No | Qué listado del marketplace de GHL utilizar para la autorización. El valor predeterminado es el listado estándar; solo es relevante si su implementación tiene configurada más de una aplicación en el marketplace. |

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

**Respuesta**

```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 `oauth_url` en el navegador del titular de la cuenta para que pueda elegir una ubicación de GHL y aprobar el acceso. El estado caduca en `expires_at` (aproximadamente 30 minutos).

### Listar conexiones de GHL

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

A diferencia de otros canales, este no es el estado de una sola conexión; enumera todas las ubicaciones que la cuenta ha conectado.

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

**Respuesta**

```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" }
      ]
    }
  ]
}
```

### Desconectar una ubicación de GHL

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

Elimina la conexión aquí, lo que detiene toda sincronización y activador para esa ubicación. Esto no desinstala la aplicación en el lado de GHL; el cliente la elimina de sus instalaciones del marketplace de GHL si también desea eso.

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

**Respuesta**

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

---

## Números de teléfono (comprar y liberar)

En lugar de conectar un número existente, puede comprar un número nuevo compatible con WhatsApp directamente. Busque números disponibles, compre uno y luego realice sondeos hasta que finalice el aprovisionamiento.

::: note
**Nota:** Los números comprados aquí son compatibles con WhatsApp. El registro del remitente de WhatsApp se ejecuta en segundo plano después de la compra, por lo que debe consultar el estado hasta que llegue a `ONLINE` antes de enviar. Los créditos se deducen en el momento de la compra y **no** se reembolsan cuando libera el número.
:::


### Paso 1 - Buscar números disponibles

```
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 | Requerido | Descripción |
|---|---|---|
| `country_code` | Sí | Código de país ISO 3166-1 alpha-2 en el que buscar (p. ej., `US`, `GB`, `NL`). |
| `type` | No | Clase de número preferida, `local` o `mobile`. Es posible que se devuelvan ambas clases. |

**Respuesta**

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

Cada resultado muestra el `purchase_credits` único y el `monthly_credits` recurrente. Un número proporcionado por la plataforma cuesta al menos 50 créditos al mes, aumentando según el precio mensual del operador, cobrado en el momento de la compra y en cada renovación. Utilice el `purchase_credits` / `monthly_credits` que devuelve la búsqueda; nunca calcule un precio usted mismo. La primera búsqueda en una cuenta nueva aprovisiona algunos recursos subyacentes, por lo que puede ser un poco más lenta que las búsquedas posteriores.

### Paso 2 - Comprar un número

```
POST /phone-numbers
```

Utilice un `phone_number` de los resultados de búsqueda.

**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 | Requerido | Descripción |
|---|---|---|
| `phone_number` | Sí | Un número devuelto por la búsqueda de números disponibles, en formato E.164. |
| `country_code` | Sí | Código de país ISO 3166-1 alpha-2 (p. ej., `US`). |
| `display_name` | No | Una etiqueta descriptiva. Por defecto es el número de teléfono. |
| `category` | No | Etiqueta de categoría opcional. |

**Respuesta**

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

El número comienza en el estado `PURCHASED`. El registro de WhatsApp procede entonces en segundo plano: `PURCHASED` -> `PENDING` -> `ONLINE`.

> Si la compra falla porque falta una dirección comercial o no se ha configurado otro detalle requerido, obtendrá un `400` con un `error` descriptivo. Configure el detalle faltante e inténtelo de nuevo.

### Paso 3 - Consultar hasta obtener ONLINE

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

Este es el endpoint compartido de estado de número de teléfono; funciona tanto para números de WhatsApp comprados como para sus otros números conectados.

**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".
```

**Respuesta**

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

### Paso 4 - Liberar un 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"
```

**Respuesta**

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

Lo que esto hace depende de a quién pertenezca el número.

Para un número **alquilado a través de la plataforma**, es una liberación real: el remitente de WhatsApp se da de baja, el número se devuelve al operador y se elimina de la cuenta, se aplica un periodo de enfriamiento de 7 días durante el cual nadie puede volver a comprar el número y no se reembolsan créditos.

Para un número **que la cuenta trajo por sí misma** (su propia cuenta de Twilio, su propia aplicación de Meta o cuenta de WhatsApp Business, o una pasarela SMS de Android), la misma llamada solo lo elimina de la cuenta. No se libera nada en el proveedor ascendente y no se registra ningún periodo de enfriamiento, por lo que el número puede volver a conectarse inmediatamente. Su registro de remitente de WhatsApp, si tenía uno, puede sobrevivir o no: el proceso de desmontaje intenta eliminar al remitente utilizando las credenciales de Twilio gestionadas por la plataforma de la cuenta. En una cuenta que aún utiliza la configuración gestionada, esas credenciales son válidas y el remitente se elimina, por lo que volver a conectarlo significa registrarlo de nuevo. En una cuenta que ha cambiado a su propio Twilio, la eliminación no puede autenticarse y el remitente permanece registrado en esa cuenta; por lo tanto, volver a conectarlo es simplemente volver a adjuntar el remitente existente.

### Agregar un número que ya posee (BYO)

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

Omite por completo el flujo de búsqueda y compra anterior. Úselo cuando la cuenta traiga su propio número (su propio Twilio, su propia cuenta de WhatsApp Business de Meta o una puerta de enlace SMS de Android) en lugar de alquilar uno a través de la plataforma. Esto solo registra el número: no se cobran créditos y no se aprovisiona nada con un proveedor aquí. El número permanece inactivo hasta que el titular de la cuenta complete el OAuth de WhatsApp para registrar un remitente en él (el mismo flujo que inicia el botón "Traiga su propio número" del panel).

| Campo | Obligatorio | Descripción |
|---|---|---|
| `phone_number` | Sí | El número a agregar, en formato E.164 (p. ej., `+14155551234`). |
| `country_code` | Sí | Código de país ISO 3166-1 alpha-2 (p. ej., `US`). |
| `display_name` | No | Una etiqueta descriptiva. El valor predeterminado es el número de teléfono. |
| `category` | No | Etiqueta de categoría 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"
  }'
```

**Respuesta** (`201 Created`):

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

Un `phone_number` que no es un número E.164 real (o que parece el número de prueba de WhatsApp de Meta, que nunca puede enviar mensajes a clientes reales) devuelve `400`. Agregar un número que ya existe en la cuenta, incluso si está escrito de forma ligeramente diferente, como las formas `+52` frente a `+521` de México, devuelve `409` en lugar de crear una fila duplicada.

### Establecer un número como principal

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

Cambia un número a `is_active: true` y todos los demás números de la cuenta a `is_active: false`, de forma atómica: la cuenta nunca termina con dos números activos, o ninguno, a mitad de la solicitud. `is_active` no se puede establecer a través del punto final de actualización general a propósito; esta llamada dedicada es la única forma de cambiar qué número es el principal.

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

**Respuesta**

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

`phone_number` aquí es el objeto de número completo (la misma forma que devuelve `GET /phone-numbers`), no solo la cadena. Un `phoneNumber` que no está en la cuenta devuelve `404`.

### Eliminar el registro de un número (sin liberarlo)

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

Una eliminación simple del registro del número en esta cuenta: no hay liberación ni cancelación de registro del lado del proveedor, y no se aplica el período de enfriamiento de 7 días como en el paso de liberación anterior. Úselo para borrar registros de BYO, WhatsApp Web, Telegram o LINE, o una entrada obsoleta, sin pasar por el flujo de liberación administrada. A diferencia de una liberación, eliminar un número que no está en la cuenta es un `404`, no un éxito silencioso.

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

**Respuesta**

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

---

## Enrutar un canal a una campaña

Conectar un canal hace que los mensajes lleguen **a** la cuenta. No decide **qué Agente de IA los responde**.

El enrutamiento es gestionado por los **Puntos de entrada** (Entry Points) en un Agente de IA, no por las campañas. Cada canal tiene un Punto de entrada predeterminado que designa al Agente que responde a los contactos nuevos y desconocidos en ese canal:

| Qué desea hacer | Llamada |
|---|---|
| Apuntar un canal al Agente que debería responderlo | `PUT /entry-points/channel-defaults` con cuerpo `{ "channel": "instagram", "agent_id": "AGENT_ID" }` |
| Comprobar si la jerarquía de Puntos de entrada está activa para la cuenta | `GET /entry-points/routing-status`, que devuelve `{ "success": true, "cutover_enabled": true }` una vez que los Puntos de entrada deciden el enrutamiento de esa cuenta |
| Dejar un canal sin ningún Agente que lo responda | `DELETE /entry-points/channel-defaults?channel=instagram` |

Hasta que un canal tenga un Punto de entrada (Entry Point), el primer mensaje de alguien con quien nunca has hablado se almacena, pero nada lo recoge y ningún asistente responde. Este es el paso que la mayoría de las integraciones pasan por alto: conectar Instagram y crear un Agente no es suficiente por sí solo; también debes apuntar el canal hacia el Agente. El conjunto completo de llamadas, incluyendo un Agente por número de WhatsApp, palabras clave y reglas de comentarios, se encuentra en la [API de Puntos de entrada](entry-points.md).

`POST /channels/campaign` todavía escribe el mapa de enrutamiento de campañas heredado por canal, documentado a continuación, pero ese mapa ya no se consulta para el enrutamiento entrante en ninguna cuenta; se conserva solo para reversiones. No desarrolle basándose en él.

### Enrutar uno o más canales (mapa de enrutamiento de campañas heredado)

`POST /channels/campaign`

**Campos de la solicitud**

| Campo | Requerido | Descripción |
|---|---|---|
| `campaign_id` | Sí | La campaña que debe responder a los nuevos contactos en estos canales. Debe pertenecer a la cuenta. |
| `channels` | Sí | Una matriz no vacía de canales para enrutar. Permitidos: `whatsapp`, `whatsapp_web`, `telegram`, `instagram`, `messenger`, `chat_widget`, `custom_channel`, `sms`, `email`. |

La ranura de enrutamiento y la lista `enabled_channels` de la campaña se actualizan juntas en una operación atómica, por lo que nunca pueden desincronizarse. Un canal ya enrutado a una campaña diferente simplemente se redirige a 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()
```

**Respuesta**

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

### Qué debe cumplirse para que el enrutamiento se active realmente

En una cuenta que todavía lee el mapa de enrutamiento de campañas heredado, el enrutamiento tiene éxito como llamada a la API, pero tres factores en la campaña deciden si un mensaje entrante real es respondido. Compruebe los tres cuando un canal enrutado permanezca en silencio.

| Requisito | Qué sucede en caso contrario |
|---|---|
| `type` es `Incoming from Unknown Contacts` o `Combined` | La solicitud es rechazada con `400`. Las campañas de salida y de palabras clave no pueden ocupar un espacio de enrutamiento. |
| `status` es `Live` | El enrutamiento se almacena pero nunca recoge nada. Una campaña `Draft` es la causa más común de "lo enruté y no sucede nada". |
| `ai_mode` es `true` | El contacto se crea y el mensaje se almacena, pero el asistente nunca responde. |

La coincidencia de palabras clave ahora reside en los Puntos de entrada: cree un Punto de entrada de tipo `keyword` en el Agente de IA que debería responder.

### Una campaña por canal

Cada canal tiene exactamente un espacio de enrutamiento heredado. Enrutar una segunda campaña al mismo canal vuelve a apuntar silenciosamente el espacio y devuelve `200`; no hay error de conflicto. La campaña anterior sigue manejando los contactos que ya tiene; simplemente deja de recibir nuevos.

### Borrar el enrutamiento de un canal

`DELETE /channels/campaign/{channel}`

Elimina el enrutamiento de un solo canal, independientemente de la campaña a la que apunte actualmente, y retira el canal de la `enabled_channels` de dicha campaña. Los nuevos contactos desconocidos en el canal ya no serán captados por ninguna campaña. Los contactos que ya están en la campaña continúan como antes.

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

**Respuesta**

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

Es idempotente: borrar un canal que nunca fue enrutado también devuelve `200`, con `cleared: false` y `campaign_id: null`. Este endpoint requiere la función de **campañas entrantes** en el plan; sin ella, obtendrá un `403`.


---

## Usa tu propia aplicación de Meta (Instagram + Messenger)

De forma predeterminada, la conexión de Instagram + Messenger se ejecuta a través de la aplicación Meta de la plataforma, por lo que el nombre de esa aplicación es lo que el titular de la cuenta ve en la pantalla de consentimiento de Facebook. Si deseas que la pantalla de consentimiento muestre **tu** marca en su lugar, puedes registrar tu propia aplicación Meta y dirigir todo el flujo a través de ella. Una vez configurado, se aplica a tu cuenta; nada cambia en las llamadas de conexión anteriores, excepto la marca.

> **Esto solo cubre Instagram + Messenger.** Las conexiones de WhatsApp, WhatsApp Web, Telegram y LINE no se ven afectadas por una aplicación de Meta personalizada.

### Lo que tu aplicación necesita primero

Esta es la parte que lleva tiempo y ocurre completamente del lado de Meta:

1. **Una aplicación** de tipo Business, con los productos de Messenger e Instagram añadidos.
2. **Acceso avanzado** (a través de la Revisión de aplicaciones de Meta) para: `pages_show_list`, `pages_messaging`, `pages_manage_metadata`, `pages_read_engagement`, `instagram_basic`, `instagram_manage_messages`. Sin el Acceso avanzado, solo las personas que tienen un rol en tu aplicación pueden completar la conexión; las conexiones de tus clientes fallarán. La Revisión de aplicaciones suele tardar unas semanas y requiere la Verificación del negocio.
3. **Una configuración de Inicio de sesión con Facebook para empresas** creada dentro de tu aplicación, otorgando los mismos permisos. Su ID de configuración numérica es por aplicación, por lo que debes crear el tuyo propio.

Si a tu aplicación le falta alguno de los permisos requeridos, la conexión fallará en el momento de conectarse con un error claro que indica qué falta (visible en el sondeo `/status` como `byo_app_missing_permissions`), en lugar de parecer que funciona y fallar en el primer mensaje.

### Paso 1 - Guarda tu aplicación

`PUT /account-config/meta-app`

| Campo | Requerido | Descripción |
|---|---|---|
| `app_id` | Sí | Tu ID de aplicación de Meta (Configuración → Básico). |
| `app_secret` | Sí | Tu secreto de aplicación de Meta. Se verifica contra Meta antes de almacenarse y luego se cifra. Nunca se devuelve mediante ningún endpoint. |
| `config_id` | Sí | El ID numérico de la configuración de Inicio de sesión con Facebook para empresas dentro de tu aplicación. |

Los tres son necesarios para el flujo de inicio de sesión de Facebook. Si solo ejecutas la vía de inserción de tokens de inicio de sesión de Instagram descrita más adelante, puedes omitirlos por completo.

```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"
  }'
```

**Respuesta**

```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"
  }
}
```

### Paso 2 - Configura tu aplicación para comunicarse con nosotros

En el panel de control de tu aplicación de Meta:

1. **Webhooks** - para los productos de Instagram y Messenger, establece la URL de devolución de llamada (Callback URL) al valor `webhook_urls` correspondiente de la respuesta, y el token de verificación (Verify token) a `verify_token`. Suscríbete a los campos `messages`, `messaging_postbacks` y `comments`.
2. **URI de redireccionamiento de OAuth válidos** - añade `https://api.youraiconnector.com/v1/auth-meta-callback-handler` para que el flujo de consentimiento pueda regresar.

`GET /account-config/meta-app` devuelve el mismo material de configuración en cualquier momento; `DELETE /account-config/meta-app` elimina la aplicación (las conexiones futuras volverán a la aplicación de la plataforma; también elimina la suscripción al webhook dentro de tu aplicación).

### Paso 3 - Conectar como de costumbre

Nada más cambia. `POST /channels/meta/connect` (y la página `connect_url` alojada) utiliza automáticamente tu aplicación para tu cuenta; el `uses_byo_meta_app: true` de la respuesta confirma qué aplicación mostrará la pantalla de consentimiento. El envío de mensajes, la selección de páginas y las desconexiones funcionan de forma idéntica.

## Traiga su propia aplicación de inicio de sesión de Instagram (push de token)

La sección anterior cubre el flujo de inicio de sesión de Facebook, donde la cuenta se conecta a través de una página de Facebook. Meta también ofrece la **API de Instagram con inicio de sesión de Instagram** (Inicio de sesión empresarial para Instagram): el titular de la cuenta se autentica en el propio Instagram, sin necesidad de una cuenta o página de Facebook.

Si su plataforma ya ejecuta su propia aplicación de Meta con ese producto, no necesita ningún flujo OAuth por nuestra parte. Sus clientes autorizan **su** aplicación y usted nos envía la credencial finalizada por cuenta:

1. Guarda las credenciales de su aplicación de Instagram una vez (para que podamos verificar sus webhooks).
2. Por cuenta, envía el ID de cuenta profesional de Instagram + el token de usuario de Instagram de larga duración que obtuvo su aplicación.
3. Apunta el webhook de mensajería de Instagram de su aplicación hacia nosotros. Los eventos de las cuentas que nunca envió se reconocen y se ignoran.
4. Usted es dueño del ciclo de vida del token: actualice los tokens en su propio sistema y envíe cada token actualizado con la misma llamada. Nosotros nunca actualizamos un token enviado.

### Lo que tu aplicación necesita primero

- El producto **Instagram** ("Configuración de API con inicio de sesión de Instagram") agregado a su aplicación de Meta. Ese producto tiene su **propio par de ID de aplicación y secreto de aplicación**, separado del ID/secreto de la aplicación de Facebook; encuéntrelos en el panel de configuración del producto.
- **Acceso avanzado** (a través de la revisión de la aplicación de Meta) para `instagram_business_basic` y `instagram_business_manage_messages` (agregue `instagram_business_manage_comments` si utiliza automatizaciones de comentarios). Sin esto, solo las personas con un rol en su aplicación pueden autorizarla.

### Paso 1 - Guarde las credenciales de su aplicación de Instagram

El mismo endpoint que el anterior: envía el par de Instagram a `PUT /account-config/meta-app`. Los campos de Facebook no son necesarios para esta vía: envía el par por sí solo si solo ejecutas el inicio de sesión de Instagram, o junto con los campos de Facebook si ejecutas ambos. Un guardado siempre describe la configuración completa, por lo que cualquier conjunto que omitas se eliminará.

| Campo | Requerido | Descripción |
|---|---|---|
| `instagram_app_id` | Juntos | El ID de aplicación numérico propio del producto de Instagram (no el ID de aplicación de Facebook). |
| `instagram_app_secret` | Juntos | El secreto de aplicación propio del producto de Instagram. Cifrado en reposo, nunca se devuelve. |

```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"
  }'
```

**Respuesta**: contiene la URL del webhook de inicio de sesión de Instagram (las URLs `instagram` y `messenger` solo aparecen cuando también se almacenan los campos de Facebook):

```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"
  }
}
```

En el panel de **Webhooks** de su aplicación para el producto de Instagram, establezca la URL de devolución de llamada (Callback URL) en `webhook_urls.instagram_login`, el token de verificación en `verify_token` y suscríbase a los campos `messages` y `comments`.

### Paso 2 - Envíe un token por cuenta

`PUT /channels/instagram-login/token`

Funciona con `sub_account_id` como cualquier otra ruta, por lo que una clave de agencia puede aprovisionar a toda su flota.

| Campo | Requerido | Descripción |
|---|---|---|
| `ig_user_id` | Sí | El **ID de cuenta profesional de Instagram**: el campo `user_id` de `GET https://graph.instagram.com/v21.0/me?fields=user_id,username`. Este es el mismo ID que los webhooks de Instagram llevan como `entry.id`. ⚠️ **No** es el campo `id` de `/me`; ese tiene alcance de aplicación y difiere según la aplicación de Meta. Enviar el ID con alcance de aplicación devuelve un `400` que indica el error. |
| `access_token` | Sí | El token de usuario de Instagram de larga duración que obtuvo su aplicación para esa cuenta. Se valida en vivo contra Instagram antes de almacenarse: el token debe funcionar y pertenecer a `ig_user_id`. |
| `expires_at` | No | Expiración ISO-8601 del token. Alternativamente, envíe `expires_in` (segundos). El valor predeterminado es 60 días. |
| `username` | No | El @handle de la cuenta; de todos modos lo leemos desde Instagram. |

```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"
  }'
```

**Respuesta**

```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 del envío, suscribimos su aplicación a los webhooks de esa cuenta (`subscribed_apps` con el token enviado), por lo que los mensajes comienzan a fluir sin ninguna llamada adicional por su parte.

**Actualización** - envíe el token actualizado al mismo endpoint con el mismo `ig_user_id`; esto actualiza el token almacenado y su fecha de caducidad en el mismo lugar.

**Conflictos** - una cuenta de Instagram nunca está activa en dos conexiones. Si la cuenta ya está conectada en otro lugar, o en esta misma cuenta a través del flujo de la página de Facebook, el push devuelve un `409` indicándole qué conexión debe desconectar primero. Una conexión de flujo de Facebook nunca se reemplaza automáticamente, ya que también podría estar dando servicio a Messenger.

### Paso 3 - Desconectar cuando un cliente se va

`DELETE /channels/instagram-login/token` (misma autenticación y `sub_account_id`) cancela la suscripción a los webhooks de la mejor manera posible y elimina la credencial almacenada. Siempre tiene éxito, incluso cuando el token ya ha caducado; y una vez que la credencial desaparece, los eventos de webhook de esa cuenta se ignoran.

---

## Consejos para crear un envoltorio (wrapper) fiable

- **Realice sondeos con moderación.** Cada pocos segundos es suficiente. Deténgase una vez que alcance un estado terminal (`connected` / `ONLINE`, o un estado de error) y establezca un tiempo de espera general razonable en el bucle (los pasos del navegador/QR caducan, consulte cada `expires_at`).
- **Codifique las URL de los números de teléfono en la ruta.** El `+` inicial debe enviarse como `%2B`. Los puntos finales también recuperan dígitos sin formato, pero la codificación es la opción predeterminada segura.
- **Nunca espere recibir secretos.** Los tokens de acceso, los secretos de canal y los tokens de página se aceptan o almacenan, pero nunca se devuelven en ninguna respuesta.
- **Gestione el control de autenticación.** Un `403` significa que el acceso a la API no está incluido en el plan, o que el canal que está conectando no está incluido en el plan de la cuenta. Consulte [Acceso a la API](../integrations/api-access.md).
- **Tenga en cuenta el límite de velocidad.** Las solicitudes autenticadas tienen un límite de 300 por minuto; un `429` significa que debe esperar y volver a intentarlo. Consulte [Autenticación](authentication.md).

## Próximos pasos

- [Autenticación](authentication.md) - las cuatro formas de autenticación aceptadas y el formato de error.
- [Acceso a la API](../integrations/api-access.md) - generación y gestión de su clave de API.
