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

# Audit and compliance

> Understand how Tracer's immutable, hash-chained audit trail meets SOX and GLBA requirements, and query validation history for compliance reporting.

export const GSOX = ({children}) => <Tooltip headline="SOX" tip="Sarbanes-Oxley Act — a U.S. law mandating strict financial record-keeping and auditing standards, requiring immutable audit trails and accurate reporting." cta="See glossary" href="/en/glossary">
    {children}
  </Tooltip>;

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>;

Auditors, compliance officers, and disputes teams use this layer to answer one question: *"Why did this transaction get this decision, and can we prove no one tampered with that answer?"*

**What changes in your operation:** evidence of a control goes from "let me pull logs from N systems and reconcile timestamps" to "here is the immutable record, cryptographically chained, showing that this transaction got this decision because this specific rule fired at this moment." That shift is the whole point.

**Trade-off to be honest about:** there is no "delete" or "edit" on audit records — by design. A `TRUNCATE` trigger at the database level blocks bulk deletion; each record's SHA-256 hash includes the previous record's hash, so tampering breaks the chain everywhere downstream. If you need to remove a record for legal reasons (such as GDPR right-to-be-forgotten on PII), the answer is data minimization upfront, not retroactive editing.

<Tip>
  **Who is this guide for?** Compliance officers and auditors checking what Tracer guarantees, disputes teams looking up validation history, and developers building reports against the audit endpoints. The compliance overview and retention sections assume zero API knowledge; the query and verify sections assume basic REST.
</Tip>

Tracer maintains a complete, immutable <GAuditTrail>audit trail</GAuditTrail> of all validation decisions. This guide explains how the audit system works and how to query validation history for compliance reporting.

## Compliance overview

***

Tracer is designed to meet the audit requirements of financial regulations including:

| Regulation                            | Requirement                                 | How Tracer complies                            |
| ------------------------------------- | ------------------------------------------- | ---------------------------------------------- |
| <GSOX>**SOX**</GSOX> (Sarbanes-Oxley) | Complete audit trail of financial decisions | Every validation is recorded with full context |
| **GLBA** (Gramm-Leach-Bliley)         | Protection of customer financial data       | Data encrypted at rest and in transit          |
| **General audit**                     | Ability to reconstruct decisions            | Immutable records with input/output snapshots  |

***

## Audit trail architecture

***

Tracer records every validation decision with full context for compliance and investigation.

### What gets recorded

Every validation creates an immutable audit record containing:

| Data                  | Description                                  |
| --------------------- | -------------------------------------------- |
| **Request snapshot**  | Complete input payload as received           |
| **Response snapshot** | Full response including decision and details |
| **Decision**          | ALLOW, DENY, or REVIEW                       |
| **Reason**            | Why the decision was made                    |
| **Evaluated rules**   | All rules that were evaluated                |
| **Matched rules**     | Rules that triggered (if any)                |
| **Limit details**     | Usage information for checked limits         |
| **Processing time**   | How long validation took                     |
| **Timestamp**         | When the validation occurred                 |

<Note>
  Audit events are **deduplicated** for transaction validations. If a validation request is retried with the same `requestId`, only the first audit event is stored. This ensures the audit trail reflects unique business events, not API retry patterns.
</Note>

### Immutability and hash chain

Audit records are **write-once and cryptographically chained**:

* Records cannot be modified after creation.
* The audit table is protected at the database level: a `TRUNCATE` trigger blocks bulk deletion.
* Each record stores a SHA-256 hash that includes the previous record's hash, forming an append-only chain. Tampering with any past record breaks the chain for every subsequent record.
* A `pg_advisory_xact_lock` serializes hash-chain writes to keep the order stable under concurrent inserts.

You can verify the chain at any time using `GET /v1/audit-events/{id}/verify`, which returns:

```json theme={null}
{
  "valid": true,
  "totalChecked": 12345,
  "firstInvalidId": null
}
```

If the chain is intact, `valid` is `true` and `firstInvalidId` is omitted. If a record was modified, `valid` is `false` and `firstInvalidId` points to the first record where the recomputed hash diverges from the stored hash. This is the cryptographic basis for SOX/GLBA tamper-evidence guarantees.

