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

# Integration guide

> Integrate Tracer with your authorization system: send complete payloads, handle ALLOW, DENY, and REVIEW decisions, and stay within an 80ms latency budget.

export const GMetadata = ({children}) => <Tooltip headline="Metadata" tip="Additional key-value information attached to entities like accounts or transactions — such as external IDs, reference numbers, or department codes." cta="See glossary" href="/en/start-here/glossary">
    {children}
  </Tooltip>;

Integrating Tracer means deciding where in your authorization flow to make the validation call, what data to send, and how to handle the three possible decisions. The pattern is short: your system collects the full transaction context, calls `POST /v1/validations`, acts on ALLOW / DENY / REVIEW, and moves on. Tracer never reaches back into your stack. There are no webhooks or callbacks, and the integration ends with the response.

**What changes in your operation:** decisioning moves from in-process logic to an external call. The call is synchronous (request/response, no webhooks), so it sits on the critical path of the transaction. Done well, it adds under 80ms p99 and gives you a single point for policy and validation history. Done poorly (no timeout, no retry strategy, no fallback), it becomes a single point of failure.

**Trade-off to be honest about:** you're adding a network hop. The good news is the contract is simple: idempotent by `requestId`, no callbacks, deterministic three-state response. The bad news is you must think about timeouts, retries, and what to do if Tracer is unreachable. Most of this guide is about that.

<Tip>
  **Who is this guide for?** Integration engineers writing the request from your system to Tracer, and architects deciding where the call sits in the flow. Risk and fraud analysts who write rules can skip ahead to the [Rules engine guide](./rule-engine.mdx). Compliance can read the [Audit and compliance guide](./audit-compliance.mdx) instead.
</Tip>

This guide covers payload requirements, the integration flow, and practices that keep the validation call inside your latency budget.

Tracer sits **outside** your ledger: it never calls Midaz. Your application orchestrates the two. It calls Tracer to validate, and submits the transaction to Midaz only if the decision is `ALLOW`. Tracer evaluates your configured **policies and limits** against the context you send, not account balances. The ledger stays the source of truth for what an account holds.

<Note>
  Midaz can also drive Tracer through an optional per-ledger reservation seam. It remains one-way, Ledger → Tracer. The rest of this guide covers the application-orchestrated HTTP pattern. The seam contract appears below.
</Note>

## Integration overview

***

Tracer expects calls from **authorization systems** (payment gateways, workflow orchestrators, or transaction processors) that need real-time validation decisions. The integration follows a simple request-response pattern:

<Frame caption="Figure 1. Integration overview with Tracer">
  <img src="https://mintcdn.com/lerian-49cb71fc/SEOef3JqTInYAAau/images/en/d2/integration-overview-tracer.svg?fit=max&auto=format&n=SEOef3JqTInYAAau&q=85&s=087d44f66ec751efe4bb38be8c964467" alt="Request-response integration where an authorization system calls Tracer for a validation decision and only submits the transaction to Midaz when the decision is ALLOW" width="994" height="284" data-path="images/en/d2/integration-overview-tracer.svg" />
</Frame>

**Key principle:** Tracer does not fetch external data during validation. Your system must provide all context needed for rule evaluation.

***

## Midaz Ledger reservation seam

***

This opt-in seam belongs to **Ledger HTTP v2**, not to Tracer HTTP v2. Ledger HTTP v1 never invokes it. Tracer's public HTTP API remains v1-only, including its reservation operations under `/v1`. The gRPC service `lerian.midaz.reservation.v1.ReservationService` is an internal service-to-service transport, not a public v2 API.

Set `TRACER_BASE_URL` to inject the seam. When it is unset, Ledger makes no reservation call. Ledger calls the injected client only when the per-ledger `tracer.mode` is `advisory` or `enforce`. An unset or `off` mode still skips reservations even when you set `TRACER_BASE_URL`. An honored per-call skip also bypasses the seam.

gRPC is the default transport: configure `TRACER_GRPC_PORT` on Tracer and point `TRACER_BASE_URL` at that gRPC listener. With `TRACER_TRANSPORT=rest`, point it at Tracer's HTTP listener instead. Both transports use the same reservation service and the same five transitions:

| Transition             | Ledger action                                                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `Reserve`              | Hold limit capacity before the transaction commits. Retries are idempotent at the `(transactionId, limitId, scopeKey, periodKey)` grain. |
| `ConfirmByTransaction` | Commit every hold for a transaction; retrying after terminal state is a no-op and can return `flipped=0`.                                |
| `ReleaseByTransaction` | Return every hold for a transaction; retrying after terminal state is a no-op and can return `flipped=0`.                                |
| `Confirm`              | Commit one hold; retrying a terminal reservation is a no-op.                                                                             |
| `Release`              | Return one hold; retrying a terminal reservation is a no-op.                                                                             |

