Volver al blog
Desarrolladores

Cómo enviar una plantilla de WhatsApp por la API de Waspy

El envío es POST /api/v2/messages con type template, language.code y los parámetros del body. Listá con status=approved, en minúscula. Crear la plantilla no está en la API.

· 12 min
Cómo enviar una plantilla de WhatsApp por la API de Waspy

Respuesta corta: para mandar una plantilla ya aprobada hacés POST https://api.waspytech.com/api/v2/messages con type: "template", template.language.code y, si el cuerpo tiene {{1}}, components de tipo body con parameters. Scope messages:write. La respuesta 201 trae status: "queued". Crear la plantilla no es un endpoint de la API v2: se crea en el panel o en Meta Business Manager.

La guía de envío está en send template. La referencia de los GET está en templates. Lo que la API no hace está en limitaciones. Cómo listarlas, sin repetir el paginado acá, está en listar plantillas.

Crear no es un POST

QuerésDónde
Dar de alta, editar o borrar el textoPanel de Waspy o Meta Business Manager
Ver si Meta ya la aprobóGET /api/v2/templates
Mandar una que ya está approvedPOST /api/v2/messages
No hay POST /templates. La palabra “crear plantilla por API” en una búsqueda describe el alta. En la v2 ese alta no existe. Si tu integración necesita el nombre aviso_despacho en es_AR, alguien lo carga en el panel y espera a que status sea approved. No hay webhook template.approved: se vuelve a consultar.

Listar solo las aprobadas

curl -sS "https://api.waspytech.com/api/v2/templates?status=approved&pageSize=50" \
  -H "Authorization: Bearer $WASPY_API_KEY"

Scope templates:read. page arranca en 1 y pageSize en 20, con tope 100. phoneNumberId filtra por canal (el UUID de Waspy, el de GET /channels).

El query status se compara igual, sin pasar a minúscula. Los valores que hay en la base son:

ValorSirve para enviar
approvedSí, si el nombre y el idioma coinciden con lo que Meta tiene
pendingNo. Meta todavía no la aprobó
rejectedNo
pausedNo
disabledNo
La guía de envío muestra status=APPROVED. Con el código de hoy eso no filtra: no hay fila APPROVED, la lista vuelve vacía y el HTTP es 200. La referencia usa approved. Usá esa.

Sin phoneNumberId, y sin includeInactive=true, el listado oculta plantillas de canales que no están active. Si acabás de conectar el número y no aparece, revisá el estado del canal antes de asumir que Meta la rechazó.

Resolver por nombre

curl -sS "https://api.waspytech.com/api/v2/templates/by-name/aviso_despacho?language=es_AR" \
  -H "Authorization: Bearer $WASPY_API_KEY"

Devuelve el match más nuevo. phoneNumberId desambigua si el mismo nombre existe en otro canal. Si no hay fila, es 404 TEMPLATE_NOT_FOUND (Template not found). Ese código es de este GET y de GET /templates/:id (el :id es un UUID). No es la respuesta del envío.

En la fila, status viene en minúscula (approved) y category en mayúscula (UTILITY, MARKETING, AUTHENTICATION). language es un string (es_AR), no el objeto { code } que pide el POST.

El POST

curl -sS -X POST https://api.waspytech.com/api/v2/messages \
  -H "Authorization: Bearer $WASPY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1847-despacho" \
  -d '{
    "phoneNumberId": "CHANNEL_UUID",
    "to": "+5491126032641",
    "type": "template",
    "clientRef": "pedido-1847",
    "template": {
      "name": "aviso_despacho",
      "language": { "code": "es_AR" },
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "María" },
            { "type": "text", "text": "#1847" }
          ]
        }
      ]
    }
  }'

type del componente va en minúscula (body). El orden de parameters es {{1}}, {{2}}, … El phoneNumberId es el UUID del canal en Waspy, no el id de Meta. to termina en E.164: si después de normalizar no es un número de WhatsApp válido, es 400 INVALID_PHONE.

La respuesta 201 trae data.id, data.status: "queued" y waMessageId: null. El id de Meta aparece cuando el estado pasa a sent. Cómo seguirlo está en estados del mensaje.

Idempotency-Key es opcional, máximo 256 caracteres, y vive 24 horas por cuenta. Si repetís la misma clave después de un 201, vuelve la respuesta guardada con el header Idempotency-Replayed: true y no se manda de nuevo. Si la primera todavía está en curso (el lock dura 30 segundos), es 409 IDEMPOTENCY_CONFLICT. clientRef es otra cosa: también hasta 256 caracteres, y vuelve en cada webhook de estado. Podés usar el número de pedido en los dos, con roles distintos.

Una plantilla no pasa por la ventana de 24 horas. SERVICE_WINDOW_EXPIRED (403) es para texto, foto o documento cuando el cliente no escribió. El canal inexistente es 400 INVALID_CHANNEL. El canal que existe pero no está active es 403 CHANNEL_NOT_CONNECTED.

Si el nombre o los parámetros no cierran

El POST no busca status === approved antes de encolar. Si no hay fila local con ese nombre e idioma, igual persiste el envío y lo manda a Meta. El rechazo llega asincrónico:

Qué vesCuándo
404 TEMPLATE_NOT_FOUNDGET /templates/:id o GET /templates/by-name/:name sin fila. No es el POST
Meta 132000La cantidad de parámetros no coincide con la plantilla. El mensaje queda failed
Meta 132001La plantilla no existe o no está aprobada en Meta
La guía de envío anota TEMPLATE_NOT_FOUND como error del envío. En el código de la ruta, ese código no sale del POST. Consultá el GET antes, y después leé errorCode en el webhook o en GET /messages/:id.

Un header con imagen no se sube por la API v2. Limitaciones lo dice: el upload no está expuesto. En el mensaje va link (URL pública) o un id de media de Meta. El cuerpo de esta nota es el caso de variables de texto.

Plan, cupo y tarifa

La API REST está en Scale (US$150 por mes) y Enterprise. Starter (US$70) y Growth (US$100) no incluyen la clave. Diez días de prueba, sin tarjeta, en el registro. El detalle está en Planes y en la página de la API.

Meta cobra el mensaje entregado, no el 201. En Argentina, desde el 1 de octubre de 2026, una utilidad sale alrededor de ARS 37,68 (~US$0,026) y un marketing alrededor de ARS 89,56 (~US$0,0618). Waspy no suma un porcentaje. La fuente es la rate card y precios de la plataforma. El desglose está en precios de la API.

El cupo de la API es 120 pedidos por minuto por cuenta, compartido por todas las claves. Un envío y un GET de plantillas suman al mismo contador. El 429 y el Retry-After están en rate limits.

Tu equipo atendiendo por WhatsApp en minutos

CRM facil pero potente. En 10 minutos tenes a tu equipo trabajando — no necesitas hablar con nadie.