Saltar al contenido principal

API de mensajería

Envía un mensaje individual, gestiona las plantillas de WhatsApp desde las que envías y lee lo que la gente te responde.

Esta no es la API de campañas. Una campaña apunta a una audiencia y se mide como campaña; estas rutas envían un mensaje a una persona, para los casos que una audiencia no describe: una actualización de pedido, una respuesta de soporte, una confirmación que decidió enviar tu propio sistema.

Todos los endpoints de esta página son relativos a la URL base: https://api-eu1.joryio.com/api - consulta la Introducción a la API.

Autenticación​

Authorization: Bearer your_api_key_or_jwt
Content-Type: application/json

Envío por WhatsApp​

Enviar un mensaje de plantilla​

POST /whatsapp/send/template

Lo único que puedes enviar a alguien fuera de una ventana de conversación abierta de 24 horas. La plantilla debe estar ya aprobada por Meta.

{
"to": "+306912345678",
"templateName": "order_shipped",
"language": "en",
"variables": { "1": "Alex", "2": "GR-40921" }
}

Enviar un mensaje de sesión​

POST /whatsapp/send/text

Texto libre, permitido solo mientras la ventana de 24 horas está abierta, es decir, dentro de las 24 horas siguientes al último mensaje entrante de esa persona. Fuera de ella devuelve 409 y debes enviar una plantilla. WhatsApp no permite iniciar una conversación con texto libre; si no estás seguro, envía una plantilla.

{ "to": "+306912345678", "text": "Your order is out for delivery." }

Plantillas​

GET /whatsapp/templates
POST /whatsapp/templates

Todas las plantillas con su estado de aprobación. Solo pueden enviarse las APPROVED. Crear una la envía a Meta para su aprobación: no es inmediata y puede rechazarse.

{
"templates": [
{ "name": "order_shipped", "language": "en", "status": "APPROVED",
"category": "UTILITY", "variables": 2 }
]
}

Plantillas de SMS (solo Delivyr)​

Solo Delivyr. Estas rutas solo funcionan en un espacio de trabajo cuyo número de SMS está vinculado a una cuenta de Delivyr. Delivyr solo envía plantillas que ha aprobado, a números de Estados Unidos y Canadá. En cualquier otro espacio de trabajo, todas las rutas salvo GET /sms/templates/accounts responden 403 con code: "delivyr_account_required":

{ "statusCode": 403, "code": "delivyr_account_required",
"message": "SMS templates are available only to workspaces that send SMS through a Delivyr account. Connect one under Integrations > Delivyr." }

Leer requiere templates:read. Crear, actualizar o eliminar requiere templates:write.

Cuentas de Delivyr del espacio de trabajo​

Devuelve [] si no hay ninguna, para que un cliente pueda comprobarlo antes de cualquier otra llamada. key es el accountKey que aceptan las demás rutas.

GET /sms/templates/accounts
[
{ "key": "6f1c2a9e-3b4d-4e5f-8a7b-9c0d1e2f3a4b", "smsAccountId": "6f1c2a9e-3b4d-4e5f-8a7b-9c0d1e2f3a4b",
"provider": "delivyr", "name": "Delivyr US" }
]

Listar plantillas​

Filtros opcionales: status (pending, approved, rejected, system) y accountKey. Solo se pueden enviar plantillas approved y system. mappableVariables son las variables que rellenas por contacto. Delivyr rellena las demás (account_dba).

GET /sms/templates?status=approved
[
{ "id": "1d2e3f40-5a6b-4c7d-8e9f-0a1b2c3d4e5f", "accountKey": "6f1c2a9e-3b4d-4e5f-8a7b-9c0d1e2f3a4b",
"provider": "delivyr", "providerTemplateId": "NMPNWAS6", "title": "We Miss You",
"body": "Hi {{first_name}}, we miss you: {{link}}", "status": "approved", "source": "portal",
"variables": ["first_name", "link"], "mappableVariables": ["first_name", "link"] }
]

Actualizar desde Delivyr​

Trae ahora de Delivyr las plantillas de la cuenta, incluidas las creadas en el portal de Delivyr, y actualiza su estado de revisión. Joryio también actualiza cada 30 minutos. Cuerpo opcional: { "accountKey": "..." }.

POST /sms/templates/sync
{ "results": [ { "accountKey": "6f1c2a9e-3b4d-4e5f-8a7b-9c0d1e2f3a4b", "added": 2, "updated": 5, "removed": 0, "total": 7 } ] }

Crear una plantilla​

Envía la plantilla a revisión de Delivyr. Empieza como pending y el estado cambia tras la revisión. Usa variables {{name}}. Pon {{link}} donde vaya un enlace: Joryio lo rellena al enviar con su propio enlace corto con seguimiento. accountKey solo hace falta si el espacio de trabajo envía con más de una cuenta de Delivyr. El texto aprobado no se puede cambiar: para cambiar la redacción, crea una plantilla nueva.

POST /sms/templates
{
"title": "Weekend offer",
"body": "Hi {{first_name}}, your weekend offer is ready: {{link}} Reply STOP to opt out.",
"tags": ["promo"]
}

Obtener o eliminar una plantilla​

Eliminar también elimina la plantilla en Delivyr. Las plantillas de sistema de Delivyr no se pueden eliminar.

GET /sms/templates/:id
DELETE /sms/templates/:id

Uso y límites​

GET /whatsapp/usage?period=monthly&startDate=2026-08-01&endDate=2026-08-31
GET /whatsapp/usage/limits?wabaId=waba_120363

