Whapier

Desarrolladores

Documentación de la API

Una API REST autenticada por token para mensajes, plantillas, contactos, campañas, flujos y el estado de entrega y el coste de cada mensaje. Crea una clave de API en Herramientas de desarrollador dentro de tu cuenta y autentica cada llamada con Authorization: Bearer <token>.

Los ejemplos completos en PHP, Node.js, Python, Java y Ruby están disponibles en la documentación dentro de la aplicación en cuanto crees una cuenta.

Referencia legible por máquinas: OpenAPI 3.1 (JSON) · Colección de Postman

Antes de empezar

Autenticación

Cada petición lleva una clave de API en la cabecera Authorization como token Bearer. Las claves se crean en Herramientas de desarrollo > Claves de API y se muestran una sola vez.

Una clave tiene ámbitos: read (todos los GET), send (los endpoints de envío, las campañas y los inicios de flujo) y manage (crear, modificar y eliminar contactos, grupos, respuestas automáticas, webhooks y plantillas, además de las acciones de la bandeja de entrada). Una clave sin el ámbito recibe un 403 que indica el ámbito que falta.

Una clave puede caducar: una clave caducada recibe un 401 con "This API key has expired". Un token ausente o desconocido también es un 401.

Una clave sandbox valida cada petición, registra el mensaje con el estado "sandbox" y no envía nada a WhatsApp - desarrolla contra la API real sin una línea de WhatsApp ni coste alguno.

Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>
Límite de peticiones

60 peticiones por minuto por clave de API; las peticiones sin clave se cuentan por dirección IP.

Cada respuesta lleva X-RateLimit-Limit y X-RateLimit-Remaining. Por encima del límite la respuesta es un 429 con statusCode 429, un message y una cabecera Retry-After en segundos.

GET /api/usage informa del límite en rate_limit.per_minute. /api y /api/v2 comparten un mismo cupo por clave.

HTTP/1.1 429 Too Many Requests
Retry-After: 42
{"statusCode": 429, "message": "Too many requests. Try again in 42 seconds."}
Idempotencia

Envía una cabecera Idempotency-Key (de 1 a 128 caracteres, única por operación) en los endpoints POST que crean algo: los envíos, las campañas, los contactos, los grupos y las respuestas automáticas.

La misma clave con la misma petición en un plazo de 24 horas devuelve la respuesta guardada con la cabecera Idempotent-Replayed: true, y no se ejecuta nada de nuevo. Un cliente que reintenta tras un tiempo de espera agotado ya no envía dos veces.

La misma clave con una petición distinta es un 422; la misma clave mientras la primera petición sigue en curso es un 409. Solo se guardan las respuestas 2xx y 4xx, así que un 5xx se puede reintentar con la misma clave.

Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
Fechas y zonas horarias

Toda fecha que devuelve la API está en UTC en ISO-8601 con su desfase, sea cual sea la zona horaria que la organización use en el panel.

Las fechas que envías (scheduled_at, since) se leen como UTC salvo que lleven un desfase.

"created_at": "2026-10-05T09:30:00+00:00"
Errores

Cada respuesta es JSON con statusCode y message; una carga rechazada añade errors, un mapa de nombre de campo a la lista de problemas.

400 entrada no válida; 401 clave ausente, no válida o caducada; 403 ámbito, suscripción, límite del plan o cuenta en revisión; 404 uuid desconocido, o que pertenece a otro espacio de trabajo; 409 conflicto (una transición de campaña imposible, una Idempotency-Key aún en curso); 422 rechazado por una regla (la ventana de 24 horas, STOP, variables de plantilla); 429 límite de peticiones; 500 culpa nuestra.

Las acciones de la bandeja de entrada (assign, close y reopen en /api/conversations/{contact_uuid}) responden 409 mientras el ticketing está desactivado en Ajustes > Tickets; leer la conversación y añadir una nota funcionan en cualquier caso.

En /api (v1) un rechazo del propio WhatsApp es un 200 con data.success false y el motivo en data.data.error (code, message, type). En /api/v2 el mismo rechazo es un HTTP 422 - consulta Versiones.

{"statusCode": 400, "message": "The given data was invalid.", "errors": {"phone": ["The phone number is not valid."]}}
Paginación

Los endpoints de listado admiten page (desde 1) y per_page (10 por defecto, máx. 100; /api/messages usa 25 por defecto) y se ordenan de más reciente a más antiguo.

Contactos, grupos, respuestas automáticas, plantillas, campañas y registros de campaña responden data, links y meta (current_page, last_page, per_page, total). /api/messages responde statusCode, data y meta (page, per_page, total, last_page).

Una conversación se pagina en cambio por cursor: GET /api/contacts/{uuid}/messages y GET /api/conversations/{contact_uuid} responden de más reciente a más antiguo con next_before; pásalo como before para obtener los mensajes anteriores.

Enviar una plantilla

Un envío de plantilla debe llevar exactamente tantos parámetros como marcadores tiene la plantilla aprobada; Meta rechaza una discrepancia con el error 132000.

GET /api/templates/{uuid} devuelve example_payload: un cuerpo completo para POST /api/send/template con el número correcto de parámetros para la cabecera, el cuerpo y los botones URL - sustituye los valores y envíalo.

