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

# API pública de difusión

> Envía plantillas de WhatsApp y mensajes de formato libre mediante REST con la API pública de difusión v2 de Eazybe, con alcance por organización.

## Descripción general

La API pública de difusión permite que sistemas externos accedan a los números de WhatsApp Business de su organización. Cada solicitud queda asociada a una organización mediante la clave API, y todos los números WABA utilizados deben pertenecer a esa organización.

Existen dos familias de endpoints en `/broadcast/public`:

* **Endpoints v2:** se autentican con la clave de su organización y son los documentados en esta página.
* **Endpoints heredados:** siguen usando la clave WABA anterior por número y no han cambiado. Consulte Endpoints heredados.

## Configuración base

| Configuración | Valor |
| - | - |
| **URL** | `https://cerberus.eazybe.com/prod/api/v2` |
| **Ruta base** | `/broadcast/public` |
| **Autenticación** | Encabezado `x-api-key` |
| **Content-Type** | `application/json` |

## Antes de comenzar

<Warning>
  **Mantenga la clave en el servidor.** Esta clave permite enviar mensajes desde todos los números de WhatsApp de su organización. Guárdela en variables de entorno o en un gestor de secretos; nunca la incluya en código web o móvil.
</Warning>

<Tip>
  **Comience aquí.** Consulte Obtener números de teléfono para saber qué números WABA puede utilizar y Obtener plantillas para confirmar que la plantilla está aprobada antes de enviar mensajes.
</Tip>

***

## Flujo de autenticación

<Steps>
  <Step>
    <h3>Usar la clave de autenticación en los encabezados de solicitud</h3>
    <p>Todos los endpoints de difusión requieren la clave de autenticación generada en el encabezado <strong>x-api-key</strong>, que puede encontrar en Espacio de trabajo → Personal.</p>

    ```http theme={null}
    x-api-key: YOUR_API_KEY
    ```

    <Note>
      Guarde su clave API de forma segura. Evite regenerarla innecesariamente.
    </Note>

    <Frame>
      <img src="https://mintcdn.com/eazybe/xv1XdtKhRcGb98Bg/images/public-broadcast-api-auth-key.png?fit=max&auto=format&n=xv1XdtKhRcGb98Bg&q=85&s=12376f0014b34e6b104c4d6c6f670936" alt="Busque la clave de autenticación de su organización en la sección Personal" width="2938" height="1670" data-path="images/public-broadcast-api-auth-key.png" />
    </Frame>
  </Step>
</Steps>

### Qué identifica la clave

La clave identifica a su organización. En los endpoints que aceptan un `fromPhoneNumber` o `phone_number`, la API verifica además que el número esté registrado en esa organización y que su registro WABA esté completo: `waba_id`, `phone_number_id`, `phone_number` y `access_token` deben estar presentes. Una discrepancia devuelve `401`, no `403`.

***

## Índice de endpoints

| Método | Punto final | Propósito | Autenticación |
| - | - | - | - |
| `GET` | `/broadcast/public/phone-numbers` | Enumere los números WABA de su organización | Requerido |
| `GET` | `/broadcast/public/templates` | Listar plantillas aprobadas para un número | Requerido |
| `POST` | `/broadcast/public/send-template-broadcast` | Enviar una plantilla a muchos destinatarios | Requerido |
| `POST` | `/broadcast/public/send-freeform` | Enviar un mensaje de formato libre | Requerido |
| `GET` | `/broadcast/public/status/:jobId` | Consultar un trabajo de difusión grande | Requerido |
| `GET` | `/broadcast/public/health` | Estado del servicio y lista de puntos finales | Ninguno |

***

## Obtener números de teléfono

Enumera todos los números de WhatsApp Business registrados en su organización. Utilice el `phone_number` devuelto como `fromPhoneNumber` en los endpoints de envío.

### Endpoint

```http theme={null}
GET /broadcast/public/phone-numbers
```

