Cómo listar plantillas de WhatsApp con la API de Waspy
GET /api/v2/templates lista las plantillas con el scope templates:read. Filtrás por estado y por canal. La API no las crea ni las edita: eso se hace en el panel o en Meta Business Manager.
Respuesta corta: para ver las plantillas de la cuenta hacés GET https://api.waspytech.com/api/v2/templates con Authorization: Bearer wspy_... y el scope templates:read. Filtrás con status (approved, pending, rejected, disabled, paused) y con phoneNumberId. La API v2 no crea, no edita y no borra plantillas. Eso se hace en el panel de Waspy o en Meta Business Manager. No hay webhook de aprobación: hay que volver a consultar.
Sirve el domingo antes de un envío: tu script tiene que saber si aviso_despacho ya está approved en es_AR, o si Meta la rechazó. Cómo redactarlas y qué mira Meta está en la guía de plantillas. La referencia de estos tres GET está en templates.
Los tres GET
| Pedido | Cuándo |
|---|---|
GET /templates | La lista, paginada. Filtros status y phoneNumberId |
GET /templates/:id | Una plantilla, por el UUID interno |
GET /templates/by-name/:name | Por el nombre que después mandás en POST /messages. Query opcional language y phoneNumberId |
templates:read. La base es https://api.waspytech.com/api/v2.
curl -sS "https://api.waspytech.com/api/v2/templates?status=approved&pageSize=50" \
-H "Authorization: Bearer $WASPY_API_KEY"
{
"data": [
{
"id": "template-uuid",
"phoneNumberId": "channel-uuid",
"metaTemplateId": "1234567890",
"name": "confirmacion_pedido",
"language": "es_AR",
"category": "UTILITY",
"status": "approved",
"components": [
{ "type": "BODY", "text": "Hola {{1}}, tu pedido {{2}} fue confirmado." }
],
"createdAt": "2026-03-15T10:00:00.000Z"
}
],
"meta": { "requestId": "...", "page": 1, "pageSize": 50, "total": 12 }
}
| Campo | Para qué |
|---|---|
id | UUID de Waspy. Es el de GET /templates/:id |
name | El string de template.name en POST /messages |
language | Código, por ejemplo es_AR |
status | Solo approved se puede enviar |
category | MARKETING, UTILITY o AUTHENTICATION |
components | HEADER, BODY, FOOTER, BUTTONS. Ahí ves las variables {{1}}, {{2}} |
metaTemplateId | El id del lado de Meta, para cruzarlo con Business Manager |
phoneNumberId | Canal, o null si la plantilla es global |
GET /templates/by-name/:name devuelve la creada más reciente (createdAt descendente).
Filtros que existen, y el que no
| Query | Dónde | Valores |
|---|---|---|
status | Solo en la lista | pending, approved, rejected, disabled, paused. En minúscula |
phoneNumberId | Lista y by-name | UUID del canal, el de GET /channels |
language | Solo en by-name | es_AR, en_US, el código que aprobaste |
page | Lista | Default 1 |
pageSize | Lista | Default 20, máximo 100 |
language no filtra GET /templates. Si necesitás “todas las de es_AR”, recorré las páginas y quedate con ese language, o llamá by-name cuando ya sabés el nombre.
Si no pasás phoneNumberId, la lista deja afuera las plantillas de números que no están active. Si pasás el UUID del canal, trae las de ese canal.
curl -sS "https://api.waspytech.com/api/v2/templates/by-name/confirmacion_pedido?language=es_AR" \
-H "Authorization: Bearer $WASPY_API_KEY"
Si no hay match, 404 TEMPLATE_NOT_FOUND. Lo mismo con un UUID de otra cuenta: no es un 403 que diga “es de otro tenant”.
Qué no hace este recurso
La página de limitaciones lo deja escrito: las plantillas se envían y se consultan, y no se crean, editan ni borran por la API. No hay POST /templates.
El alta es el panel (Templates → Crear template) o Meta Business Manager. Meta revisa el texto. Qué suele rechazar está en por qué rechazan una plantilla.
Tampoco hay template.approved ni template.rejected entre los webhooks. Los eventos públicos son message.received, message.status_changed, conversation.created y contact.created. Para el estado de una plantilla, el camino es este GET.
Cada cuánto consultar
La API v2 permite 120 pedidos por minuto por cuenta. Lo comparten todas las claves de esa cuenta. Cada respuesta trae X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset (segundos hasta que se renueva la ventana). Si te pasás, es 429 RATE_LIMITED y el header Retry-After, en segundos. El detalle está en rate limits.
Una plantilla no cambia de estado cada segundo. Consultar by-name cada varios minutos, o una pasada de la lista antes del envío del día, alcanza. Un loop sin pausa se come el cupo que también usan POST /messages y los webhooks que tu sistema confirma con un GET.
Del nombre aprobado al envío
Cuando status es approved, el envío es otro recurso: POST /messages o el lote, con scope messages:write. El nombre y el idioma tienen que coincidir con lo que devolvió este GET. Las variables del BODY van en components.
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-confirmado" \
-d '{
"phoneNumberId": "UUID-DEL-CANAL",
"to": "+5491155554444",
"type": "template",
"template": {
"name": "confirmacion_pedido",
"language": { "code": "es_AR" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Sofía" },
{ "type": "text", "text": "1847" }
]
}
]
}
}'
El lote de hasta 100 está en mensajes en lote. La clave, el 401 y el 403 están en idempotencia y scopes.
Una plantilla es la vía cuando la ventana de 24 horas está cerrada. Si el cliente escribió hace menos de un día, podés mandar texto libre. La regla está en la ventana de 24 horas.
Qué cobra Meta, según la categoría
El category del JSON es el que factura Meta, por mensaje entregado, al país del destinatario. En Argentina, desde el 1 de octubre de 2026:
category | Dólares | Pesos |
|---|---|---|
MARKETING | US$0,0618 | ARS 89,56 |
UTILITY | US$0,026 | ARS 37,68 |
AUTHENTICATION | US$0,026 | ARS 37,68 |
Un aviso de despacho con un cupón adentro puede dejar de ser UTILITY. Mirá el category que devolvió el GET, no el nombre que le pusiste.
Errores de esta llamada
| HTTP | error.code | Cuándo |
|---|---|---|
| 401 | UNAUTHORIZED | Falta el header, o la clave no existe |
| 403 | FORBIDDEN | La clave tiene scopes y no incluye templates:read |
| 404 | TEMPLATE_NOT_FOUND | El id o el nombre no están en tu cuenta |
| 400 | VALIDATION_ERROR | phoneNumberId que no es un UUID, o un status fuera de la lista |
| 429 | RATE_LIMITED | Más de 120 pedidos por minuto en la cuenta |
API key missing required scope: templates:read.
En resumen
- Listá con
GET /api/v2/templates?status=approved. - Resolvé un nombre con
GET /templates/by-name/:name?language=es_AR. - No hay alta ni baja por REST, ni webhook de aprobación.
- Mandá solo si
statusesapproved, conPOST /messages.
La clave se crea en el panel, en Scale (US$150/mes) y Enterprise. El inbox y las plantillas del equipo están desde el plan de US$70. Si todavía no hay número en la API, abrí la cuenta: 10 días, sin tarjeta. La superficie pública está en la API.