WhatsApp factura por conversación de 24 horas, no por mensaje, así que el número de mensajes no predice el coste. La ruta de uso devuelve las filas de uso guardadas de las cuentas del espacio de trabajo: una fila por cuenta y periodo, con el periodo más reciente primero. Parámetros (todos opcionales): wabaId (una sola cuenta), period (daily o monthly), startDate / endDate (fechas ISO; filas cuyo periodo empieza en startDate o después y termina en endDate o antes). Cada fila lleva además organizationId, workspaceId, createdAt y updatedAt.

[
{
"id": "usage_9f2c", "wabaId": "waba_120363", "period": "monthly",
"periodStart": "2026-08-01", "periodEnd": "2026-08-31",
"conversations": { "marketing": 4120, "utility": 8802, "authentication": 310, "service": 1904, "total": 15136 },
"messages": { "sent": 31204, "delivered": 30877, "read": 22410, "failed": 327, "received": 4810 },
"estimatedCost": 812.4,
"currency": "USD"
}
]

La ruta de límites muestra el nivel que Meta permite para tu número y cuánto has consumido: alcanzar el techo hace fallar los envíos, no los encola. wabaId es opcional: sin él los límites son los de la cuenta predeterminada y las tres cifras de uso devuelven 0, porque el uso solo se consulta para una cuenta indicada por nombre.

{
"tier": "TIER_10K",
"dailyLimit": 10000,
"dailyConversations": 3410,
"monthlyConversations": 41200,
"monthlyEstimatedCost": 812.4,
"qualityRating": "GREEN"
}

Los mensajes de un contacto​

GET /messages?userId=665f1e2a9b3c4d5e6f7a8b9c&direction=outbound&channel=email&limit=50&offset=0

Lo que enviaste a un contacto y lo que te envió, del más reciente al más antiguo: cada message.sent de una campaña o un recorrido (email, SMS, WhatsApp, push, Viber - no los nodos webhook) y las respuestas que devuelve GET /messages/inbound. Es la pestaña Mensajes del perfil del contacto.

ParámetroDescripción
userIdObligatorio. El id del contacto en Joryio o su externalId. Un contacto desconocido devuelve una página vacía.
directionOpcional. outbound (envíos) o inbound (respuestas); sin él, ambos.
channelOpcional. Un canal, p. ej. email, sms, whatsapp, push.
limitOpcional. 50 por defecto, limitado a 1-200.
offsetOpcional. Filas a saltar, 0 por defecto, como máximo 5000.

Response:

{
"data": [
{ "direction": "outbound", "channel": "email", "eventName": "message.sent",
"timestamp": "2026-10-28 10:00:00.000", "subject": "Your cart is still here",
"messageId": "0100019a2b3c", "status": "opened",
"source": { "kind": "journey", "id": "8f0c2d1e-5b7a-4c3e-9f1a-2d3e4f5a6b7c", "name": "Cart recovery" },
"body": null, "from": null, "toNumber": null, "account": null, "messageType": null },
{ "direction": "inbound", "channel": "sms", "eventName": "sms.message.received",
"timestamp": "2026-10-27 18:02:00.000", "body": "thanks!", "from": "+306912345678",
"toNumber": "+302101234567", "account": "Support line", "messageType": "text",
"subject": null, "messageId": null, "source": null, "status": null }
],
"total": 2
}

Un envío indica en source qué lo envió (kind es journey o campaign; name es null cuando ya no se puede resolver), el subject del email y el status: el resultado más avanzado registrado para ese mensaje, sent, delivered, opened, clicked, o failed / bounced, que prevalecen sobre los demás. Una respuesta lleva from, toNumber, account y body como en /messages/inbound. La ruta lee los últimos 12 meses; total es el tamaño de la página, así que pagina con offset hasta que una página vuelva con menos filas que limit.

Mensajes entrantes​

GET /messages/inbound?userId=665f1e2a9b3c4d5e6f7a8b9c&channel=whatsapp&limit=50&offset=0
GET /sms/messages?userId=665f1e2a9b3c4d5e6f7a8b9c&limit=50

Lo que un contacto te ha enviado, en WhatsApp y SMS: las filas salen de los eventos <channel>.message.received del contacto. userId (el id de Joryio del contacto, el id que devuelve la Users API) es obligatorio; sin él la ruta responde una página vacía. channel filtra a un solo canal (sms o whatsapp); limit es 50 por defecto y se acota a 1-200; offset es 0 por defecto. total es el tamaño de la página devuelta, no un recuento total: pagina con offset hasta que una página vuelva más corta que limit. No hay cursor since en esta ruta. toNumber es el número al que llegó el mensaje y account la etiqueta de la cuenta receptora; ambos son null en mensajes registrados antes de que se empezaran a sellar.

{
"data": [
{ "channel": "whatsapp", "from": "+306912345678", "toNumber": "+302101234567",
"account": "Support line", "body": "when will it arrive?", "messageType": "text",
"eventName": "whatsapp.message.received", "timestamp": "2026-08-27T09:41:00Z" }
],
"total": 1
}

GET /sms/messages devuelve los SMS de un contacto (la línea de tiempo del perfil), del más reciente al más antiguo, como un array JSON. userId es obligatorio (sin él la ruta responde []); limit es 50 por defecto y se acota a 1-200. direction es inbound u outbound. Esta ruta no tiene estado de entrega del operador: la entrega y el fallo son eventos en la línea de tiempo del contacto (GET /users/:id/events).

[
{ "id": "sms_411", "body": "STOP", "phoneNumber": "+306912345678",
"direction": "inbound", "messageType": "text", "accountId": "acct_7a1",
"createdAt": "2026-08-26T18:02:00Z" }
]