### Solicitud

```bash theme={null}
curl "https://cerberus.eazybe.com/prod/api/v2/broadcast/public/phone-numbers" \
  -H "x-api-key: $API_KEY"
```

### Respuesta

**200: números de teléfono obtenidos correctamente**

```json theme={null}
{
  "status": true,
  "status_code": 200,
  "message": "Phone numbers fetched successfully",
  "data": [
    {
      "phone_number": "14844634680",
      "waba_id": "1099052972334241",
      "phone_number_id": "894234333763517"
    }
  ]
}
```

Una matriz `data` vacía significa que la clave es válida pero aún no hay ningún número WABA conectado a la organización. Ese es un estado de configuración, no un error.

***

## Obtener plantillas

Devuelve las plantillas de WhatsApp aprobadas y disponibles para uno de tus números. Llame a esto antes de una difusión: enviar un nombre de plantilla no aprobado o mal escrito falla en toda la solicitud.

### Endpoint

```http theme={null}
GET /broadcast/public/templates
```

### Parámetros de consulta

| Parámetro | Tipo | Requerido | Descripción |
| - | - | - | - |
| `phone_number` | cadena | Requerido | Un número WABA perteneciente a su organización, solo dígitos |

### Solicitud

```bash theme={null}
curl "https://cerberus.eazybe.com/prod/api/v2/broadcast/public/templates?phone_number=14844634680" \
  -H "x-api-key: $API_KEY"
```

### Respuesta

**200 — Plantillas obtenidas correctamente**

```json theme={null}
{
  "status": true,
  "status_code": 200,
  "message": "Templates fetched successfully",
  "data": [
    {
      "name": "eazybe_temp",
      "status": "APPROVED",
      "category": "MARKETING",
      "language": "en",
      "components": [
        { "type": "BODY", "text": "Hello {{1}}, your code is {{2}}." }
      ]
    }
  ]
}
```

Cuente los marcadores de posición `{{n}}` en el cuerpo; eso es exactamente cuántos valores debe proporcionar la matriz `templateParams` de cada destinatario.

***

## Enviar plantilla de difusión

Envía una plantilla aprobada a una lista de destinatarios. El comportamiento cambia con el tamaño de la lista: hasta 1000 destinatarios, la difusión se pone en cola de forma sincrónica; encima de eso, se divide en partes y se procesa como un trabajo en segundo plano que se procesa como un trabajo en segundo plano cuyo estado puede consultar.

### Endpoint

```http theme={null}
POST /broadcast/public/send-template-broadcast
```

### Encabezados

| Encabezado | Requerido | Descripción |
| - | - | - |
| `x-api-key` | Requerido | Clave de autenticación de la organización |
| `Content-Type` | Requerido | `application/json` |
| `Idempotency-Key` | Opcional | Cadena única, segura para volver a intentarlo |

### Cuerpo

| Campo | Tipo | Requerido | Descripción |
| - | - | - | - |
| `broadcastName` | cadena | Requerido | Etiqueta de campaña que se muestra en los informes |
| `fromPhoneNumber` | cadena | Requerido | Tu número WABA, desde Obtener números de teléfono |
| `templateName` | cadena | Requerido | Nombre de plantilla aprobada |
| `templateLanguage` | cadena | Requerido | Código de idioma, p.e. `en` |
| `templateType` | cadena | Requerido | `MARKETING`, `UTILITY`, etc. |
| `templateId` | cadena | Opcional | ID de plantilla de WhatsApp |
| `globalTemplateParams` | cadena\[] | Opcional | Valores predeterminados aplicados a destinatarios sin sus propios parámetros |
| `data` | objeto\[] | Requerido | Destinatarios, 1 a 10.000 |

### Objeto destinatario

