Volver al blog
Desarrolladores

API de WhatsApp para desarrolladores: autenticación, plantillas y errores

La API pública de Waspy está en api.waspytech.com/api/v2. Cómo autenticarte, qué error devuelve una key inválida, cómo listar y enviar plantillas, y cómo evitar duplicados con idempotencia.

· 12 min
API de WhatsApp para desarrolladores: autenticación, plantillas y errores

Respuesta corta: la API pública de Waspy está en https://api.waspytech.com/api/v2. Te autenticás con Authorization: Bearer wspy_.... Un texto se manda como { "text": { "body": "..." } }, no como un string suelto. Si no hay clave, o la clave no existe, la API responde 401 con código UNAUTHORIZED. Si la clave no tiene el permiso, responde 403 con código FORBIDDEN. Las plantillas se listan por REST (el filtro status va en minúscula) y se envían con POST /messages; crearlas por REST no está expuesto: se crean en el panel o con la herramienta create_template del MCP.

El 16 de octubre de 2026 pegamos dos pedidos de solo lectura a producción, sin enviar ningún mensaje. Sin header, la API respondió:

{"error":{"code":"UNAUTHORIZED","message":"Missing authorization header","status":401},"meta":{"requestId":"..."}}

Con una clave inventada que empieza con wspy_, respondió UNAUTHORIZED y "Invalid API key". Si un ejemplo viejo de documentación habla de INVALID_API_KEY o INSUFFICIENT_SCOPE, no es lo que acepta ni lo que devuelve la API hoy.

Para quién es y en qué plan está

Esta API es para sistemas: un ERP, una tienda, un script, n8n. No es para que una persona entre al panel. Está en los planes Scale y Enterprise. Starter y Growth no crean claves. Los precios de la plataforma están en Planes; lo que cobra Meta por cada mensaje, en cuánto cuesta la API en 2026.

La referencia navegable está en docs.waspytech.com. Esta guía sigue el código de la API y las respuestas de producción, que son las que tu cliente va a ver.

Scopes

Al crear la clave en Configuración → Claves de API podés marcar permisos. Si no marcás ninguno, la clave puede hacer todo lo que la API permite. Si marcás alguno, cada ruta exige el suyo:

ScopePermite
messages:read / messages:writeLeer o enviar mensajes
contacts:read / contacts:writeLeer o editar contactos
conversations:read / conversations:writeLeer chats o escribir en ellos (notas, estado)
templates:readListar plantillas. No existe templates:write en la API REST
channels:readVer los números conectados
account:readVer la cuenta
media:read / media:writeBajar o subir archivos
webhooks:read / webhooks:writeAdministrar webhooks
social:readMétricas de Instagram y Facebook
El mensaje exacto cuando falta un permiso es API key missing required scope: , con código FORBIDDEN.

Mandar un texto

Primero necesitás el UUID del número, que sale de GET /api/v2/channels (scope channels:read). No es el id de teléfono de Meta: es el id interno de Waspy.

Dentro de la ventana de 24 horas (el cliente te escribió hace menos de un día) podés mandar texto libre. Fuera de esa ventana, la API responde 403 SERVICE_WINDOW_EXPIRED y solo acepta una plantilla. La regla está explicada en la ventana de 24 horas.

curl -sS https://api.waspytech.com/api/v2/messages \
  -H "Authorization: Bearer $WASPY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1847-texto" \
  -d '{
    "phoneNumberId": "UUID-DEL-CANAL",
    "to": "+5491155554444",
    "type": "text",
    "text": { "body": "Hola Laura, ya registramos el pago del pedido 1847." },
    "clientRef": "pedido-1847"
  }'

El teléfono va en E.164. Para un celular argentino es +549 + código de área sin 0 + número sin 15. La API también acepta el número sin el + y lo normaliza; si después de eso sigue siendo inválido, responde 400 INVALID_PHONE.

Un 201 devuelve el mensaje en data (con id, status, conversationId) y meta.requestId. El clientRef (hasta 256 caracteres) vuelve en cada webhook de estado.

Idempotencia

Si el script se corta después de enviar y no llega a guardar el id, un reintento crea un segundo WhatsApp. El header Idempotency-Key (hasta 256 caracteres) lo evita:

  • La primera vez, Waspy manda el mensaje y guarda la respuesta 24 horas.
  • Si repetís la misma clave, devuelve esa misma respuesta y agrega el header Idempotency-Replayed: true. No manda otro mensaje.
  • Si dos pedidos con la misma clave llegan juntos, el segundo recibe 409 IDEMPOTENCY_CONFLICT ("already being processed"). Esperá y reintentá: cuando el primero termine, el replay te da el resultado.