POST /api/templates envía una plantilla a Meta desde una forma JSON plana y responde con el motivo de Meta cuando se rechaza; POST /api/templates/sync descarga las plantillas de Meta tras un cambio en Meta Business Manager.

GET https://whapier.com/api/templates/{{uuid}}
{"data": {"name": "order_update", "variables": {"header": 0, "body": 1, "buttons": 0}, "example_payload": {"phone": "+40721000001", "template": {"name": "order_update", "language": {"code": "en"}, "components": [{"type": "body", "parameters": [{"type": "text", "text": "Ana"}]}]}}}}
Webhooks

Los webhooks salientes se configuran en Herramientas de desarrollo > Webhooks o por la API (POST /api/webhooks): una URL https y los eventos que se quieren recibir. El secreto de firma se genera por ti y se muestra una sola vez. Cada entrega es un POST con un cuerpo JSON que lleva event y data.

La cabecera X-Whapier-Signature es "sha256=" seguido del HMAC-SHA256 del cuerpo bruto de la petición con ese secreto, en hexadecimal. Vuelve a calcularlo sobre los bytes que recibiste y compáralo antes de confiar en la carga.

Responde 2xx en menos de 15 segundos. Una entrega que falla se reintenta dos veces más, a los 60 segundos y a los 5 minutos; tras el tercer fallo el evento se descarta.

Los eventos: message.received y message.sent; message.status cuando Meta informa de un mensaje como delivered, read o failed (con el fallo y la campaña de la que vino); campaign.finished con los recuentos finales; ticket.created y ticket.closed; contact.opted_out y contact.opted_in; contact, group y autoreply created, updated y deleted; template.status.update, template.quality.update y account.update tal como los envía Meta. GET /api/webhooks/events los lista con una descripción y POST /api/webhooks/{uuid}/test entrega un ejemplo.

X-Whapier-Signature: sha256=5d41402abc4b2a76b9719d911017c592...
{"event": "message.status", "data": {"uuid": "64968a32-fdd9-4c1d-8941-b8e538036616", "wam_id": "wamid.HBgM...", "status": "delivered", "timestamp": "2026-10-05T09:30:00+00:00", "failure": null, "contact": {"uuid": "81a2d165-41e2-43f2-847e-d69e08cba0e0", "phone": "+40721000001"}, "campaign": null}}
{"event": "campaign.finished", "data": {"uuid": "3f1c2d0e-5b7a-4c8d-9e0f-1a2b3c4d5e6f", "name": "October offers", "status": "completed", "counts": {"contacts": 120, "sent": 118, "delivered": 110, "read": 64, "failed": 2}, "finished_at": "2026-10-05T09:30:00+00:00"}}
message.received
message.sent
message.status
message.status.update
contact.created
contact.updated
contact.deleted
contact.opted_out
contact.opted_in
group.created
group.updated
group.deleted
autoreply.created
autoreply.updated
autoreply.deleted
campaign.finished
ticket.created
ticket.closed
template.status.update
template.quality.update
account.update
Versiones: v1 y v2

/api (versión 1) sigue admitida tal como está. /api/v2 sirve los mismos endpoints, cargas y campos con una diferencia: el estado HTTP refleja el resultado.

En v2 un rechazo de WhatsApp en un envío es un HTTP 422 cuyo cuerpo tiene statusCode 422, el message, error (code, message y type de WhatsApp) y data (la respuesta de v1), en lugar de un 200 con data.success false. Siempre que el statusCode de un cuerpo difiera del estado HTTP, el estado HTTP sigue a statusCode.

Todo lo demás - autenticación, ámbitos, límite de peticiones, idempotencia, fechas, paginación - es idéntico, y la misma clave funciona en ambas.

POST https://whapier.com/api/v2/send
{"statusCode": 422, "message": "Template name does not exist", "error": {"code": 132001, "message": "Template name does not exist", "type": "OAuthException"}, "data": {"success": false, "message": "Template name does not exist", "data": {"error": {"code": 132001}}}}

Verificación

GET /api/verify Verificar la clave de API
# Confirma que el token Bearer es válido y que la suscripción de la organización está activa antes de llamar a otros endpoints.
# HTTP 200: token correcto. 401: token ausente o no válido. 403: suscripción inactiva.

curl -X GET \
https://whapier.com/api/verify \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Contactos

GET /api/contacts Obtener la lista de contactos
# Parámetros de consulta opcionales: ?page=1&per_page=10 (per_page máx. 100).
# Los más recientes primero. Respuesta: data (objetos de contacto), links y meta (current_page, last_page, per_page, total).
# Un contacto: id, uuid, phone, first_name, last_name, email, address, metadata, groups (uuid, name), source, opted_in_at, opted_out_at, created_at, updated_at.
# Filtros de búsqueda, combinados con AND: phone (cualquier formato que el país de la organización pueda leer; 400 cuando no es un número), email (exacto, sin distinguir mayúsculas), q (de 2 a 128 caracteres, buscado en nombre, email y número), group (el uuid de un grupo de contactos; 400 si es desconocido), opted_out (true | false), updated_since (una fecha, UTC), source (api, manual, import, entry point...).