| Campo | Tipo | Requerido | Descripción |
| - | - | - | - |
| `countryCode` | cadena | Requerido | Sólo dígitos, no `+`. p.ej. `91` |
| `toPhoneNumber` | cadena | Requerido | Sólo dígitos, sin código de país |
| `templateParams` | cadena\[] | Opcional | Valores para `{{1}}`, `{{2}}`… Anula los valores globales. |

### Solicitud

```bash theme={null}
curl -X POST "https://cerberus.eazybe.com/prod/api/v2/broadcast/public/send-template-broadcast" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: festive-run-1" \
  -d '{
    "broadcastName": "Festive Offer Campaign",
    "fromPhoneNumber": "14844634680",
    "templateName": "eazybe_temp",
    "templateLanguage": "en",
    "templateType": "MARKETING",
    "templateId": "1530766784804741",
    "globalTemplateParams": ["Valued customer", "FESTIVE10"],
    "data": [
      {
        "countryCode": "91",
        "toPhoneNumber": "9675360316",
        "templateParams": ["Vineet", "VIP20"]
      },
      {
        "countryCode": "91",
        "toPhoneNumber": "8077378155"
      }
    ]
  }'
```

En este ejemplo, el primer destinatario obtiene sus propios valores y el segundo recurre a `globalTemplateParams`.

### Respuestas

**200: en cola (1000 destinatarios o menos)**

```json theme={null}
{
  "status": true,
  "status_code": 200,
  "message": "Bulk broadcast queued successfully",
  "data": { }
}
```

**202: Aceptado para procesamiento en segundo plano (más de 1000 destinatarios)**

```json theme={null}
{
  "status": true,
  "status_code": 202,
  "message": "Broadcast accepted for background processing",
  "data": {
    "jobId": "pub_4f8c2a1e9b7d43c6a5e0f2b18d3c9a67",
    "status": "PROCESSING",
    "totalRecipients": 1001,
    "chunkSize": 1000,
    "totalChunks": 2
  }
}
```

Conserve el `jobId`; es la única forma de comprobar cómo finalizó la difusión. Consulte Obtener estado de difusión con ese identificador.

**400: se superó el límite de destinatarios**

```json theme={null}
{
  "status": false,
  "status_code": 400,
  "message": "Recipient limit exceeded",
  "data": {
    "error": {
      "message": "Template broadcast cannot exceed 10000 recipients per request."
    }
  }
}
```

**400 — Plantilla no aprobada**

```json theme={null}
{
  "status": false,
  "status_code": 400,
  "message": "Template is not approved or not found",
  "data": {
    "error": {
      "message": "Template is not approved or not found for this WABA phone number."
    }
  }
}
```

<Warning>
  **Los destinatarios duplicados se rechazan, no se fusionan.** Si el mismo par `countryCode` + `toPhoneNumber` aparece dos veces, toda la solicitud falla con un `400` nombrando el índice del duplicado. Deduplica tu lista antes de enviarla.
</Warning>

***

## Enviar mensaje de formato libre

Envía un solo mensaje a un destinatario. Los ocho tipos de contenido comparten este endpoint: el campo `type` decide qué otros campos son obligatorios.

### Endpoint

```http theme={null}
POST /broadcast/public/send-freeform
```

### Campos comunes

| Campo | Tipo | Requerido | Descripción |
| - | - | - | - |
| `fromPhoneNumber` | cadena | Requerido | Su número WABA |
| `toPhoneNumber` | cadena | Requerido | Destinatario con código de país, solo dígitos |
| `type` | cadena | Requerido | Uno de los ocho tipos siguientes |

### Campos específicos de tipo

| `type` | Requerido | Opcional | Extensiones de archivo aceptadas |
| - | - | - | - |
| `text` | `text` | — | — |
| `image` | `url` | `caption` | jpg, jpeg, png |
| `document` | `url` | `name`, `caption` | pdf, txt, doc, docx, ppt, pptx, xls, xlsx |
| `audio` | `url` | — | aac, amr, mp3, m4a, mp4, ogg, opus |
| `video` | `url` | `caption` | mp4, 3gp |
| `sticker` | `url` | — | web |
| `location` | `latitude`, `longitude` | `name`, `address` | — |
| `interactive` | `interactive` | — | — |

