> ## 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 y callbacks

> Emite eventos del ciclo de vida de Matcher hacia herramientas externas mediante webhooks salientes, y recibe callbacks de resolución para que JIRA o ServiceNow mantengan las excepciones sincronizadas.

Los webhooks habilitan la comunicación en tiempo real entre Matcher y los sistemas externos. Esta guía cubre las notificaciones de eventos salientes y los callbacks de resolución entrantes.

## Resumen

***

Matcher admite comunicación bidireccional por webhook y mantiene tus herramientas operativas sincronizadas con cada evento de conciliación en tiempo real.

* **Webhooks salientes**: el enrutamiento de excepciones envía las excepciones a destinos externos: JIRA, ServiceNow o un endpoint de webhook HTTP que configures
* **Callbacks entrantes**: los sistemas externos avisan a Matcher cuando ejecutan una acción

Cuando el enrutamiento de excepciones envía una excepción a un destino de webhook, Matcher entrega una solicitud HTTP firmada a tu endpoint. Sistemas externos como JIRA o ServiceNow pueden entonces enviar callbacks para actualizar el estado de la excepción o cerrar elementos de forma automática. Este flujo de ida y vuelta mantiene tus herramientas sincronizadas sin intervención manual.

Más allá del envío de excepciones, Matcher publica su catálogo completo de eventos de ciclo de vida en el backbone de streaming de la plataforma. Esos eventos te llegan como un stream, no como webhooks HTTP.

<Frame caption="Flujo de ida y vuelta entre Matcher y los sistemas externos.">
  <img src="https://mintcdn.com/lerian-49cb71fc/RAVxFNT8MNA4GWjO/images/es/d2/matcher-webhooks-callbacks.svg?fit=max&auto=format&n=RAVxFNT8MNA4GWjO&q=85&s=99ec890e217b205f92297728e195bbf2" alt="Matcher Webhooks Callbacks" width="724" height="520" data-path="images/es/d2/matcher-webhooks-callbacks.svg" />
</Frame>

## Eventos salientes

***

Matcher emite eventos cuando ocurren acciones significativas en el proceso de conciliación. Matcher publica el catálogo de abajo en el backbone de streaming. Los eventos de excepción también llegan a endpoints de webhook HTTP mediante el [enrutamiento de excepciones](/es/products/matcher/configuration/matcher-exception-routing).

### Eventos disponibles

Matcher define su catálogo de eventos de forma centralizada. Las tablas de abajo agrupan por dominio los eventos que más se consumen.

**Configuración**

| Evento                           | Disparador                                  | Uso típico                              |
| -------------------------------- | ------------------------------------------- | --------------------------------------- |
| `reconciliation_context.created` | Contexto de conciliación creado             | Sincronización de aprovisionamiento     |
| `reconciliation_context.updated` | Metadatos o estado del contexto modificados | Seguimiento de cambios de configuración |
| `reconciliation_context.deleted` | Contexto eliminado                          | Desmontaje downstream                   |
| `reconciliation_source.created`  | Fuente creada dentro de un contexto         | Incorporación de fuentes                |
| `match_rule.created`             | Regla de coincidencia creada                | Auditoría de cambios de reglas          |
| `match_rule.reordered`           | Prioridades de reglas reordenadas           | Auditoría de cambios de reglas          |

**Discovery**

| Evento                             | Disparador                                      | Uso típico                |
| ---------------------------------- | ----------------------------------------------- | ------------------------- |
| `fetcher_connection.synced`        | Conexión e instantánea de esquema sincronizadas | Monitoreo de Discovery    |
| `fetcher_connection.unreachable`   | Conexión marcada como inalcanzable              | Alertas de conectividad   |
| `extraction_request.created`       | Solicitud de extracción creada                  | Monitoreo de extracciones |
| `extraction_request.submitted`     | Extracción aceptada por el motor de extracción  | Monitoreo de extracciones |
| `extraction_request.completed`     | Extracción completada con artefacto             | Disponibilidad de datos   |
| `extraction_request.failed`        | Extracción fallida                              | Alertas de error          |
| `extraction_request.cancelled`     | Extracción cancelada                            | Monitoreo del pipeline    |
| `extraction_request.bridged`       | Extracción vinculada a un trabajo de ingesta    | Monitoreo del pipeline    |
| `extraction_request.bridge_failed` | Falló el puente hacia la ingesta                | Alertas de error          |