curl -X GET \
"https://whapier.com/api/contacts?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
GET /api/contacts/{uuid} Obtener un contacto
# El objeto contacto de Obtener la lista de contactos más unread_messages, last_message_at y ticket (status, assigned_to: name, email).
# ticket es null mientras el ticketing está desactivado en Ajustes > Tickets, o cuando la conversación aún no tiene ticket. 404 cuando el uuid es desconocido, está eliminado o pertenece a otro espacio de trabajo.

curl -X GET \
https://whapier.com/api/contacts/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/contacts Añadir contacto
# Campos del cuerpo JSON:
#   phone (obligatorio) — número internacional; se valida y se guarda en formato E.164. Debe ser único en cada organización (400 si está duplicado).
#   first_name, last_name, email (opcionales) — omítelos o envíalos como null o "" para contactos sin nombre.
#   street, city, state, zip, country (opcionales) — se guardan en el JSON de address.
#   metadata (opcional) — cualquier valor serializable en JSON.
#   group (opcional) — array de cadenas con los UUID de grupos de contactos.
#   marketing_opt_in (opcional, booleano) — true registra el consentimiento de marketing en el contacto (registro de consentimiento del RGPD, source: api); false registra una baja. Omítelo para no modificar el consentimiento.
# Respuestas: 200 éxito { statusCode, id, message }; 400 error de validación; 403 suscripción inactiva o límite de contactos alcanzado.
# Acepta una cabecera Idempotency-Key (consulta Antes de empezar).

curl -X POST \
https://whapier.com/api/contacts \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "first_name": "Ana",
    "last_name": "Pop",
    "email": "ana@example.com",
    "group": [
        "d746e4c5-db24-4fb2-be03-590421344d69"
    ],
    "marketing_opt_in": true
}'
POST /api/contacts/bulk Crear o actualizar contactos en bloque
# contacts (obligatorio, de 1 a 500 objetos) con los campos de Añadir contacto por fila. Una fila cuyo phone ya existe en el espacio de trabajo se actualiza con los campos presentes (como hace Editar contacto); un número nuevo se crea.
# Cada fila va por su cuenta: una fila errónea falla sola y las demás pasan. Respuesta: { statusCode, created, updated, failed: [ { index, phone, errors } ], limit_reached, message }.
# La creación se detiene en el límite de contactos del plan: limit_reached pasa a true y las filas que no cupieron aparecen en failed. Las actualizaciones nunca cuentan para el límite. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/contacts/bulk \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "contacts": [
        {
            "phone": "+40721000001",
            "first_name": "Ana",
            "last_name": "Pop",
            "email": "ana@example.com"
        },
        {
            "phone": "0721 000 002",
            "first_name": "Ion",
            "group": [
                "d746e4c5-db24-4fb2-be03-590421344d69"
            ]
        },
        {
            "phone": "not-a-phone",
            "first_name": "Nobody"
        }
    ]
}'
PUT /api/contacts/{uuid} Editar contacto
# Los mismos campos JSON que en Añadir contacto. {uuid} en la ruta es el id del contacto que se devolvió al crearlo.
# phone es opcional en una actualización; si lo cambias, el nuevo número no puede pertenecer a otro contacto (400 duplicado).
# Se permite actualizar con el mismo phone que ya tiene este contacto.
# Los campos que no envíes conservan su valor; envía un campo como null o "" para vaciarlo. Las claves de metadata que envíes se fusionan con las guardadas, y las demás se conservan.
# marketing_opt_in (opcional, booleano) — true registra el consentimiento de marketing (source: api), false registra una baja; si se omite, el consentimiento no cambia.
# 404 cuando el uuid es desconocido, está eliminado o pertenece a otro espacio de trabajo.

curl -X PUT \
https://whapier.com/api/contacts/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "first_name": "Ana",
    "last_name": "Popescu",
    "metadata": {
        "plan": "platinum"
    }
}'
DELETE /api/contacts/{uuid} Eliminar contacto
# Elimina el contacto de forma lógica (soft delete); desaparece de todas las listas a la vez. 404 cuando el uuid es desconocido o ya está eliminado.
# Respuesta: { statusCode, id, message }.

curl -X DELETE \
https://whapier.com/api/contacts/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Grupos de contactos

GET /api/contact-groups Obtener la lista de grupos de contactos
# Parámetros de consulta opcionales: ?page=1&per_page=10 (per_page máx. 100).
# Cada grupo: uuid, name, contacts_count (miembros activos), created_at, updated_at.

curl -X GET \
"https://whapier.com/api/contact-groups?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/contact-groups Añadir grupo de contactos
# name (obligatorio) debe ser único entre los grupos de la organización (400 si ya existe).
# Respuesta: { statusCode, id, message }. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/contact-groups \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "Lead 5"
}'
PUT /api/contact-groups/{uuid} Editar grupo de contactos
# El mismo cuerpo que en Añadir grupo de contactos. 404 para un uuid desconocido.

curl -X PUT \
https://whapier.com/api/contact-groups/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "Lead 5"
}'
DELETE /api/contact-groups/{uuid} Eliminar grupo de contactos
# Elimina el grupo y sus pertenencias; los contactos en sí se conservan. 404 para un uuid desconocido.

