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

# Revisar una transacción denegada

> Lee una decisión DENY o REVIEW de Tracer para encontrar la regla o el límite detrás de ella, recupera el registro almacenado más tarde y verifica su evento de auditoría durante una auditoría.

export const GAuditTrail = ({children}) => <Tooltip headline="Audit trail" tip="A chronological, immutable record of every action and transaction in the system — essential for regulatory compliance and dispute resolution." cta="See glossary" href="/en/glossary">
    {children}
  </Tooltip>;

Una validación devolvió `DENY` o `REVIEW` y alguien pregunta por qué. Esta guía te lleva desde la decisión que devolvió Tracer hasta una regla con nombre o un límite de gasto con nombre. De ahí llega al registro almacenado y al <GAuditTrail>rastro de auditoría</GAuditTrail> que puedes entregar a un auditor meses después.

**Lo que cambia en tu operación:** la respuesta a "¿por qué se bloqueó esto?" deja de ser una búsqueda en logs. Cada decisión llega con los identificadores de lo que la produjo. El mismo registro responde por id años después, y su evento de auditoría se puede comprobar contra la cadena de hashes.

<Tip>
  **¿Para quién es esta guía?** Desarrolladores que integran la llamada de validación, equipos de soporte y disputas que responden preguntas de clientes, y responsables de compliance que preparan evidencia. Los pasos 1 a 4 solo necesitan la respuesta que ya tienes; los pasos 5 a 8 usan los endpoints de consulta.
</Tip>

## Antes de empezar

***

* [ ] Tracer en ejecución y alcanzable, con una API key — consulta [Primeros pasos](./getting-started.mdx)
* [ ] Una respuesta `DENY` o `REVIEW` con la que trabajar, o el `validationId` de una
* [ ] Familiaridad con lo que hacen las reglas y los límites — consulta el [Motor de reglas](./rule-engine.mdx) y los [Límites de gasto](./spending-limits.mdx)

Todas las llamadas de abajo envían la API key como `X-API-Key`.

***

## Paso 1: Lee la decisión que devolvió Tracer

***

`POST /v1/validations` responde con la decisión completa. Una solicitud nueva responde `201`; repetir un `requestId` responde `200` con la decisión que Tracer ya registró para esa clave.

```bash theme={null}
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)

curl -X POST http://localhost:4020/v1/validations \
  -H "X-API-Key: your-secure-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "7c3f1b8e-5a2d-4c6b-9e1f-0a4d8b2c6e30",
    "transactionType": "CARD",
    "subType": "purchase",
    "amount": "1500.00",
    "currency": "BRL",
    "transactionTimestamp": "'"$TS"'",
    "account": {
      "accountId": "550e8400-e29b-41d4-a716-446655440100",
      "type": "checking",
      "status": "active"
    },
    "merchant": {
      "merchantId": "550e8400-e29b-41d4-a716-446655440103",
      "category": "5411",
      "country": "BR",
      "name": "Acme Store"
    },
    "metadata": {
      "channel": "mobile"
    }
  }'
```

Una denegación producida por una regla se ve así:

```json theme={null}
{
  "validationId": "8f14e45f-ea3b-4c6e-9a1c-2b7d5e0a91c4",
  "requestId": "7c3f1b8e-5a2d-4c6b-9e1f-0a4d8b2c6e30",
  "decision": "DENY",
  "matchedRuleIds": [
    "3c8a5b21-9f4d-4e17-8b6a-1d2e3f405162"
  ],
  "evaluatedRuleIds": [
    "3c8a5b21-9f4d-4e17-8b6a-1d2e3f405162",
    "a6b7c8d9-0e1f-4a2b-9c3d-4e5f60718293"
  ],
  "reason": "Rule matched with DENY action",
  "totalRulesLoaded": 2,
  "truncated": false,
  "limitUsageDetails": [],
  "processingTimeMs": 9.4,
  "evaluatedAt": "2026-07-31T14:05:09.481Z"
}
```

