# Webhooks (/docs/webhooks)

<Endpoint method="POST" path="/v1/webhook-endpoints" href="/docs/api/webhooks/create-webhook-endpoint" />

```bash
curl "$CAIBO_API/v1/webhook-endpoints" \
  -H "Authorization: Bearer $CAIBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-empresa.com/webhooks/caibo",
    "description": "Producción",
    "event_types": ["payin.completed", "payout.completed", "payout.failed"]
  }'
```

- `url`: `https`, pública, en el puerto 443.
- `event_types`: los tipos que quieres recibir. Vacío u omitido: **todos**.
- La respuesta trae `secret` (`whsec_…`) **una sola vez**. Si lo pierdes, [rótalo](#rotar-el-secreto).
- Hasta 10 webhooks por cuenta y por entorno.

## Lo que recibes [#lo-que-recibes]

```http
POST /webhooks/caibo HTTP/1.1
Content-Type: application/json
User-Agent: psp-webhooks/1 (live)
Psp-Signature: t=1790769600,v1=c0c9eaafdea23b0f8c992b380b286917c6459b0729b2496b8452fa695215e6b8
Psp-Event-Id: evt_01m3sgb02af7nb0jf73vwv2sd5
Psp-Delivery-Id: whd_01m3sgb03a...
Psp-Delivery-Attempt: 1
```

```json
{
  "id": "evt_01m3sgb02af7nb0jf73vwv2sd5",
  "type": "payout.completed",
  "schema_version": 1,
  "occurred_at": "2026-10-18T14:00:41.126938Z",
  "livemode": true,
  "data": { "id": "pout_01m3sgb02af7nb0jf73vwv2sd4", "status": "completed" }
}
```

`data` es el recurso completo, con la misma forma que te da la API. Ver [Eventos](/docs/events).

## Cómo responder [#cómo-responder]

<Steps>
<Step>
### Verifica la firma [#verifica-la-firma]
Con el cuerpo **tal como llegó**, antes de parsearlo. Ver [Firmas](/docs/webhook-signatures).
</Step>
<Step>
### Responde 2xx enseguida [#responde-2xx-enseguida]
Tienes **10 segundos**. Guarda el evento y procésalo después, en una cola. El cuerpo de tu respuesta se ignora. Una redirección (`3xx`) cuenta como fallo.
</Step>
<Step>
### Descarta repetidos [#descarta-repetidos]
La entrega es "al menos una vez": el mismo evento puede llegar dos veces. Usa `Psp-Event-Id` (el `id` del evento) para no procesarlo dos veces.
</Step>
</Steps>

## Reintentos [#reintentos]

Si no respondes `2xx` a tiempo, reintentamos hasta **10 veces** en unas 23 horas:

| Intento | Espera antes | | Intento | Espera antes |
| --- | --- | --- | --- | --- |
| 2 | 30 s | | 7 | 2 h |
| 3 | 2 min | | 8 | 4 h |
| 4 | 10 min | | 9 | 8 h |
| 5 | 30 min | | 10 | 8 h |
| 6 | 1 h | | | |

Cada espera varía ±20 %. Después de 10 fallos seguidos, el webhook pasa a `failing` y le enviamos una entrega de prueba cada 15 minutos; la primera que responde bien lo devuelve a `active`. Ningún evento se pierde: siempre está en `GET /v1/events`.

**Orden:** una entrega a la vez por webhook, y los eventos de un mismo recurso llegan en orden.

## Herramientas [#herramientas]

| | |
| --- | --- |
| <Endpoint method="POST" path="/v1/webhook-endpoints/{id}/ping" href="/docs/api/webhooks/ping-webhook-endpoint" /> | Envía un `webhook.ping` de prueba (hasta 5 por minuto). |
| <Endpoint method="GET" path="/v1/webhook-endpoints/{id}/deliveries" href="/docs/api/webhooks/list-webhook-deliveries" /> | El historial de entregas, con su estado y error. |
| <Endpoint method="POST" path="/v1/events/{id}/redeliver" href="/docs/api/webhooks/redeliver-event" /> | Reenvía un evento a todos tus webhooks, o solo a `endpoint_id`. |
| <Endpoint method="PATCH" path="/v1/webhook-endpoints/{id}" href="/docs/api/webhooks/update-webhook-endpoint" /> | Cambia `url`, `description`, `event_types` o pausa con `active: false`. |

## Rotar el secreto [#rotar-el-secreto]

<Endpoint method="POST" path="/v1/webhook-endpoints/{id}/rotate-secret" href="/docs/api/webhooks/rotate-webhook-secret" />

Devuelve un `secret` nuevo. Durante **24 horas** firmamos con los dos (el nuevo primero), así actualizas tu servidor sin perder avisos. `previous_secret_expires_at` dice cuándo deja de valer el anterior.