**Ingesta**

| Evento                | Disparador                                      | Uso típico                      |
| --------------------- | ----------------------------------------------- | ------------------------------- |
| `ingestion.completed` | Importación de archivo finalizada               | Monitoreo del pipeline de datos |
| `ingestion.failed`    | Importación de archivo fallida                  | Alertas de error                |
| `transaction.ignored` | Transacción no conciliada marcada como ignorada | Registro de auditoría           |

**Coincidencia**

| Evento                       | Disparador                                   | Uso típico                          |
| ---------------------------- | -------------------------------------------- | ----------------------------------- |
| `match_run.completed`        | Trabajo de coincidencia finalizado           | Monitoreo de trabajos, informes     |
| `match_run.failed`           | Trabajo de coincidencia fallido              | Alertas de error                    |
| `match_group.confirmed`      | Grupo de coincidencia confirmado             | Actualizaciones downstream          |
| `match_group.unmatched`      | Coincidencia confirmada revertida            | Seguimiento de correcciones         |
| `transaction.matched`        | Transacción marcada como conciliada          | Registro de auditoría               |
| `transaction.pending_review` | Un candidato no automático necesita revisión | Disparadores de la cola de revisión |
| `fee_variance.created`       | Variación de comisión detectada              | Investigación de comisiones         |

**Excepciones y disputas**

| Evento                            | Disparador                                       | Uso típico                     |
| --------------------------------- | ------------------------------------------------ | ------------------------------ |
| `exception.assigned`              | Excepción asignada a un responsable              | Notificación al usuario        |
| `exception.resolved`              | Excepción resuelta                               | Sincronización de estado       |
| `exception.dispatched`            | Excepción enviada a un destino externo           | Creación de tickets            |
| `exception.callback_processed`    | Callback externo procesado                       | Sincronización de estado       |
| `exception.force_match_resolved`  | Excepción resuelta mediante coincidencia forzada | Workflows de aprobación        |
| `exception.adjust_entry_resolved` | Excepción resuelta mediante asiento de ajuste    | Registro de auditoría          |
| `exception_comment.added`         | Comentario agregado a una excepción              | Sincronización de colaboración |
| `exception_comment.deleted`       | Comentario de excepción eliminado                | Sincronización de colaboración |
| `dispute.opened`                  | Disputa abierta para una excepción               | Seguimiento de disputas        |
| `dispute.won`                     | Disputa cerrada como ganada                      | Sincronización de estado       |
| `dispute.lost`                    | Disputa cerrada como perdida                     | Sincronización de estado       |
| `evidence.submitted`              | Evidencia enviada a una disputa                  | Seguimiento de disputas        |

**Gobernanza e informes**

| Evento                     | Disparador                                | Uso típico                      |
| -------------------------- | ----------------------------------------- | ------------------------------- |
| `audit_log.created`        | Entrada de registro de auditoría agregada | Monitoreo de cumplimiento       |
| `archive_metadata.created` | Ciclo de vida de archivo iniciado         | Monitoreo de archivado          |
| `archive.uploaded`         | Objeto de archivo subido                  | Monitoreo de archivado          |
| `archive.completed`        | Archivo verificado y completado           | Monitoreo de archivado          |
| `actor.pseudonymized`      | Mapeo de actor seudonimizado              | Monitoreo de cumplimiento       |
| `export_job.created`       | Trabajo de exportación encolado           | Monitoreo de exportaciones      |
| `export_job.succeeded`     | Trabajo de exportación completado         | Disponibilidad de descarga      |
| `export_job.failed`        | Trabajo de exportación fallido            | Alertas de error                |
| `export_job.expired`       | Artefacto de exportación vencido          | Ciclo de vida de la exportación |

