Ricky logorickyDocs

Enviar mensajes

Inicia conversaciones de WhatsApp con plantillas, sigue cada envío y responde dentro de la ventana de 24 horas.

Esta guía recorre el flujo de un sistema de negocio, por ejemplo un ERP que avisa a un cliente que sus lentes están listos:

  1. Elegir el canal y la plantilla.
  2. Enviar la plantilla al teléfono del cliente.
  3. Seguir el envío.
  4. Recibir la respuesta del cliente.
  5. Responder con texto mientras la ventana está abierta.

La clave necesita los permisos conversations:read, templates:read y messages:send.

1. Elige el canal y la plantilla

Lista los canales y toma el id del número de WhatsApp desde el que vas a escribir.

curl https://getricky.ai/api/v1/channels \
  -H "Authorization: Bearer $RICKY_API_KEY"
200 OK
{
  "data": [
    {
      "id": "0b6f2c1e-7d4a-4c8e-9f3b-2a1d5e6c7b80",
      "type": "whatsapp",
      "name": "Óptica Central",
      "address": "+50720001000",
      "whatsapp_business_account_id": "102938475610293",
      "connected_at": "2026-09-02T14:10:00.000Z"
    }
  ],
  "next_cursor": null
}

Lista las plantillas. Ricky las lee de Meta en el momento.

curl https://getricky.ai/api/v1/templates \
  -H "Authorization: Bearer $RICKY_API_KEY"
200 OK
{
  "data": [
    {
      "id": "1478523690147852",
      "name": "pedido_listo",
      "language": "es",
      "category": "UTILITY",
      "status": "APPROVED",
      "whatsapp_business_account_id": "102938475610293",
      "parameter_format": "POSITIONAL",
      "components": [
        {
          "type": "BODY",
          "text": "Hola {{1}}, tus lentes están listos en {{2}}. Puedes retirarlos desde el {{3}}."
        },
        {
          "type": "FOOTER",
          "text": "Óptica Central"
        }
      ]
    }
  ],
  "next_cursor": null
}

Usa una plantilla que cumpla tres condiciones:

  • status es APPROVED. Ricky rechaza las demás con 422 rule_violation.
  • whatsapp_business_account_id coincide con el del canal.
  • Conoces sus variables: {{1}}, {{2}}… si parameter_format es POSITIONAL, o {{nombre}} si es NAMED.

2. Envía la plantilla

POST /conversations envía una plantilla a un teléfono. Ricky busca o crea el contacto y reutiliza la conversación de ese número en ese canal. No necesitas crear el contacto antes. phone lleva el código de país, por ejemplo +50760001234.

curl https://getricky.ai/api/v1/conversations \
  -H "Authorization: Bearer $RICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-48213-listo" \
  -d '{
    "channel_id": "0b6f2c1e-7d4a-4c8e-9f3b-2a1d5e6c7b80",
    "phone": "+50760001234",
    "template": {
      "id": "1478523690147852",
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Ana" },
            { "type": "text", "text": "Óptica Central Costa del Este" },
            { "type": "text", "text": "viernes 16 de octubre" }
          ]
        }
      ]
    }
  }'
202 Accepted
{
  "data": {
    "id": "8f0c2d4e-6a1b-4c3d-8e9f-0a1b2c3d4e5f",
    "conversation_id": "3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09",
    "status": "queued",
    "message_id": null,
    "failure_reason": null,
    "created_at": "2026-10-14T15:30:02.118Z"
  }
}

202 significa que Ricky aceptó el envío y lo puso en cola. Todavía no significa que WhatsApp lo aceptó. Guarda id (el ID del envío) y conversation_id.

Deriva la Idempotency-Key de un dato tuyo, como el número de pedido. Así, si tu sistema se reinicia y repite el envío, Ricky no manda el mensaje dos veces. Consulta Idempotencia.

Variables de la plantilla

components usa el formato de la API de WhatsApp Cloud. Cada elemento llena las variables de una parte de la plantilla:

typeUso
headerVariables del encabezado: texto, o un archivo image, video o document con su link HTTPS público.
bodyVariables del cuerpo, en orden.
buttonVariables de un botón. Indica sub_type (url o quick_reply) e index, la posición del botón desde "0".

En una plantilla NAMED, cada parámetro de texto lleva parameter_name:

{
  "type": "body",
  "parameters": [{ "type": "text", "parameter_name": "nombre", "text": "Ana" }]
}

Un encabezado con imagen y un botón con URL variable:

[
  {
    "type": "header",
    "parameters": [{ "type": "image", "image": { "link": "https://example.com/lentes.jpg" } }]
  },
  {
    "type": "button",
    "sub_type": "url",
    "index": "0",
    "parameters": [{ "type": "text", "text": "48213" }]
  }
]

Una plantilla sin variables se envía con "components": [] o sin components.

Errores frecuentes

RespuestaCausa
404 not_foundEl canal no existe, está desconectado, o la plantilla no existe.
422 rule_violationEl canal no es de WhatsApp, o la plantilla no está APPROVED.
502 upstream_errorRicky no pudo leer las plantillas en Meta. Sigue Estado desconocido.

