Tipo de página: docs

Webhooks

Contratos verificados de webhooks de mensajería y financiamiento, manejo de duplicados y comportamiento actual de reconciliación.

Respuesta directa

Contratos verificados de webhooks de mensajería y financiamiento, manejo de duplicados y comportamiento actual de reconciliación.

Contenido de la página

# Webhooks

TextTree actualmente acepta callbacks de proveedores desde dos límites:

- eventos del proveedor de mensajería
- Peer para eventos de financiamiento

Todas las rutas de webhooks son endpoints JSON servidos por Phoenix.

## Endpoints

- callbacks de eventos firmados del proveedor de mensajería
- callbacks entrantes firmados del proveedor de mensajería
- `POST /webhooks/peer/events`

Los endpoints de webhooks de clientes se configuran desde la página autenticada de Webhooks. Las rutas de
webhooks de proveedores anteriores son los callbacks entrantes de proveedores que TextTree usa para actualizar
el estado de mensajes, bajas (opt-out) y financiamiento.

## Tipos de eventos de cliente

Suscribe tu endpoint a los eventos que tu aplicación necesita:

- `message.sent`
- `message.delivered`
- `message.failed`
- `message.received`
- `conversation.created`
- `conversation.updated`
- `opt_out.created`

## Forma de los eventos de cliente

Los payloads de webhooks de clientes usan una envoltura estable para que los receptores puedan enrutar por `type` e
inspeccionar el objeto de dominio en `data`.

```json
{
  "id": "evt_123",
  "type": "message.delivered",
  "created_at": "2026-04-28T14:00:00Z",
  "data": {
    "message": {
      "id": "msg_123",
      "status": "delivered",
      "phone_number": "+15551234567",
      "segments": 1,
      "cost_cents": 1
    }
  }
}
```

Devuelve cualquier respuesta `2xx` para marcar la entrega como exitosa. Las respuestas no `2xx` se conservan para
depuración y reintento/reproducción desde la consola.

## Verificación de firmas

La verificación de webhooks es obligatoria en la fase alfa actual.

- Las solicitudes del proveedor de mensajería deben incluir el encabezado de firma configurado
- Las solicitudes del proveedor de SMS deben incluir los encabezados de marca de tiempo y firma configurados
- Las solicitudes de Peer deben incluir `x-peer-timestamp` y `x-peer-signature`
- Los valores de firma usan el formato `sha256=<hex>`

La firma se calcula como un HMAC-SHA256 sobre la marca de tiempo y la representación canonicalizada
del payload de TextTree, no sobre los bytes crudos de la solicitud. TextTree rechaza pares de marca de tiempo/firma
faltantes, obsoletos, futuros o no coincidentes; la ventana de frescura por defecto es de cinco minutos.

## Respuesta de aceptación

Un webhook verificado que pasa la validación de forma devuelve `202 Accepted`:

```json
{
  "id": "8fca25fd-d7b7-4f58-8bdd-d4ab4ca3ae97",
  "status": "received",
  "provider": "messaging_provider",
  "event_type": "message.delivered",
  "external_id": "provider_evt_123",
  "duplicate": false
}
```

Si el mismo proveedor envía nuevamente el mismo `event_type` y `external_id`, TextTree devuelve el
registro existente con `"duplicate": true` y no encola trabajo descendente duplicado.

## Respuestas de falla

- `401` con `{"error":"invalid_signature"}` cuando la verificación falla
- `422` con `{"error":"invalid_webhook_payload"}` cuando la solicitud está firmada pero le faltan los
  campos requeridos de identidad del evento

## Reproducción desde la consola

Toda entrega fallida de webhook de cliente debería ser reproducible después de que corrijas el endpoint. El flujo
de reproducción conserva el payload original y registra un nuevo intento de entrega con código de respuesta, latencia y
cuerpo de respuesta.

```txt
Webhooks → select endpoint → open failed delivery → Replay webhook
```

## Manejo de eventos de mensajería

Los payloads del proveedor de mensajería se normalizan a partir de claves como `type`, `id`, `message_id` y `metadata`.

### Eventos de mensaje reconciliados

- `message.sent`
- `message.delivered`
- `message.failed`

Estos actualizan el estado descendente del mensaje y pueden completar identificadores de proveedor en el mensaje almacenado.

### Eventos de baja (opt-out) reconciliados

- `recipient.opted_out`
- `contact.opted_out`
- `message.opted_out`
- `message.unsubscribed`

En la V1, estos crean o actualizan supresiones a nivel de workspace con metadatos del proveedor. Las
supresiones de workspace bloquean los envíos puntuales desde la UI, los envíos de la API para desarrolladores, los envíos MCP y las
entregas de campañas para ese número de teléfono.

## Manejo de eventos de Peer

Los payloads de Peer se normalizan a partir de claves como `event`, `event_id`, `session_id`, `amount_cents`
y `metadata`.

### Eventos de financiamiento reconciliados

- `funding.completed`
- `funding.failed`
- `funding.expired`

Los eventos de financiamiento completados actualizan el estado de la sesión de financiamiento y crean créditos idempotentes en el libro contable.
Los eventos de financiamiento fallidos y expirados actualizan el estado almacenado de la sesión de financiamiento para revisión del operador.

## Ejemplos

### Evento de mensajería entregado

```json
{
  "type": "message.delivered",
  "id": "provider_evt_123",
  "message_id": "msg_123"
}
```

### Evento de baja (opt-out) de mensajería

```json
{
  "type": "recipient.opted_out",
  "id": "provider_evt_456",
  "phone_number": "+15551234567",
  "message_id": "msg_123"
}
```

### Evento de financiamiento de Peer

```json
{
  "event": "funding.completed",
  "event_id": "peer_evt_123",
  "session_id": "session_123",
  "amount_cents": 500
}
```

## Notas operativas

- Los eventos de proveedor se persisten antes de la reconciliación asíncrona.
- Los eventos fallidos permanecen visibles en `/app` con motivos de falla y controles de reproducción.
- La reproducción está pensada para la recuperación por parte del operador después de que se corrijan los problemas de datos subyacentes o del proveedor.
- Los eventos desconocidos pero firmados válidamente pueden almacenarse e ignorarse hasta que la capa de dominio los admita.
Markdown