LimitRail Developer Console
Public API workbench for OAuth clients
API https://stg-api.limitrail.com OAuth https://stg-oauth.limitrail.com Token missing Docs IT Docs EN Swagger
Integration playbook

LimitRail configuration and integration guide

Operational documentation to configure the system and integrate the public APIs.

LimitRail - configuration and integration guide

This guide explains how to configure LimitRail and integrate it from an external system. It is written for two readers:

  • product or operations users configuring products, operations, fees and limits;
  • developers integrating the public APIs from a core system, integration platform or digital channel.

LimitRail does not authorize money movement and does not own account balances. LimitRail decides policy: it calculates fees, checks limits, reserves capacity and produces explainable evidence. The calling system remains responsible for executing the financial operation.

Jump to the operational guide: Appendix A - Operational configuration and integration playbook.

1. What LimitRail does

LimitRail is a policy, pricing and limits engine. The calling system sends:

  • external account;
  • operation code;
  • amount and currency, when required;
  • runtime metadata, when policy depends on operation-specific facts;
  • evaluation mode: preview only, reserve capacity or immediate commit.

LimitRail returns:

  • policy decision: ALLOWED, DENIED_BY_POLICY, WARNING, FEE_CALCULATED;
  • calculated fees;
  • impacted limits and counters;
  • resolved policy and provenance;
  • used, missing or unused dimensions;
  • optional reservation id to commit or cancel.
%%{init: {"theme": "base", "themeVariables": {"background": "#ffffff", "mainBkg": "#ffffff", "primaryColor": "#f8fafc", "primaryTextColor": "#0f172a", "primaryBorderColor": "#475569", "lineColor": "#1d4ed8", "textColor": "#0f172a"}}}%%
flowchart LR
    BO[LimitRail backoffice] --> CFG[Product, operations, dimensions, condition plan]
    CFG --> PUB[Published policy version]
    INT[Core / integrator system] --> API[LimitRail public API]
    PUB --> API
    API --> OUT[Decision, fees, counters, explanation]
    OUT --> INT

2. Backoffice configuration

2.1 Product

The product represents a commercial product or account type, for example a standard, business or premium account.

Field Example Why it matters
code STD_ACCOUNT Stable code used by integrations and policies.
name Account Standard Human-readable name for operators and demos.
currencyCode EUR Default pricing currency when runtime request does not specify one.
status ACTIVE Only active products should be used at runtime.
defaultConditionPlanId ... Links the product to pricing and limit rules.

Practical rule: the product code is a technical contract. Do not rename it casually after an integrator starts using it.

2.2 Operation catalog

An operation is the operational code received from the calling system. If the core sends A14, LimitRail must know A14. A separate mapping is not required when the received code is already the shared canonical code.

Field Example Runtime effect
code A14 Code sent as operationCode in the runtime call.
name International wire transfer Human-readable text for UI, audit and documentation.
familyCode TRANSFER Groups similar operations.
channelCode WIRE_TRANSFER Feeds the system dimension channel.
requiresAmount true If amount is missing, the runtime call is rejected.
requiresPricingCoverage true Readiness reports missing pricing.
requiresLimitCoverage true Readiness reports missing limit rules.

Example:

{
  "code": "A14",
  "name": "International wire transfer",
  "familyCode": "TRANSFER",
  "channelCode": "WIRE_TRANSFER",
  "requiresAmount": true,
  "requiresPricingCoverage": true,
  "requiresLimitCoverage": true
}

2.3 Policy dimensions

Policy dimensions are the governed vocabulary of values the engine may use to decide. They are not free notes and they are not generic transaction descriptions. A dimension answers a clear product question: "can this value change a fee, a limit, a counter or an audit explanation?"

Business questions that become dimensions:

  • Is the wire domestic or international?
  • Is the customer retail, business, VIP or employee?
  • Did the operation come from mobile, branch, API or batch?
  • Is the payment instant?
  • Is the customer resident or non-resident?
  • Should this value split the limit counters or only choose which rule applies?
Field Meaning
code Technical dimension name. Runtime normalizes it to lower case in the context.
displayName Human-readable name.
dataType STRING, DECIMAL, INTEGER, BOOLEAN, DATE, ENUM.
source Where the value comes from: REQUEST_METADATA, ACCOUNT_ATTRIBUTE, SYSTEM.
allowedValues Optional allowed values. Use it when the value must be controlled, for example only ITA, FRA, DEU, ESP, USA as supported countries. If XXX arrives, LimitRail rejects the request instead of applying the wrong rule.
isRuleEnabled Whether this information can make a pricing or limit rule apply or not apply. Example: if wire_country_destination = FRA, apply international fee; if customer_tier = GOLD, apply reduced fee.
canUseInCounterScopes Whether this information can become part of the limit key, meaning how LimitRail accumulates consumption. Example: a daily wire limit can be one shared container, or one container per destination country. If enabled for wire_country_destination, consumption for FRA can be counted in a different container from USA.
isSensitive Whether the value must be hidden from readable responses and logs. Example: a risk dimension can influence policy, but should not appear in usedDimensions if sensitive.

2.3.1 Reading these fields without jargon

allowedValues: make sure the integrator sends valid values

Use it when unexpected values must be rejected. For example, if wire_country_destination accepts only ITA, FRA, DEU, ESP, USA, then a request with wire_country_destination = "UNKNOWN_COUNTRY" is rejected. This prevents a transaction from being treated as a generic case when the product has no rule for that value.

isRuleEnabled: use the value to choose which rule applies

This option means: "rules are allowed to read this information". A pricing or limit rule can say:

  • apply this fee only when wire_country_destination is not ITA;
  • apply this limit only when customer_tier is BASIC;
  • do not apply this fee when employee = true;
  • block the operation when risk_segment = HIGH and amount is above a threshold.

In other words, isRuleEnabled decides whether the dimension can participate in policy logic.

Received value Configured rule Result
wire_country_destination = ITA international fee only when country is not ITA international fee not applied
wire_country_destination = FRA international fee only when country is not ITA international fee applied
customer_tier = GOLD reduced fee for GOLD customers reduced fee applied
employee = true waive fee for employees fee waived

canUseInCounterScopes: use the value to split limit consumption

This option does not decide whether a rule applies. It decides how consumption is counted.

Example 1: one limit for the operation.

  • Rule: maximum 10,000 EUR per day for A14 wires.
  • Scope: account + operation.
  • Result: all A14 wires for the same account consume the same daily limit.

Example 2: one limit per destination country.

  • Rule: maximum 10,000 EUR per day for A14, split by wire_country_destination.
  • Scope: account + operation + wire_country_destination.
  • Result: 6,000 EUR to FRA and 6,000 EUR to USA consume two different counters.

This is powerful but must be used carefully. Never use highly variable values, such as transaction id, free text description or operation reference, to split counters: they would create too many counters and hurt cost and performance.

isSensitive: use the value without showing it

A sensitive dimension can influence policy but should not be shown in readable responses. Example: risk_segment = HIGH can block or tighten a limit, but you may not want to expose it to the caller or to non-authorized operational screens.

2.4 Three kinds of dimensions