curl -X DELETE \
https://whapier.com/api/contact-groups/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Respuestas automáticas

GET /api/canned-replies Obtener la lista de respuestas automáticas
# Parámetros de consulta opcionales: ?page=1&per_page=10 (per_page máx. 100).
# Cada respuesta: uuid, name, trigger, match_criteria, response_type, response (el texto, o null para multimedia), file (name, url) o null, created_at, updated_at.

curl -X GET \
"https://whapier.com/api/canned-replies?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/canned-replies Añadir respuesta automática
# Cuerpo: name y trigger (máx. 255), match_criteria (exact match | contains), response_type (text | image | audio), response.
# Para text, response es la respuesta (máx. 4096). Para image o audio envía multipart/form-data con el archivo como response (png/jpg hasta 5 MB; mp3/aac/ogg/amr/opus/m4a hasta 16 MB); sin archivo la petición es un 400.
# Respuesta: { statusCode, id, message }. 403 cuando se alcanza el límite de respuestas automáticas del plan. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/canned-replies \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "About Us",
    "trigger": "what do you do?",
    "match_criteria": "contains",
    "response_type": "text",
    "response": "We sell shoes"
}'
PUT /api/canned-replies/{uuid} Editar respuesta automática
# Los mismos campos que en Añadir respuesta automática. Una actualización sin archivo conserva el multimedia que ya tiene. 404 para un uuid desconocido.

curl -X PUT \
https://whapier.com/api/canned-replies/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "About Us",
    "trigger": "what do you do?",
    "match_criteria": "contains",
    "response_type": "text",
    "response": "We sell shoes and clothes"
}'
DELETE /api/canned-replies/{uuid} Eliminar respuesta automática
# 404 para un uuid desconocido. Respuesta: { statusCode, id, message }.

curl -X DELETE \
https://whapier.com/api/canned-replies/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Líneas de WhatsApp

GET /api/whatsapp-connections Listar conexiones
# Devuelve el uuid de cada línea (úsalo como whatsapp_connection en /api/send). No incluye ids internos de la base de datos.
# Cada fila: uuid, type, name, phone, display_phone_number y, para una línea de WhatsApp Business, verified_name y number_status. 403 cuando no hay ninguna línea conectada.

curl -X GET \
https://whapier.com/api/whatsapp-connections \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Mensajes

POST /api/send Enviar mensaje
# Opcional: whatsapp_connection (UUID de GET /api/whatsapp-connections). Omítelo para usar la línea predeterminada de la organización.
# phone (obligatorio) — en cualquier formato; se normaliza a E.164 con el país de la organización. Un número nuevo se convierte en contacto (first_name, last_name, email opcionales).
# message (obligatorio, máx. 4096); header y footer (opcionales, máx. 60); buttons (opcional, hasta 3 de { id, title }).
# Los mensajes de texto libre necesitan una ventana de 24 horas abierta: 422 cuando el último mensaje del cliente tiene más de un día. 403: suscripción, límite del plan, cupo diario de prueba o cuenta en revisión.
# Respuesta: 200 { statusCode, data: { success, data: { chat: { uuid, wam_id, status, type, created_at, contact } } } }. Un rechazo de WhatsApp es un 200 con data.success false y data.data.error en /api; un HTTP 422 en /api/v2.
# Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/send \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "message": "Hello Ana, how are you?",
    "whatsapp_connection": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "header": "Example Shoes",
    "footer": "Reply STOP to opt out",
    "buttons": [
        {
            "id": "yes",
            "title": "Fine"
        },
        {
            "id": "no",
            "title": "Not well"
        }
    ]
}'
POST /api/send/media Enviar archivo multimedia
# Opcional: whatsapp_connection (UUID de GET /api/whatsapp-connections). Omítelo para usar la línea predeterminada de la organización.
# media_type (image | video | audio | document | sticker); media_url (https, un host público; se comprueba con una petición HEAD contra los límites de tamaño y tipo de WhatsApp - 400 con el motivo en caso contrario); caption (opcional, no para audio); file_name (obligatorio para document).
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/media \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "media_type": "image",
    "media_url": "https://cdn.example.com/offers/october.jpg",
    "caption": "Our October offers"
}'
POST /api/send/template Enviar mensaje de plantilla
# Opcional: "whatsapp_connection": "<uuid>" para una línea WABA concreta (UUID de GET /api/whatsapp-connections).
# template.name y template.language ("en" o { "code": "en" }) son obligatorios; components sigue la forma propia de WhatsApp (header, body, parámetros de botones).
# Funciona fuera de la ventana de 24 horas. A un contacto que se dio de baja (STOP) no se le envía una plantilla MARKETING: 422. Las plantillas de utilidad y de autenticación sí se envían.
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/template \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "template": {
        "name": "order_update",
        "language": {
            "code": "en"
        },
        "components": [
            {
                "type": "header",
                "parameters": [
                    {
                        "type": "text",
                        "text": "48213"
                    }
                ]
            },
            {
                "type": "body",
                "parameters": [
                    {
                        "type": "text",
                        "text": "Ana"
                    },
                    {
                        "type": "text",
                        "text": "48213"
                    },
                    {
                        "type": "date_time",
                        "date_time": {
                            "fallback_value": "October 7, 2026"
                        }
                    }
                ]
            },
            {
                "type": "button",
                "sub_type": "url",
                "index": "0",
                "parameters": [
                    {
                        "type": "text",
                        "text": "48213"
                    }
                ]
            }
        ]
    }
}'
POST /api/send/location Enviar ubicación
# latitude (de -90 a 90) y longitude (de -180 a 180) obligatorios; name y address opcionales.
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/location \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "latitude": 46.77120000000000032969182939268648624420166015625,
    "longitude": 23.623599999999999710098563809879124164581298828125,
    "name": "Example Shoes",
    "address": "Str. Memorandumului 12, Cluj-Napoca"
}'
POST /api/send/contacts Enviar tarjetas de contacto
# contacts (de 1 a 10 tarjetas) con la forma contacts[] de WhatsApp: name.formatted_name obligatorio; phones[], emails[], addresses[], org opcionales.
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/contacts \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "contacts": [
        {
            "name": {
                "formatted_name": "Example Shoes Support",
                "first_name": "Support"
            },
            "phones": [
                {
                    "phone": "+40700000000",
                    "type": "WORK"
                }
            ],
            "emails": [
                {
                    "email": "support@example.com",
                    "type": "WORK"
                }
            ]
        }
    ]
}'
POST /api/send/reaction Enviar reacción
# message_id (el wam_id del mensaje al que se reacciona) y emoji (obligatorio; "" quita la reacción).
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/reaction \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "message_id": "wamid.HBgMNDA3MjEwMDAwMDEVAgARGBI5QkY2RkE3RDlCMjc2RjI2QkEA",
    "emoji": "👍"
}'
POST /api/send/location-request Solicitar ubicación
# message (obligatorio, máx. 1024): el texto que se muestra encima del botón "Enviar ubicación".
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/location-request \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "message": "Where should we deliver?"
}'
POST /api/send/request-contact-info Solicitar datos de contacto
# message (obligatorio, máx. 1024): pide a un usuario de WhatsApp que solo tiene nombre de usuario que comparta su número de teléfono.
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/request-contact-info \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "message": "Could you share your number so we can call you back?"
}'
POST /api/send/carousel Enviar carrusel
# message (obligatorio, máx. 1024) y cards (de 2 a 10): header { type: image | video, link } obligatorio; body (máx. 160), url_button { text, url } y quick_replies (hasta 3 de { id, title }) opcionales.
# Las mismas comprobaciones y respuesta que Enviar mensaje.

