Consultar mensajes y cuentas
Además de enviar, la API te deja leer: el estado de un mensaje puntual, listados con filtros para reconciliar en lote, y tus cuentas de WhatsApp. Todo con la misma autenticación x-api-key y siempre acotado a tu cuenta.
Tip
Para enterarte de los cambios de estado en tiempo real no hagas polling: usá webhooks. Estos endpoints brillan para reconciliar (ej.: "todos mis mensajes sent de las últimas 48 h") o como plan B.
GET /messages/{id}
Devuelve un mensaje tuyo. {id} puede ser el messageId de Arplyx o tu propio externalId — usá el que tengas a mano.
curl https://api.arplyx.com/messages/turno-4812-recordatorio \
-H "x-api-key: ak_live_xxxxxxxx..."
Respuesta 200:
{
"messageId": "b7a19c4d-…",
"externalId": "turno-4812-recordatorio",
"recipient": "+15550100124",
"channel": "whatsapp_meta",
"status": "read",
"attemptCount": 1,
"lastErrorCode": null,
"broadcastId": null,
"whatsappAccountId": "9d2f81b3-…",
"createdAt": "2026-07-04T12:00:00.000Z",
"sentAt": "2026-07-04T12:00:02.000Z",
"deliveredAt": "2026-07-04T12:00:04.000Z",
"readAt": "2026-07-04T12:01:10.000Z",
"failedAt": null,
"providerMessageId": "wamid.HBgLMTU1NTAxMDAxMjQVAgARGBI…",
"expiresAt": "2026-07-05T12:00:00.000Z",
"sandbox": false,
"processing": {
"queueState": "done",
"attemptCount": 1,
"maxAttempts": 5,
"nextAttemptAt": null,
"lastAttemptAt": "2026-07-04T12:00:02.000Z"
},
"error": null
}
| Campo | Descripción |
|---|---|
status |
Ver ciclo de vida. Un 201 al enviar no prueba entrega: este campo sí. |
sentAt / deliveredAt / readAt / failedAt |
Timestamp de cada transición; null si (aún) no ocurrió. |
providerMessageId |
El id del proveedor (el wamid de Meta) cuando el envío fue aceptado; null antes de eso. Sirve para correlacionar con los recibos de Meta. |
attemptCount |
Intentos de envío realizados. |
lastErrorCode |
Código del último error de envío (null si no hubo). Se mantiene por compatibilidad; el detalle está en error. |
error |
null si no hubo error. Si lo hubo: code (código de Arplyx, ver catálogo de errores de envío), message y provider con el detalle saneado de Meta cuando existe: code, subcode, message, fbtraceId, source (send_attempt o status_webhook). Nunca incluye tokens ni payloads completos. |
processing |
Estado operativo del worker: queueState, attemptCount, maxAttempts, nextAttemptAt (cuándo se reintenta, si corresponde) y lastAttemptAt. |
expiresAt |
Cuándo se purga el registro (ver vida útil). |
sandbox |
true si se envió con una clave de sandbox (no cuenta para la cuota). |
broadcastId |
Presente si el mensaje nació de un broadcast. |
Errores: 404 message_not_found si el id no existe, no es tuyo o el mensaje ya fue purgado por la retención de tu plan (3–30 días según plan). Diseñá tu reconciliación asumiendo que los mensajes viejos desaparecen.
GET /messages (listado)
Lista tus mensajes, los más recientes primero, con filtros y paginación por cursor.
curl "https://api.arplyx.com/messages?status=sent&since=2026-07-02T00:00:00Z&limit=100" \
-H "x-api-key: ak_live_xxxxxxxx..."
Parámetros de query (todos opcionales):
| Parámetro | Descripción |
|---|---|
status |
pending, queued, sent, delivered, read o failed. |
externalId |
Un externalId exacto. |
providerMessageId |
Un wamid exacto: útil para encontrar el mensaje a partir de un recibo de Meta. |
channel |
whatsapp_meta. |
whatsappAccountId |
Solo mensajes de esa cuenta. |
since / until |
Rango sobre createdAt (ISO 8601). |
limit |
1–100, default 50. |
cursor |
El nextCursor de la página anterior. |
Respuesta 200:
{
"messages": [ { "…mismo shape que GET /messages/{id}…" } ],
"nextCursor": "MTc1MTYzMDQwMDAwMHxiN2ExOWM0ZA"
}
nextCursoresnullcuando no hay más páginas. Es un cursor opaco: pasalo tal cual, no lo construyas.- Un cursor mal formado responde
400 invalid_cursor.
Ejemplo de reconciliación — "¿qué pasó con todo lo que quedó sent en las últimas 48 h?":
curl "https://api.arplyx.com/messages?status=sent&since=$(date -u -d '48 hours ago' +%FT%TZ)" \
-H "x-api-key: ak_live_xxxxxxxx..."
GET /whatsapp-accounts
Lista tus cuentas de WhatsApp vinculadas, con el whatsappAccountId que necesitás para enviar.
curl https://api.arplyx.com/whatsapp-accounts \
-H "x-api-key: ak_live_xxxxxxxx..."
Respuesta 200:
{
"accounts": [
{
"id": "9d2f81b3-…",
"type": "META_OFFICIAL",
"status": "CONNECTED",
"phoneNumber": "+15550100124",
"displayName": "Estudio Pilates Centro",
"connectedAt": "2026-06-11T14:30:00.000Z",
"channel": "whatsapp_meta",
"lastErrorCode": null,
"meta": {
"phoneNumberId": "1234567890",
"wabaId": "9876543210",
"wabaVerification": "verified",
"verifiedName": "Estudio Pilates Centro"
},
"sendPolicy": { "mode": "PRODUCTION", "testRecipientsCount": null },
"webhook": { "integrationId": null, "callbackUrl": "https://api.arplyx.com/webhooks/whatsapp" },
"capabilities": {
"text": { "supported": true, "enabled": true },
"media": { "supported": true, "enabled": true },
"template": { "supported": true, "enabled": true },
"namedTemplateParameters": { "supported": true, "enabled": true, "scope": "body" },
"templateManagement": { "supported": true, "enabled": false, "verifiedAt": null },
"statusWebhooks": { "supported": true, "enabled": true }
}
}
]
}
| Campo | Valores |
|---|---|
type |
META_OFFICIAL o WHATSAPP_DIRECT |
status |
PENDING, AUTHENTICATING, CONNECTED, DISCONNECTED, BANNED, ERROR |
channel |
El valor a usar al enviar: whatsapp_meta o whatsapp_direct. |
lastErrorCode |
Último error de la cuenta (por ejemplo AUTH_REJECTED), null si no hay. |
meta |
Solo Meta Official: phoneNumberId, wabaId, wabaVerification (verified, unverifiable o unreachable, según lo que pudo comprobar el token al vincular) y verifiedName. null en Direct. |
sendPolicy |
Solo Meta Official: mode DISABLED (nada sale), TEST (solo a los destinatarios de prueba; testRecipientsCount dice cuántos hay, nunca cuáles), PRODUCTION (a cualquier destinatario) o GLOBAL (cuenta anterior a las políticas). En TEST, un envío a otro destinatario termina failed con SANDBOX_BLOCKED. |
webhook |
Solo Meta Official: integrationId de la app propia (o null si usa la app compartida) y la callbackUrl que debe estar configurada en Meta. |
capabilities |
Por capacidad, supported (lo que Arplyx sabe hacer con ese tipo de cuenta) y enabled (lo habilitado para esta cuenta y plan). namedTemplateParameters.scope es siempre body. templateManagement.enabled indica si el catálogo de plantillas está disponible. |
GET /whatsapp-accounts/{accountId}/templates
Lista el catálogo de plantillas de una cuenta Meta Official, leído en vivo desde su WABA. Requiere plan Starter o superior, una API key con templateAccess read o manage, y que el operador de Arplyx haya cargado la credencial de gestión de la cuenta.
curl "https://api.arplyx.com/whatsapp-accounts/9d2f81b3-…/templates?status=APPROVED&limit=50" \
-H "x-api-key: ak_live_xxxxxxxx..."
| Parámetro | Descripción |
|---|---|
status |
APPROVED, PENDING, REJECTED, PAUSED, DISABLED, IN_APPEAL. |
name |
Filtro por nombre (contiene). |
limit / cursor |
Paginación; nextCursor es null cuando Meta no tiene más. |
Cada plantilla trae id (en Meta), name, language, category, status, rejectedReason, parameterFormat (NAMED o POSITIONAL), parameterCount, components tal como los devuelve Meta y qualityScore cuando existe.
POST /whatsapp-accounts/{accountId}/templates
Crea una plantilla solo con cuerpo de texto (BODY) en la WABA de la cuenta. Requiere templateAccess manage. Meta la deja en PENDING hasta revisarla; la aprobación no la decide Arplyx.
{
"name": "turno_recordatorio",
"language": "es_AR",
"category": "UTILITY",
"parameterFormat": "NAMED",
"body": "Hola {{nombre}}, te recordamos tu turno el {{fecha}}.",
"examples": [{ "name": "nombre", "text": "Juan" }, { "name": "fecha", "text": "13/10" }]
}
Reglas: name en minúsculas, números y guion bajo; variables nombradas {{nombre}} o posicionales {{1}} sin mezclar; un ejemplo por variable. Header, footer, botones y medios no se crean desde Arplyx. Errores: 409 template_already_exists, 422 template_rejected_by_meta (Meta rechazó la definición; el cuerpo trae el detalle saneado), 403 template_access_insufficient (la clave no tiene templateAccess suficiente), 422 template_management_unavailable (sin credencial de gestión), 503 meta_unavailable / meta_unreachable, 429 meta_rate_limited.
Mayúsculas vs. minúsculas
Ojo al parsear: type usa el formato del modelo interno (META_OFFICIAL, mayúsculas), mientras que el campo channel al enviar usa minúsculas (whatsapp_meta). La correspondencia es META_OFFICIAL → whatsapp_meta.