<Note>
  The audit trail is designed for compliance audits. You can reconstruct exactly what happened for any validation, even years later, and prove that the records were not modified after the fact.
</Note>

***

## Data retention

***

Tracer retains data according to regulatory requirements and operational needs.

### Retention periods

| Data type                    | Retention period                                                          | Reason                                               |
| ---------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------- |
| **Validation records**       | 7 years minimum                                                           | SOX/GLBA compliance requirement                      |
| **Rules (active/inactive)**  | Indefinite                                                                | Operational continuity                               |
| **Rules / Limits (deleted)** | Soft-deleted: row kept indefinitely for audit, excluded from API listings | Compliance trail must outlive operational visibility |
| **Limits**                   | Indefinite                                                                | Operational continuity                               |
| **Application logs**         | 90 days                                                                   | Debugging and troubleshooting                        |

### Compliance considerations

* **SOX requirement:** Maintain records for 7 years from the date of the audit report
* **GLBA requirement:** Retain records demonstrating compliance with privacy rules
* **Data export:** Records can be exported for external audit systems

***

## Querying validation history

***

Use the `GET /v1/validations` endpoint to query historical validations.

### Basic query

```http theme={null}
GET /v1/validations
X-API-Key: {api_key}
```

Returns validations in cursor-paginated reverse chronological order. There is no implicit time window — use `start_date` and `end_date` to scope the result, otherwise the response covers everything within the retention period.

### Filtered query

```http theme={null}
GET /v1/validations?start_date=2026-01-01T00:00:00Z&end_date=2026-01-31T23:59:59Z&decision=DENY
X-API-Key: {api_key}
```

### Available filters

| Parameter           | Type    | Description                       |
| ------------------- | ------- | --------------------------------- |
| `start_date`        | RFC3339 | Start of date range (inclusive)   |
| `end_date`          | RFC3339 | End of date range (exclusive)     |
| `decision`          | enum    | Filter by ALLOW, DENY, or REVIEW  |
| `account_id`        | UUID    | Filter by account                 |
| `segment_id`        | UUID    | Filter by segment                 |
| `portfolio_id`      | UUID    | Filter by portfolio               |
| `transaction_type`  | enum    | Filter by CARD, WIRE, Pix, CRYPTO |
| `matched_rule_id`   | UUID    | Filter by rule that matched       |
| `exceeded_limit_id` | UUID    | Filter by limit that was exceeded |

### Date format requirement

<Warning>
  Date parameters must use RFC3339 format with mandatory timezone. Date-only formats are rejected.
</Warning>

**Valid:**

```
start_date=2026-01-01T00:00:00Z
start_date=2026-01-01T00:00:00-03:00
```

**Invalid:**

```
start_date=2026-01-01  (rejected - missing time and timezone)
```

### Pagination

Results use cursor-based pagination. The response includes `nextCursor` and `hasMore` fields to navigate through results.

| Parameter | Default | Maximum | Description                              |
| --------- | ------- | ------- | ---------------------------------------- |
| `limit`   | 100     | 1000    | Results per page                         |
| `cursor`  | -       | -       | Pagination cursor from previous response |

<Note>
  When using cursor pagination, `sort_by` and `sort_order` are fixed from the original query.
</Note>

### Sorting

```http theme={null}
GET /v1/validations?sort_by=created_at&sort_order=DESC
```

| Parameter    | Options                            | Default      |
| ------------ | ---------------------------------- | ------------ |
| `sort_by`    | `created_at`, `processing_time_ms` | `created_at` |
| `sort_order` | ASC, DESC                          | DESC         |

***

## Getting validation details

***

Retrieve complete details for a specific validation using `GET /v1/validations/{id}`.

The response contains everything needed to understand a validation decision:

* **Request snapshot**: The complete input payload as received
* **Response snapshot**: Full response including decision and reason
* **Evaluated rules**: All rules that were checked
* **Matched rules**: Rules that triggered (if any)
* **Limit details**: Usage information for checked limits
* **Timestamps**: When the validation occurred and processing time