<Warning>
  **La extensión se lee desde la ruta URL, no desde el archivo.** Un PNG real servido desde una URL que termina en `.webp` se rechaza para `image`, y una URL sin extensión alguna se rechaza para cada tipo de medio. Las cadenas de consulta se ignoran, por lo que `…/photo.png?v=2` está bien.
</Warning>

### Texto

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "text",
  "text": "Your order #4821 has shipped."
}
```

### Imagen

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "image",
  "url": "https://cdn.example.com/offer.jpg",
  "caption": "This week only"
}
```

### Documento

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "document",
  "url": "https://cdn.example.com/invoice-4821.pdf",
  "name": "Invoice_4821.pdf",
  "caption": "Your invoice is ready"
}
```

### Audio

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "audio",
  "url": "https://cdn.example.com/voice-note.mp3"
}
```

### Vídeo

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "video",
  "url": "https://cdn.example.com/demo.mp4",
  "caption": "60-second walkthrough"
}
```

### Pegatina

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "sticker",
  "url": "https://cdn.example.com/thumbs-up.webp"
}
```

### Ubicación

`latitude` y `longitude` deben ser números JSON, no cadenas. La latitud acepta −90 a 90, longitud −180 a 180.

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "location",
  "latitude": 28.4595,
  "longitude": 77.0266,
  "name": "Eazybe HQ",
  "address": "Sector 44, Gurugram, Haryana 122003"
}
```

### Interactivo: botones

Hasta **3** botones de respuesta. Se rechaza más de tres.

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "interactive",
  "interactive": {
    "type": "button",
    "header": { "type": "text", "text": "Renew your plan" },
    "body": { "text": "Your subscription ends in 3 days. Renew now?" },
    "footer": { "text": "Reply STOP to opt out" },
    "action": {
      "buttons": [
        { "type": "reply", "reply": { "id": "renew_yes", "title": "Renew now" } },
        { "type": "reply", "reply": { "id": "renew_later", "title": "Remind me later" } }
      ]
    }
  }
}
```

### Interactivo — lista

Hasta **10** filas contadas en *todas* las secciones combinadas, no por sección.

```json theme={null}
{
  "fromPhoneNumber": "14844634680",
  "toPhoneNumber": "919675360316",
  "type": "interactive",
  "interactive": {
    "type": "list",
    "header": { "type": "text", "text": "Pick a plan" },
    "body": { "text": "Choose the plan that fits your team." },
    "action": {
      "button": "View plans",
      "sections": [
        {
          "title": "Monthly",
          "rows": [
            { "id": "starter_m", "title": "Starter", "description": "3 seats included" },
            { "id": "growth_m", "title": "Growth", "description": "10 seats included" }
          ]
        }
      ]
    }
  }
}
```

### Respuesta

**200: la misma forma para los ocho tipos**

```json theme={null}
{
  "status": true,
  "status_code": 200,
  "message": "Free-form message queued successfully",
  "data": { }
}
```

***

## Obtener estado de difusión

Devuelve el progreso de un trabajo de difusión grande, los que devolvieron `202` y `jobId`. Las difusiones de 1.000 destinatarios o menos no crean un trabajo y no tienen ningún estado que consultar.

### Endpoint

```http theme={null}
GET /broadcast/public/status/:jobId
```

### Solicitud

```bash theme={null}
curl "https://cerberus.eazybe.com/prod/api/v2/broadcast/public/status/pub_4f8c2a1e9b7d43c6a5e0f2b18d3c9a67" \
  -H "x-api-key: $API_KEY"
```

### Respuestas

**200: registro del trabajo más un resumen del progreso del fragmento**