curl -X POST \
https://whapier.com/api/send/carousel \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "phone": "+40721000001",
    "message": "Our new arrivals",
    "cards": [
        {
            "header": {
                "type": "image",
                "link": "https://cdn.example.com/shoes/1.jpg"
            },
            "body": "Runner, 89 EUR",
            "url_button": {
                "text": "See it",
                "url": "https://example.com/shoes/1"
            }
        },
        {
            "header": {
                "type": "image",
                "link": "https://cdn.example.com/shoes/2.jpg"
            },
            "body": "Trail, 119 EUR",
            "quick_replies": [
                {
                    "id": "trail",
                    "title": "Tell me more"
                }
            ]
        }
    ]
}'

Flujos

POST /api/flows/{uuid}/start Iniciar un flujo
# contact (obligatorio): el uuid o el phone del contacto (normalizado con el país de la organización).
# 200 { statusCode, success: true, message }; 404 flujo o contacto desconocido; 422 flujo no publicado; 409 el contacto ya está en una sesión de flujo.
# Todo lo que envíe el flujo está sujeto a la ventana de 24 horas salvo que use nodos de plantilla.

curl -X POST \
https://whapier.com/api/flows/{{uuid}}/start \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "contact": "+40721000001"
}'

Campañas

GET /api/campaigns Listar campañas
# Opcional: ?page=1&per_page=10&search=
# Cada campaña: uuid, name, status, scheduled_at, created_at, template (uuid, name, language, category, status), whatsapp_connection (uuid, name, type), audience (type: all | group | segment, uuid, name), counts (contacts, sent, delivered, read, failed).