### Payload de entrega del webhook

Los envíos de excepciones a un destino de webhook llevan un payload consistente con `eventId`, `eventType`, `timestamp`, la instantánea de la excepción bajo `data` e información de enrutamiento y trazas bajo `metadata`:

```json theme={null}
{
  "eventId": "0e8f1c2a-5b6d-4f3e-9a7b-1c2d3e4f5a6b",
  "eventType": "exception.dispatched",
  "timestamp": "2026-01-20T10:30:00Z",
  "data": {
    "exceptionId": "9b2f4e6a-1c3d-4e5f-8a9b-0c1d2e3f4a5b",
    "transactionId": "7a1b3c5d-9e8f-4a2b-b6c7-d8e9f0a1b2c3",
    "severity": "HIGH",
    "status": "PENDING",
    "amount": "15000.00",
    "currency": "USD",
    "reason": "No matching ledger entry found",
    "sourceType": "LEFT",
    "createdAt": "2026-01-20T10:29:15Z",
    "dueAt": "2026-01-23T10:29:15Z"
  },
  "metadata": {
    "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
    "target": "WEBHOOK",
    "queue": "ops-review",
    "ruleName": "high-value-unmatched",
    "assignee": "ops-team"
  }
}
```

Matcher omite `data.dueAt` y los campos de `metadata` `traceId`, `queue`, `ruleName` y `assignee` cuando no tienen valor. Los eventos del catálogo de streaming (las tablas de arriba) siguen sus propios esquemas por evento en el stream de eventos. Matcher no los entrega con esta forma HTTP.

## Callbacks entrantes

***

Los sistemas externos envían callbacks a Matcher para actualizar el estado de una excepción después de procesarla. El endpoint de callback acepta actualizaciones de estado, notas de resolución y cambios de responsable desde cualquier sistema externo.

### Procesar un callback

El header `X-Callback-Token` autentica el endpoint de callback. Un JWT de operador no lo autentica. El header lleva un token opaco de la superficie de [credenciales de callback](#callback-credentials). Cada campo de abajo es obligatorio. `dueAt` y `updatedAt` aceptan `null`, y `payload` puede ser un objeto vacío:

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/callback" \
 -H "X-Callback-Token: ***" \
 -H "X-Idempotency-Key: callback-jira-1234" \
 -H "Content-Type: application/json" \
 -d '{
   "callbackType": "status_update",
   "externalSystem": "JIRA",
   "externalIssueId": "RECON-1234",
   "status": "RESOLVED",
   "resolutionNotes": "Verified: amount difference is expected bank wire fee",
   "assignee": "john.doe@company.com",
   "dueAt": null,
   "updatedAt": "2026-01-20T14:30:00Z",
   "payload": {}
 }'