```json theme={null}
{
  "status": true,
  "status_code": 200,
  "message": "Broadcast status fetched successfully",
  "data": {
    "jobId": "pub_4f8c2a1e9b7d43c6a5e0f2b18d3c9a67",
    "status": "QUEUED_FOR_PROCESSING",
    "totalRecipients": 1001,
    "totalChunks": 2,
    "summary": { }
  }
}
```

**404** — Trabajo de difusión no encontrado

**401** — El trabajo pertenece a una organización diferente

Un trabajo creado por otra organización devuelve `401` en lugar de `404`; los ID de trabajo no se pueden enumerar entre organizaciones.

***

## Comprobación de estado

Confirma que el servicio está activo y enumera las rutas que expone actualmente. No se necesita clave API.

### Endpoint

```http theme={null}
GET /broadcast/public/health
```

```json theme={null}
{
  "status": "ok",
  "timestamp": "2026-07-22T11:04:33.812Z",
  "service": "Public Broadcast API",
  "version": "2.0.0",
  "endpoints": {
    "generate-key": "POST /broadcast/public/generate-key",
    "template-broadcast": "POST /broadcast/public/send-template-broadcast",
    "freeform": "POST /broadcast/public/send-freeform",
    "templates": "GET /broadcast/public/templates",
    "phone-numbers": "GET /broadcast/public/phone-numbers",
    "status": "GET /broadcast/public/status/:jobId",
    "single": "POST /broadcast/public/send-single",
    "bulk-standard": "POST /broadcast/public/send-bulk",
    "health": "GET /broadcast/public/health"
  }
}
```

***

## Reglas de validación

Estos se ejecutan antes de que algo llegue a WhatsApp, por lo que un rechazo aquí no cuesta cuota de mensajes. Cada falla devuelve `400` con `message: "Validation error"` y un motivo específico.

| Regla | Se aplica a | Sobre el fracaso |
| - | - | - |
| Los números de teléfono deben constar únicamente de dígitos, sin `+`, espacios ni guiones | Ambos envían puntos finales | `toPhoneNumber` / `countryCode` no válido |
| Al menos un destinatario | Difusión de plantilla | Se requiere lista de destinatarios |
| Sin duplicado `countryCode` • `toPhoneNumber` | Difusión de plantilla | Destinatario duplicado encontrado en el índice *n* |
| Máximo 10.000 destinatarios | Difusión de plantilla | Se superó el límite de destinatarios |
| La plantilla debe existir y estar aprobada para ese número | Difusión de plantilla | La plantilla no está aprobada o no se encuentra |
| `text` debe ser una cadena que no esté vacía | Forma libre, tipo `text` | se requiere texto para mensajes de texto |
| `url` debe ser http o https | Tipos de medios de formato libre | La URL debe ser una URL http o https válida |
| La extensión de URL debe estar en la lista permitida | Tipos de medios de formato libre | Lista las extensiones aceptadas |
| Latitud −90 a 90, longitud −180 a 180, ambos números | Forma libre, tipo `location` | Nombra la coordenada infractora |
| El tipo interactivo debe ser `button` o `list` | Forma libre, tipo `interactive` | el tipo interactivo debe ser botón o lista |
| 1 a 3 botones | Botones interactivos | No puede exceder los 3 botones |
| 1 a 10 filas en todas las secciones | Lista interactiva | No puede exceder de 10 filas |

***

## Límites de solicitudes

Los límites se cuentan en una ventana fija de 60 segundos. La mayoría tiene como alcance su clave API **y** el número de teléfono en la solicitud, por lo que dos números de envío diferentes no compiten por el mismo presupuesto.

| Punto final | Límite | Contado por |
| - | - | - |
| `send-template-broadcast` | 10/minuto | clave + fromPhoneNumber |
| `send-freeform` | 20/minuto | clave + fromPhoneNumber |
| `templates` | 20/minuto | clave + phone\_number |
| `phone-numbers` | 30/minuto | clave |
| `status` | 30/minuto | clave |

