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

# Spending limits

> Guide to configuring and managing spending limits in Tracer for transaction control and customer protection.

**Spending limits** in Tracer allow you to control transaction amounts by scope (account, portfolio, segment) and period (daily, weekly, monthly, custom, or per-transaction). Limits are evaluated in real-time alongside rules, providing comprehensive transaction governance.

## Why use spending limits

***

* **Customer protection**: Detect overspending and return DENY decisions for unauthorized large transactions
* **Risk management**: Monitor exposure per account, segment, or portfolio
* **Flexible scoping**: Apply limits at different granularity levels
* **Real-time tracking**: Monitor usage and remaining amounts instantly
* **Automatic resets**: Daily, weekly, monthly, and custom limits reset automatically
* **Time windows**: Restrict limit enforcement to specific hours of the day
* **Custom periods**: Define date-bound limits for campaigns, promotions, or compliance requirements

By the end of this guide, you will:

* Understand limit types, time windows, and scoping options
* Create and configure spending limits with period-based controls
* Monitor limit usage in real-time
* Manage the limit lifecycle

***

## Core concepts

***

Understand the building blocks of spending limits.

### Limit types

Tracer supports five types of spending limits:

| Type              | Description                                     | Reset behavior                                        |
| ----------------- | ----------------------------------------------- | ----------------------------------------------------- |
| `DAILY`           | Maximum amount per day                          | Resets at midnight UTC                                |
| `WEEKLY`          | Maximum amount per week                         | Resets every Monday at 00:00 UTC                      |
| `MONTHLY`         | Maximum amount per month                        | Resets on 1st of month, midnight UTC                  |
| `CUSTOM`          | Maximum amount within a user-defined date range | Resets at `customEndDate` + 1 day at midnight UTC     |
| `PER_TRANSACTION` | Maximum amount per single transaction           | No tracking; each transaction evaluated independently |

### Time windows

Time windows restrict **when** a limit is enforced during the day. When a transaction occurs outside the configured time window, the limit is **skipped** (not enforced) and the transaction is allowed to proceed without counting against that limit.

* **Format**: `HH:MM` (24-hour, UTC)
* **Both fields required**: If `activeTimeStart` is set, `activeTimeEnd` must also be set (and vice versa)
* **Half-open interval**: Start is inclusive, end is exclusive `[start, end)`
* **Overnight windows supported**: Setting `activeTimeStart: "20:00"` and `activeTimeEnd: "06:00"` creates a window from 8 PM to 6 AM UTC

<Note>
  Time windows can be applied to **any** limit type (DAILY, WEEKLY, MONTHLY, CUSTOM, or PER\_TRANSACTION). If no time window is configured, the limit is active 24/7.
</Note>

**Example: PIX compliance**

A financial institution needs to enforce lower PIX transfer limits during nighttime hours (as recommended by BACEN):

* `limitType`: `DAILY`
* `maxAmount`: `"1000.00"`
* `activeTimeStart`: `"20:00"`
* `activeTimeEnd`: `"06:00"`
* Scope: PIX transactions

Transactions between 20:00 and 06:00 UTC are checked against the R\$ 1,000 limit. Transactions outside this window are not affected by this limit.

### Custom periods

Custom periods define a **date range** during which a limit is active. This is useful for campaigns, promotions, seasonal events, or compliance requirements with specific date boundaries.

* **Required fields**: `customStartDate` and `customEndDate` (only for `CUSTOM` type)
* **Half-open interval**: Start is inclusive, end is exclusive `[start, end)`
* **Maximum duration**: 5 years
* **Cannot be in the past**: The `customEndDate` must not be entirely before the current date

<Warning>
  The `customStartDate` and `customEndDate` fields are **required** for `CUSTOM` limits and **forbidden** for other limit types.
</Warning>

**Example: Black Friday campaign**

A retailer wants to set a special spending limit for the Black Friday period:

* `limitType`: `CUSTOM`
* `maxAmount`: `"100000.00"`
* `customStartDate`: `"2026-11-25T00:00:00Z"`
* `customEndDate`: `"2026-11-30T00:00:00Z"`
* Scope: CARD transactions in the retail segment

Usage accumulates across the entire 5-day period. After November 30, the limit is no longer enforced.

### Combining time windows and custom periods

Time windows and custom periods can be used together on `CUSTOM` limits. When combined, a transaction must be within **both** the custom period **and** the time window to be evaluated against the limit.

