> ## 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.

# Webhooks

> CRUD de suscripciones a webhooks salientes.

Las suscripciones se gestionan vía API REST. Cada operación está scoped al tenant de tu API key — nunca podés ver ni modificar suscripciones de otros tenants.

**Scopes:** `webhooks:read` (GET), `webhooks:write` (POST/PATCH/DELETE/test/replay).

## GET /webhooks

Lista las suscripciones del tenant.

```bash theme={null}
curl https://api.waspytech.com/api/v2/webhooks \
  -H "Authorization: Bearer wspy_..."
```

## POST /webhooks

Crea una suscripción nueva. El `secret` se devuelve **una sola vez**.

```bash theme={null}
curl -X POST https://api.waspytech.com/api/v2/webhooks \
  -H "Authorization: Bearer wspy_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://tu-bot.example.com/webhook/waspy",
    "events": ["message.received", "message.status_changed"],
    "description": "Bot de soporte"
  }'
```

| Campo         | Tipo               | Requerido | Descripción                                          |
| ------------- | ------------------ | --------- | ---------------------------------------------------- |
| `url`         | string (URL HTTPS) | Sí        | Endpoint que recibe los POST                         |
| `events`      | string\[]          | Sí        | Uno o más de los [eventos válidos](/webhooks/events) |
| `description` | string             | No        | Etiqueta libre para tu propio uso                    |

## GET /webhooks/:id

Devuelve una suscripción individual (sin secret).

## PATCH /webhooks/:id

Actualiza `url`, `events`, `description` o `enabled`. Si pasás `enabled: true`, se limpia el estado de auto-disable y se resetea el contador de fallas.

## DELETE /webhooks/:id

Borra la suscripción.

## POST /webhooks/:id/test

Encola un evento sintético `message.received` con datos dummy. Útil para validar firma + URL en desarrollo.

<Note>
  **Test deliveries SÍ aparecen en `GET /webhooks/:id/deliveries`** (con statusCode, errorMessage, etc), pero **NO actualizan** `lastDeliveryAt`, `lastDeliveryStatus` ni `consecutiveFailures` en la subscription — para no mezclar bookkeeping de pruebas con producción. Si querés confirmar que el test golpeó tu endpoint, consultá `GET /webhooks/:id/deliveries?status=all&limit=5` justo después.
</Note>

La response incluye `samplePayload` con el envelope exacto que se va a entregar a tu URL — útil para configurar la verificación de firma localmente antes de triggerear:

```json theme={null}
{
  "data": {
    "subscriptionId": "uuid",
    "enqueued": true,
    "event": "message.received",
    "samplePayload": {
      "event": "message.received",
      "webhookId": "uuid",
      "deliveredAt": "2026-04-19T...",
      "attempt": 1,
      "data": { /* MessagePublic + contact */ }
    }
  },
  "meta": { "requestId": "..." }
}
```

## GET /webhooks/:id/deliveries

Lista los intentos de entrega individuales de una suscripción. Cada delivery (incluso fallido) genera una fila acá. Cursor-based, newest first.

**Scope:** `webhooks:read`

| Query    | Default | Descripción                           |
| -------- | ------- | ------------------------------------- |
| `cursor` | —       | Cursor opaco de la respuesta anterior |
| `limit`  | `50`    | Resultados por página (máx 100)       |
| `status` | `all`   | `success`, `failed` o `all`           |

```bash theme={null}
curl "https://api.waspytech.com/api/v2/webhooks/{id}/deliveries?status=failed&limit=20" \
  -H "Authorization: Bearer wspy_..."
```

Response:

```json theme={null}
{
  "data": [
    {
      "id": "delivery-uuid",
      "subscriptionId": "sub-uuid",
      "event": "message.received",
      "attempt": 3,
      "success": false,
      "statusCode": 500,
      "errorMessage": null,
      "responseBodyTruncated": "Internal server error",
      "payloadHash": "sha256-hex",
      "deliveredAt": null,
      "createdAt": "2026-04-19T..."
    }
  ],
  "meta": { "requestId": "...", "cursor": "...", "hasMore": true }
}
```

Los `id` que devuelve son los `deliveryId` que podés pasarle a `POST /webhooks/:id/replay/:deliveryId`.

## POST /webhooks/:id/rotate-secret

Genera un nuevo secret HMAC e invalida el anterior **inmediatamente**. Las entregas en cola que se hayan firmado con el secret viejo van a fallar la verificación en tu endpoint.

**Scope:** `webhooks:write`

```bash theme={null}
curl -X POST https://api.waspytech.com/api/v2/webhooks/{id}/rotate-secret \
  -H "Authorization: Bearer wspy_..."
```

```json theme={null}
{
  "data": {
    "id": "sub-uuid",
    "secret": "whsec_NUEVO_SECRET_AQUI"
  },
  "meta": { "requestId": "..." }
}
```

El nuevo secret se devuelve **una sola vez**. Guardalo y actualizá tu `WASPY_WEBHOOK_SECRET` antes de la próxima delivery.

## POST /webhooks/:id/replay/:deliveryId

Re-encola un delivery histórico (de la tabla `webhook_deliveries`). Útil para reintentar manualmente entregas que fallaron después de exhaustar reintentos automáticos.

<Note>
  El payload exacto del delivery original no se almacena verbatim (sólo su hash). El replay re-fires un evento con el mismo `event` y datos sintéticos — tu integración debe ser idempotente sobre `data.id` para que esto sea seguro.
</Note>