System dimensions

They are derived by runtime and must not be sent in metadata:

Dimension Value
operation Resolved operation code, for example A14.
channel Operation channel code, for example WIRE_TRANSFER.
segment Account segment code, when present.
product Account product code.
currency Request currency or account currency.

Request metadata

These values change per operation. They are sent in metadata on the runtime call or commit.

Examples:

  • wire_country_destination = "ITA": wire destination country;
  • beneficiary_bank_country = "DEU": beneficiary bank country;
  • channel_risk_score = 72: score calculated by the channel;
  • is_instant = true: instant transfer flag.

Use request metadata when the decision depends on the specific transaction.

Account attributes

These values are stable account facts. They are saved on the account contract or through account attribute APIs.

Examples:

  • customer_tier = "GOLD";
  • residency = "IT";
  • risk_segment = "LOW";
  • employee = true.

Use account attributes when the decision depends on the account profile, not on one transaction.

2.5 Example: why metadata matters

Scenario: international wires to non-domestic countries cost more and have a daily limit split by destination country.

Configuration:

  • dimension wire_country_destination, source REQUEST_METADATA, type ENUM;
  • allowed values: ITA, FRA, DEU, ESP, USA;
  • pricing rule: if wire_country_destination != "ITA", apply international fee. In plain words: destination country chooses which fee applies;
  • limit rule: custom counter perimeter with wire_country_destination. In plain words: destination country also decides where limit consumption is accumulated.

Runtime request payload:

{
  "externalAccountRef": "000100000203",
  "operationCode": "A14",
  "amount": 2500.00,
  "currencyCode": "EUR",
  "externalOperationRef": "wire-2026-000001",
  "occurredAtUtc": "2026-06-13T10:15:00Z",
  "metadata": {
    "wire_country_destination": "FRA"
  },
  "mode": "PREVIEW"
}

Engine effect:

  • operation, channel, product, currency are added automatically;
  • wire_country_destination is validated against the registry;
  • rule conditions reading wire_country_destination can activate or exclude pricing and limits;
  • if the dimension is part of the counter perimeter, the counter is not only account + operation, but also wire_country_destination;
  • the response includes the dimension in usedDimensions if it is not sensitive.

2.6 Payment account limit and fee scenarios

This section uses generic scenarios to show how product requirements become LimitRail configuration. It does not copy a specific operating catalog. The point is to clarify which facts should become operations, which facts should become dimensions, and which rules the engine can enforce directly.

2.6.1 What is native and what requires external data

Product requirement LimitRail fit Configuration approach
Fixed fee for an operation, for example 0.35 EUR on a specific service Native today Pricing rule FIXED on the operation code.
Free operation Native today Pricing rule WAIVER, or no pricing rule if you do not need to show a zero fee. Use WAIVER when the inclusion must be explicit.
Monthly transaction volume limit, for example 2,400 EUR per account Native today Blocking limit rule with AMOUNT, MONTHLY, and counter scope ACCOUNT_GLOBAL, ACCOUNT_CHANNEL or ACCOUNT_OPERATION depending on the intended perimeter.
Annual included operation count, then fee Native today when the allowance is per operation code Pricing rule TIERED, metric COUNT, period YEARLY, with a zero-fee first tier and a paid catch-all tier.
Annual allowance shared across multiple operation codes Works when modeled with one canonical operation code Today tiered pricing accumulates by account + operation. If one allowance must be shared by multiple variants, use one operation code and a dimension to distinguish the detail. If you keep separate codes, the allowance remains separate per code.
Fee based on the single operation amount, for example free up to 35 EUR and then 0.30 EUR Works when the caller sends an amount band as metadata Rule conditions read governed dimensions, not the raw amount field directly. Configure an amount_band dimension and let the caller send UP_TO_35 or ABOVE_35.
Monthly fee based on average balance Works only if the average balance is supplied externally LimitRail does not calculate average balances. The accounting system calculates average_monthly_balance and sends it as monthly-fee metadata or stores it as an account attribute.
Maximum account balance, for example 3,600 EUR Works only if the caller supplies the projected balance LimitRail does not own balances. The caller must send projected_balance_after_operation; LimitRail can block when that value exceeds the threshold.
Temporary single-operation exception Works as an explicit policy input, not as the full approval process The caller can send limit_exception_granted = true or use a temporary account override. The approval workflow remains in the operational system that owns roles and permissions.

2.6.2 Suggested product and operation catalog

Example product:

{
  "code": "RETAIL_EUR_ACCOUNT",
  "name": "Retail EUR Account",
  "currencyCode": "EUR",
  "status": "ACTIVE"
}

Neutral operation catalog example:

Operation code Functional name Direction Channel Amount required Why it exists
ACCOUNT_OPENING Account opening NEUTRAL ACCOUNT_SERVICE No Use it when opening fees must be explicit, even when zero.
MONTHLY_MAINTENANCE Monthly account maintenance DEBIT ACCOUNT_SERVICE No Scheduled operation used by the caller to calculate a recurring account fee.
INCOMING_STANDARD_TRANSFER Incoming standard transfer CREDIT TRANSFER Yes Can consume funding capacity or check maximum balance posture.
INCOMING_INSTANT_TRANSFER Incoming instant transfer CREDIT TRANSFER Yes Same incoming-transfer domain, but separately identifiable for pricing or audit.
OUTGOING_STANDARD_TRANSFER Outgoing standard transfer DEBIT TRANSFER Yes Can consume monthly limits and annual included allowance.
OUTGOING_INSTANT_TRANSFER Outgoing instant transfer DEBIT TRANSFER Yes May have a different fee or allowance from the standard transfer.
INTERNATIONAL_TRANSFER External-area transfer DEBIT TRANSFER Yes Typical fixed-fee or stricter-limit use case.
DIRECT_DEBIT_COLLECTION Direct debit collection DEBIT DIRECT_DEBIT Yes Can be free while still consuming global limits.
DIRECT_DEBIT_REVOCATION Direct debit revocation DEBIT DIRECT_DEBIT No Can carry a fixed operational fee.
DIRECT_DEBIT_CHARGEBACK Direct debit chargeback DEBIT DIRECT_DEBIT No Can carry a higher fixed operational fee.
P2P_PAYMENT Person-to-person payment DEBIT DIGITAL_PAYMENT Yes Can be free below an amount threshold and paid above it.
MERCHANT_PAYMENT Merchant payment DEBIT DIGITAL_PAYMENT Yes Usually free on the customer side.
PUBLIC_SERVICE_PAYMENT Public service payment DEBIT DIGITAL_PAYMENT Yes Can carry a fixed service fee.
TRANSFER_REVERSAL Transfer reversal or refund DEBIT EXCEPTION No Operational fee for exception handling.

2.6.3 Dimensions to configure and why

These dimensions cover real fee and limit scenarios without hiding policy logic inside the external integrator.