For multi-tenant calls, REST forwards the tenant in the `X-Tenant-Id` header and gRPC forwards it as `x-tenant-id` metadata. Neither transport places it in the reservation message.

The listener may trust this value only over direct mTLS or behind a verified service-mesh sidecar. With `TRACER_TLS_MODE=mtls`, each side presents and verifies certificates. The `mesh` and empty TLS modes require a sidecar that enforces mTLS. Without one, the process-to-listener connection is plaintext and an untrusted caller can spoof the tenant. Tracer enables its gRPC listener only when you set `TRACER_GRPC_PORT`.

Failure semantics are explicit. A reservation denial is a successful response, not a transport error. The `advisory` mode records the outcome and proceeds. The `enforce` mode rejects the transaction before any balance movement, regardless of `failPosture`. The `failPosture` setting applies to any reserve-call error: `closed` rejects and `open` proceeds without a reservation.

Confirm and release failures are warning-level, non-blocking operations. The Tracer TTL reaper reconciles a missed terminal transition.

## Payload-Complete Pattern

***

Tracer uses the **Payload-Complete Pattern**. Every request must carry all context required for validation. This design ensures:

| Benefit                 | Description                                                         |
| ----------------------- | ------------------------------------------------------------------- |
| **Predictable latency** | No external calls during validation; response time stays under 80ms |
| **Simplicity**          | Single request contains everything needed for decision              |
| **Reliability**         | No dependency on external services during validation                |
| **Flexibility**         | Your system controls data freshness and enrichment logic            |

### Your responsibilities

As the integrating system, you are responsible for:

1. **Enriching the payload** with account, segment, portfolio, and merchant data before calling Tracer
2. **Providing accurate context** for rule and limit evaluation. Tracer cannot fetch missing data
3. **Handling the decision** (ALLOW, DENY, or REVIEW) appropriately in your workflow
4. **Implementing retry logic** if Tracer is temporarily unavailable
5. **Managing review workflows** when Tracer returns `REVIEW`. Tracer does not include case management

<Warning>
  Tracer validates what you send. If your payload lacks context (e.g., account status, segment membership), rules that depend on that data cannot evaluate correctly. Always ensure payloads are complete before submission.
</Warning>

### Tracer's responsibilities

Tracer is responsible for:

1. **Evaluating rules** against the provided context
2. **Checking limits** against current usage
3. **Storing validation history** for investigation and reporting
4. **Returning decision** with detailed information

***

## Integration flow

***

Follow these steps to integrate your system with Tracer.

### Step 1: Prepare the transaction context

Before calling Tracer, gather all relevant data from your systems:

<Frame caption="Figure 2. Preparing transaction context">
  <img src="https://mintcdn.com/lerian-49cb71fc/SEOef3JqTInYAAau/images/en/d2/integration-flow-tracer.svg?fit=max&auto=format&n=SEOef3JqTInYAAau&q=85&s=d988019ee7d8f486fce76b423b55d941" alt="Steps for preparing the transaction context and calling Tracer, from gathering data in your systems to acting on the returned decision" width="1744" height="700" data-path="images/en/d2/integration-flow-tracer.svg" />
</Frame>

### Step 2: Call Tracer API

Send a POST request to `/v1/validations` with the complete transaction context including:

* Transaction details (type, subType, amount, asset, timestamp)
* Account information (required)
* Optional: segment, portfolio, merchant, and custom <GMetadata>metadata</GMetadata>

For complete payload structure and field details, see the [API reference](/en/reference/products/tracer/validate-transaction).

### Step 3: Handle the response

Process the decision returned by Tracer:

| Decision | Action                                                     |
| -------- | ---------------------------------------------------------- |
| `ALLOW`  | Proceed with the transaction                               |
| `DENY`   | Reject the transaction; show reason to user if appropriate |
| `REVIEW` | Queue for manual review in your review system              |

The response includes the `validationId` for correlation with validation history, details about which rules matched, and current limit usage information.

### Using metadata

Metadata allows you to pass custom fields that your rules can evaluate. Use this for context like channel, device information, customer tier, or any business-specific attributes.

<Note>
  Metadata keys must be alphanumeric with underscores only, maximum 64 characters. Maximum 50 entries per request.
</Note>

***

## Request idempotency

***

Validation requests are **idempotent** based on the `requestId` field. If you send the same `requestId` twice, Tracer returns the cached result from the first request instead of reprocessing.

| Response code | Meaning                                            |
| ------------- | -------------------------------------------------- |
| `201 Created` | New validation processed                           |
| `200 OK`      | Duplicate request detected; cached result returned |

The response body is identical in both cases. Your client should handle both status codes as success.

**Why it matters:** Network timeouts and retries can cause duplicate requests. Without idempotency, a retried request could double-count against limits or create duplicate validation records. The `requestId` ensures exactly-once processing semantics.