```

El campo `externalSystem` identifica el sistema externo que procesó la excepción. Los valores comunes incluyen `"JIRA"`, `"SERVICENOW"` o `"WEBHOOK"`, pero los callbacks pueden informar cualquier identificador de sistema. Omitir cualquiera de los nueve campos obligatorios devuelve un `422`.

#### Respuesta

```json theme={null}
{
  "status": "accepted"
}
```

<Tip>
  Referencia de API: [Procesar callback](/es/reference/products/matcher/process-exception-callback)
</Tip>

Cuando Matcher procesa un callback, actualiza el estado de la excepción y registra la resolución en el registro de auditoría. Usa el header `X-Idempotency-Key` para evitar el procesamiento duplicado.

<h3 id="automatic-retry-for-failed-callbacks">
  Reintento automático para callbacks fallidos
</h3>

Si un callback anterior con la misma clave de idempotencia falló durante el procesamiento, Matcher intenta de forma automática readquirir el lock de idempotencia y reprocesar el callback. Esto significa que no necesitas generar una nueva clave de idempotencia cuando reintentas un callback fallido. Reenvía la misma solicitud y Matcher se encarga de la recuperación.

El comportamiento de reintento se aplica solo a los callbacks con un estado interno `failed`. Matcher sigue deduplicando los callbacks que se completaron con éxito.

<h2 id="callback-credentials">
  Credenciales de callback
</h2>

***

Un Bearer token opaco autentica los callbacks entrantes. El sistema externo envía este token en el header `X-Callback-Token`. Emites, listas, rotas y revocas estas **credenciales de callback** a través de una superficie CRUD dedicada bajo `/v1/exceptions/callbacks/credentials`. Cada credencial pertenece al tenant de quien llama. Matcher almacena solo el hash SHA-256 del token del lado del servidor. Las respuestas de emisión y de rotación devuelven el token en bruto **exactamente una vez**.

| Acción              | Método y ruta                                                     | Notas                                                                                                                                                                                  |
| ------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Emitir credencial   | `POST /v1/exceptions/callbacks/credentials`                       | Crea una credencial y devuelve el token en bruto una vez (`201`).                                                                                                                      |
| Listar credenciales | `GET /v1/exceptions/callbacks/credentials`                        | Lista los metadatos de las credenciales del tenant (nunca los tokens en bruto).                                                                                                        |
| Rotar credencial    | `POST /v1/exceptions/callbacks/credentials/{credentialId}/rotate` | Sustituye de forma atómica una credencial activa por una recién emitida (la misma etiqueta) y devuelve el nuevo token en bruto una vez; la anterior se revoca en la misma transacción. |
| Revocar credencial  | `DELETE /v1/exceptions/callbacks/credentials/{credentialId}`      | Revoca una credencial de forma terminal (`204`); auditado solo por adición.                                                                                                            |

<Tip>
  Referencia de API:

  * [Emitir credencial de callback](/es/reference/products/matcher/mint-callback-credential)
  * [Listar credenciales de callback](/es/reference/products/matcher/list-callback-credentials)
  * [Rotar credencial de callback](/es/reference/products/matcher/rotate-callback-credential)
  * [Revocar credencial de callback](/es/reference/products/matcher/revoke-callback-credential)
</Tip>

### Emitir una credencial

El cuerpo de la solicitud es opcional. Proporciona `externalSystem` como una etiqueta legible por el operador para el sistema que este token autentica.

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/callbacks/credentials" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "externalSystem": "stripe" }'
```

La respuesta `201` (`CredentialSecretResponse`) devuelve:

| Campo            | Descripción                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `token`          | Bearer token en bruto, expuesto **una vez**. Configúralo como valor del header `X-Callback-Token` en el sistema externo.                 |
| `credentialId`   | Id sustituto de la credencial emitida (se usa para rotar/revocar).                                                                       |
| `createdAt`      | Hora de emisión (RFC 3339, UTC).                                                                                                         |
| `externalSystem` | La etiqueta devuelta para confirmación.                                                                                                  |
| `webhookUrlHint` | Forma informativa de la URL de callback entrante para configurar por fuera; el placeholder `{exceptionId}` se completa en cada callback. |

<Warning>
  Solo las respuestas de emisión y de rotación muestran el `token` en bruto. Guárdalo de forma segura al recibirlo. No puedes recuperarlo otra vez. Si pierdes o filtras el token, rota o revoca la credencial.
</Warning>

### Rotar una credencial

```bash cURL theme={null}
curl -X POST "https://api.matcher.example.com/v1/exceptions/callbacks/credentials/{credentialId}/rotate" \
 -H "Authorization: Bearer $TOKEN"
```

La rotación devuelve un nuevo `CredentialSecretResponse` (un nuevo token en bruto) y revoca la credencial anterior de forma atómica, así los llamadores externos no sufren ninguna interrupción cuando cambias el token.

### Revocar una credencial