For example, a `CUSTOM` limit with `customStartDate` Nov 25 to `customEndDate` Nov 30 and a time window of `09:00` to `18:00` would only enforce the limit during business hours within the Black Friday period.

### Scopes

Scopes define which transactions a limit applies to. Unlike rules, **every limit must have at least one scope object** — limits cannot be global.

Within a single scope object, the supported fields are:

* `segmentId` - Apply to transactions from a specific segment
* `portfolioId` - Apply to transactions from a specific portfolio
* `accountId` - Apply to transactions from a specific account
* `merchantId` - Apply to transactions to a specific merchant
* `transactionType` - Apply to specific transaction types (CARD, WIRE, PIX, CRYPTO)
* `subType` - Apply to a specific transaction subtype (e.g., `debit`, `credit`)

**Matching semantics:**

* **Within one scope object:** fields combine with AND. A field that is not specified is treated as a wildcard (matches any value). At least one field must be set — empty scope objects (`{}`) are rejected with TRC-0125.
* **Across multiple scope objects on the same limit:** they combine with OR. The limit applies if **any** scope object matches the transaction.

**No hierarchy between limits.** When a transaction matches multiple limits (for example, both an account-level and a segment-level limit), Tracer checks **all** applicable limits independently in a single transaction. The transaction is denied as soon as any one of them is exceeded.

### Usage tracking

For `DAILY`, `WEEKLY`, `MONTHLY`, and `CUSTOM` limits, Tracer tracks:

* **Current usage** - Total amount consumed in the current period (as a decimal value)
* **Utilization percent** - Percentage of limit used
* **Reset time** - When the limit will reset to zero

Usage counters are retained for **90 days** after expiration for auditing purposes, then automatically cleaned up by a background worker.

***

## How limits work

***

Tracer evaluates limits during every validation request.

### Limit check flow

When a transaction is validated, Tracer checks all applicable limits:

<Frame caption="Figure 1. How spending limits work">
  <img src="https://mintcdn.com/lerian-49cb71fc/Mx6bI7Rs1ieyNuQg/images/en/docs/how-limits-works.jpg?fit=max&auto=format&n=Mx6bI7Rs1ieyNuQg&q=85&s=463909c7fb64cb6d5b549cfae7be1150" alt="" width="8180" height="1955" data-path="images/en/docs/how-limits-works.jpg" />
</Frame>

1. **Find limits** - Query all active limits matching the transaction scope
2. **Check time window** - If the limit has a time window configured, verify the transaction timestamp falls within the window. If outside, the limit is **skipped**
3. **Check custom period** - If the limit is `CUSTOM`, verify the transaction timestamp falls within the custom date range. If outside, the limit is **skipped**
4. **Calculate projected usage** - Add transaction amount to current usage
5. **Compare threshold** - Check if projected usage exceeds limit amount
6. **Return result** - If any applicable limit is exceeded — or any DENY rule matches — Tracer returns a DENY decision (your system should then block the transaction)

<Note>
  Limit checks and counter increments are **transactional**. If a transaction is denied (by limits or rules) or flagged for review, all counter increments are rolled back atomically. This prevents limit leakage from partial operations.
</Note>

<Note>
  When a limit is **skipped** during evaluation, `limitUsageDetails[i]` includes `skipped: true` and a `skipReason` field with one of two values:

  * `"outside_time_window"` — current server time is outside the limit's `activeTimeStart`/`activeTimeEnd` window
  * `"outside_custom_period"` — current server time is outside the limit's `customStartDate`/`customEndDate` range

  Skipped limits are reported for transparency but do **not** participate in the DENY decision, and their counters are **not** incremented. The window check uses **server time**, not the client-supplied `transactionTimestamp`, to prevent timestamp-manipulation attacks.
</Note>

### Example scenario

A corporate segment has a daily limit of R\$ 50,000 (`"50000.00"`) for CARD transactions.

If current usage is R$ 45,000 and a new transaction of R$ 8,000 arrives:

* Projected usage: R$ 45,000 + R$ 8,000 = R\$ 53,000
* Limit: R\$ 50,000
* Result: Tracer returns **DENY** decision (your system should block the transaction)

***

## Create a limit

***

Create limits using `POST /v1/limits`. Limits are created in `DRAFT` status by default.

A limit requires:

* **name**: A descriptive name (e.g., "Daily Corporate Card Limit")
* **limitType**: DAILY, WEEKLY, MONTHLY, CUSTOM, or PER\_TRANSACTION
* **maxAmount**: Maximum amount as a decimal value (e.g., `"50000.00"`)
* **currency**: ISO 4217 currency code (e.g., BRL, USD)
* **scopes**: At least one scope to define which transactions it applies to

Optional fields:

* **activeTimeStart**: Start of the daily time window in `HH:MM` format (e.g., `"09:00"`)
* **activeTimeEnd**: End of the daily time window in `HH:MM` format (e.g., `"17:00"`)
* **customStartDate**: Start date for `CUSTOM` limits (ISO 8601 timestamp, required for CUSTOM)
* **customEndDate**: End date for `CUSTOM` limits (ISO 8601 timestamp, required for CUSTOM)

<Note>
  Limit names must be globally unique (across all limits). Name comparison is case-insensitive and ignores extra whitespace. Attempting to create or update a limit with a duplicate name returns a `409 Conflict` response.
</Note>

For complete payload structure and field details, see the [API reference](/reference/tracer/create-limit).

***

## List and query limits

***

Query limits for management and auditing using `GET /v1/limits`.

### Query parameters

| Parameter          | Type    | Description                                                             |
| ------------------ | ------- | ----------------------------------------------------------------------- |
| `name`             | string  | Filter by name (case-insensitive partial match)                         |
| `status`           | string  | Filter by status (DRAFT, ACTIVE, INACTIVE)                              |
| `limit_type`       | string  | Filter by limit type (DAILY, WEEKLY, MONTHLY, CUSTOM, PER\_TRANSACTION) |
| `account_id`       | string  | Filter by scope: account ID                                             |
| `segment_id`       | string  | Filter by scope: segment ID                                             |
| `portfolio_id`     | string  | Filter by scope: portfolio ID                                           |
| `merchant_id`      | string  | Filter by scope: merchant ID                                            |
| `transaction_type` | string  | Filter by scope: transaction type (CARD, WIRE, PIX, CRYPTO)             |
| `sub_type`         | string  | Filter by scope: subtype (e.g., debit, credit)                          |
| `limit`            | integer | Items per page (default: 10, max: 100)                                  |
| `cursor`           | string  | Pagination cursor                                                       |
| `sort_by`          | string  | Sort field: `created_at`, `updated_at`, `name`, `max_amount`            |
| `sort_order`       | string  | Sort direction: `ASC`, `DESC` (default: DESC)                           |

### Get a specific limit

Use `GET /v1/limits/{id}` to retrieve the full limit definition including scopes and current status.

***

## Query limit usage

***

Monitor limit consumption using `GET /v1/limits/{id}/usage`.

The response includes:

* **currentUsage**: Amount consumed in current period
* **utilizationPercent**: Percentage of limit used
* **nearLimit**: True when usage exceeds 80% (for proactive management)
* **resetAt**: When the limit resets (DAILY, WEEKLY, MONTHLY, and CUSTOM only)

<Note>
  The `nearLimit` flag activates at 80% utilization, allowing proactive management before limits are exceeded.
</Note>

***

## Update a limit

***

Update limits using `PATCH /v1/limits/{id}`. The `limitType` and `currency` fields are immutable and cannot be changed after creation.

<Warning>
  Changing the limit amount does not reset current usage. If you reduce a limit below current usage, subsequent transactions will be denied until the period resets.
</Warning>

***

## Limit lifecycle

***

Limits follow the same lifecycle as rules:

<Frame caption="Figure 2. Spending limits lifecycle">
  <img src="https://mintcdn.com/lerian-49cb71fc/8leb2Q6OJnXkKkXw/images/en/docs/rules-limits-lifecycle-tracer.jpg?fit=max&auto=format&n=8leb2Q6OJnXkKkXw&q=85&s=496d7cc1759133be56df41608e96147d" alt="" width="4338" height="3688" data-path="images/en/docs/rules-limits-lifecycle-tracer.jpg" />
</Frame>

### States

| State      | Description                                                         |
| ---------- | ------------------------------------------------------------------- |
| `DRAFT`    | Limit created but not active; can be modified freely                |
| `ACTIVE`   | Limit is checked during validations                                 |
| `INACTIVE` | Limit is not checked; preserved for audit trail; can be reactivated |
| `DELETED`  | Permanently removed; does not appear in listings                    |

