API pública de Yallegó

Documentación para integradores externos que consumen la API de Yallegó con una clave de API (Authorization: Bearer yk_live_...). Si estás implementando el panel administrativo o la app Android del propio proyecto, usa en cambio el contrato interno docs/06_API_CONTRACT.md del repositorio (incluye las superficies de panel, dispositivo y plataforma).

Base URL: https://api.yallego.app/v1 Especificación máquina-legible: ../openapi.yaml (OpenAPI 3.1)

Contenido

GuíaContenido
01 — Inicio rápidoCrear una clave, hacer la primera solicitud, registrar el primer webhook
02 — AutenticaciónFormato de la clave, alcances, claves por plan, GET /v1/me y errores de autenticación
03 — TransaccionesConsultar, confirmar y disputar cobros capturados
04 — WebhooksRegistrar endpoints, probar, rotar el secreto, ver entregas
05 — DispositivosConsultar el estado de los dispositivos de captura (solo lectura)
06 — Catálogo de eventosTodos los eventos que puede emitir un webhook, con su payload completo
07 — Verificación de firmaCómo validar que un evento entrante viene de Yallegó, con ejemplos en Node.js, Python y PHP
08 — Política de reintentosCalendario de reintentos de webhooks y desactivación automática
09 — Límites de tasaLímites por plan y cómo interpretar las cabeceras X-RateLimit-*
10 — IdempotenciaReintentar escrituras con Idempotency-Key sin duplicar nada

Qué NO cubre esta guía

  • /internal/v1/* — exclusivo de la app Android de captura, autenticado con token de dispositivo.
  • /platform/v1/* — administración interna de Yallegó, no es una superficie pública.
  • Rutas de /v1 que solo aceptan sesión del panel (registro, miembros, catálogo de billeteras, suscripción) — una clave de API no puede llamarlas.

Errores

Toda respuesta de error tiene la misma forma:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "La transacción no existe.",
    "request_id": "b7f3a2e1-..."
  }
}
  • code es estable: decide con él, no con message, que es un texto en español para mostrar.
  • details aparece solo en algunos errores: el 400 de validación (fields, abajo), el 409 de confirm y dispute (status, note), el 429 (limit, window_seconds, retry_after, scope), el 422 de cupo del plan (limit, current) y los de Idempotency-Key.
  • request_id es el X-Request-Id que enviaste o, si no enviaste uno, uno que genera Yallegó. También llega en la cabecera X-Request-Id de toda respuesta.

Errores de validación

Un cuerpo o una query que no cumplen el esquema de la ruta (un campo que no existe, un valor fuera de un enum, un texto demasiado largo, una URL de webhook http://) responden 400 VALIDATION_ERROR con un mensaje genérico y, en details.fields, cada campo que falló:

// 400 Bad Request
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "La solicitud contiene datos inválidos.",
    "details": {
      "fields": [
        {
          "path": "subscribed_events[1]",
          "code": "invalid_value",
          "message": "Evento desconocido. Usa uno de: transaction.created, …"
        },
        { "path": "secret", "code": "unknown_field", "message": "No es un campo admitido." }
      ]
    },
    "request_id": "b7f3a2e1-..."
  }
}
  • path es la ruta del campo en notación de punto y corchetes (subscribed_events[0], device.label). Vacío si el problema es el cuerpo o la query completa (por ejemplo, un PATCH sin ningún campo).
  • code dice qué pasó y es estable: required (falta), invalid_type (otro tipo), invalid_value (fuera de los valores permitidos), invalid_format (URL, UUID, fecha), too_small / too_big (largo o rango), unknown_field (el cuerpo no admite ese campo) o invalid (una regla propia de la ruta).
  • message es un texto en español para mostrar o registrar; no lo compares.
  • La respuesta nunca repite el valor que enviaste, solo qué se esperaba.
  • Llegan hasta 20 campos por respuesta.

Estas validaciones propias de una ruta traen un mensaje específico y no traen fields:

Casomessagedetails
Cursor que no salió de la API«El cursor de paginación no es válido.»—
Idempotency-Key inválida«La cabecera Idempotency-Key debe tener entre 1 y 255 caracteres ASCII visibles, sin espacios.»{ "header": "Idempotency-Key" }
Registrar una URL cuyo dominio no resuelve«No se pudo resolver el dominio del webhook.»—
Registrar una URL que apunta a una red interna«La URL del webhook no puede apuntar a una red interna.»—

Un cuerpo que no es JSON válido responde 400 VALIDATION_ERROR con el mensaje genérico y sin details; como cualquier otra respuesta, trae request_id y la cabecera X-Request-Id.

Soporte

Ante dudas sobre la integración, escribe a soporte con el request_id del error o la cabecera X-Request-Id de la respuesta (está en toda respuesta, exitosa o no) — acelera el diagnóstico.