Skip to main content
ZoPay Connect mantiene un log de eventos como registro canónico de cada efecto colateral visible para el partner. Cada evento es un objeto durable e inmutable, consultable vía API. Los webhooks son la entrega push de esos eventos; el log de eventos es la entrega pull. Úsalos juntos: el webhook es notificación; el log es auditoría, fuente de verdad y herramienta de recuperación.

Dos superficies, una fuente de verdad

┌─────────────────────────────────────────────────────────┐
│  Llega un depósito on-chain                             │
│  (o el simulador lo dispara en sandbox)                 │
└──────────────────────────┬──────────────────────────────┘


              ┌────────────────────────────┐
              │  connect_events row        │  ← registro canónico
              │  evt_01KT…                 │     (durable, inmutable,
              │                            │      replicable)
              └────────────┬───────────────┘


              ┌────────────────────────────┐
              │  webhook_deliveries row    │  ← estado de entrega
              │                            │     (cuerpo + firma se
              │                            │      generan en cada
              │                            │      intento)
              └────────────┬───────────────┘


                ┌──────────────────────┐
                │  HTTP POST a tu URL  │
                └──────────────────────┘
La fila del evento se escribe dentro de la misma transacción de base de datos que la transición de estado subyacente (el payment intent pasando a completed, la dirección recibiendo un depósito). Existe haya o no webhook configurado — partners sin URL configurada igual pueden hacer GET /events y recuperar todo lo registrado.

Forma del objeto

{
  "id": "evt_01KT76B86MR17Y20YWCPTTMZCT",
  "object": "event",
  "type": "payment_intent.completed",
  "livemode": true,
  "related_to": "8c3f1d4a-1e22-4b6f-9a72-9d4eb6c20fce",
  "related_to_kind": "payment_intent",
  "api_version": "v1",
  "created_at": "2026-06-04T10:32:14.872Z",
  "data": {
    "ref_code": "ABC12",
    "amount": 100.0,
    "currency": "USDT",
    "address_receiver": "<destination address>",
    "address_sender": "<sender address>",
    "network": "SOL",
    "hash": "<on-chain tx hash>",
    "status": "completed",
    "metadata": {}
  }
}
CampoNotas
idULID con prefijo evt_ + 26 chars Crockford base32. Mismo id aparece en el body de webhook (event_id) y en el header X-ZoPay-Event-Id de cada entrega y replay.
objectSiempre event. Discriminador estilo Stripe.
typeTipo del evento (ver abajo).
livemodetrue si el evento se produjo en modo live; false en sandbox.
related_toUUID del objeto principal del evento. Para payment_intent.completed, el id del intent.
related_to_kindTabla a la que apunta related_to. Hoy: payment_intent.
api_versionVersión del schema del payload. Hoy v1. Cambios incompatibles bumpan; eventos viejos se leen en su versión original.
created_atTimestamp del servidor cuando se escribió la fila.
dataPayload de negocio. Su forma depende de type.

Tipos de evento

payment_intent.completed

Un payment intent llegó al estado completed — los depósitos acumulados igualaron o superaron el monto solicitado.
  • related_to_kind: payment_intent
  • related_to: UUID del intent
  • Webhook wire name: payment.completed (legacy; el tipo canónico para filtrar en GET /events?type=… es payment_intent.completed).
  • data: ver forma arriba.

address.deposit.received (próximamente)

Un depósito aterrizó en una dirección estándar provisionada con POST /addresses (no asociada a un intent). Es la señal de wallet whitelabel: tu usuario depositó crypto en la dirección que minteaste para él y necesitas acreditar su balance interno.
  • related_to_kind: address
  • related_to: UUID de la dirección
  • Webhook wire name: address.deposit.received
Regla de precedencia: si el depósito cae en una dirección de payment intent → payment_intent.completed. Si cae en una dirección estándar sin intent → address.deposit.received. Nunca ambos. Tu handler debe rutear por type, no por la URL — así estará listo cuando este tipo se active.

Operaciones

  • Listar eventosGET /events con filtros por type, related_to, livemode, from/to, cursor, limit.
  • Consultar un eventoGET /events/{event_id}.
  • Replicar un eventoPOST /events/{event_id}/replay encola una nueva entrega webhook con el mismo evt_….

Receta: “Mi handler nunca recibió el webhook del evento X”

  1. Encuentra el evento con GET /events?related_to=<intent-id> o por rango temporal ?from=…&to=….
  2. Inspecciona el evento (GET /events/{id}). Si falta entero, el problema está aguas arriba del log — abre ticket con el hash on-chain.
  3. Revisa el estado de entrega en el dashboard (delivery rows asociadas).
  4. Arregla lo que sea (servidor caído, secreto rotado, endpoint deshabilitado, etc.).
  5. Replay: POST /events/{id}/replay con Idempotency-Key. El X-ZoPay-Event-Id será el mismo, así que tu deduplicación lo reconocerá.
  6. Confirma en el dashboard que la entrega nueva llegó a delivered.
Para recuperación masiva (ventana de outage), lista por rango temporal y replica en bucle — el rate limit por clave pacea naturalmente.