Una línea de la API de WhatsApp · Autenticación Bearer con una llave frl_live_… ·
Webhook firmado con X-Hub-Signature-256
Conecta tu propio servidor a una línea de WhatsApp de Frepi. Usa el mismo formato de la Cloud API de Meta: en tu código solo cambian tres cosas — la dirección, la llave y el secreto del webhook. Cada envío de tu servidor aparece en Conversaciones de Frepi, con la marca «API».
https, un nombre de host público) y elige qué eventos
quieres recibir. Frepi envía un evento de prueba; si tu servidor responde 200, la API queda activa.| Cloud API de Meta | Frepi | |
|---|---|---|
| Dirección | https://graph.facebook.com/v27.0 | https://app.frepi.ai/api/meta/v27.0 |
| Autorización | Token de Meta | Tu llave de Frepi, en el mismo encabezado Authorization: Bearer |
| Firma del webhook | Secreto de tu app de Meta | El secreto de Frepi, en el mismo encabezado X-Hub-Signature-256 |
El segmento de versión (v27.0) se acepta con el formato v + número + punto + dígito,
y se ignora: Frepi llama a Meta con su propia versión. El encabezado Host no cambia nada: la línea sale de la
llave. Los ids son los de Meta: el phone_number_id y el id de la cuenta (WABA) de tu línea los ves en la línea
en Frepi.
Authorization: Bearer frl_live_…
?access_token= se rechaza (las URL llegan a los registros).| Para qué | Método y ruta (igual que en la Cloud API de Meta) |
|---|---|
| Enviar texto y plantillas (a una persona o a un grupo) | POST /{phone_number_id}/messages |
| Bajar archivos recibidos | GET /{media_id} · GET /{media_id}/download |
| Crear y borrar grupos | POST /{phone_number_id}/groups · DELETE /{group_id} |
| Aprobar pedidos de entrada a un grupo | POST /{group_id}/join_requests |
| Crear y listar plantillas | POST · GET /{waba_id}/message_templates |
Cualquier otra ruta responde 403 (código 10), con el formato de error de Meta. No hay ruta
para subscribed_apps, register, deregister, el PIN de verificación en dos pasos,
otro número, otra cuenta o la app. Todas las respuestas llevan Cache-Control: private, no-store.
curl -X POST https://app.frepi.ai/api/meta/v27.0/$PHONE_NUMBER_ID/messages \
-H "Authorization: Bearer $FREPI_LINE_KEY" \
-H "X-Frepi-Idempotency-Key: pedido-8812" \
-H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","recipient_type":"individual","to":"573001234567",
"type":"text","text":{"body":"Hola Ana, tu pedido salió hoy."}}'
{"messaging_product":"whatsapp","contacts":[{"input":"573001234567","wa_id":"573001234567"}],
"messages":[{"id":"wamid.HBgM…"}]}
wa_id repite el to que enviaste (sin el +). Frepi no aplica la
ventana de 24 horas: el envío va a Meta, y si la ventana está cerrada Meta lo dice en el estado del webhook (131047),
igual que en Meta. context.message_id se acepta pero todavía no se envía como respuesta citada.
curl -X POST https://app.frepi.ai/api/meta/v27.0/$PHONE_NUMBER_ID/messages \
-H "Authorization: Bearer $FREPI_LINE_KEY" -H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","to":"573001234567","type":"template",
"template":{"name":"pedido_enviado","language":{"code":"es"},
"components":[{"type":"body","parameters":[{"type":"text","text":"Ana"},{"type":"text","text":"8812"}]}]}}'
La plantilla debe estar aprobada y ser de esta línea (o de la cuenta, sin línea). Por ahora solo se
aceptan parámetros del body (con parameter_name en una plantilla con nombres), y el botón de la
plantilla de invitación a un grupo (abajo). Cualquier otro parámetro responde 400 código 132000.
curl -X POST https://app.frepi.ai/api/meta/v27.0/$PHONE_NUMBER_ID/messages \
-H "Authorization: Bearer $FREPI_LINE_KEY" -H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","recipient_type":"group","to":"'$GROUP_ID'",
"type":"text","text":{"body":"Hola a todos"}}'
El grupo debe ser de esta línea y estar activo. La invitación a un grupo se envía a una persona con la
plantilla de invitación de la línea y el enlace (o el código) del grupo como parámetro de su botón:
{"type":"button","sub_type":"url","index":"0","parameters":[{"type":"text","text":"<código>"}]}.
El enlace debe ser el actual de un grupo de esta línea.
curl -X POST https://app.frepi.ai/api/meta/v27.0/$PHONE_NUMBER_ID/groups \
-H "Authorization: Bearer $FREPI_LINE_KEY" -H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","subject":"Clientes Midas","description":"Novedades",
"join_approval_mode":"approval_required"}'
curl -X POST https://app.frepi.ai/api/meta/v27.0/$GROUP_ID/join_requests \
-H "Authorization: Bearer $FREPI_LINE_KEY" -H "Content-Type: application/json" \
-d '{"messaging_product":"whatsapp","join_requests":["'$JOIN_REQUEST_ID'"]}'
curl -X DELETE https://app.frepi.ai/api/meta/v27.0/$GROUP_ID \
-H "Authorization: Bearer $FREPI_LINE_KEY"
Crear responde el id del grupo, o el request_id cuando Meta lo crea después (el id
llega entonces en el evento group_lifecycle_update del webhook). Sin join_approval_mode, entra
quien tenga el enlace. Aprobar responde {"messaging_product":"whatsapp","approved_join_requests":[…]}.
curl "https://app.frepi.ai/api/meta/v27.0/$WABA_ID/message_templates?status=APPROVED&limit=50" \
-H "Authorization: Bearer $FREPI_LINE_KEY"
curl -X POST https://app.frepi.ai/api/meta/v27.0/$WABA_ID/message_templates \
-H "Authorization: Bearer $FREPI_LINE_KEY" -H "Content-Type: application/json" \
-d '{"name":"pedido_enviado","language":"es","category":"UTILITY",
"components":[{"type":"BODY","text":"Hola {{1}}, tu pedido {{2}} salió hoy.",
"example":{"body_text":[["Ana","8812"]]}}]}'
La lista trae las plantillas que ya están en Meta: las de esta línea y las de su cuenta (WABA), nunca las
de otra cuenta del espacio (filtros name, status, language; limit hasta
200). Se pagina como en Meta: paging.cursors.before / after, y los enlaces
paging.next / paging.previous cuando hay otra página (o envía after= /
before= con un cursor). Crear responde
{"id","status":"PENDING","category"}. Por ahora no se crean plantillas con encabezado de imagen, video o
documento (400, media_header_unsupported), ni con parámetros en el encabezado o los botones.
curl https://app.frepi.ai/api/meta/v27.0/$MEDIA_ID -H "Authorization: Bearer $FREPI_LINE_KEY" curl -o archivo https://app.frepi.ai/api/meta/v27.0/$MEDIA_ID/download -H "Authorization: Bearer $FREPI_LINE_KEY"
El media_id es el que llegó en un evento del webhook de esta línea. La respuesta es la de Meta,
con la url cambiada a la descarga de Frepi (la URL de Meta nunca sale de Frepi). La descarga llega como
adjunto (Content-Disposition: attachment, X-Content-Type-Options: nosniff). Hasta 100 MB por
archivo y 4 descargas a la vez por línea. Si un archivo pasa los 100 MB sin declarar su tamaño, la descarga se corta con
error (respuesta incompleta): no la guardes como completa.
Siempre el formato de Meta:
{"error":{"message":"…","type":"OAuthException","code":100,"error_subcode":33,
"error_data":{"messaging_product":"whatsapp","details":"…"},"is_transient":false,"fbtrace_id":"…"}}
| Caso | HTTP | code / subcode | error_data.details |
|---|---|---|---|
| Llave ausente, desconocida o revocada | 401 | 190 | — |
| Llave vencida | 401 | 190 / 463 | key_expired |
| API apagada, línea no habilitada, facturación pendiente, ruta no disponible | 403 | 10 | not_permitted |
| Un id que no es de esta línea, o que no existe (la misma respuesta) | 400 | 100 / 33 | — |
| Cuerpo inválido | 400 | 100 | el campo |
| Demasiadas llamadas | 429 | 4 (is_transient: true) | rate_limited |
| Contacto excluido en Frepi | 403 | 10 | recipient_excluded |
X-Frepi-Idempotency-Key repetida con otro cuerpo | 400 | 100 | idempotency_key_reused |
| No se sabe si el envío salió | 500 | 131000 (is_transient: false) | outcome_unknown |
| Meta rechazó el envío | 400 | el código de Meta | la clase de falla de Frepi |
| Frepi no puede enviar ahora (envíos pausados, almacenamiento) | 503 | 2 (is_transient: true) | el motivo |
| Parámetros de plantilla no soportados | 400 | 132000 | parameters_unsupported |
Ante outcome_unknown no reenvíes: el mensaje puede haber salido. Mira el estado en
el webhook. Los errores que Meta da después del envío (131047, 131026, …) llegan en el estado del webhook, byte a byte.
Meta no tiene clave de idempotencia: sin encabezado, cada llamada es un envío nuevo. Con el encabezado de Frepi
X-Frepi-Idempotency-Key (8 a 64 caracteres de A-Z a-z 0-9 _ -), repetir la llamada con el mismo
cuerpo responde el mismo wamid sin un segundo envío. Con otro cuerpo, otra plantilla u otro destinatario
(to) responde idempotency_key_reused. Dos llamadas simultáneas con la misma clave y destinatarios
distintos pueden enviarse las dos: no reutilices una clave para otro destinatario. Mientras el primer envío está en curso o sin confirmar responde
outcome_unknown. En la creación de un grupo, el mismo encabezado evita crear dos grupos.
20 llamadas por segundo por línea (ráfaga de 40), todas las rutas juntas, también las rechazadas, y
también durante las 24 horas en que la llave anterior y la nueva conviven tras una rotación (comparten el límite). Una
llave revocada o vencida tiene su propio límite pequeño (1 por segundo) y nunca gasta el de la llave activa. Por
encima: 429, código 4, Retry-After: 1. Un cuerpo de más de 256 KB responde 400. Meta permite 80 mensajes por segundo por número, así que el límite de Frepi es el
que manda.
Frepi envía a tu dirección un POST con el cuerpo que Meta envió para este número, filtrado a esta línea
(solo los campos que elegiste: mensajes y estados, grupos, plantillas, calidad del número).
X-Hub-Signature-256: sha256=<hex HMAC-SHA256(secreto, cuerpo)> ← igual que Meta X-Frepi-Delivery: <uuid> ← el mismo en cada reintento X-Frepi-Timestamp: <unix> X-Frepi-Signature: t=<unix>,v1=<hex HMAC-SHA256(secreto, "<unix>." + cuerpo)>
El esquema de Meta no tiene marca de tiempo. Si quieres protegerte de repeticiones, revisa que t esté a
menos de 5 minutos y descarta un X-Frepi-Delivery que ya procesaste.
200 en menos de 1 segundo (en menos de 400 ms si llegas a unos 40 eventos por segundo) y
procesa después. Frepi espera 10 segundos como máximo.Retry-After, y descarta el evento a las 24 horas.recipient_excluded) y sus eventos no
se te envían.Ayuda: escribe al equipo de Frepi desde la plataforma. Esta guía cubre la primera versión; los grupos (leer, editar, enlace, participantes), la subida de archivos y «marcar como leído» llegan después.