Saltar al contenido principal

Campañas

Una campaña envía una plantilla aprobada a un público: todos los contactos que selecciona un filtro, con las variables de cada uno rellenas. Por la API montas, revisas, lanzas y sigues la campaña — el envío en sí corre en un proceso aparte, al ritmo que definas, sin gastar el límite de envío de tu clave.

El camino de una campaña

materializing es el armado de la lista de destinatarios: el público se congela en el lanzamiento, y quien entre en la base después no la recibe. Una campaña también puede terminar en canceled o, cuando nadie del público puede recibirla, en failed.

1. Crear el borrador

curl -X POST https://api.joinotify.com/broadcasts \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "Black Friday",
"phoneNumberId": "106540352242922",
"templateName": "promo_black_friday",
"templateLanguage": "es",
"audienceId": "cm1aud1ence01",
"variables": {
"body": {
"1": { "source": "contact", "key": "first_name", "fallback": "cliente" },
"2": { "source": "static", "value": "BLACK20" }
}
},
"sendWindow": { "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "20:00" },
"timezone": "Europe/Madrid"
}'

Solo name es obligatorio; el resto puede venir después, por PATCH. Nada se revisa al guardar — la revisión ocurre en la estimación, la prueba y el lanzamiento.

Variables. Cada marcador de la plantilla necesita una fuente: contact (un dato del contacto, como first_name), attribute (un campo personalizado) o static (un valor fijo). Las dos primeras exigen un fallback para quien no tiene el dato. Una plantilla con encabezado de medio necesita headerMedia — sube el archivo con POST /broadcasts/media y usa el id.

Público. Un público guardado (audienceId) o un filtro solo de esta campaña (audienceFilter), con el mismo árbol de condiciones. Crea públicos reutilizables con POST /audiences y revisa a quién seleccionan con POST /audiences/preview.

2. Estimar y probar

POST /broadcasts/{id}/estimate revisa toda la campaña y responde a quién alcanzaría ahora:

{
"data": {
"recipients": 4800,
"total": 5230,
"skipped": { "opted_out": 120, "no_consent": 290, "suppressed": 20 },
"cap": 50000,
"firstCampaign": false,
"overCap": 0,
"remainingQuota": 18000,
"portfolioLimit": 10000,
"estimatedCost": { "micros": 297600000, "currency": "eur", "byCountry": [], "unpriced": 0 }
}
}

Después, envíate la campaña con POST /broadcasts/{id}/test y { "to": "+5541987111527" }. La prueba sale en el momento, fuera de las métricas, y muestra las variables rellenas con los valores de respaldo.

Quién recibe una plantilla de marketing

Solo los contactos con consentimiento (opted_in) y fuera de la lista de supresión. Las plantillas de utilidad no dependen del consentimiento, pero siguen omitiendo números inválidos. Ver Contactos y consentimiento.

3. Lanzar

curl -X POST https://api.joinotify.com/broadcasts/cm1bro4dc4st01/launch \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "scheduledAt": "2026-11-27T12:00:00+01:00" }'

Sin scheduledAt, la campaña empieza en el momento. La respuesta es 202. El lanzamiento rechaza con 422:

typePor qué
audience_emptyNadie del público puede recibirla
broadcast_limit_reachedEl público supera el techo de destinatarios (limit dice cuál)
quota_exceededEl plan no tiene más mensajes este mes

El techo crece con el historial. La primera campaña de la cuenta va a hasta 1.000 personas; las siguientes, a hasta diez veces la mayor campaña ya terminada — siempre dentro del límite del plan. Es lo que protege la calidad de un número nuevo de un envío demasiado grande.

Editar una campaña programada la devuelve a borrador: hay que lanzarla de nuevo.

4. Seguir

  • GET /broadcasts/{id}/stats — contadores, tasas, clics por botón, fallos agrupados, costo por país y la calidad del número antes y ahora.
  • GET /broadcasts/{id}/timeseries — la evolución en franjas de 15 minutos.
  • GET /broadcasts/{id}/recipients y /export — cada destinatario, con estado y error.
  • Los eventos broadcast.paused y broadcast.completed en tu webhook.

Pausas automáticas

Una campaña se detiene sola cuando seguir perjudicaría al número: calidad roja, plantilla pausada por Meta, cuenta restringida, cuota del plan agotada, demasiados errores seguidos. El motivo queda en pauseReason y llega en el evento broadcast.paused — la tabla completa está en el catálogo de eventos.

Dos pausas se resuelven solas y traen resumeAfter: send_window (fuera de la ventana de envío) y throughput (Meta retuvo el caudal; reintenta a los 15 minutos, 1 hora y 4 horas). Las demás esperan a que alguien corrija la causa y llame a POST /broadcasts/{id}/resume.

Envíos sin respuesta

Cuando Meta no responde a un envío, el destinatario queda unknown. No se repite solo: el mensaje puede haber llegado, y repetir lo enviaría dos veces. Si decides reenviar, POST /broadcasts/{id}/resend-unknown con { "confirm": true } devuelve esos destinatarios a la cola.

Alcance de la clave

Una API key restringida a algunos números solo ve y crea campañas en esos números. Los públicos guardados son de toda la cuenta: /audiences responde 403 para ella.

El público de una campaña estimada o lanzada con esa clave — guardado o escrito como filtro — queda restringido a los contactos que tienen conversación en sus números, y el recorte se congela en el lanzamiento, junto con el público. En el envío de prueba, un contactId fuera de ese recorte responde 404. Una campaña lanzada con una clave sin restricción, o desde el panel, va a toda la base.