**Idempotency contract:**

* Same `requestId` → Same response (guaranteed)
* Different `requestId` → Independent processing (even if transaction data is identical)

<Warning>
  Always generate a unique `requestId` (UUID) for each new transaction. Reusing a `requestId` from a previous transaction will return the old result, not process the new transaction.
</Warning>

***

## Authentication

***

Tracer supports two authentication modes. You can use them independently or together.

### API key authentication

The simplest option. Send your API key in the `X-API-Key` header with every request.

| Environment variable              | Description                                                            |
| --------------------------------- | ---------------------------------------------------------------------- |
| `API_KEY_ENABLED`                 | Enable API key authentication (default: `false`)                       |
| `API_KEY`                         | The secret key value                                                   |
| `API_KEY_ENABLED_ONLY_VALIDATION` | Use API key only for the `/v1/validations` endpoint (default: `false`) |

### Plugin authentication (Access Manager)

For enterprise deployments, Tracer can delegate authentication to the [Lerian Access Manager](/en/platform/access-manager/auth-plugin). This enables centralized authentication across all Lerian services.

| Environment variable  | Description                                                |
| --------------------- | ---------------------------------------------------------- |
| `PLUGIN_AUTH_ENABLED` | Enable plugin authentication (default: `false`)            |
| `PLUGIN_AUTH_ADDRESS` | URL of the auth service (default: `http://localhost:4000`) |

### Authentication priority

When you enable both modes, Tracer uses this priority:

1. If `PLUGIN_AUTH_ENABLED=true` and the endpoint has no API-key-only flag → Plugin auth
2. If `API_KEY_ENABLED=true` or the endpoint carries the API-key-only flag → API key auth

Infrastructure endpoints (health checks, version probe, OpenAPI spec) bypass authentication and are not part of the public `/v1/*` API surface documented in this reference.

<Note>
  You can configure the `/v1/validations` endpoint for API-key-only authentication via `API_KEY_ENABLED_ONLY_VALIDATION=true`. This is useful in high-throughput scenarios where plugin auth adds unacceptable latency. **This flag is incompatible with multi-tenant mode** (`MULTI_TENANT_ENABLED=true`). The service fails to start with error code `0458`.
</Note>

### Multi-tenant authentication

When `MULTI_TENANT_ENABLED=true`, Tracer runs in multi-tenant mode and the authentication model changes:

* **Plugin auth is mandatory.** The service fails to start with error code `0457` if `PLUGIN_AUTH_ENABLED=false`.
* **Every `/v1/*` request must carry a JWT bearer token** issued by [Access Manager](/en/platform/access-manager): `Authorization: Bearer <jwt>`.
* **`tenantId` comes from the JWT claim**, not from a header, path, body, metadata, or rule scope. There is no `X-Tenant-ID` header. The tenant identifier has no effect anywhere other than the token claim.
* Each tenant operates on its own PostgreSQL database. The multi-tenancy platform service resolves the tenant-specific connection at request time.
* **Public endpoints (`/health`, `/readyz`, `/metrics`, `/version`) stay unauthenticated** in multi-tenant mode too. The bearer-token requirement applies only to `/v1/*`.

If the JWT is absent, malformed, or expired, the request returns HTTP 401 with `"code": "Unauthenticated"`. Missing API keys return the same code, with no separate TRC code.

One case is distinct. A token that **parses but carries no `sub` claim** returns HTTP 401 and error code `0474` up front. The `sub` claim is what the audit writer uses to attribute the action to a principal. Tracer fails loudly rather than recording the change against a generic system actor. Make sure your Access Manager tokens always carry it.

If the multi-tenant deployment hits its per-instance tenant cap, requests for cold tenants return HTTP 503 with error code `0466` and a `Retry-After` header. The client should back off and retry. The cap auto-resets as the LRU pool evicts cold tenants.

See [Multi-tenancy](/en/platform/multi-tenancy) for the platform-wide tenant model.

***

## Performance considerations

***

Optimize your integration for low latency and high reliability.

### Timeout budget

Tracer targets a response in under **80ms (p99)**. Configure your client timeout accordingly:

| Configuration      | Recommended value |
| ------------------ | ----------------- |
| Client timeout     | 100ms             |
| Connection timeout | 50ms              |
| Read timeout       | 100ms             |

### Retry strategy

Implement retry logic for transient failures:

```
On 5xx error or timeout:
  - Wait 10ms
  - Retry once
  - If still failing, apply fallback policy
```

<Warning>
  Do not retry on 4xx errors. These indicate invalid requests that will fail again. For retries on 5xx/timeout, reuse the same requestId to take advantage of idempotency.
</Warning>

### Fallback behavior

Decide what happens when Tracer is unavailable:

| Strategy             | When to use                                                   |
| -------------------- | ------------------------------------------------------------- |
| **Fail-open**        | Allow transaction if Tracer is down (prioritize availability) |
| **Fail-closed**      | Deny transaction if Tracer is down (prioritize security)      |
| **Queue for review** | Queue transaction for manual review                           |

Your choice depends on your risk tolerance and business requirements.

<Warning>
  **Common integration pitfalls:**

  * **"My retry created a duplicate validation."** Reuse the same `requestId` across retries. Tracer deduplicates by that field. The second call returns the cached result (HTTP 200) without creating another validation. If you generate a fresh UUID on every retry, you defeat idempotency.
  * **"My client times out after 30 seconds but Tracer keeps processing."** Tracer respects its own deadlines (\~80ms p99 target). If your client gives up on the call, Tracer still spends that work on a response. Set client timeout aggressively (100ms) and trust the retry path.
  * **"Tracer rejects my validation with error code `0421` (timestamp too old) on legitimate transactions."** Default tolerance is 24 hours. Check your server clock and the `transactionTimestamp` you're sending. If you batch-process late, set the timestamp to the actual transaction moment, not the moment you're calling Tracer.
  * **"Test transactions show up in production validation history."** Tracer records every validation, including from test/staging environments calling the same Tracer instance. Use `metadata.environment` (or similar) to tag and filter test traffic if you share Tracer between environments.
</Warning>

***

## Data freshness

***

Since you control the payload enrichment, data freshness is your responsibility. Tracer trusts the data you provide and cannot detect stale information.

| Data type            | Freshness recommendation             | Risk if stale                                     |
| -------------------- | ------------------------------------ | ------------------------------------------------- |
| Account status       | Real-time or near real-time          | Transactions on suspended accounts may be allowed |
| Segment membership   | Can be cached (changes infrequently) | Wrong limits or rules may apply                   |
| Portfolio assignment | Can be cached (changes infrequently) | Incorrect scope matching                          |
| Merchant data        | Can be cached with periodic refresh  | Risk rules may not trigger correctly              |

<Warning>
  Stale data leads to incorrect decisions. If you suspended an account but your cache shows it as active, Tracer will allow transactions that it should deny. Your enrichment layer is the source of truth for Tracer.
</Warning>

***

## Date and time format

***

All datetime fields must use **RFC3339 format** with mandatory timezone:

**Valid formats:**

```
2026-01-30T10:30:00Z           (UTC)
2026-01-30T10:30:00-03:00      (São Paulo timezone)
2026-01-30T00:00:00+00:00      (UTC explicit)
```

**Invalid formats:**

```
2026-01-30                     (date only - rejected)
2026-01-30T10:30:00            (missing timezone - rejected)
```

***

## Integration checklist

***

Before going to production, verify:

* [ ] Your API Key is in place and secure
* [ ] Each request includes a unique requestId (UUID)
* [ ] Client handles both 201 and 200 responses as success
* [ ] Your client timeout is 100ms
* [ ] Your retry logic covers 5xx errors
* [ ] You chose a fallback behavior
* [ ] Your payload carries all required fields
* [ ] Timestamps use RFC3339 format with timezone
* [ ] Asset codes are uppercase ISO 4217
* [ ] Your system handles each decision (ALLOW/DENY/REVIEW)
* [ ] Your system logs validation IDs for validation-history correlation

***

## Example integration (pseudocode)

***

```python theme={null}
def validate_transaction(transaction):
    # Step 1: Enrich payload
    payload = {
        "requestId": generate_uuid(),
        "transactionType": transaction.type,
        "amount": transaction.amount,
        "asset": transaction.asset.upper(),
        "transactionTimestamp": now_rfc3339(),
        "account": get_account_context(transaction.account_id),
        "segment": get_segment_context(transaction.segment_id),
        "merchant": get_merchant_context(transaction.merchant_id),
        "metadata": transaction.custom_fields
    }

    # Step 2: Call Tracer
    try:
        response = http_post(
            url="https://tracer.example.com/v1/validations",
            headers={"X-API-Key": API_KEY},
            json=payload,
            timeout_ms=100
        )
    except Timeout:
        return apply_fallback_policy()
    except ServerError:
        return retry_once_or_fallback()

    # Step 3: Handle decision
    if response.decision == "ALLOW":
        return proceed_with_transaction()
    elif response.decision == "DENY":
        return reject_transaction(response.reason)
    elif response.decision == "REVIEW":
        return queue_for_manual_review(response.validationId)
```

***

## Next steps

***

* **[Rules engine](./rule-engine.mdx)** - Create rules that evaluate against the context you provide
* **[Spending limits](./spending-limits.mdx)** - Configure limits that apply to your transaction scopes
* **[Validation history and compliance](./audit-compliance.mdx)** - Query validation history and use it in your compliance processes