curl -X GET \
"https://whapier.com/api/campaigns?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/campaigns Crear campaña (plantilla WABA con variables)
# Úsalo cuando whatsapp_connection sea una línea de WhatsApp Cloud (WABA) de GET /api/whatsapp-connections.
#
# REQUISITOS PREVIOS
# - template: UUID de una fila de GET /api/templates (de la organización), NO la cadena con el nombre público de la plantilla.
# - Ajusta el número de variables a tu plantilla aprobada: abre los metadatos de la plantilla en la interfaz o en Meta; BODY usa {{1}}, {{2}}, … en orden.
# - Debes enviar un parámetro de cuerpo por cada marcador de BODY, en el mismo orden. Un número que no coincida con la plantilla aprobada se rechaza de antemano (422), no lo rechaza WhatsApp después.
#
# NOTAS SOBRE LOS CAMPOS
# - contacts: "all" o el uuid de un grupo de contactos; o segment: el uuid de un segmento guardado (uno de los dos, no ambos). send_immediately: true, o scheduled_at (una fecha, leída como UTC).
# - header: objeto opcional; si se omite equivale a { "format": "TEXT", "parameters": [] }.
# - body: usa body.parameters[] para las variables. Formato de la API (recomendado): { "type": "text", "value": "literal for {{1}}" } por cada hueco.
#   Alternativa (como en el panel): { "selection": "static", "value": "..." } o { "selection": "dynamic", "value": "first name" } (asigna campos del contacto).
# - footer / buttons: opcionales; usa [] o {} si no se usan. Las plantillas con variables de URL o de respuesta rápida necesitan buttons[] con la misma forma que el formulario de campaña de la web.
# - Cabecera multimedia (IMAGE|VIDEO|DOCUMENT): header.parameters[] con type (IMAGE|VIDEO|DOCUMENT), "selection": "link", "value": "https://..." (URL pública; la API no puede subir archivos).
#
# RESPUESTA: 200 { "statusCode": 200, "uuid": "...", "message": "..." }. Consulta GET /api/campaigns/{uuid} para el informe y GET /api/campaigns/{uuid}/logs para el estado por destinatario, o suscríbete al webhook campaign.finished.
# 403 cuando se alcanza el límite de campañas del plan. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/campaigns \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "Billing reminder blast",
    "whatsapp_connection": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "contacts": "all",
    "send_immediately": true,
    "template": "281a3383-985c-4084-bcb0-9ec523c952de",
    "header": {
        "format": "TEXT",
        "parameters": []
    },
    "body": {
        "parameters": [
            {
                "type": "text",
                "value": "Ana"
            },
            {
                "type": "text",
                "value": "48213"
            },
            {
                "type": "text",
                "value": "7 October 2026"
            }
        ]
    },
    "footer": {},
    "buttons": []
}'
GET /api/campaigns/{uuid} Obtener una campaña con su informe
# El objeto campaña de Listar campañas más report: counts (contacts, sent, delivered, read, failed, pending), failure_reasons (code, title, count - los diez más frecuentes), started_at, finished_at (null hasta que la campaña termina), paused_reason, paused_detail y paused_at (null salvo que el estado sea paused).
# paused_reason es user (pausada desde el panel o la API), admin, failing, failure_rate o quality (pausada por la plataforma; paused_detail explica por qué). 404 para una campaña desconocida.

curl -X GET \
https://whapier.com/api/campaigns/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/campaigns/{uuid}/pause Pausar una campaña
# Sin cuerpo. Solo una campaña scheduled u ongoing puede pausarse: 409 en caso contrario, con el estado en que está la campaña. Los registros se quedan como están y reanudar los retoma.
# Respuesta: { statusCode, status, message } - status es el nuevo estado de la campaña. 404 para una campaña desconocida.

curl -X POST \
https://whapier.com/api/campaigns/{{uuid}}/pause \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'
POST /api/campaigns/{uuid}/resume Reanudar una campaña
# Sin cuerpo. Solo una campaña paused puede reanudarse: 409 en caso contrario. Una campaña pausada antes de su hora vuelve a scheduled; una pausada durante el envío vuelve a ongoing.
# Respuesta: { statusCode, status, message }. 404 para una campaña desconocida.

curl -X POST \
https://whapier.com/api/campaigns/{{uuid}}/resume \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'
POST /api/campaigns/{uuid}/cancel Cancelar una campaña
# Sin cuerpo. Una campaña scheduled, ongoing o paused puede cancelarse: 409 en caso contrario. Lo que no se envió queda sin enviar; el informe sigue mostrando a quién se llegó.
# Respuesta: { statusCode, status, message }. 404 para una campaña desconocida.

curl -X POST \
https://whapier.com/api/campaigns/{{uuid}}/cancel \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'
GET /api/campaigns/{uuid}/logs Registro de la campaña
# Opcional: ?page=1&per_page=10&search= (busca por nombre o teléfono del contacto).
# Cada fila: contact (uuid, phone, first_name, last_name), status, message (uuid, wam_id, status) o null, error (code, title, reason) o null, retry_count, created_at, updated_at. 404 para una campaña desconocida.

curl -X GET \
"https://whapier.com/api/campaigns/{{uuid}}/logs?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Plantillas

GET /api/templates Listar plantillas
# Parámetros de consulta opcionales: ?page=1&per_page=10 (per_page máx. 100).
# Cada plantilla: uuid, name, language, category, status, components (tal como los aprobó WhatsApp), variables (header, body, buttons: cuántos marcadores admite cada parte), rejection (por qué se rechazó, o null), quality_score, updated_at.

curl -X GET \
"https://whapier.com/api/templates?page=1&per_page=10" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
GET /api/templates/{uuid} Obtener una plantilla con un ejemplo listo para enviar
# El objeto plantilla de Listar plantillas más example_payload: un cuerpo completo para POST /api/send/template con un parámetro por marcador (los ejemplos con los que se envió la plantilla, o un valor que nombra la posición), un enlace para una cabecera multimedia y un sufijo por cada botón URL con variable.
# Consulta opcional: whatsapp_connection (el uuid de una línea) cuando la misma plantilla existe en varias líneas. 404 para un uuid desconocido.