Dimension Source Type Values Recommended configuration Practical use
transfer_speed REQUEST_METADATA ENUM STANDARD, INSTANT isRuleEnabled = true, canUseInCounterScopes = false Use it when one operation code represents multiple variants and you need different fees for standard and instant transfers. It should not split counters if the limit is shared across all transfers.
destination_area REQUEST_METADATA ENUM DOMESTIC, REGIONAL, INTERNATIONAL isRuleEnabled = true, canUseInCounterScopes = true only when limits must be separate by area Use it to charge external-area transfers differently or to keep separate limits by destination area.
amount_band REQUEST_METADATA ENUM UP_TO_35, ABOVE_35 isRuleEnabled = true, canUseInCounterScopes = false Use it for rules like "free up to 35 EUR, then 0.30 EUR". The caller derives the band from the amount and LimitRail uses it to select the fee.
projected_balance_after_operation REQUEST_METADATA DECIMAL No fixed list isRuleEnabled = true, canUseInCounterScopes = false, isStoredInRuntimeLog = true when audit evidence is needed Use it to block incoming operations that would take the account above its maximum balance. The value must come from the system that owns balances.
average_monthly_balance REQUEST_METADATA or ACCOUNT_ATTRIBUTE DECIMAL No fixed list isRuleEnabled = true, canUseInCounterScopes = false Use it to decide whether to apply a monthly maintenance fee. If it changes every month, send it as metadata on MONTHLY_MAINTENANCE.
customer_profile ACCOUNT_ATTRIBUTE ENUM STANDARD, PREMIUM, STAFF, RESTRICTED isRuleEnabled = true, canUseInCounterScopes = false Use it for commercial or operational conditions tied to the account profile. Example: staff fee waiver, restricted profile with lower limits.
limit_exception_granted REQUEST_METADATA BOOLEAN true/false isRuleEnabled = true, canUseInCounterScopes = false, isSensitive = true if it should not be shown Use it when an operational system has approved a single-operation exception. LimitRail reads the flag and applies a different rule path.

Important: enable canUseInCounterScopes only when the value should truly split counters. For destination_area, it may make sense: 1,000 EUR to a domestic area and 1,000 EUR to an international area can consume different containers. For amount_band, it usually does not: splitting counters by amount band makes the limit harder to explain and rarely matches the commercial behavior.

2.6.4 Example: monthly transaction volume of 2,400 EUR

Goal: each account can move at most 2,400 EUR per month across monitored operations.

Configuration:

  • create limit rule LIM_MONTHLY_VOLUME_2400;
  • MetricType = AMOUNT;
  • PeriodType = MONTHLY;
  • LimitAmount = 2400.00;
  • IsBlocking = true;
  • CounterScopeMode = ACCOUNT_GLOBAL when the limit is unique per account;
  • OperationScope = GLOBAL when all policy-evaluated operations should contribute;
  • WindowTimeZone = Europe/Rome if the month must follow the Italian calendar.

Effect: if the account has already consumed 2,180 EUR this month and a 300 EUR operation arrives, the engine projects 2,480 EUR and returns DENIED_BY_POLICY with rule code LIM_MONTHLY_VOLUME_2400.

2.6.5 Example: 9 transfers included per year, then 0.35 EUR

Goal: the first 9 yearly transfers are included; from the tenth transfer onward the customer pays 0.35 EUR per operation.

Native configuration when the allowance is per operation code:

  • pricing rule FEE_OUT_TRANSFER_YEARLY_TIER;
  • CalculationMethod = TIERED;
  • MetricType = COUNT;
  • PeriodType = YEARLY;
  • OperationScope = OPERATION_CODE;
  • OperationCode = OUTGOING_STANDARD_TRANSFER;
  • tiers:
[
  { "upTo": 9, "amount": 0.00 },
  { "upTo": null, "amount": 0.35 }
]

How to read it: while the yearly counter for OUTGOING_STANDARD_TRANSFER is below 9, the fee is zero. On the next transfer, the fee becomes 0.35 EUR.

Attention: today tiered pricing accumulates by account + operation. If one allowance must be shared by OUTGOING_STANDARD_TRANSFER and OUTGOING_INSTANT_TRANSFER, use one of these approaches:

  • model one canonical operation code OUTGOING_TRANSFER and distinguish standard versus instant with the transfer_speed dimension;
  • or accept separate allowances, one per operation code.

2.6.6 Example: person-to-person payment free up to 35 EUR

Goal: person-to-person payment is free up to 35 EUR; above 35 EUR it costs 0.30 EUR.

Because rule conditions read governed dimensions, configure this dimension:

{
  "code": "amount_band",
  "displayName": "Amount band",
  "dataType": "ENUM",
  "source": "REQUEST_METADATA",
  "allowedValues": ["UP_TO_35", "ABOVE_35"],
  "isRuleEnabled": true
}

The caller sends:

API request - preview above threshold:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "P2P_PAYMENT",
  "amount": 48.00,
  "currencyCode": "EUR",
  "metadata": {
    "amount_band": "ABOVE_35"
  },
  "mode": "PREVIEW"
}

API response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_P2P_ABOVE_35",
  "originalAmount": 48.00,
  "totalFees": 0.30,
  "currencyCode": "EUR",
  "usedDimensions": {
    "amount_band": "ABOVE_35",
    "operation": "P2P_PAYMENT"
  }
}

Pricing:

  • WAIVER rule when amount_band = UP_TO_35;
  • FIXED 0.30 EUR rule when amount_band = ABOVE_35.

This is explicit and auditable: LimitRail does not guess the band, but it can explain which band it received and which fee it applied.

2.6.7 Example: monthly maintenance fee based on average balance

Goal: monthly fee is free when the average monthly balance is at least 220 EUR; otherwise it is 0.70 EUR.

LimitRail does not calculate the average balance. The balance-owning system must calculate it and call LimitRail with a scheduled operation:

API request - periodic fee:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "MONTHLY_MAINTENANCE",
  "amount": 0,
  "currencyCode": "EUR",
  "metadata": {
    "average_monthly_balance": 186.40
  },
  "mode": "PREVIEW"
}

API response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_MONTHLY_MAINTENANCE_LOW_BALANCE",
  "totalFees": 0.70,
  "currencyCode": "EUR",
  "usedDimensions": {
    "average_monthly_balance": 186.40,
    "operation": "MONTHLY_MAINTENANCE"
  }
}

Configuration:

  • dimension average_monthly_balance, type DECIMAL, source REQUEST_METADATA;
  • pricing rule FIXED 0.70 EUR;
  • condition: average_monthly_balance < 220.

If the value is 186.40, the fee applies. If the value is 220 or higher, the rule does not match and no maintenance fee is charged.

2.6.8 Example: maximum account balance

Goal: prevent an incoming credit from taking the account balance above 3,600 EUR.

LimitRail does not own the booked balance. The caller must send the projected balance after the operation:

{
  "externalAccountRef": "acc_000203",
  "operationCode": "INCOMING_STANDARD_TRANSFER",
  "amount": 280.00,
  "currencyCode": "EUR",
  "metadata": {
    "projected_balance_after_operation": 3650.00
  },
  "mode": "PREVIEW"
}