| Campo                              | Qué te dice                                                                                                  |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `validationId`                     | La clave de esta decisión. Todo lo que sigue en esta guía parte de ella                                      |
| `decision`                         | `ALLOW`, `DENY` o `REVIEW`                                                                                   |
| `reason`                           | Qué produjo la decisión — consulta el [Paso 2](#paso-2-distingue-una-denegación-por-regla-de-una-por-límite) |
| `matchedRuleIds`                   | Cada regla que coincidió con esta transacción, sea cual sea la acción que lleva                              |
| `evaluatedRuleIds`                 | Las reglas que Tracer evaluó para esta transacción                                                           |
| `limitUsageDetails`                | Una entrada por cada límite de gasto que Tracer verificó                                                     |
| `totalRulesLoaded` / `truncated`   | Cuántas reglas se cargaron y si `MAX_RULES_PER_REQUEST` recortó el conjunto                                  |
| `processingTimeMs` / `evaluatedAt` | Cuánto tardó la evaluación y cuándo corrió                                                                   |

Para el esquema completo de solicitud y respuesta, consulta [Validar una transacción](/es/reference/tracer/validate-transaction).

<Note>
  Guarda el `validationId` junto a tu propio registro de la transacción. Es la clave que toma el [Paso 5](#paso-5-recupera-el-registro-más-tarde) y también el `resourceId` del evento de auditoría en el [Paso 7](#paso-7-obtén-el-evento-de-auditoría-detrás-de-la-decisión).
</Note>

***

## Paso 2: Distingue una denegación por regla de una por límite

***

Lee `reason` primero. Nombra lo que produjo la decisión y te dice cuál de los dos pasos siguientes tomar.

| `reason`                        | Qué pasó                                                                        | Ve a                                                                                         |
| ------------------------------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `Rule matched with DENY action` | Coincidió una regla cuya acción es `DENY`                                       | [Paso 3](#paso-3-nombra-la-regla)                                                            |
| `limit_exceeded`                | El monto empujaría un límite de gasto más allá de su techo                      | [Paso 4](#paso-4-nombra-el-límite-de-gasto)                                                  |
| `No matching rules found`       | Ninguna regla coincidió, así que Tracer aplicó `DEFAULT_DECISION_WHEN_NO_MATCH` | Revisa ese ajuste en la página de [variables de entorno](./tracer-environment-variables.mdx) |

`reason` lleva el mismo texto en decisiones `REVIEW` — `Rule matched with REVIEW action` — y el Paso 3 lee una revisión igual que lee una denegación.

<Note>
  En una denegación por regla, `limitUsageDetails` vuelve vacío porque Tracer se detiene antes de la verificación de límites — no porque ningún límite aplique a la cuenta.
</Note>

***

## Paso 3: Nombra la regla

***

`matchedRuleIds` lista cada regla que coincidió, sea cual sea la acción que lleva cada una — una regla `DENY` y una regla `ALLOW` pueden aparecer en la misma denegación. Recupera cada una y lee su `action` para encontrar la regla detrás de la decisión:

```http theme={null}
GET /v1/rules/3c8a5b21-9f4d-4e17-8b6a-1d2e3f405162
X-API-Key: {api_key}
```

La regla lleva el `name`, la `description`, la `expression`, la `action` y los `scopes` que un colega necesita para ver por qué disparó — consulta [Recuperar una regla](/es/reference/tracer/retrieve-rule).

Compara `matchedRuleIds` con `evaluatedRuleIds` cuando la pregunta es la opuesta — "¿por qué mi regla *no* disparó?". Una regla ausente de `evaluatedRuleIds` no fue evaluada para esta transacción, así que empieza por su estado y su scope antes que por su expresión.

<Tip>
  Una regla eliminada después de la decisión ya no responde en `GET /v1/rules/{id}`. Su historia, incluido quién la eliminó, queda en el rastro de auditoría — consulta [Auditoría y compliance](./audit-compliance.mdx).
</Tip>

***

## Paso 4: Nombra el límite de gasto

***

En una denegación `limit_exceeded`, `limitUsageDetails` tiene una entrada por cada límite que Tracer verificó, y las entradas marcadas con `"exceeded": true` son las que el monto empujaría más allá de su techo:

```json theme={null}
{
  "validationId": "8f14e45f-ea3b-4c6e-9a1c-2b7d5e0a91c4",
  "requestId": "7c3f1b8e-5a2d-4c6b-9e1f-0a4d8b2c6e30",
  "decision": "DENY",
  "matchedRuleIds": [],
  "evaluatedRuleIds": [
    "a6b7c8d9-0e1f-4a2b-9c3d-4e5f60718293"
  ],
  "reason": "limit_exceeded",
  "totalRulesLoaded": 2,
  "truncated": false,
  "limitUsageDetails": [
    {
      "limitId": "b2d4f6a8-1c3e-4507-9b8d-6f0a2c4e6810",
      "limitAmount": "50000",
      "scope": "(segment:2f1c9b7e-40a5-4d18-9c6b-3e7f5a0d1b24,transactionType:CARD)",
      "period": "DAILY",
      "currentUsage": "51500",
      "attemptedAmount": "1500",
      "exceeded": true
    },
    {
      "limitId": "d4f6a8b2-3e5c-4709-8b6d-0a2c4e681012",
      "limitAmount": "400000",
      "scope": "(account:550e8400-e29b-41d4-a716-446655440100)",
      "period": "MONTHLY",
      "currentUsage": "128400",
      "attemptedAmount": "1500",
      "exceeded": false
    }
  ],
  "processingTimeMs": 14.2,
  "evaluatedAt": "2026-07-31T14:05:09.481Z"
}
```

Lee la entrada excedida como la aritmética de la denegación: `attemptedAmount` contra `limitAmount`, con `currentUsage` reportando lo que el período actual de ese límite y el scope que coincidió tendrían si esta transacción se permitiera. En la entrada de arriba, una compra de `1500` llevaría un techo diario de `50000` hasta `51500`.

Un límite `PER_TRANSACTION` no mantiene conteo, así que su entrada reporta `currentUsage` como `0` y `attemptedAmount` contra `limitAmount` es toda la comparación.

`GET /v1/limits/{limitId}` da el nombre y la configuración actuales del límite — consulta [Recuperar un límite](/es/reference/tracer/retrieve-limit). El registro de la decisión conserva el techo, el período y el scope tal como estaban cuando se tomó la decisión, así que un límite cambiado desde entonces no cambia lo que dice el registro.

Para cómo cuenta cada período y cómo se acumula el consumo, consulta [Límites de gasto](./spending-limits.mdx).

***

## Paso 5: Recupera el registro más tarde

***

Cada decisión queda almacenada bajo su `validationId`:

```http theme={null}
GET /v1/validations/8f14e45f-ea3b-4c6e-9a1c-2b7d5e0a91c4
X-API-Key: {api_key}
```

El registro responde con el contexto de la transacción que Tracer evaluó — `transactionType`, `amount`, `currency`, `transactionTimestamp`, `account` y los opcionales `segment`, `portfolio`, `merchant` y `metadata` — más los mismos `decision`, `reason`, `matchedRuleIds`, `evaluatedRuleIds` y `limitUsageDetails` que llevaba la respuesta original, y un `createdAt`. Consulta [Recuperar una validación](/es/reference/tracer/retrieve-validation).

Este es el registro para leer en voz alta en una disputa: es la entrada y el resultado en un solo documento, y no cambia cuando las reglas o los límites cambian después.

***

## Paso 6: Encuentra registros cuando no tienes el id

***

`GET /v1/validations` lista las decisiones almacenadas, de la más reciente a la más antigua, con paginación por cursor:

```http theme={null}
GET /v1/validations?decision=DENY&account_id=550e8400-e29b-41d4-a716-446655440100&start_date=2026-07-01T00:00:00Z&end_date=2026-07-31T23:59:59Z
X-API-Key: {api_key}
```

<Warning>
  **Una consulta sin fechas cubre los últimos 90 días, no todo el período de retención.** Tracer aplica esa ventana por defecto solo cuando faltan tanto `start_date` como `end_date`. Envía una de las dos — o ambas — para alcanzar un rango más antiguo.
</Warning>

Dos filtros responden las preguntas para las que existe esta guía:

* `matched_rule_id={ruleId}` — cada decisión almacenada con la que coincidió esta regla
* `exceeded_limit_id={limitId}` — cada decisión almacenada que este límite detuvo

Cada resultado es un resumen: `validationId`, `decision`, `reason`, `amount`, `currency`, `transactionType`, `accountId`, `matchedRuleIds`, `exceededLimitIds`, `processingTimeMs` y `createdAt`. Toma el `validationId` del que te interesa y recupéralo con el Paso 5 para tener el registro completo. Consulta [Listar validaciones](/es/reference/tracer/list-validations) para todos los filtros y los campos de paginación.

***

## Paso 7: Obtén el evento de auditoría detrás de la decisión

***

El rastro de auditoría registra la decisión con el `validationId` como `resourceId` del evento:

```http theme={null}
GET /v1/audit-events?resource_type=transaction&resource_id=8f14e45f-ea3b-4c6e-9a1c-2b7d5e0a91c4
X-API-Key: {api_key}
```

```json theme={null}
{
  "auditEvents": [
    {
      "hash": "a3f1e2b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2",
      "previousHash": "b4e2f3a5c6d7e8f9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3",
      "eventId": "1d0a6f52-7b3c-4e8d-9a01-5c2b6e7f8091",
      "eventType": "TRANSACTION_VALIDATED",
      "createdAt": "2026-07-31T14:05:09.492Z",
      "action": "VALIDATE",
      "result": "DENY",
      "resourceId": "8f14e45f-ea3b-4c6e-9a1c-2b7d5e0a91c4",
      "resourceType": "transaction",
      "actor": {
        "actorType": "api_key",
        "id": "tracer-default",
        "name": "",
        "ipAddress": "203.0.113.42"
      },
      "context": {
        "request": {
          "requestId": "7c3f1b8e-5a2d-4c6b-9e1f-0a4d8b2c6e30",
          "transactionType": "CARD",
          "amount": "1500",
          "currency": "BRL"
        },
        "response": {
          "decision": "DENY",
          "reason": "Rule matched with DENY action",
          "matchedRuleIds": [
            "3c8a5b21-9f4d-4e17-8b6a-1d2e3f405162"
          ],
          "evaluatedRuleIds": [
            "3c8a5b21-9f4d-4e17-8b6a-1d2e3f405162",
            "a6b7c8d9-0e1f-4a2b-9c3d-4e5f60718293"
          ],
          "totalRulesLoaded": 2,
          "truncated": false,
          "limitUsageDetails": [],
          "processingTimeMs": 9.4
        }
      }
    }
  ],
  "hasMore": false
}
```

Lo que el evento de auditoría agrega al registro de la validación:

| Campo              | Por qué importa en una revisión                                                                                                                                          |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `actor`            | Quién hizo la llamada — la identidad detrás de la solicitud y la IP del cliente                                                                                          |
| `result`           | La decisión, indexada para filtrar, así que `result=DENY` acota una consulta                                                                                             |
| `context.request`  | El snapshot de la solicitud registrado con la decisión                                                                                                                   |
| `context.response` | La decisión, el motivo y los identificadores de reglas tal como se devolvieron                                                                                           |
| `eventId`          | La clave para el [Paso 8](#paso-8-verifica-el-evento-de-auditoría-en-una-auditoría) y para [Recuperar un evento de auditoría](/es/reference/tracer/retrieve-audit-event) |

<Warning>
  La ventana por defecto de 90 días también aplica aquí: `GET /v1/audit-events` sin `start_date` ni `end_date` cubre los últimos 90 días. Envía el rango que quieres cuando la decisión sea más antigua que eso.
</Warning>

El mismo endpoint lleva el ciclo de vida de reglas y límites — quién los creó, activó o eliminó. Consulta [Listar eventos de auditoría](/es/reference/tracer/list-audit-events) para los filtros, y [Auditoría y compliance](./audit-compliance.mdx) para los tipos de evento y los períodos de retención.

***

## Paso 8: Verifica el evento de auditoría en una auditoría

***

Pasa el `eventId` al endpoint de verificación:

```http theme={null}
GET /v1/audit-events/1d0a6f52-7b3c-4e8d-9a01-5c2b6e7f8091/verify
X-API-Key: {api_key}
```

```json theme={null}
{
  "isValid": true,
  "totalChecked": 12345,
  "message": "Hash chain integrity verified successfully"
}
```

`isValid: true` establece que cada registro, desde el primero hasta el que indicaste, sigue coincidiendo con el hash almacenado con él. Cada uno también enlaza con el hash del registro anterior, así que dentro de ese tramo ningún registro fue eliminado, reordenado ni tuvo su fecha alterada. `totalChecked` reporta cuántos registros cubrió la verificación.

En una verificación fallida, `isValid` es `false`, `message` reporta la manipulación y `firstInvalidId` lleva un número de secuencia interno del registro divergente. Ese número no es un id de evento de auditoría, así que no es un valor para pasar a `GET /v1/audit-events/{id}`.

Consulta [Verificar un evento de auditoría](/es/reference/tracer/verify-audit-event).

<Note>
  Entrega los dos juntos: el evento recuperado en el Paso 7 es el contenido de la decisión, y el resultado de la verificación es la evidencia de que la cadena que lo sostiene está intacta. La llamada de verificación informa sobre la cadena — no devuelve el registro.
</Note>

***

## Errores comunes

***

<Warning>
  **Lo que suele salir mal en una revisión:**

  * **"Mi consulta del año pasado volvió vacía."** Una consulta sin `start_date` ni `end_date` cubre los últimos 90 días. Envía el rango que quieres.
  * **"`matchedRuleIds` tiene tres entradas y solo una denegó."** El arreglo lleva cada regla que coincidió, sea cual sea la acción que lleva. Recupera cada regla y lee su `action` (Paso 3).
  * **"`limitUsageDetails` está vacío en una denegación."** La decisión vino de una regla, no de un límite. Lee `reason` (Paso 2).
  * **"`currentUsage` es mayor que lo que el cliente gastó de verdad."** Es la cifra proyectada, con el monto intentado ya sumado. En un límite excedido el contador no se incrementó, así que el consumo almacenado no incluye esta transacción.
  * **"La regla que disparó ya no existe."** Las reglas eliminadas dejan de responder en `GET /v1/rules/{id}`. Consulta su ciclo de vida con `GET /v1/audit-events?resource_type=rule&resource_id={ruleId}`.
</Warning>

### Códigos de error

| Código          | Estado | Qué cambiar                                                                                   |
| --------------- | ------ | --------------------------------------------------------------------------------------------- |
| `0065`          | 400    | El id en la ruta no es un UUID                                                                |
| `0432`          | 404    | Ninguna validación almacenada tiene ese `validationId`                                        |
| `0381`          | 404    | Ningún evento de auditoría tiene ese `eventId`                                                |
| `0077`          | 400    | Una fecha no está en RFC3339 con zona horaria — envía `2026-07-01T00:00:00Z`, no `2026-07-01` |
| `0083`          | 400    | `end_date` cae antes de `start_date`                                                          |
| `0431`          | 400    | Un filtro de `GET /v1/validations` lleva un valor que el endpoint no acepta                   |
| `0080` / `0331` | 400    | `limit` está por encima de 1000, o no es positivo                                             |
| `0334`          | 400    | `cursor` se envió junto con `sort_by` o `sort_order` — el cursor ya los lleva                 |

La lista completa está en la [lista de errores de Tracer](/es/reference/tracer/tracer-error-list).

***

## Referencia rápida

***

| Paso                             | Método | Endpoint                       |
| -------------------------------- | ------ | ------------------------------ |
| Validar una transacción          | POST   | `/v1/validations`              |
| Encontrar decisiones almacenadas | GET    | `/v1/validations`              |
| Recuperar una decisión           | GET    | `/v1/validations/{id}`         |
| Nombrar una regla                | GET    | `/v1/rules/{id}`               |
| Nombrar un límite                | GET    | `/v1/limits/{id}`              |
| Encontrar eventos de auditoría   | GET    | `/v1/audit-events`             |
| Recuperar un evento de auditoría | GET    | `/v1/audit-events/{id}`        |
| Verificar la cadena de hashes    | GET    | `/v1/audit-events/{id}/verify` |