curl -X GET \
https://whapier.com/api/templates/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/templates Crear plantilla
# name (letras minúsculas, dígitos y guiones bajos), category (MARKETING | UTILITY) y language (un código de idioma de Meta como en o pt_BR) son obligatorios; whatsapp_connection (el uuid de una línea de WhatsApp Business) es opcional - si no, la línea por defecto.
# header (opcional): { format: TEXT, text (máx. 60, un solo {{1}} permitido), examples } o { format: IMAGE | VIDEO | DOCUMENT, media_url: un enlace https público a un archivo de ejemplo }.
# body.text (obligatorio, máx. 1024, marcadores {{1}}, {{2}}, ...) con body.examples, uno por marcador; footer (opcional, máx. 60); buttons (opcional, hasta 10): { type: QUICK_REPLY | URL | PHONE_NUMBER | COPY_CODE, text, url, phone_number, example }.
# Respuesta: 200 { statusCode, uuid, status, message } - status es PENDING hasta que Meta la revisa (el webhook template.status.update te avisa). 400 con el motivo de Meta cuando Meta la rechaza; las plantillas AUTHENTICATION aún no pueden enviarse por la API. Una clave sandbox no puede enviar (403). Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/templates \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "name": "order_update",
    "category": "UTILITY",
    "language": "en",
    "header": {
        "format": "TEXT",
        "text": "Order {{1}}",
        "examples": [
            "48213"
        ]
    },
    "body": {
        "text": "Hi {{1}}, your order {{2}} ships on {{3}}.",
        "examples": [
            "Ana",
            "48213",
            "October 7"
        ]
    },
    "footer": "Example Shoes",
    "buttons": [
        {
            "type": "URL",
            "text": "Track",
            "url": "https://example.com/track/{{1}}",
            "example": "48213"
        },
        {
            "type": "QUICK_REPLY",
            "text": "Stop updates"
        }
    ]
}'
POST /api/templates/sync Sincronizar plantillas desde Meta
# Sin cuerpo. Descarga de Meta las plantillas de cada línea de WhatsApp Business, como hace el botón de actualizar del panel, para que una plantilla cambiada en Meta Business Manager aparezca también aquí.
# Respuesta: { statusCode, synced, message } - synced es cuántas plantillas se añadieron o cambiaron. Una clave sandbox no puede sincronizar (403); 400 cuando Meta rechazó la petición.

curl -X POST \
https://whapier.com/api/templates/sync \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'

Webhooks

GET /api/webhooks Listar webhooks
# Cada fila: uuid, url, events, secret_preview (los últimos seis caracteres del secreto de firma), created_at, updated_at. El secreto completo se muestra una sola vez, en la respuesta de Añadir webhook.
# 403 cuando el módulo Webhooks no está en el plan.

curl -X GET \
https://whapier.com/api/webhooks \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
GET /api/webhooks/events Listar eventos
# Cada evento que un webhook puede recibir: data (name, description). La página Webhooks del panel lee el mismo catálogo.
# Una entrega es { event, data }; la forma de data por evento está en Antes de empezar > Webhooks, y POST /api/webhooks/{uuid}/test entrega un ejemplo de cualquiera de ellos.

curl -X GET \
https://whapier.com/api/webhooks/events \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/webhooks Añadir webhook
# url (obligatorio, https, único por organización) y events (obligatorio, uno o más nombres de GET /api/webhooks/events).
# Respuesta: { statusCode, uuid, secret, message } - la única vez que se devuelve el secreto completo; guárdalo para comprobar X-Whapier-Signature. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/webhooks \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "url": "https://example.com/hooks/whapier",
    "events": [
        "message.received",
        "message.status",
        "campaign.finished"
    ]
}'
PUT /api/webhooks/{uuid} Editar webhook
# Mismo cuerpo que Añadir webhook. La lista events sustituye a la guardada; el secreto se mantiene. 404 para un uuid desconocido.

curl -X PUT \
https://whapier.com/api/webhooks/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "url": "https://example.com/hooks/whapier",
    "events": [
        "message.received",
        "message.status"
    ]
}'
DELETE /api/webhooks/{uuid} Eliminar webhook
# Elimina el endpoint y sus suscripciones; ya no se le entrega nada. Respuesta: { statusCode, uuid, message }. 404 para un uuid desconocido.

curl -X DELETE \
https://whapier.com/api/webhooks/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/webhooks/{uuid}/test Enviar un evento de prueba
# event (obligatorio): un nombre de GET /api/webhooks/events. La carga de ejemplo se envía por POST a la url como { event, data, test: true }, firmada con el secreto en X-Whapier-Signature.
# Respuesta: { statusCode, event, delivered, http_status, message } - delivered es false con el estado con que respondió tu endpoint, o http_status null cuando no se pudo alcanzar. 404 para un uuid desconocido.

curl -X POST \
https://whapier.com/api/webhooks/{{uuid}}/test \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "event": "message.status"
}'

Estado de los mensajes