Possible configuration:

  • dimension projected_balance_after_operation, type DECIMAL, source REQUEST_METADATA;
  • limit rule LIM_MAX_BALANCE_3600;
  • MetricType = AMOUNT;
  • PeriodType = SINGLE;
  • LimitAmount = 0;
  • IsBlocking = true;
  • condition: projected_balance_after_operation > 3600.

How to read it: the rule is not using historical counters; it is using the balance value calculated by the balance-owning system. When projected balance exceeds 3,600 EUR, any positive incoming amount is denied by policy. For tolerances or exceptions, add limit_exception_granted and a more permissive rule path.

2.6.9 Example: temporary exception

Goal: allow a single operation even though it breaches the standard policy, because an authorized operator granted an exception.

Configuration:

  • dimension limit_exception_granted, type BOOLEAN, source REQUEST_METADATA;
  • isRuleEnabled = true;
  • isSensitive = true if it should not be exposed in readable responses;
  • standard blocking rule without exception;
  • alternative rule path, or no block, when limit_exception_granted = true.

Runtime request payload:

{
  "externalAccountRef": "acc_000203",
  "operationCode": "INCOMING_STANDARD_TRANSFER",
  "amount": 280.00,
  "currencyCode": "EUR",
  "metadata": {
    "projected_balance_after_operation": 3650.00,
    "limit_exception_granted": true
  },
  "mode": "RESERVE_CAPACITY",
  "idempotencyKey": "reserve-acc_000203-20260613-001"
}

LimitRail can record that the decision was evaluated with an exception, but it does not replace the approval workflow. Who approved the exception, with which permissions and for how long, remains owned by the upstream operational system.

3. Condition plan and versions

The condition plan is the rule package. A version can be:

  • DRAFT: editable;
  • PUBLISHED: used by runtime and read-only;
  • ARCHIVED: historical.

Public runtime APIs do not accept version override. The public runtime call always resolves the correct published version for the account, segment or product. Draft comparison belongs to the admin simulation surface, not /v1/policy/evaluate.

3.1 Policy resolution order

When a runtime policy request arrives, LimitRail resolves policy in this order:

  1. account-specific override;
  2. segment-specific policy;
  3. product default policy.

This means:

  • an account-specific override is an exception for one account, for example a negotiated limit or a temporary commercial condition;
  • a segment-specific policy is used only when a group of accounts inside the same product must follow different published rules, for example student, premium, employee, non-resident, or migration accounts;
  • the product default policy is the normal setup and is used when no account override and no segment-specific policy exist.

Segment is optional. Do not create segments just because the field exists. Use a segment only when the business can explain why that group must have different fees, limits, or operational constraints from the product default.

If no published condition plan version is found, rules cannot be applied and the reason code reports missing policy.

4. Account onboarding

4.1 Account contract

An account contract links an external account to a LimitRail product.

Recommended payload:

{
  "externalAccountRef": "000100000203",
  "productId": "865e0567-7927-4680-a2f5-e4c71921b7bf",
  "segmentCode": "PRIVATE",
  "openedAtUtc": "2026-06-13T09:00:00Z",
  "attributes": {
    "customer_tier": "GOLD",
    "residency": "IT"
  }
}

Notes:

  • externalAccountRef must be opaque and stable;
  • do not use IBAN, name, email, tax id or personal data;
  • attributes are validated against dimensions with source ACCOUNT_ATTRIBUTE;
  • account insert and attributes should be atomic.

4.2 Segment

Segment is optional. It is a business grouping used by policy resolution, not a mandatory customer classification field.

Use it when the same product has more than one published policy because a group of accounts needs different rules. Typical examples:

  • PRIVATE;
  • BUSINESS;
  • VIP;
  • NON_RESIDENT;
  • EMPLOYEE.

For example, if PAYMENT_ACCOUNT has one standard policy and one employee policy, accounts with segmentCode = EMPLOYEE can resolve to the employee condition plan version. Accounts without a segment, or with a segment that has no assignment, fall back to the product default.

If no segment policies exist, leave segmentCode empty. Do not invent segments just to fill the field.

4.3 Account override

An account override assigns a published policy to one specific account. It is useful for:

  • corporate agreements;
  • negotiated conditions;
  • temporary commercial exceptions;
  • customers with special limits.

If no override exists, runtime uses the segment-specific policy when available; otherwise it uses the product default policy.

5. OAuth client and scopes

Public integration uses OAuth2/OIDC with client_credentials.

Token request:

POST /connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
client_id=lr_...
client_secret=...
scope=limitrail.catalog.read limitrail.accounts.read limitrail.accounts.write limitrail.evaluate limitrail.usage.write

Scopes:

Scope Purpose
limitrail.catalog.read Discover products, operations and dimensions.
limitrail.accounts.read Read accounts, resolved pricing and counters.
limitrail.accounts.write Register accounts and attributes.
limitrail.evaluate Preview, reserve or direct commit.
limitrail.usage.write Commit, cancel reservation, reverse usage.
limitrail.audit.read Read runtime audit when logging is enabled.

The token expires. The client must renew it before expiry and must not store it as a permanent credential.

Recommended technical flow

>Core: access_token
Core->>API: GET /v1/products
Core->>API: GET /v1/operations
Core->>API: POST /v1/account-contracts
Core->>API: POST /v1/policy/evaluate
Core->>API: POST /v1/usage/commit
-->

### Step 1 - Catalog discovery

The integrator reads the catalog to verify codes and dimensions:

