> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Reacciona a los eventos de Bank Transfer en tiempo real con webhooks para notificaciones de transferencias completadas, fallidas, de chargeback y de conciliación, sin sondeo.

Los webhooks permiten que tu sistema reaccione a los eventos de transferencia en tiempo real, sin sondeo. El plugin envía una notificación a tu endpoint cuando una transferencia se completa, falla o necesita atención.

## Eventos disponibles

***

Cada evento indica los tipos de transferencia a los que aplica (entre paréntesis), cuándo se dispara y la acción recomendada.

### Ciclo de vida de la transferencia (TED OUT, P2P)

#### `transfer.initiated` (TED OUT)

* **Disparador**: el plugin creó el registro de la transferencia TED OUT después de confirmar la iniciación.
* **Acción**: actualiza el estado de la transferencia en tu sistema. Muestra "transferencia en curso" al cliente.

#### `transfer.processing_started` (TED OUT)

* **Disparador**: la transferencia TED OUT entró en procesamiento (ruta de estados CREATED a PENDING a PROCESSING).
* **Acción**: muestra al cliente que la transferencia está en curso.

#### `transfer.rejected` (TED OUT)

* **Disparador**: JD SPB rechazó la solicitud de transferencia antes de aceptarla (datos inválidos o violación de una regla).
* **Acción**: avisa al cliente del rechazo. El plugin ya canceló la retención de fondos.

#### `transfer.completed` (P2P)

* **Disparador**: la transferencia P2P liquidó con éxito.
* **Acción**: avisa al cliente. Genera un comprobante. Actualiza la vista del saldo.

### Conciliación (TED OUT, TED IN)

#### `transfer.reconciliation_required`

* **Disparador**: una transferencia con resultado desconocido pasó a conciliación.
* **Acción**: sigue la transferencia como pendiente. No supongas éxito ni fallo.

#### `transfer.reconciliation_resolved`

* **Disparador**: la conciliación terminó y la transferencia llegó a un resultado final.
* **Acción**: actualiza la transferencia a su estado final.

#### `transfer.reconciliation_exhausted`

* **Disparador**: la conciliación se detuvo después del número máximo de intentos.
* **Acción**: escala la transferencia para revisión manual de un operador.

#### `transfer.reconciliation_failed`

* **Disparador**: un intento de conciliación encontró un error determinista, que hizo fallar la transferencia.
* **Acción**: trata la transferencia como fallida e investiga.

#### `transfer.reconciliation_manual_retry_requested`

* **Disparador**: un operador devolvió una transferencia a la cola de conciliación para otro intento.
* **Acción**: registra la intervención manual y su motivo. El cambio de estado y este hecho no están acoplados de forma atómica. Si el evento no llega, confirma el estado de la transferencia con la API.

### Transferencias entrantes (TED IN)

#### `transfer_incoming.completed`

* **Disparador**: el plugin recibió una TED entrante, encontró al destinatario y aplicó el crédito.
* **Acción**: avisa al destinatario que los fondos llegaron. Actualiza la vista del saldo.

#### `transfer_incoming.chargeback`

* **Disparador**: llegó un mensaje de chargeback para una TED IN completada (STR0010R2).
* **Acción**: congela el monto acreditado. Empieza una revisión con tu equipo de compliance.

#### `transfer_incoming.undeliverable`

* **Disparador**: el plugin no pudo acreditar una TED entrante (por ejemplo, no encontró la cuenta del destinatario).
* **Acción**: investiga la transferencia. El plugin puede devolverla al banco de origen.

### Devoluciones e iniciación

#### `transfer_outgoing.devolution_notified` (TED OUT)

* **Disparador**: llegó una devolución (devolução) para una transferencia saliente.
* **Acción**: concilia los fondos devueltos contra la transferencia original.

#### `payment_initiation.created` (TED OUT, P2P)

* **Disparador**: el plugin creó una iniciación de pago (el paso previo a la transferencia).
* **Acción**: opcional. Sigue las iniciaciones que esperan confirmación.

<Note>
  Para TED OUT, el plugin todavía no emite `transfer.completed`. El SPB confirma la finalización de TED OUT de forma asíncrona, y un release futuro agregará este evento. Hasta entonces, consulta el estado de TED OUT con el endpoint [Get Transfer](/es/reference/interfaces/ted-jd/retrieve-transfer) o con el endpoint de conciliación.
</Note>

## Configurar webhooks

***

Los webhooks funcionan por tenant. Registras un destino de una de dos formas.

**API de autoservicio (recomendado).** Registra uno o más endpoints HTTPS con la API de registro de webhooks. El servidor genera un `signingSecret` al crearlo y lo devuelve **una sola vez**. Guárdalo de forma segura. Úsalo para verificar la firma en cada evento entregado. También puedes listar, actualizar, deshabilitar y eliminar registros, rotar el secreto de firma y consultar los tipos de evento aceptados. El plugin deriva el tenant propietario del Bearer token, nunca de un header de solicitud.

* [Crear un registro de webhook](/es/reference/interfaces/ted-jd/create-webhook): `POST /v1/webhooks`
* [Listar los registros de webhook](/es/reference/interfaces/ted-jd/list-webhooks): `GET /v1/webhooks`
* [Obtener](/es/reference/interfaces/ted-jd/get-webhook), [actualizar](/es/reference/interfaces/ted-jd/update-webhook) y [eliminar](/es/reference/interfaces/ted-jd/delete-webhook) un registro
* [Rotar el secreto de firma](/es/reference/interfaces/ted-jd/rotate-webhook-signing-secret): `POST /v1/webhooks/{webhookId}/signing-secret/rotate`
* [Listar los tipos de evento admitidos](/es/reference/interfaces/ted-jd/list-webhook-event-types): `GET /v1/webhooks/event-types`