La clave es por cuenta. Usá algo estable del negocio (pedido-1847), no un Date.now().

Listar plantillas y mandar una

GET /api/v2/templates (scope templates:read) lista las plantillas. El query status solo acepta minúsculas: pending, approved, rejected, disabled, paused. APPROVED no es un valor válido.

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

También están GET /api/v2/templates/:id y GET /api/v2/templates/by-name/:name?language=es_AR&phoneNumberId=UUID, que resuelve el nombre aprobado (el mismo string que usás al enviar) sin que tengas que guardar el UUID.

No hay POST /api/v2/templates. Crear una plantilla y mandarla a revisión de Meta se hace desde el panel, o con la herramienta create_template del conector MCP (scope templates:write, solo en el MCP). Cómo redactarlas para que Meta las apruebe está en la guía de plantillas.

Para enviarla, el cuerpo usa type: "template" y los parámetros del cuerpo en orden {{1}}, {{2}}, …:

curl -sS https://api.waspytech.com/api/v2/messages \
  -H "Authorization: Bearer $WASPY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-1847-aviso" \
  -d '{
    "phoneNumberId": "UUID-DEL-CANAL",
    "to": "+5491155554444",
    "type": "template",
    "template": {
      "name": "aviso_despacho",
      "language": { "code": "es_AR" },
      "components": [{
        "type": "body",
        "parameters": [
          { "type": "text", "text": "Laura" },
          { "type": "text", "text": "1847" },
          { "type": "text", "text": "360000012345678" }
        ]
      }]
    },
    "clientRef": "pedido-1847"
  }'

Esa plantilla tiene que estar aprobada en ese número. Un aviso de despacho es utilidad: en Argentina, desde el 1 de octubre de 2026, Meta cobra alrededor de ARS 37,68 por mensaje entregado. El ejemplo completo con Google Sheets está en enviar WhatsApp desde una planilla.

Hasta 100 mensajes por pedido entran por POST /api/v2/messages/batch. Cada ítem se acepta, se saltea o falla por separado. Con respectOptOut en true (el default), quien se dio de baja se saltea con reason: "opted_out".

Errores que vas a ver

Todos vienen como { "error": { "code", "message", "status" }, "meta": { "requestId" } }.

HTTPCódigoCuándo
400VALIDATION_ERROR, MISSING_REQUIRED_FIELD, INVALID_PHONE, INVALID_MESSAGE_TYPE, INVALID_CHANNELEl cuerpo o el número no cierran
401UNAUTHORIZEDFalta el header, la clave no existe, está revocada o venció
403FORBIDDENA la clave le falta el scope
403SERVICE_WINDOW_EXPIREDTexto libre fuera de las 24 horas
403CHANNEL_NOT_CONNECTEDEl número no está activo
404TEMPLATE_NOT_FOUND y similaresEl id no es de tu cuenta
409IDEMPOTENCY_CONFLICTEsa clave ya se está procesando
429RATE_LIMITEDMás de 120 pedidos por minuto en la cuenta
El tope de 120 por minuto es por cuenta, no por clave. La respuesta trae Retry-After.

Waspy, Cloud API directa, Twilio, 360dialog y Z-API

CaminoQué programás vosCuándo tiene sentido
Cloud API de Meta, directaTodo: token de sistema, webhooks de Meta, plantillas, ventana, reintentosTenés un equipo que quiere ser el proveedor
TwilioLa integración contra la API de Twilio, que a su vez habla con MetaYa estás metido en el ecosistema de Twilio
360dialogLa integración contra su API de acceso a WhatsAppQuerés la Cloud API cruda, sin inbox
Z-APIUna API que se conecta leyendo un QR, como WhatsApp WebNo es la API oficial: el número puede bloquearse
API de WaspyPedidos a /api/v2 y, si hace falta, webhooks firmadosQuerés el inbox del equipo y la integración en el mismo número
Waspy no agrega un markup sobre lo que cobra Meta. Meta le factura los mensajes al negocio. El alta del número es el registro embebido de Meta: no es una sesión de WhatsApp Web.

Para recibir eventos en vez de consultar, la guía siguiente es webhooks de Waspy en Node y Python. Para que Claude o Cursor hablen con la bandeja sin que escribas un cliente, el camino es el MCP.

Si el número de la tienda todavía no está en la API, abrí la cuenta y conectalo desde el panel. La clave wspy_ se crea después, en Scale.

Tu equipo atendiendo por WhatsApp en minutos

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