***

## Querying audit events

***

Beyond validation records, Tracer also exposes a generic audit event log via `GET /v1/audit-events`. This is the only way to see lifecycle changes for rules and limits — who created, updated, activated, deactivated, drafted, or deleted them.

### Event types

| `eventType`                             | When emitted                                                                  |
| --------------------------------------- | ----------------------------------------------------------------------------- |
| `TRANSACTION_VALIDATED`                 | A validation request was processed (also available via `GET /v1/validations`) |
| `RULE_CREATED` / `RULE_UPDATED`         | Rule created or modified                                                      |
| `RULE_ACTIVATED` / `RULE_DEACTIVATED`   | Rule moved into ACTIVE / INACTIVE                                             |
| `RULE_DRAFTED`                          | Rule moved from INACTIVE back to DRAFT for re-editing                         |
| `RULE_DELETED`                          | Rule soft-deleted                                                             |
| `LIMIT_CREATED` / `LIMIT_UPDATED`       | Limit created or modified                                                     |
| `LIMIT_ACTIVATED` / `LIMIT_DEACTIVATED` | Limit moved into ACTIVE / INACTIVE                                            |
| `LIMIT_DRAFTED`                         | Limit moved from INACTIVE back to DRAFT for re-editing                        |
| `LIMIT_DELETED`                         | Limit soft-deleted                                                            |

### Filters

`GET /v1/audit-events` accepts the same date-range and scope filters as `GET /v1/validations`, plus filters tailored to lifecycle events:

| Parameter                                                                             | Type      | Description                                                                                       |
| ------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------- |
| `start_date` / `end_date`                                                             | RFC3339   | Date range (inclusive start, exclusive end)                                                       |
| `event_type`                                                                          | enum      | Filter by event type (see table above)                                                            |
| `action`                                                                              | enum      | `VALIDATE`, `CREATE`, `UPDATE`, `DELETE`, `ACTIVATE`, `DEACTIVATE`, `DRAFT`                       |
| `result`                                                                              | enum      | `SUCCESS`, `FAILED` (for CRUD) or `ALLOW`, `DENY`, `REVIEW` (for validations)                     |
| `resource_type`                                                                       | enum      | `transaction`, `rule`, `limit`                                                                    |
| `resource_id`                                                                         | UUID      | The affected resource ID                                                                          |
| `actor_type` / `actor_id`                                                             | string    | `user` or `system`, plus the actor ID                                                             |
| `account_id` / `segment_id` / `portfolio_id` / `transaction_type` / `matched_rule_id` | UUID/enum | Same scope filters as validations                                                                 |
| `limit`, `cursor`, `sort_by`, `sort_order`                                            | —         | Cursor pagination (default `limit` 100, max 1000; `sort_by` accepts `created_at` or `event_type`) |

### Use cases

* **Who activated this rule?** `GET /v1/audit-events?resource_type=rule&resource_id={ruleId}&action=ACTIVATE`
* **All rule changes last week:** `GET /v1/audit-events?resource_type=rule&start_date=...&end_date=...`
* **All limit deletions in 2026:** `GET /v1/audit-events?resource_type=limit&action=DELETE&start_date=2026-01-01T00:00:00Z`

### Single event detail

Use `GET /v1/audit-events/{id}` to retrieve a specific audit record, including the snapshot of state at the time of the event.

***

## Compliance reporting scenarios

***

Common queries for audit and compliance reporting.

### Scenario 1: Audit investigation

"Why was this transaction denied on January 15th?"

```http theme={null}
GET /v1/validations/{id}
```

The response shows the exact request received, all rules evaluated, which rule or limit caused the denial, and the timestamp.

### Scenario 2: Monthly compliance report

"Show all denied transactions for corporate accounts in January"

```http theme={null}
GET /v1/validations?start_date=2026-01-01T00:00:00Z&end_date=2026-02-01T00:00:00Z&decision=DENY&segment_id=corporate-segment-uuid&limit=1000
```

### Scenario 3: Rule effectiveness analysis

"Which transactions were denied by a specific fraud rule?"

