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_destinationis notITA; - apply this limit only when
customer_tierisBASIC; - do not apply this fee when
employee = true; - block the operation when
risk_segment = HIGHand 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
A14wires. - Scope: account + operation.
- Result: all
A14wires 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 bywire_country_destination. - Scope: account + operation +
wire_country_destination. - Result: 6,000 EUR to
FRAand 6,000 EUR toUSAconsume 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, sourceREQUEST_METADATA, typeENUM; - 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,currencyare added automatically;wire_country_destinationis validated against the registry;- rule conditions reading
wire_country_destinationcan activate or exclude pricing and limits; - if the dimension is part of the counter perimeter, the counter is not only
account + operation, but alsowire_country_destination; - the response includes the dimension in
usedDimensionsif 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_GLOBALwhen the limit is unique per account;OperationScope = GLOBALwhen all policy-evaluated operations should contribute;WindowTimeZone = Europe/Romeif 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_TRANSFERand distinguish standard versus instant with thetransfer_speeddimension; - 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:
WAIVERrule whenamount_band = UP_TO_35;FIXED 0.30 EURrule whenamount_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, typeDECIMAL, sourceREQUEST_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, typeDECIMAL, sourceREQUEST_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, typeBOOLEAN, sourceREQUEST_METADATA; isRuleEnabled = true;isSensitive = trueif 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:
- account-specific override;
- segment-specific policy;
- 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:
externalAccountRefmust be opaque and stable;- do not use IBAN, name, email, tax id or personal data;
attributesare validated against dimensions with sourceACCOUNT_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.
6. Recommended technical flow

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_ACCOUNTexists; - verify that
A14is 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:
- commit an existing reservation;
- direct commit through
POST /v1/policy/evaluatewithmode = 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
A14wires 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
idempotencyKeyfor every state-writing call. - Do not reuse the same idempotency key for different payloads.
- Renew tokens before expiry.
- Treat
400,401,403,404,409,422as 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 |