```http
GET /v1/products?take=25
GET /v1/operations?take=25
GET /v1/policy-dimensions

Practical use:

  • verify that STD_ACCOUNT exists;
  • verify that A14 is active;
  • discover which metadata is accepted and where it must come from;
  • build client-side validation without hardcoding everything.

Step 2 - Account onboarding

For every existing or new account:

POST /v1/account-contracts
Authorization: Bearer <token>
Content-Type: application/json

If accounts are in the millions, load them in batches. The UI should search by prefix and load by cursor/page, not count everything.

Step 3 - Runtime preview before execution

Preview writes no counters and creates no reservation. Use it to show fees, verify policy or run a pre-check.

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "000100000203",
  "operationCode": "A14",
  "amount": 2500.00,
  "currencyCode": "EUR",
  "externalOperationRef": "wire-2026-000001",
  "metadata": {
    "wire_country_destination": "FRA"
  },
  "mode": "PREVIEW"
}

Typical response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_A14_1",
  "originalAmount": 2500.00,
  "totalFees": 0.40,
  "currencyCode": "EUR",
  "conditionPlanCode": "STD_ACCOUNT_PLAN",
  "conditionPlanVersionNumber": 1,
  "fees": [
    {
      "ruleCode": "FEE_A14_1",
      "amount": 0.40,
      "currencyCode": "EUR"
    }
  ],
  "usedDimensions": {
    "operation": "A14",
    "channel": "WIRE_TRANSFER",
    "product": "STD_ACCOUNT",
    "currency": "EUR",
    "wire_country_destination": "FRA"
  },
  "missingDimensions": [],
  "unusedDimensions": []
}

Step 4 - Reserve capacity

Reserve creates a temporary limit capacity reservation. Use it when the operation can fail after the initial policy check.

{
  "externalAccountRef": "000100000203",
  "operationCode": "A14",
  "amount": 2500.00,
  "currencyCode": "EUR",
  "externalOperationRef": "wire-2026-000001",
  "metadata": {
    "wire_country_destination": "FRA"
  },
  "mode": "RESERVE_CAPACITY",
  "idempotencyKey": "reserve-wire-2026-000001"
}

If the decision is allowed, the response contains:

  • capacityReservationId;
  • capacityReservationExpiresAtUtc;
  • calculated fees and limit impacts.

Step 5 - Commit usage

Commit confirms counter usage. It can happen in two ways:

  1. commit an existing reservation;
  2. direct commit through POST /v1/policy/evaluate with mode = COMMIT_USAGE.

Reservation commit:

{
  "capacityReservationId": "2e337d1a-0b51-4fd5-a90a-4dcc7a47fd50",
  "idempotencyKey": "commit-wire-2026-000001",
  "externalAccountRef": "000100000203",
  "operationCode": "A14",
  "amount": 2500.00,
  "currencyCode": "EUR",
  "externalOperationRef": "wire-2026-000001",
  "metadata": {
    "wire_country_destination": "FRA"
  }
}

The reservation must match account, operation, amount, currency and external operation reference. If it does not match, runtime rejects the commit.

Step 6 - Cancel reservation

Cancel is used when a reservation should not become committed usage:

{
  "capacityReservationId": "2e337d1a-0b51-4fd5-a90a-4dcc7a47fd50",
  "reason": "Operation expired before execution"
}

Step 7 - Reverse usage

Reverse compensates already committed usage. It is not the same as cancel:

  • cancel = uncommitted reservation;
  • reverse = already applied usage event.
{
  "reversalIdempotencyKey": "reverse-wire-2026-000001",
  "reason": "Transfer cancelled after settlement rejection",
  "reversedAtUtc": "2026-06-13T11:20:00Z"
}

7. How to read a denied decision

DENIED_BY_POLICY means LimitRail found a blocking limit rule that would be exceeded. reasonCode contains the rule code that blocked the request.

Useful fields:

Field How to read it
decision Policy outcome. It is not final financial authorization.
reasonCode ALLOWED, NO_CONDITION_PLAN or the rule code that produced the outcome.
ruleResults Details of evaluated rules.
limitImpacts Counter scopes that would be impacted.
usedDimensions Dimensions that entered the context and are not sensitive.
missingDimensions Dimensions referenced by rules or scopes but missing from context.
unusedDimensions Metadata sent by the caller but not referenced by rules or scopes.

Example: if reasonCode = "LIM_A14_1" and ruleResults shows projected amount over the threshold, the correct explanation is: "policy denied the request because rule LIM_A14_1 would be exceeded".

8. Counter scope: why it matters

Counter scope is the counter perimeter: it decides "where" the consumption of an operation is accumulated. This is one of the most important product concepts, because the same limit can mean very different things.

Simple example:

  • a customer sends three A14 wires of 1,000 EUR each;
  • LimitRail must decide whether those 3,000 EUR consume one global account limit, only the wire-transfer limit, the whole wire channel limit, or a separate limit by destination country.

This choice is not technical only: it is product behavior. It defines how the product controls customer activity.

Mode Aggregation
ACCOUNT_OPERATION account + operation. Example: daily total for A14 on the account.
ACCOUNT_CHANNEL account + channel. Example: total WIRE_TRANSFER channel.
ACCOUNT_GLOBAL account only. Example: global daily limit.
CUSTOM_DIMENSIONS account + configured dimensions. Example: account + operation + destination country.

8.1 Business examples

Global daily account limit

Goal: the customer cannot consume more than 20,000 EUR per day across all monitored operations.

Configuration:

  • mode: ACCOUNT_GLOBAL;
  • period: DAILY;
  • metric: AMOUNT;
  • threshold: 20,000 EUR.

Result: wires, payments and other operations consume the same daily account container.

Limit by operation type

Goal: the customer can perform at most 10,000 EUR per day of international wires A14.

Configuration:

  • mode: ACCOUNT_OPERATION;
  • operation: A14;
  • period: DAILY;
  • threshold: 10,000 EUR.

Result: only A14 operations consume that counter. Other operations have separate counters.

Limit by channel

Goal: limit the whole WIRE_TRANSFER channel, regardless of the single operation code.

Configuration:

  • mode: ACCOUNT_CHANNEL;
  • channel: WIRE_TRANSFER;
  • period: DAILY;
  • threshold: 15,000 EUR.

Result: multiple operation codes in the same channel consume the same limit.

Limit by destination country

Goal: track wires to different countries separately.

Configuration:

  • mode: CUSTOM_DIMENSIONS;
  • dimension: wire_country_destination;
  • period: DAILY;
  • threshold: 10,000 EUR per country.

Result: consumption to FRA is not summed with consumption to USA. Each country has its own counter for that account.

If a dimension is used in the counter perimeter, it must be present in the request or configured with includeWhenMissing. Be careful with high-cardinality dimensions: using transaction id, operation reference or free text description as a scope would create too many counters and hurt cost and performance.

9. Audit and runtime logging

Audit & Explain shows only saved evaluations. If logging mode is NONE, the page remains empty.

Logging mode:

  • NONE: no evaluation is logged;
  • DENIED_ONLY: only denied decisions are logged;
  • SAMPLED: a sample is logged;
  • FULL: everything is logged, useful for demos or diagnosis, expensive in production.

Audit queries always require From and To. This avoids scans on large runtime tables.

10. Practical rules for integrators

  • Always use uppercase enums: ACTIVE, PREVIEW, RESERVE_CAPACITY, COMMIT_USAGE, DAILY, MONTHLY.
  • Do not send personal data in externalAccountRef.
  • Send only metadata registered in the dimension registry.
  • Use idempotencyKey for every state-writing call.
  • Do not reuse the same idempotency key for different payloads.
  • Renew tokens before expiry.
  • Treat 400, 401, 403, 404, 409, 422 as functional errors.
  • Use prefix search and cursor/load-more for large lists.

Appendix A - Operational configuration and integration playbook

This playbook is a prescriptive checklist. It starts with the simplest cases and moves toward more advanced combinations. Each scenario states:

  • where to configure it in the backoffice;
  • which field to set;
  • which value to use;
  • what effect it has in the engine;
  • which API call the calling system must make.

A.1 Minimum product setup

Goal: have an active product with operations, a published policy and OAuth credentials.

Step Where to go What to set Example value Effect
1 Product Workspace Product code RETAIL_EUR_ACCOUNT Stable code the integrator discovers through /v1/products.
2 Product Workspace currencyCode EUR Product base currency.
3 Operation Catalog Operation code OUTGOING_TRANSFER Code the caller sends in operationCode.
4 Operation Catalog direction DEBIT Tells runtime that the operation consumes capacity.
5 Operation Catalog channelCode TRANSFER Enables channel-level limits and fees.
6 Operation Catalog requiresAmount true Runtime calls are rejected when amount is missing.
7 Condition plans Create condition plan RETAIL_EUR_PLAN Container for pricing and limit rules.
8 Policy Studio Publish version PUBLISHED Only a published version is used by runtime APIs.
9 Product Workspace Link plan to product defaultConditionPlanId Runtime resolves the policy from the product when no override exists.
10 Developer access Create OAuth client required scopes Caller can obtain tokens and call public APIs.

Minimum calls from the calling system:

POST /connect/token
GET /v1/products?take=25
GET /v1/operations?take=25
GET /v1/policy-dimensions
POST /v1/account-contracts
POST /v1/policy/evaluate

Token request:

POST /connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
client_id=lr_...
client_secret=...
scope=limitrail.catalog.read limitrail.accounts.read limitrail.accounts.write limitrail.evaluate limitrail.usage.write

Token response:

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "limitrail.catalog.read limitrail.accounts.read limitrail.accounts.write limitrail.evaluate limitrail.usage.write"
}

Essential discovery response:

[
  {
    "id": "865e0567-7927-4680-a2f5-e4c71921b7bf",
    "externalCoreProductRef": null,
    "code": "RETAIL_EUR_ACCOUNT",
    "name": "Retail EUR Account",
    "status": "ACTIVE",
    "currencyCode": "EUR"
  }
]

A.2 Explicitly free operation

Use this when you want to prove that an operation is included in the product, not merely missing a rule.

Where to go Field Value
Operation Catalog code MERCHANT_PAYMENT
Operation Catalog direction DEBIT
Operation Catalog requiresAmount true
Policy Studio > Pricing CalculationMethod WAIVER
Policy Studio > Pricing OperationScope OPERATION_CODE
Policy Studio > Pricing OperationCode MERCHANT_PAYMENT

Effect: the response can show a zero fee and make clear that the free operation is a product decision.

API request - fee preview:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "MERCHANT_PAYMENT",
  "amount": 42.00,
  "currencyCode": "EUR",
  "mode": "PREVIEW"
}

API response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_MERCHANT_PAYMENT_INCLUDED",
  "originalAmount": 42.00,
  "totalFees": 0.00,
  "currencyCode": "EUR",
  "fees": [
    {
      "code": "FEE_MERCHANT_PAYMENT_INCLUDED",
      "amount": 0.00,
      "currencyCode": "EUR",
      "description": "Merchant payment included"
    }
  ],
  "usedDimensions": {
    "operation": "MERCHANT_PAYMENT",
    "channel": "DIGITAL_PAYMENT",
    "product": "RETAIL_EUR_ACCOUNT",
    "currency": "EUR"
  }
}

A.3 Fixed fee per operation

Use this when every execution of the operation costs the same amount.

Where to go Field Value
Operation Catalog code PUBLIC_SERVICE_PAYMENT
Policy Studio > Pricing CalculationMethod FIXED
Policy Studio > Pricing FixedAmount 0.35
Policy Studio > Pricing CurrencyCode EUR
Policy Studio > Pricing AppliesWhen ON_EVALUATE

Effect: each runtime request for PUBLIC_SERVICE_PAYMENT returns a 0.35 EUR fee when the policy resolves and no condition excludes the rule.

API request - fee preview:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "PUBLIC_SERVICE_PAYMENT",
  "amount": 88.00,
  "currencyCode": "EUR",
  "mode": "PREVIEW"
}

API response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_PUBLIC_SERVICE_PAYMENT",
  "originalAmount": 88.00,
  "totalFees": 0.35,
  "currencyCode": "EUR",
  "fees": [
    {
      "code": "FEE_PUBLIC_SERVICE_PAYMENT",
      "amount": 0.35,
      "currencyCode": "EUR",
      "description": "Public service payment fee"
    }
  ]
}

A.4 Percentage fee with minimum and maximum

Use this for fees proportional to the amount, with commercial guardrails.

Where to go Field Value
Policy Studio > Pricing CalculationMethod PERCENTAGE
Policy Studio > Pricing PercentageRate 0.0025
Policy Studio > Pricing MinAmount 0.20
Policy Studio > Pricing MaxAmount 4.50
Policy Studio > Pricing FeeBase OPERATION_AMOUNT

Effect: on 800 EUR the theoretical fee is 2.00 EUR. The minimum avoids very small fees; the maximum caps high-value transactions.

A.5 Monthly volume limit per account

Use this when the product has a monthly movement cap.

Where to go Field Value
Policy Studio > Limits Code LIM_MONTHLY_VOLUME_2400
Policy Studio > Limits MetricType AMOUNT
Policy Studio > Limits PeriodType MONTHLY
Policy Studio > Limits LimitAmount 2400.00
Policy Studio > Limits CounterScopeMode ACCOUNT_GLOBAL
Policy Studio > Limits OperationScope GLOBAL
Policy Studio > Limits IsBlocking true

Effect: all operations evaluated by the policy consume the same monthly account container. If the new amount takes the total above 2,400 EUR, decision becomes DENIED_BY_POLICY.

API request - reserve capacity:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "OUTGOING_TRANSFER",
  "amount": 300.00,
  "currencyCode": "EUR",
  "externalOperationRef": "op-20260613-0001",
  "mode": "RESERVE_CAPACITY",
  "idempotencyKey": "reserve-op-20260613-0001"
}

API response when capacity is reserved:

{
  "decision": "ALLOWED",
  "reasonCode": "ALLOWED",
  "capacityReservationId": "2e337d1a-0b51-4fd5-a90a-4dcc7a47fd50",
  "capacityReservationExpiresAtUtc": "2026-06-13T10:20:00Z",
  "limitImpacts": [
    {
      "metricType": "AMOUNT",
      "periodType": "MONTHLY",
      "projectedAmount": 300.00
    }
  ]
}

API request - commit after the operation is executed:

POST /v1/usage/commit
Authorization: Bearer <token>
Content-Type: application/json

{
  "capacityReservationId": "2e337d1a-0b51-4fd5-a90a-4dcc7a47fd50",
  "externalAccountRef": "acc_000203",
  "operationCode": "OUTGOING_TRANSFER",
  "amount": 300.00,
  "currencyCode": "EUR",
  "externalOperationRef": "op-20260613-0001",
  "idempotencyKey": "commit-op-20260613-0001"
}

API response:

{
  "usageEventId": "7c62f3dd-4ef0-48d0-a95c-9b7f8c0f2a10",
  "status": "APPLIED"
}

A.6 Limit by operation code

Use this when only one operation family must be limited without affecting other operations.

Where to go Field Value
Policy Studio > Limits OperationScope OPERATION_CODE
Policy Studio > Limits OperationCode OUTGOING_TRANSFER
Policy Studio > Limits MetricType AMOUNT
Policy Studio > Limits PeriodType DAILY
Policy Studio > Limits LimitAmount 900.00
Policy Studio > Limits CounterScopeMode ACCOUNT_OPERATION

Effect: only OUTGOING_TRANSFER consumes this daily limit. A merchant payment can have a different limit or no specific limit.

A.7 Limit by channel

Use this when multiple operation codes must consume the same limit, for example all transfer-channel operations.

Where to go Field Value
Operation Catalog channelCode TRANSFER on all related operations
Policy Studio > Limits OperationScope OPERATION_CHANNEL
Policy Studio > Limits ChannelCode TRANSFER
Policy Studio > Limits MetricType AMOUNT
Policy Studio > Limits PeriodType DAILY
Policy Studio > Limits LimitAmount 1200.00
Policy Studio > Limits CounterScopeMode ACCOUNT_CHANNEL

Effect: OUTGOING_TRANSFER and OUTGOING_INSTANT_TRANSFER, when both have channelCode = TRANSFER, consume the same daily container.

A.8 Included operations, then fee

Use this for an included allowance: first N operations free, then fixed fee.

Where to go Field Value
Policy Studio > Pricing CalculationMethod TIERED
Policy Studio > Pricing MetricType COUNT
Policy Studio > Pricing PeriodType YEARLY
Policy Studio > Pricing OperationCode OUTGOING_TRANSFER
Policy Studio > Pricing Tiers[0].UpTo 9
Policy Studio > Pricing Tiers[0].Amount 0.00
Policy Studio > Pricing Tiers[1].UpTo empty
Policy Studio > Pricing Tiers[1].Amount 0.35

Effect: while the account yearly counter for OUTGOING_TRANSFER is below 9, the fee is zero. On the next transfer, the fee is 0.35 EUR.

Modeling note: tiered pricing today counts by account + operation. If you want one allowance for standard and instant variants, configure one operation code OUTGOING_TRANSFER and use transfer_speed as a dimension.

A.9 Standard / instant variant on the same operation code

Use this when you want a shared allowance but still need to distinguish standard from instant behavior.

Where to go Field Value
Dimension Registry code transfer_speed
Dimension Registry dataType ENUM
Dimension Registry source REQUEST_METADATA
Dimension Registry allowedValues STANDARD, INSTANT
Dimension Registry isRuleEnabled true
Dimension Registry canUseInCounterScopes false
Policy Studio > Pricing Standard rule condition transfer_speed = STANDARD
Policy Studio > Pricing Instant rule condition transfer_speed = INSTANT

Effect: the tiered counter remains on OUTGOING_TRANSFER, so the allowance is shared. The fee can still change by transfer_speed.

API request - preview with variant:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "OUTGOING_TRANSFER",
  "amount": 120.00,
  "currencyCode": "EUR",
  "metadata": {
    "transfer_speed": "INSTANT"
  },
  "mode": "PREVIEW"
}

API response:

{
  "decision": "FEE_CALCULATED",
  "reasonCode": "FEE_OUTGOING_TRANSFER_INSTANT",
  "totalFees": 0.35,
  "currencyCode": "EUR",
  "usedDimensions": {
    "operation": "OUTGOING_TRANSFER",
    "channel": "TRANSFER",
    "transfer_speed": "INSTANT",
    "product": "RETAIL_EUR_ACCOUNT",
    "currency": "EUR"
  }
}

A.10 Fee by destination area

Use this when a transfer fee depends on where the transfer goes.

Where to go Field Value
Dimension Registry code destination_area
Dimension Registry dataType ENUM
Dimension Registry source REQUEST_METADATA
Dimension Registry allowedValues DOMESTIC, REGIONAL, INTERNATIONAL
Dimension Registry isRuleEnabled true
Dimension Registry canUseInCounterScopes false for fee-only use
Policy Studio > Pricing Rule 1 WAIVER when destination_area = DOMESTIC
Policy Studio > Pricing Rule 2 FIXED 0.65 EUR when destination_area = INTERNATIONAL

Effect: the calling system declares destination area, LimitRail validates the value and applies the right fee.

A.11 Separate limit by destination area

Use this when consumption toward different areas should not land in the same counter.

Where to go Field Value
Dimension Registry destination_area.canUseInCounterScopes true
Policy Studio > Limits CounterScopeMode CUSTOM_DIMENSIONS
Policy Studio > Limits Scope dimension destination_area
Policy Studio > Limits MetricType AMOUNT
Policy Studio > Limits PeriodType MONTHLY
Policy Studio > Limits LimitAmount 1800.00

Effect: 800 EUR to DOMESTIC and 800 EUR to INTERNATIONAL consume two different counters. Use this only when the product truly requires separate limits by area.

A.12 Fee above amount threshold

Use this when the single operation amount decides which fee applies.

Where to go Field Value
Dimension Registry code amount_band
Dimension Registry dataType ENUM
Dimension Registry source REQUEST_METADATA
Dimension Registry allowedValues UP_TO_35, ABOVE_35
Dimension Registry isRuleEnabled true
Policy Studio > Pricing Free rule WAIVER when amount_band = UP_TO_35
Policy Studio > Pricing Paid rule FIXED 0.30 EUR when amount_band = ABOVE_35

Caller responsibility: derive the band from the amount and send it in metadata.

{
  "externalAccountRef": "acc_000203",
  "operationCode": "P2P_PAYMENT",
  "amount": 48.00,
  "currencyCode": "EUR",
  "metadata": {
    "amount_band": "ABOVE_35"
  },
  "mode": "PREVIEW"
}

A.13 Monthly fee based on average balance

Use this when a recurring fee depends on a value calculated by the accounting system.

Where to go Field Value
Operation Catalog code MONTHLY_MAINTENANCE
Dimension Registry code average_monthly_balance
Dimension Registry dataType DECIMAL
Dimension Registry source REQUEST_METADATA
Dimension Registry isRuleEnabled true
Policy Studio > Pricing CalculationMethod FIXED
Policy Studio > Pricing FixedAmount 0.70
Policy Studio > Pricing Condition average_monthly_balance < 220

Caller responsibility: calculate the average balance and call LimitRail at period end or in the billing schedule.

{
  "externalAccountRef": "acc_000203",
  "operationCode": "MONTHLY_MAINTENANCE",
  "amount": 0,
  "currencyCode": "EUR",
  "metadata": {
    "average_monthly_balance": 186.40
  },
  "mode": "PREVIEW"
}

A.14 Maximum account balance

Use this when incoming credits must be blocked if they would take the account above a threshold. LimitRail does not calculate the balance: it reads the projected balance supplied by the calling system.

Where to go Field Value
Dimension Registry code projected_balance_after_operation
Dimension Registry dataType DECIMAL
Dimension Registry source REQUEST_METADATA
Dimension Registry isRuleEnabled true
Policy Studio > Limits Code LIM_MAX_BALANCE_3600
Policy Studio > Limits PeriodType SINGLE
Policy Studio > Limits IsBlocking true
Policy Studio > Limits Condition projected_balance_after_operation > 3600

API request - projected balance check:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "INCOMING_STANDARD_TRANSFER",
  "amount": 280.00,
  "currencyCode": "EUR",
  "metadata": {
    "projected_balance_after_operation": 3650.00
  },
  "mode": "PREVIEW"
}

API response:

{
  "decision": "DENIED_BY_POLICY",
  "reasonCode": "LIM_MAX_BALANCE_3600",
  "originalAmount": 280.00,
  "totalFees": 0.00,
  "currencyCode": "EUR",
  "ruleResults": [
    {
      "ruleCode": "LIM_MAX_BALANCE_3600",
      "decision": "DENIED_BY_POLICY",
      "metricType": "AMOUNT",
      "periodType": "SINGLE"
    }
  ],
  "usedDimensions": {
    "projected_balance_after_operation": 3650.00,
    "operation": "INCOMING_STANDARD_TRANSFER"
  }
}

Effect: policy denies because projected balance exceeds the threshold. Financial execution remains the caller's responsibility.

A.15 Account profile or customer segment

Use this when conditions depend on a stable account characteristic.

Where to go Field Value
Dimension Registry code customer_profile
Dimension Registry source ACCOUNT_ATTRIBUTE
Dimension Registry dataType ENUM
Dimension Registry allowedValues STANDARD, PREMIUM, STAFF, RESTRICTED
Dimension Registry isRuleEnabled true
Account contracts attributes.customer_profile PREMIUM
Policy Studio Condition customer_profile = PREMIUM

Effect: the caller does not need to resend this value on every runtime request. LimitRail reads it from account attributes.

API request - account contract creation:

POST /v1/account-contracts
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "productId": "865e0567-7927-4680-a2f5-e4c71921b7bf",
  "attributes": {
    "customer_profile": "PREMIUM"
  }
}

API response:

{
  "id": "146dbd98-242f-4f5d-9fc0-b5dbb497cbb5",
  "externalAccountRef": "acc_000203",
  "productId": "865e0567-7927-4680-a2f5-e4c71921b7bf",
  "productCode": "RETAIL_EUR_ACCOUNT",
  "status": "ACTIVE"
}

A.16 Single-operation exception

Use this when an operational system approves an exception and LimitRail must consider it during evaluation.

Where to go Field Value
Dimension Registry code limit_exception_granted
Dimension Registry dataType BOOLEAN
Dimension Registry source REQUEST_METADATA
Dimension Registry isRuleEnabled true
Dimension Registry isSensitive true if it should not appear in response
Policy Studio Restrictive rule applies when limit_exception_granted does not exist or is false
Policy Studio Alternative rule applies when limit_exception_granted = true

API request - exception with reservation:

POST /v1/policy/evaluate
Authorization: Bearer <token>
Content-Type: application/json

{
  "externalAccountRef": "acc_000203",
  "operationCode": "INCOMING_STANDARD_TRANSFER",
  "amount": 280.00,
  "currencyCode": "EUR",
  "metadata": {
    "projected_balance_after_operation": 3650.00,
    "limit_exception_granted": true
  },
  "mode": "RESERVE_CAPACITY",
  "idempotencyKey": "reserve-exception-000203-001"
}

API response:

{
  "decision": "ALLOWED",
  "reasonCode": "ALLOWED",
  "capacityReservationId": "4dfef774-f177-4642-8fb4-7c3dfaf09ea0",
  "usedDimensions": {
    "projected_balance_after_operation": 3650.00,
    "operation": "INCOMING_STANDARD_TRANSFER"
  }
}

Effect: LimitRail evaluates with the exception. It does not decide who may approve it; that remains owned by the upstream operational system.

A.17 Correct runtime flow: preview, reserve, commit

Use this when the operation can be checked first and executed after an external phase.

Step API When to use it What it writes
1 POST /v1/policy/evaluate with mode = PREVIEW Show fees or check policy without committing capacity Nothing
2 POST /v1/policy/evaluate with mode = RESERVE_CAPACITY Temporarily reserve capacity while the caller executes the operation Temporary reservation
3 POST /v1/usage/commit Confirm that the operation was executed Committed usage and counters
4 POST /v1/capacity-reservations/{id}/cancel Cancel a reservation that was not executed Cancels reservation
5 POST /v1/usage/{id}/reverse Compensate usage that was already committed Usage reversal

Practical rule: always use idempotencyKey for reserve, commit and reverse. Do not reuse the same key for different payloads.

A.18 What the caller should read to show limits and fees to an end user

Channel need API Use
Show resolved account pricing GET /v1/accounts/{externalAccountRef}/pricing Fees applicable to the product/account.
Show committed account counters GET /v1/accounts/{externalAccountRef}/usage-counters Limit consumption state, without raw operational events.
Simulate an operation before execution POST /v1/policy/evaluate with PREVIEW Show fees and potential denied outcome before user confirmation.
Commit capacity during execution POST /v1/policy/evaluate with RESERVE_CAPACITY Avoid race conditions between check and execution.

API request - resolved pricing:

GET /v1/accounts/acc_000203/pricing
Authorization: Bearer <token>

API response:

{
  "externalAccountRef": "acc_000203",
  "productCode": "RETAIL_EUR_ACCOUNT",
  "conditionPlanCode": "RETAIL_EUR_PLAN",
  "versionNumber": 1,
  "currencyCode": "EUR",
  "pricing": [
    {
      "operationCode": "MERCHANT_PAYMENT",
      "method": "WAIVER",
      "summary": "Included in the account package"
    },
    {
      "operationCode": "PUBLIC_SERVICE_PAYMENT",
      "method": "FIXED",
      "amount": 0.35,
      "currencyCode": "EUR"
    }
  ]
}

Real use: this is the call an end-user channel can use to show how much an operation costs before starting a payment. It does not consume counters.

API request - committed counters:

GET /v1/accounts/acc_000203/usage-counters
Authorization: Bearer <token>

API response:

{
  "externalAccountRef": "acc_000203",
  "counters": [
    {
      "operationCode": "OUTGOING_TRANSFER",
      "metricType": "AMOUNT",
      "periodType": "MONTHLY",
      "committedAmount": 620.00,
      "limitAmount": 2400.00,
      "remainingAmount": 1780.00,
      "currencyCode": "EUR"
    },
    {
      "operationCode": "OUTGOING_TRANSFER",
      "metricType": "COUNT",
      "periodType": "YEARLY",
      "committedCount": 4,
      "includedCount": 9,
      "remainingIncludedCount": 5
    }
  ]
}

Real use: this is the call an app or portal can use to show "you still have 1,780 EUR available this month" or "you still have 5 included operations". It should not expose raw events or accounting movements.

A.19 Quick coverage matrix

Use case Main configuration Required caller input
Free operation Pricing WAIVER account, operation, amount when required
Fixed fee Pricing FIXED account, operation, amount when required
Percentage fee Pricing PERCENTAGE account, operation, amount
N included operations, then fee Pricing TIERED, COUNT, period account, operation, idempotency on writes
Monthly volume cap Limit AMOUNT, MONTHLY account, operation, amount
Maximum operation count Limit COUNT, period account, operation
Channel limit ACCOUNT_CHANNEL operation with consistent channelCode
Area limit dimension in CUSTOM_DIMENSIONS metadata destination_area
Area fee dimension with isRuleEnabled metadata destination_area
Fee above amount threshold dimension amount_band metadata amount_band calculated by caller
Fee from average balance dimension average_monthly_balance metadata calculated by balance-owning system
Maximum balance dimension projected_balance_after_operation projected balance calculated by balance-owning system
Customer profile account attribute attribute on account contract
Single-operation exception dimension limit_exception_granted upstream-authorized boolean metadata