### Transitions

| Operation  | From            | To       | Description                                      |
| ---------- | --------------- | -------- | ------------------------------------------------ |
| Create     | -               | DRAFT    | Limits are created in DRAFT status by default    |
| Activate   | DRAFT, INACTIVE | ACTIVE   | Start checking this limit                        |
| Deactivate | ACTIVE          | INACTIVE | Stop checking this limit                         |
| Draft      | INACTIVE        | DRAFT    | Return to draft for editing                      |
| Delete     | DRAFT, INACTIVE | DELETED  | Permanently remove (cannot delete ACTIVE limits) |

***

## Best practices

***

Recommendations for effective limit management.

### Naming

* **Be descriptive** - Include the scope and type in the name
* **Use consistent patterns** - e.g., "Daily {Segment} {Type} Limit"

| Less clear  | More clear                       |
| ----------- | -------------------------------- |
| `Limit 1`   | `Daily Corporate Card Limit`     |
| `VIP limit` | `Monthly VIP PIX Limit`          |
| `BF promo`  | `Custom Black Friday Card Limit` |

### Scope design

* **Start broad, refine as needed** - Begin with segment-level limits, add account-level for exceptions
* **Avoid overlapping scopes** - Multiple limits on the same scope can cause confusion
* **Use transaction types** - Different payment methods may need different limits

### Time window design

* **Use for regulatory compliance** - BACEN nighttime PIX limits are a common use case
* **Consider timezone impact** - Time windows use UTC; account for your users' local timezone offset
* **Combine with custom periods** - Use time windows inside custom periods for precise campaign controls

### Monitoring

* **Watch nearLimit flags** - Proactively contact customers nearing limits
* **Review denied transactions** - High denial rates may indicate limits are too restrictive
* **Adjust seasonally** - Consider temporary limit increases during high-spending periods or use `CUSTOM` limits for specific date ranges

***

## Quick reference

***

Key endpoints and configuration options.

### Endpoints

| Operation        | Method | Endpoint                     |
| ---------------- | ------ | ---------------------------- |
| Create limit     | POST   | `/v1/limits`                 |
| List limits      | GET    | `/v1/limits`                 |
| Get limit        | GET    | `/v1/limits/{id}`            |
| Update limit     | PATCH  | `/v1/limits/{id}`            |
| Activate limit   | POST   | `/v1/limits/{id}/activate`   |
| Deactivate limit | POST   | `/v1/limits/{id}/deactivate` |
| Draft limit      | POST   | `/v1/limits/{id}/draft`      |
| Delete limit     | DELETE | `/v1/limits/{id}`            |
| Get usage        | GET    | `/v1/limits/{id}/usage`      |

### Limit types

| Type              | Reset                | Use case                                  |
| ----------------- | -------------------- | ----------------------------------------- |
| `DAILY`           | Midnight UTC         | Daily spending caps                       |
| `WEEKLY`          | Monday 00:00 UTC     | Weekly budget controls                    |
| `MONTHLY`         | 1st of month         | Monthly budgets                           |
| `CUSTOM`          | End of custom period | Campaigns, promotions, compliance windows |
| `PER_TRANSACTION` | None                 | Single transaction limits                 |

### Optional fields

| Field             | Type               | Applies to  | Description                       |
| ----------------- | ------------------ | ----------- | --------------------------------- |
| `activeTimeStart` | string (`HH:MM`)   | All types   | Start of daily enforcement window |
| `activeTimeEnd`   | string (`HH:MM`)   | All types   | End of daily enforcement window   |
| `customStartDate` | ISO 8601 timestamp | CUSTOM only | Start of custom period (required) |
| `customEndDate`   | ISO 8601 timestamp | CUSTOM only | End of custom period (required)   |

### Scope fields

| Field             | Type   | Description                                           |
| ----------------- | ------ | ----------------------------------------------------- |
| `segmentId`       | UUID   | Match transactions from this segment                  |
| `portfolioId`     | UUID   | Match transactions from this portfolio                |
| `accountId`       | UUID   | Match transactions from this account                  |
| `merchantId`      | UUID   | Match transactions to this merchant                   |
| `transactionType` | enum   | Match this transaction type (CARD, WIRE, PIX, CRYPTO) |
| `subType`         | string | Match this transaction subtype (max 50 chars)         |