### Al superar un límite

```json theme={null}
{
  "status": false,
  "status_code": 429,
  "message": "Rate limit exceeded",
  "data": {
    "error": {
      "message": "Rate limit exceeded. Try again in 43 seconds."
    }
  }
}
```

El mensaje le indica exactamente cuántos segundos quedan en la ventana. Espere tanto tiempo en lugar de volver a intentarlo inmediatamente: un reintento inmediato no consume nada pero devuelve el mismo error.

***

## Manejo de errores

Cada error utiliza el mismo sobre:

```json theme={null}
{
  "status": false,
  "status_code": 400,
  "message": "Short summary",
  "data": {
    "error": {
      "message": "Specific detail about what to fix"
    }
  }
}
```

### Códigos de estado

| Código | Significado | Qué hacer |
| - | - | - |
| `200` | En cola o recuperado | — |
| `202` | Difusión grande aceptada | Consulte el estado con el `jobId` |
| `400` | Fallo de validación o límite | Lea `data.error.message` y corrija la carga útil |
| `401` | Problema de clave, número o propiedad del trabajo | Vea la tabla a continuación |
| `404` | Trabajo de difusión no encontrado | Compruebe el `jobId` |
| `409` | Mismo `Idempotency-Key` aún procesándose | Espere y sondee; no reenviar |
| `429` | Límite de solicitudes excedido | Espere los segundos nombrados en el mensaje |
| `500` | Envío fallido en sentido descendente | Vuelva a intentarlo con el mismo `Idempotency-Key` |
| `503` | Se agotó el tiempo de espera de la base de datos de autenticación | Reintentar con retroceso |

### Los cuatro errores 401

Los cuatro devuelven `401` pero significan cosas diferentes:

| Mensaje | Causa | Arreglar |
| - | - | - |
| Se requiere clave API | Sin encabezado `x-api-key` | Añade el encabezado |
| Clave API no válida. Organización no encontrada. | Ninguna organización tiene ese `auth_key` | Verifique la clave y estará en el entorno correcto |
| No se encontró ninguna cuenta WABA para esta organización. | El número no está registrado en su organización | Utilice un número de Obtener números de teléfono |
| La cuenta WABA está incompleta. Campos faltantes:… | Falta el registro WABA `waba_id`, `phone_number_id`, `phone_number` o `access_token` | Finalizar la incorporación de WABA para ese número |

<Note>
  **Reintentar política.** Reintentar `500` y `503` con retroceso exponencial. Nunca vuelva a intentar `400` o `401`: la misma solicitud fallará de manera idéntica. En `429`, espere el intervalo en el que aparecen los nombres de las respuestas. Envíe siempre un `Idempotency-Key` en las solicitudes que desee volver a intentar.
</Note>

***

## Endpoints heredados

<Warning>
  **Autenticación diferente.** Estos tres son anteriores a la versión 2 y aún usan la clave WABA por número anterior, no la de su organización `auth_key`. No han cambiado y siguen siendo compatibles, pero las nuevas integraciones deberían utilizar los endpoints v2 anteriores.
</Warning>

```
POST /broadcast/public/generate-key      no auth · 5/min
```

Genera una clave API heredada para un número de teléfono. Toma `phoneNumber`, no un ID de organización.

```json theme={null}
{ "phoneNumber": "919876543210" }
```

```
POST /broadcast/public/send-single       legacy key · 30/min
```

Envía un mensaje de plantilla. Los campos de destinatario se encuentran en el nivel superior en lugar de en una matriz `data`.

```
POST /broadcast/public/send-bulk         legacy key · 10/min
```

Envío masivo de plantillas, con un límite de **1000** destinatarios: no hay fragmentación ni identificación de trabajo. Exceder el límite devuelve `400` en el cuerpo de la respuesta con una línea de estado HTTP 200, a diferencia del endpoint v2 que devuelve.


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