De tu primer envío a un webhook firmado.
Una API REST para WhatsApp, SMS, RCS, email y push, con claves de prueba y de producción, reintentos seguros y eventos firmados. Todo el contrato público está en openapi.json.
Las claves de prueba empiezan por ccx_test_ y usan una cartera de prueba. No llega nada a ningún cliente.
- Crea una clave de APIccx_test_••••••••Se muestra una vez
- Envía el mensajePOST /messages201 en cola
- Recibe el estado en tu webhookmessage.deliveredFirma válida
Tu primer envío, en tres pasos.
Empieza con una clave de prueba: usa la cartera de prueba y un envío simulado, así que nada llega a ningún cliente y no se cobra nada de verdad.
curl -X POST https://api.messages.ccxcompany.co/messages \ -H "Authorization: Bearer $CCX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: pedido-4821-enviado" \ -d '{ "numberId": "num_…", "to": "+34…", "type": "text", "payload": { "text": { "body": "Tu pedido ha salido para entrega" } } }'
Cuerpo resumido; el objeto completo del mensaje está en openapi.json. Los valores con … son de ejemplo.
Claves, ámbitos y reintentos seguros.
Cada llamada lleva Authorization: Bearer con tu clave. Cada ruta exige un ámbito, y una clave solo puede hacer lo que permiten sus ámbitos.
Reintenta sin enviar dos veces.
En los envíos de WhatsApp, SMS y push, la cabecera Idempotency-Key te deja repetir la misma llamada durante 24 horas. Misma clave y mismo cuerpo devuelven la respuesta original. Misma clave con otro cuerpo devuelve 409. Pruébalo.
- Idempotency-Key: pedido-4821 · cuerpo A201msg_01… creado y en cola
- Idempotency-Key: pedido-4821 · cuerpo A201Respuesta original de msg_01…, no se envió nada otra vez.
- Idempotency-Key: pedido-4821 · cuerpo B409CONFLICT · IDEMPOTENCY_CONFLICT
Respuestas simuladas y resumidas. Los valores con … son de ejemplo.
Webhooks firmados, con reentrega.
Registra la URL, elige los eventos y guarda el secreto. Cada entrega lleva X-CCX-Signature, X-CCX-Event, X-CCX-Delivery-Id y X-CCX-Idempotency-Key.
- 5 s
- 30 s
- 2 min
- 10 min
- 1 h
import { createHmac, timingSafeEqual } from "node:crypto"; // X-CCX-Signature: t=<unix>,v1=<hex>export function firmaValida(secreto, cuerpo, cab) { const p = Object.fromEntries( cab.split(",").map((kv) => kv.split("="))); if (Math.abs(Date.now() / 1000 - p.t) > 300) return false; const esperado = createHmac("sha256", secreto) .update(p.t + "." + cuerpo).digest("hex"); return esperado.length === p.v1.length && timingSafeEqual(Buffer.from(esperado, "hex"), Buffer.from(p.v1, "hex"));}
Calcúlala sobre el cuerpo en bruto, tal como llegó, sin volver a serializar el JSON. La ventana de 5 minutos en la marca t bloquea la repetición de eventos antiguos.
- message.sent
- message.delivered
- message.read
- message.failed
- message.received
- template.approved
- template.rejected
- sms.delivered
- sms.received
- email.delivered
- email.bounced
- email.opened
- wallet.low_balance
Cuando la respuesta no es 2xx.
Todo error llega como problem details (RFC 7807), con un code estable que tu código puede comparar. Elige uno para ver su respuesta.
HTTP/1.1 409Content-Type: application/problem+json { "type": "about:blank", "title": "conflict", "code": "CONFLICT", "status": 409, "detail": "idempotency key reused with a different payload", "meta": { "code": "IDEMPOTENCY_CONFLICT" }}
Ejemplo de cuerpo de error, tal como lo devuelve la API.
Lo que puedes hacer con la API.
Las rutas de abajo forman parte del contrato público. Lo que no está en openapi.json no es contrato y puede cambiar.
Guías para lo que más se atasca.
Respuestas cortas a las dudas que aparecen cuando la integración sale de la pizarra.
Acentos cambiados antes del envío: á pasa a a. Todo el mensaje cabe en GSM-7, así que sigue valiendo 160 caracteres por segmento.
Contado como lo cuenta la factura. POST /v2/sms/preview devuelve los segmentos y el precio antes de enviar. El SMS va a números de Brasil; las rutas internacionales son bajo consulta.
Meta rechazó mi plantilla. ¿Y ahora?
Casi siempre es formato, no contenido: una promoción declarada como utilidad, una variable al principio o al final del cuerpo, o un botón de URL sin un prefijo fijo. Antes de enviarla a Meta, el editor del panel avisa cuando el texto usa lenguaje promocional agresivo, cuando una plantilla de marketing no tiene botón de baja y cuando el cuerpo supera los 1024 caracteres.
¿Cuánto tarda la aprobación del número?
Son dos pasos. La verificación del número, con el código por SMS o llamada, es rápida. La verificación del negocio, que desbloquea límites de envío más altos, la hace la propia Meta, y el plazo es suyo.
¿Cuántos segmentos me costará un SMS?
Hasta 160 caracteres es un segmento; a partir de ahí, uno cada 152. Con emoji, el mensaje va en Unicode: hasta 70 es un segmento, a partir de ahí uno cada 67. Los caracteres acentuados que no existen en GSM-7 se sustituyen por su equivalente sin tilde cuando todo el mensaje cabe en GSM-7, así que los acentos no encarecen. POST /v2/sms/preview devuelve los segmentos y el precio antes de enviar. El SMS va a números de Brasil; las rutas internacionales son bajo consulta.
¿Qué pasa si se acaba el saldo?
La cartera es prepago y se descuenta en el momento del envío. Sin saldo, SMS, RCS y email responden 402, y los envíos de WhatsApp y push fallan con el estado de saldo insuficiente. Una campaña de SMS que se queda sin saldo a mitad de envío se pausa con el motivo en pantalla, y Reanudar envía los mensajes rechazados por falta de saldo junto con el resto. La cartera de prueba es independiente y nunca cobra dinero real.
Mis emails llegan a spam
Publica SPF, DKIM y DMARC del dominio, sube el volumen poco a poco en un dominio nuevo, quita las direcciones no válidas y separa el transaccional del marketing en subdominios. Las direcciones que rebotan pasan a la lista de supresión.
Mi webhook no recibe los eventos
Comprueba que la URL responde 2xx en menos de 10 segundos, que la firma se calcula sobre el cuerpo en bruto y que el webhook está activo, no pausado. Un 4xx de tu lado (salvo 408 y 429) no se reintenta; los 5xx y la falta de respuesta sí, tras 5 s, 30 s, 2 min, 10 min y 1 h.
¿Cómo invito a mi equipo y defino permisos?
Invita por email y elige uno de los nueve roles, de propietario a solo lectura. La organización puede exigir verificación en dos pasos a todos los miembros, con 30 días de margen. No hay cuota por usuario, así que nadie tiene que compartir el acceso.
¿Atascado en una ruta? Habla con quienes mantienen la API.
Si creas la cuenta desde la web, recibes 5 US$ de crédito de prueba.