```http theme={null}
GET /v1/validations?matched_rule_id=fraud-rule-uuid&decision=DENY&limit=1000
```

### Scenario 4: Limit utilization review

"Which transactions exceeded spending limits this month?"

```http theme={null}
GET /v1/validations?start_date=2026-01-01T00:00:00Z&end_date=2026-02-01T00:00:00Z&exceeded_limit_id=daily-limit-uuid
```

***

## Best practices for compliance

***

Recommendations for maintaining audit readiness.

### Record keeping

* **Store validation IDs** in your transaction records for easy cross-reference
* **Log the requestId** you send to Tracer for correlation
* **Export regularly** if you need records in external audit systems

<Warning>
  **Common pitfalls when reading the audit trail:**

  * **"I can see the validation but the rule that fired was already deleted."** Deleted rules are soft-deleted — the row stays in the database, but it doesn't appear in `GET /v1/rules`. To investigate, query `GET /v1/audit-events?resource_type=rule&resource_id={ruleId}` for the lifecycle of that rule, including its activations and the eventual delete.
  * **"Two retries of the same `requestId` only produced one audit event."** This is by design (deduplication via `idx_audit_events_validation_dedup`). The audit trail reflects unique business events, not API retry patterns. If your retry produced a different decision, that's worth investigating — Tracer should return the original cached response.
  * **"`/verify` says the chain is broken on a record I haven't touched."** The chain links every record to the previous one, so a tamper or DB corruption anywhere makes everything downstream report invalid. `firstInvalidId` points to the actual divergence — start the investigation there, not at the record you queried.
</Warning>

### Audit preparation

* **Test queries** before audit season to ensure you can retrieve needed data
* **Verify date ranges** work correctly with your timezone requirements
* **Document your retention policy** alignment with Tracer's 7-year retention

### Investigation workflow

When investigating a specific transaction:

1. **Find the validation ID** from your transaction logs or Tracer history
2. **Retrieve full details** using GET /v1/validations/{id}
3. **Review the request snapshot** to see what data was provided
4. **Check matched rules** to understand why the decision was made
5. **Verify limit status** if limits were involved

***

## No-match behavior and audit

***

When a validation runs and no rule matches, Tracer returns a configured default decision instead of treating the empty match as an error. This is a **per-request fallback**, not an infrastructure resilience strategy.

The decision is governed by the `DEFAULT_DECISION_WHEN_NO_MATCH` environment variable (default: `ALLOW`).

| Scenario                          | Behavior                                                           | Audit record                                                |
| --------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------- |
| No rules match                    | Returns `DEFAULT_DECISION_WHEN_NO_MATCH` (default `ALLOW`)         | Recorded with `reason: "No matching rules found"`           |
| Evaluation timeout                | Returns HTTP 504 with error code `0422`                            | No validation record; an audit failure event may be emitted |
| Database error during limit check | Returns HTTP 500; the entire validation transaction is rolled back | No validation record persisted                              |

<Note>
  Set `DEFAULT_DECISION_WHEN_NO_MATCH=DENY` for fail-closed semantics in high-security deployments. The service logs a warning at startup if this remains at the default `ALLOW`.
</Note>

Infrastructure failures (database down, cache stale, timeout) do **not** fall back to ALLOW — they surface as HTTP errors to the client and the original transaction has no audit record. Operators should monitor `tracer_audit_persist_failures_total` and the `/readyz` endpoint to detect these cases.

***

## Quick reference

***

Key endpoints and retention information.

### Endpoints

| Operation         | Method | Endpoint                       |
| ----------------- | ------ | ------------------------------ |
| List validations  | GET    | `/v1/validations`              |
| Get validation    | GET    | `/v1/validations/{id}`         |
| List audit events | GET    | `/v1/audit-events`             |
| Get audit event   | GET    | `/v1/audit-events/{id}`        |
| Verify hash chain | GET    | `/v1/audit-events/{id}/verify` |

### Retention summary

| Data                | Retention  |
| ------------------- | ---------- |
| Validation records  | 7+ years   |
| Active rules/limits | Indefinite |
| Application logs    | 90 days    |