**Habilitar la entrega (operador/entorno).** Define `WEBHOOK_ENABLED=true` para activar la entrega saliente. La entrega también requiere RabbitMQ y el outbox de streaming (`STREAMING_ENABLED=true`). Los destinos vienen de los registros de arriba. No hay una única variable de entorno con un endpoint estático. Ajustas el comportamiento por entrega (timeout y máximo de reintentos) en runtime con systemplane, no con variables de entorno. Consulta [Configuración de Bank Transfer](/es/interfaces/ted-jd/ted-configuration).

## Estructura del payload

***

El plugin entrega cada evento como un POST HTTPS. El cuerpo de la solicitud es el payload del evento en JSON. El tipo de evento y la firma viajan en headers HTTP, no en el cuerpo.

| Header                | Valor                                                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `Content-Type`        | `application/json`.                                                                                                      |
| `X-Webhook-Event`     | El tipo de evento, por ejemplo `transfer.completed`.                                                                     |
| `X-Webhook-Timestamp` | Hora de entrega como timestamp Unix (segundos). La firma cubre este valor.                                               |
| `X-Webhook-Signature` | Firma HMAC-SHA256 sobre el timestamp y el cuerpo, con la clave `signingSecret` del registro. Formato: `v1,sha256=<hex>`. |

Los campos del cuerpo dependen del tipo de evento. Cada payload lleva `tenantId`, y los eventos con ámbito de transferencia también llevan `transferId`. Los montos son cadenas decimales en la moneda de la cuenta, no centavos (por ejemplo, `100.00`).

Este es un cuerpo de ejemplo para `transfer.completed` en una transferencia P2P:

```json theme={null}
{
  "transferId": "019c96a0-ab10-7cde-f1a2-0e1f2a3b4c5d",
  "initiationId": "019c96a0-9a01-7bcd-e0f1-2a3b4c5d6e7f",
  "tenantId": "019c96a0-0a98-7287-9a31-786e0809c769",
  "ledgerId": "019c96a0-1b20-7def-a1b2-c3d4e5f60718",
  "senderAccountId": "019c96a0-2c30-7ef0-b2c3-d4e5f6071829",
  "recipientAccountId": "019c96a0-3d40-7f01-c3d4-e5f60718293a",
  "midazTransactionId": "019c96a0-cd10-7eee-bbbb-3333bbbb4444",
  "confirmationNumber": "20260121001",
  "status": "COMPLETED",
  "transferType": "P2P",
  "amount": "100.00",
  "feeAmount": "0.00",
  "totalAmount": "100.00",
  "completedAt": "2026-01-21T17:35:00Z"
}
```

El payload de `transfer.completed` lleva los montos, las cuentas y el `midazTransactionId`. Para los eventos con un payload más pequeño, o para leer el registro completo de la transferencia, obtén la transferencia desde [Get Transfer](/es/reference/interfaces/ted-jd/retrieve-transfer) con su `transferId`.

<Note>
  Los campos del payload cambian según el tipo de evento. Para leer todos los campos de una transferencia, usa el endpoint [Get Transfer](/es/reference/interfaces/ted-jd/retrieve-transfer).
</Note>

## Manejar los fallos de entrega

***

Tu endpoint debe responder con un estado 2xx dentro de 5 segundos (el valor predeterminado de `webhook.timeout_ms`). Si no lo hace, el plugin reintenta la entrega con backoff exponencial y jitter completo. Después del primer intento, el plugin hace hasta 3 intentos más (el valor predeterminado de `webhook.max_retries`), lo que da 4 intentos de entrega en total. La base del backoff es de 1 segundo y se duplica en cada intento. El jitter completo aplica a cada espera:

| Intento     | Espera antes de este intento |
| ----------- | ---------------------------- |
| 1 (inicial) | Inmediata                    |
| 2           | Aleatoria en `[0, 1000 ms]`  |
| 3           | Aleatoria en `[0, 2000 ms]`  |
| 4           | Aleatoria en `[0, 4000 ms]`  |

Después de que fallan todos los intentos (4 de forma predeterminada), el evento pasa a una cola dead-letter (DLQ). Configura alertas sobre la DLQ para detectar temprano los fallos de entrega persistentes. Ajusta `webhook.max_retries` con systemplane si tu endpoint necesita un presupuesto de reintentos más largo o más corto. El ajuste `webhook.retry_backoff_ms` controla el backoff de reconexión con el broker, no el calendario de reintentos HTTP por entrega de arriba.

Para una entrega confiable, sigue estas reglas:

* Responde dentro de 5 segundos.
* Usa HTTPS con un certificado válido.
* Devuelve 200 incluso para los eventos que ignoras.
* Mueve el procesamiento pesado a una cola en segundo plano. Mantén rápido el handler del webhook.

## Idempotencia

***

<Note>
  Tu endpoint puede recibir el mismo evento más de una vez. Usa el `transferId` del cuerpo y el header `X-Webhook-Event` para deduplicar. Si ya procesaste esa combinación, devuelve 200 y no hagas nada más.
</Note>

## Para desarrolladores

***

Para el código de validación de firma (JavaScript, Python, Go), la implementación de reintentos y la checklist completa de integración, consulta la [guía para desarrolladores de Bank Transfer](/es/interfaces/ted-jd/ted-developer-guide).