```bash cURL theme={null}
curl -X DELETE "https://api.matcher.example.com/v1/exceptions/callbacks/credentials/{credentialId}" \
 -H "Authorization: Bearer $TOKEN"
```

La revocación es terminal: la credencial ya no puede autenticar callbacks entrantes.

## Seguridad de los webhooks

***

### Verificación de firma

Cuando configuras un secreto compartido de webhook, Matcher firma cada entrega con un HMAC-SHA256 sobre el **cuerpo bruto de la solicitud**. Matcher envía la firma en el header `X-Signature-256`, con el formato `sha256=<hex-digest>`:

```
X-Signature-256: sha256=abc123...
```

Cada entrega también lleva un header `X-Idempotency-Key`, así los receptores pueden deduplicar los reintentos.

**Proceso de verificación:**

1. Calcula el HMAC-SHA256 del cuerpo bruto de la solicitud con el secreto compartido del webhook
2. Antepón `sha256=` al digest hexadecimal
3. Compara (en tiempo constante) con el header `X-Signature-256`

**Ejemplo (Node.js):**

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
 const expectedSignature = crypto
 .createHmac('sha256', secret)
 .update(payload)
 .digest('hex');

 return `sha256=${expectedSignature}` === signature;
}
```

**Ejemplo (Python):**

```python theme={null}
import hmac
import hashlib

def verify_webhook(payload, signature, secret):
 expected = hmac.new(
 secret.encode(),
 payload,
 hashlib.sha256
 ).hexdigest()
 return f"sha256={expected}" == signature
```

### Postura de red

Matcher se despliega en tu propia infraestructura, así que las entregas de webhook salen por el egress de tu despliegue. No existe un rango de IP fijo de Lerian para agregar a una lista de permitidos. Sirve los endpoints de webhook sobre HTTPS con un certificado válido. Como protección contra SSRF, Matcher se niega a entregar a direcciones IP privadas o de loopback, a menos que el despliegue las permita de forma explícita (solo en desarrollo).

## Lógica de reintentos

***

Matcher reintenta las entregas de webhook fallidas con backoff exponencial.

### Política de reintentos predeterminada

De forma predeterminada, Matcher reintenta una entrega fallida hasta **3 veces**. Los retrasos siguen un backoff exponencial desde una base de **1 segundo**, con jitter para distribuir los reintentos. El espaciado exacto varía de un intento a otro en lugar de seguir una escalera fija.

### Condiciones de reintento

Hay reintentos para:

* Respuestas HTTP 429
* Respuestas HTTP 5xx
* Errores de transporte (fallas de conexión, timeouts)

Sin reintento para:

* Otras respuestas HTTP 4xx

## Mejores prácticas

***

<AccordionGroup>
  <Accordion title="Verifica las firmas de los webhooks">
    Verifica siempre la firma HMAC antes de procesar los payloads de webhook. Esto evita solicitudes falsificadas.
  </Accordion>

  <Accordion title="Responde rápido">
    Devuelve una respuesta 2xx en menos de 5 segundos. Procesa el evento de forma asíncrona si hace falta.
  </Accordion>

  <Accordion title="Maneja los duplicados de forma idempotente">
    Las entregas pueden llegar más de una vez. Deduplica por el header `X-Idempotency-Key` o por el `eventId` del payload.
  </Accordion>

  <Accordion title="Monitorea la salud de la entrega">
    Configura alertas para las tasas de falla de webhooks. Investiga pronto las fallas persistentes.
  </Accordion>
</AccordionGroup>

## Próximos pasos

***

<Card title="Enrutamiento de excepciones" icon="route" href="/es/products/matcher/configuration/matcher-exception-routing" horizontal>
  Configura cómo las excepciones disparan eventos de webhook.
</Card>

<Card title="Fuentes externas" icon="building-columns" href="/es/products/matcher/integrations/matcher-external-sources" horizontal>
  Configura fuentes de datos que pueden enviar datos por webhooks.
</Card>