3. Sigue el envío

Consulta el envío con su id:

curl https://getricky.ai/api/v1/sends/8f0c2d4e-6a1b-4c3d-8e9f-0a1b2c3d4e5f \
  -H "Authorization: Bearer $RICKY_API_KEY"
200 OK
{
  "data": {
    "id": "8f0c2d4e-6a1b-4c3d-8e9f-0a1b2c3d4e5f",
    "conversation_id": "3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09",
    "status": "sent",
    "message_id": "a7e5c3b1-9d8f-4a6e-b2c4-1d3f5e7a9b0c",
    "failure_reason": null,
    "created_at": "2026-10-14T15:30:02.118Z"
  }
}
statusSignificado
queuedRicky todavía lo está entregando al proveedor y reintenta si hace falta.
sentEl proveedor aceptó el mensaje.
failedFalló de forma definitiva. failure_reason explica la causa.

Consulta con espera creciente, por ejemplo a los 2, 4 y 8 segundos, hasta que el estado deje de ser queued.

Cuando el estado es sent, message_id es el mensaje en la conversación: el mismo id que muestra la lista de mensajes y que trae el webhook message.sent.

sent no significa entregado ni leído. Para eso, lista los mensajes de la conversación: cada mensaje saliente trae su status, por ejemplo sent, delivered, read o failed, y las horas sent_at, delivered_at y read_at.

4. Recibe la respuesta

Suscribe un webhook al evento message.received. Consulta Webhooks.

Evento message.received
{
  "id": "5b8e0f3a-2c51-4b8e-9d0e-6f1a2b3c4d5e",
  "type": "message.received",
  "occurred_at": "2026-10-14T15:42:10.000Z",
  "organization_id": "0d9c4b7e-1f2a-4c3d-8e5f-6a7b8c9d0e1f",
  "data": {
    "conversation_id": "3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09",
    "message_id": "a7e5c3b1-9d8f-4a6e-b2c4-1d3f5e7a9b0c"
  }
}

Ricky entrega los eventos en pasadas cada 15 minutos, así que el evento puede llegar varios minutos después del mensaje. Si tu proceso necesita la respuesta antes, consulta la conversación.

El evento no trae el texto. Lista los mensajes de la conversación y busca message_id. La lista empieza por el más reciente.

curl https://getricky.ai/api/v1/conversations/3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09/messages \
  -H "Authorization: Bearer $RICKY_API_KEY"
200 OK
{
  "data": [
    {
      "id": "a7e5c3b1-9d8f-4a6e-b2c4-1d3f5e7a9b0c",
      "conversation_id": "3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09",
      "direction": "inbound",
      "type": "text",
      "text": "Gracias, paso el viernes en la tarde.",
      "sender": { "type": "contact", "name": null },
      "status": "delivered",
      "media": null,
      "provider_message_id": "wamid.HBgLNTA3NjAwMDEyMzQVAgASGBQzRUIwRDFCMkM0RTZGOEEwQjlDMgA=",
      "created_at": "2026-10-14T15:42:11.204Z",
      "sent_at": "2026-10-14T15:42:10.000Z",
      "delivered_at": null,
      "read_at": null,
      "failed_at": null,
      "edited_at": null,
      "deleted_at": null
    }
  ],
  "next_cursor": "eyJpZCI6ImE3ZTVjM2IxIn0"
}

Si el mensaje trae un archivo, media.url es un enlace firmado que vale una hora. Es null mientras Ricky no haya descargado el archivo.

5. Responde con texto

Mientras la ventana de WhatsApp está abierta, responde con texto en la misma conversación. La ventana dura 24 horas desde el último mensaje del cliente.

curl https://getricky.ai/api/v1/conversations/3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09/messages \
  -H "Authorization: Bearer $RICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-48213-respuesta-1" \
  -d '{ "type": "text", "text": "Perfecto, Ana. Te esperamos el viernes." }'

La respuesta es 202 con un envío, igual que en el paso 2. Síguelo con GET /sends/{send_id}.

Con la ventana cerrada, Ricky responde 422 outside_messaging_window:

422 Unprocessable Entity
{
  "error": {
    "code": "outside_messaging_window",
    "message": "…",
    "request_id": "7c1e9a3b-5d2f-4e8a-b6c0-1f3d5a7b9e2c"
  }
}

En ese caso, envía una plantilla en la misma conversación:

curl https://getricky.ai/api/v1/conversations/3f6d2a10-8b7c-4e1d-9a2f-5c4b3e2d1a09/messages \
  -H "Authorization: Bearer $RICKY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-48213-recordatorio" \
  -d '{
    "type": "template",
    "template": {
      "id": "1478523690147852",
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Ana" },
            { "type": "text", "text": "Óptica Central Costa del Este" },
            { "type": "text", "text": "viernes 16 de octubre" }
          ]
        }
      ]
    }
  }'

Las plantillas solo existen en WhatsApp: en una conversación de Instagram o Messenger, una plantilla recibe 422 rule_violation. En esos canales se envía texto dentro de la ventana de respuesta de cada uno. El texto admite hasta 4096 caracteres; en Messenger, hasta 2000.

En esta página