GET /api/messages Listar mensajes
# Consulta opcional: status (sent | delivered | read | failed | accepted | played), direction (inbound | outbound), since y until (fechas, UTC), contact (el uuid de un contacto), campaign (el uuid de una campaña: los mensajes que produjo), wam_id (un mensaje por su id de WhatsApp), api_key (el uuid de una clave, o current: los mensajes enviados con la clave que hace la petición), page, per_page (25 por defecto, máx. 100).
# Respuesta: { statusCode, data: [ message ], meta: { page, per_page, total, last_page } }. 404 cuando contact, campaign o api_key es desconocido.
# Un mensaje: uuid, wam_id, direction, status, type, content, created_at, delivered_at, read_at, failed_at, failure (code, title, reason, retryable) o null, pricing (category, model, billable, amount, currency, country), api_key (uuid, name) o null cuando no se envió por la API, contact (uuid, phone, first_name, last_name).
# content según type - text: body, header, footer, buttons (type, text); template: name, language, body; image, video, audio, document, sticker: caption, media (type, url, filename); interactive y button: text, payload (lo que pulsó el cliente); location: latitude, longitude, name, address; contacts: contacts (name, phones); reaction: emoji, wam_id.

curl -X GET \
"https://whapier.com/api/messages?status=failed&per_page=25" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
GET /api/contacts/{uuid}/messages Conversación con un contacto
# Los mismos objetos mensaje que Listar mensajes, para un solo contacto, de más reciente a más antiguo. Consulta opcional: per_page (25 por defecto, máx. 100), before (el uuid del mensaje más antiguo que ya tienes).
# Respuesta: { statusCode, data: [ message ], meta: { per_page, has_more, next_before } } - pasa next_before como before para obtener los mensajes anteriores. 404 para un contacto o un cursor desconocido.

curl -X GET \
"https://whapier.com/api/contacts/{{uuid}}/messages?per_page=25" \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
GET /api/messages/{uuid} Obtener un mensaje
# El mismo objeto de mensaje más history: una lista de { status, timestamp, recorded_at }, cada cambio de estado que WhatsApp notificó. 404 para un uuid desconocido.

curl -X GET \
https://whapier.com/api/messages/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Uso

GET /api/usage Plan y uso
# Datos de la respuesta: plan (name, status, valid_until), limits (contacts, messages, campaigns: used, limit; limit null = ilimitado), trial_daily_messages (solo cuentas de prueba), messages_sent (today, last_7_days, last_30_days), estimated_cost (last_30_days: amount, currency), rate_limit (per_minute).

curl -X GET \
https://whapier.com/api/usage \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'

Bandeja de entrada

GET /api/conversations/{contact_uuid} Obtener una conversación
# Respuesta data: contact (uuid, phone, first_name, last_name), ticketing (si Ajustes > Tickets está activado), ticket (status, assigned_to: name, email; opened_at, waiting_since, resolved_at) o null, messages (uuid, wam_id, direction, type, text, status, created_at - los 50 más recientes, primero el más nuevo), next_before.
# Consulta opcional: before (el uuid del mensaje más antiguo que tienes) para los mensajes anteriores; 400 cuando el cursor no es un mensaje de este contacto. 404 para un contacto desconocido.

curl -X GET \
https://whapier.com/api/conversations/{{uuid}} \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Accept: application/json'
POST /api/conversations/{contact_uuid}/assign Asignar una conversación
# user_email (obligatorio, admite null): el email de un miembro activo de la organización; null desasigna el ticket. Una conversación sin ticket recibe uno.
# Respuesta: { statusCode, data: ticket, message } con el ticket tal como lo muestra Obtener una conversación. 409 mientras el ticketing está desactivado; 422 cuando ningún miembro activo de la organización tiene ese email; 404 para un contacto desconocido.

curl -X POST \
https://whapier.com/api/conversations/{{uuid}}/assign \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "user_email": "maria@example.com"
}'
POST /api/conversations/{contact_uuid}/close Cerrar una conversación
# Sin cuerpo. Resuelve el ticket (status closed, resolved_at fijado), como el botón de la bandeja de entrada. Respuesta: { statusCode, data: ticket, message }.
# 409 mientras el ticketing está desactivado; 404 para un contacto desconocido.

curl -X POST \
https://whapier.com/api/conversations/{{uuid}}/close \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'
POST /api/conversations/{contact_uuid}/reopen Reabrir una conversación
# Sin cuerpo. Reabre un ticket cerrado (status open, resolved_at borrado); un ticket abierto se devuelve tal cual. Respuesta: { statusCode, data: ticket, message }.
# 409 mientras el ticketing está desactivado; 404 para un contacto desconocido.

curl -X POST \
https://whapier.com/api/conversations/{{uuid}}/reopen \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{}'
POST /api/conversations/{contact_uuid}/notes Añadir una nota interna
# note (obligatorio, máx. 5000): visible para los agentes en el hilo, nunca se envía al contacto. Funciona con el ticketing activado o desactivado.
# Respuesta: { statusCode, uuid, created_at, message }. 404 para un contacto desconocido. Acepta una cabecera Idempotency-Key.

curl -X POST \
https://whapier.com/api/conversations/{{uuid}}/notes \
-H 'Authorization: Bearer <YOUR_AUTHORIZATION_TOKEN>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
    "note": "Customer called; wants the order delivered on Friday."
}'

Consigue una clave de API en dos minutos

Prueba gratis, sin tarjeta: el panel de desarrollador está incluido en todos los planes.

Empezar prueba gratis