Consultar una plantilla de WhatsApp por nombre: GET /templates/by-name en Waspy
GET /api/v2/templates/by-name/:name devuelve la plantilla por el nombre que después mandás en POST /messages. Scope templates:read. Solo status approved se envía. No hay POST /templates.
Respuesta corta: si tu integración ya tiene el nombre de la plantilla y no el UUID, el pedido es GET https://api.waspytech.com/api/v2/templates/by-name/:name con Authorization: Bearer wspy_... y el scope templates:read. Los query opcionales son language y phoneNumberId. La respuesta trae la fila más nueva que matchea. Solo status: "approved" se puede enviar. POST /api/v2/templates no existe.
El listado paginado está en listar plantillas. El envío, en enviar una plantilla. La referencia pública está en templates. Donde esa página dice “nombre aprobado” y el handler no filtra el estado, abajo va lo que hace apps/api/src/routes/v2/templates.ts.
La clave pública entra en Scale (US$150 por mes) y Enterprise. Starter y Growth no la incluyen. Los planes están en Planes y el módulo, en la API.
Los tres GET, y para qué sirve cada uno
| Pedido | Cuándo lo usás |
|---|---|
GET /templates?status=approved | Auditoría: todas las que se pueden mandar, de a páginas |
GET /templates/by-name/:name | El CRM guarda pedido_despachado y necesita el estado y las variables |
GET /templates/:id | Ya tenés el UUID interno (el id de una respuesta anterior) |
templates:read. Una clave sin lista de scopes entra a todo. Si la lista existe y le falta ese scope, es 403 FORBIDDEN. Los scopes están en scopes.
El listado pagina con page (default 1) y pageSize (default 20, máximo 100), y acepta status y phoneNumberId. language no filtra el listado. by-name es una sola fila: no trae page ni total.
Consultar por nombre
curl -sS "https://api.waspytech.com/api/v2/templates/by-name/pedido_despachado?language=es_AR" \
-H "Authorization: Bearer $WASPY_API_KEY"
{
"data": {
"id": "template-uuid",
"phoneNumberId": "channel-uuid",
"metaTemplateId": "1234567890",
"name": "pedido_despachado",
"language": "es_AR",
"category": "UTILITY",
"status": "approved",
"components": [
{ "type": "BODY", "text": "Hola {{1}}, tu pedido {{2}} ya salió. Seguimiento: {{3}}." }
],
"createdAt": "2026-10-02T13:00:00.000Z"
},
"meta": { "requestId": "..." }
}
| Campo | Para qué |
|---|---|
name | El string de template.name en POST /messages |
language | Tiene que coincidir con template.language.code del envío |
status | pending, approved, rejected, disabled o paused |
components | HEADER, BODY, FOOTER, BUTTONS. Ahí están las {{1}} |
category | MARKETING, UTILITY o AUTHENTICATION: es lo que factura Meta |
id | UUID de Waspy, para el GET /templates/:id de después |
metaTemplateId | El id del lado de Meta, para cruzarlo con Business Manager |
phoneNumberId | Canal, o null si la plantilla es global |
createdAt descendente y se queda con una fila. No exige status = approved. Si cargaste el mismo nombre dos veces, ves la más nueva, que puede seguir en pending mientras la anterior ya estaba approved. Por eso el if del envío mira data.status, no solo el 200.
const name = 'pedido_despachado';
const url = new URL(
'https://api.waspytech.com/api/v2/templates/by-name/' + encodeURIComponent(name),
);
url.searchParams.set('language', 'es_AR');
url.searchParams.set('phoneNumberId', process.env.WASPY_CHANNEL_ID);
const res = await fetch(url, {
headers: { Authorization: 'Bearer ' + process.env.WASPY_API_KEY },
});
if (res.status === 404) {
throw new Error('TEMPLATE_NOT_FOUND');
}
if (!res.ok) {
throw new Error('Waspy ' + res.status);
}
const { data } = await res.json();
if (data.status !== 'approved') {
// pending, rejected, paused o disabled: no encolar el POST /messages
throw new Error('plantilla ' + data.status);
}
phoneNumberId del query tiene que ser un UUID. Si pasás el id de teléfono de Meta, el schema responde 400 VALIDATION_ERROR. El UUID del canal sale de GET /api/v2/channels.
Si no hay fila con ese nombre (y ese idioma o canal, cuando los pasaste), es 404 TEMPLATE_NOT_FOUND. Un UUID de otra cuenta en GET /templates/:id es el mismo 404: no es un 403 que diga “es de otro tenant”.
Por id, cuando ya lo tenés
curl -sS "https://api.waspytech.com/api/v2/templates/TEMPLATE_UUID" \
-H "Authorization: Bearer $WASPY_API_KEY"
El id del path es UUID. Un slug o el metaTemplateId no pasan el schema: 400 VALIDATION_ERROR. El JSON es el mismo objeto que by-name. Este GET tampoco filtra por approved.
Qué no hace este recurso
| Querés | Qué hay |
|---|---|
| Crear, editar o borrar por REST | No. No hay POST /api/v2/templates |
| Alta de una plantilla de texto | Panel, o MCP create_template con templates:write |
| Refrescar el estado desde Meta | MCP get_template_status (templates:read). El GET REST lee la copia local |
Webhook template.approved | No existe |
| Enviar | Otro pedido: POST /api/v2/messages, scope messages:write |
create_template valida el cuerpo (variables en orden, sin promo dentro de una utilidad) y lo manda a revisión. No le escribe a ningún cliente y no es un endpoint de /api/v2. La guía del conector está en conectar MCP.
La página de templates avisa que un envío con una plantilla que no está approved recibe un error de Meta. El POST /messages de la v2 encola por nombre: buildTemplateContent busca la definición local para armar el texto y no rechaza un pending antes de la cola. El filtro lo hacés vos, con este GET, antes de encolar. Cómo queda el 201 está en enviar una plantilla.
Cada cuánto consultar
La API v2 permite 120 pedidos por minuto por cuenta. Lo comparten todas las claves. Un loop de by-name sin pausa se come el cupo que también usa el POST /messages. Una pasada antes del envío del día, o cada varios minutos mientras Meta revisa, alcanza.
Qué cobra Meta si el status es approved
El category del JSON es el que factura, 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 |
pedido_despachado puede dejar de ser UTILITY: mirá el category que devolvió el GET.
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 nombre o el UUID no están en tu cuenta |
| 400 | VALIDATION_ERROR | phoneNumberId que no es UUID, o un :id que no es UUID |
| 429 | RATE_LIMITED | Más de 120 pedidos por minuto en la cuenta |
