Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.waspytech.com/llms.txt

Use this file to discover all available pages before exploring further.

Esta página existe para que sepas exactamente qué se puede y qué no se puede hacer hoy con la API pública. Si algo de lo siguiente es bloqueante para vos, contactanos.

Webhooks

  • Sin streaming / WebSocket público. Si necesitás latencia sub-segundo, hablá con nosotros.
  • Eventos de templates no implementados todavía. No existe template.approved / template.rejected. Mientras tanto, polleá GET /templates o GET /templates/by-name/:name.
  • Replay de delivery histórico: el endpoint POST /webhooks/:id/replay/:deliveryId re-fires un evento sintético del mismo event name. El payload original no se almacena verbatim (solo su hash), por eso tu integración debe ser idempotente sobre data.id.
  • POST /webhooks/:id/test simula un message.received con datos dummy. La response incluye samplePayload con el envelope exacto que se entrega.

Mensajes

  • Templates se pueden enviar y consultar pero no crear/editar/borrar vía API. Usá el panel de Waspy o Meta Business Manager.
  • Campañas no tienen endpoints v2 todavía (existen vía API interna del panel).
  • Reacciones outbound no están soportadas vía API.

Media

  • Upload vía API v2 no está expuesto todavía. Para enviar media, pasá link (URL pública) o id (media ID de Meta) en el body de POST /messages. La descarga inbound vía GET /media/:id sí funciona.
  • Si tu integración necesita subir blobs (imágenes generadas en runtime, audios) y no tenés CDN propio, esto es un gap. Avisanos.

Conversations

  • Typing off se acepta pero no manda nada a Meta — Meta no soporta apagar manualmente el typing (se auto-clarea ~25s).
  • Mark-as-read marca la última inbound con waMessageId por conversación. Si necesitás marcar un mensaje específico, usá POST /messages/:waMessageId/read.

Origin tracking

  • Para mensajes anteriores a abril 2026, origin se deriva de campos legacy. Ver Handoff: legacy fallback para los criterios exactos.
  • Para mensajes nuevos siempre se setea explícitamente vía metadata.origin.

Schemas

  • WebhookEventEnvelope.data y Message.content son unknown en el OpenAPI spec (no discriminated union por evento/tipo). El payload real sí tiene shape definida — ver catálogo de eventos y los ejemplos por tipo en Messages. Pendiente: tipar discriminated union para que clientes de TypeScript inferieran sin asumir.

Rate limits

  • 120 req/min por API key (no por canal). Si tenés múltiples números bajo la misma cuenta, comparten cuota — para cuotas independientes hoy hay que tener API keys separadas.
  • 80 mensajes/seg por número (límite Meta, no de Waspy).

Errores de servidor

A partir de abril 2026 los errores 5xx se serializan SIEMPRE como { error: { code: "INTERNAL_ERROR", message: "Internal server error" } }. Códigos internos (ej. SQLSTATE de PostgreSQL como 23503) no se filtran al cliente. El detalle se loggea server-side bajo originalCode para que ops pueda diagnosticar.

Resiliencia y reintentos del integrador

Waspy corre sobre Cloud Run con auto-scaling. Esto significa que vas a recibir respuestas transitorias 503 ocasionales durante:
  • Despliegues (rolling restart de instancias)
  • Cold starts cuando el tráfico salta de 0 instancias activas
  • Caídas momentáneas de la conexión a la DB
Recomendación obligatoria para tu integración:
  1. Implementá retry con backoff exponencial ante 503, 429 y errores de red. Sugerencia: [1s, 2s, 4s, 8s] con jitter.
  2. Para POST /messages usá el header Idempotency-Key para que los retries no generen duplicados.
  3. Si recibís 429, respetá el header Retry-After (segundos hasta que se libera tu rate limit).
  4. Después de 4-5 intentos fallidos, escalá a alerta — probable incidente del lado nuestro.
Ejemplo de wrapper en TypeScript:
async function sendWithRetry(body: unknown, idempotencyKey: string) {
  const delays = [1000, 2000, 4000, 8000];
  for (const delay of [...delays, null]) {
    const res = await fetch('https://api.waspytech.com/api/v2/messages', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${WASPY_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey,
      },
      body: JSON.stringify(body),
    });
    if (res.ok) return res.json();
    if (res.status < 500 && res.status !== 429) return res.json(); // 4xx no-retryable
    if (delay === null) throw new Error('Max retries exhausted');
    const retryAfterSec = parseInt(res.headers.get('Retry-After') || '0', 10);
    await new Promise(r => setTimeout(r, retryAfterSec ? retryAfterSec * 1000 : delay));
  }
}

Service window helper

Hoy el campo serviceWindowExpiresAt aparece dentro de cada ConversationPublic (response de GET /conversations/:id y de la lista). NO existe todavía un endpoint corto tipo GET /contacts/:id/can-send-text. Si tu integración necesita decidir entre texto libre vs template antes de enviar, hacé GET /conversations/by-phone/:phone y leé serviceWindowExpiresAt.

Sandbox / test mode

No existe un canal sandbox que sea no-op. Cualquier mensaje que envíes desde tu integración llega al usuario real de WhatsApp. Para desarrollo seguro:
  • Usá un número WhatsApp de test (Meta provee uno gratis al crear app)
  • O un número personal tuyo como to
  • POST /webhooks/:id/test SÍ es un sandbox para probar la firma + URL sin generar mensaje real

Cloud Run 503 sin envelope JSON

Cuando Cloud Run rota una instancia o tiene cold start, devuelve 503 Service Unavailable con body text/html (no nuestro envelope JSON { error: { code, message } }). El integrador debe distinguir 503 con Content-Type: text/html (Cloud Run, retry) vs 5xx con application/json (app, ver mensaje).

Lo que sí está cubierto

Account, Channels, Contacts, Conversations (read/typing/mark-as-read), Messages (incluye origin para handoff), Templates (lectura + by-name), Media (descarga), Webhooks (CRUD + deliveries + rotate-secret + test + replay). Si algo no está documentado y lo necesitás para tu integración, decinos.