Description
Purpose and use
Tariffs define what the bank charges, when it charges, and which customer or transaction context receives which price. They cover one-off fees, percentage fees, subscriptions, velocity limits, channel pricing, and product-specific fee behavior.
Who uses this. Product managers, finance operations, billing teams, treasury, and support teams use tariffs when defining customer pricing and explaining charges.
How it works. A tariff groups fee ranges, calculation methods, transaction types, channels, currencies, and velocity rules. During a transfer, subscription run, or fee calculation, the matching tariff determines the amount, currency, and posting behavior.
What users do. Users define or clone tariffs, set fee ranges and channels, configure velocity limits, review subscription charge history, and verify the tariff assigned to a customer or product.
Outcomes and side effects. Tariff changes affect future fees, customer disclosures, subscription billing, and transfer economics. Posted fees flow into ledger entries and audit history through the billing or transfer process that applies the tariff.
Related manuals: Products, Transfers, FX, Ops.
Overview
The Tariffs API provides functionality for managing pricing and fee calculations:
- Tariff management (CRUD operations)
- Fee range definitions
- Velocity rules
- Fee calculations
- Multi-currency support
- Channel-based pricing
- Asset-specific fees
Core Concepts
Fee Calculation Methods
fixed: Fixed amount feepercentage: Percentage of transaction amountgreater: Greater of fixed or percentagelesser: Lesser of fixed or percentagesum: Sum of fixed and percentage
Transaction Types
- Transaction type filtering is available for both fee ranges and velocity rules
- Valid values: Product codes of type
transfers(e.g.,iwt,owt,int,own,crw) oranyfor all transaction types - The transaction type must be an active product in the
products.productstable withtype = 'transfers' - Use
anyto apply the rule to all transaction types
Velocity Types
transaction_count: Limit number of transactionsamount: Limit total transaction amount
Velocity Intervals
once: One-time limitdaily: Daily limitweekly: Weekly limitmonthly: Monthly limitquarterly: Quarterly limit (3 months)annually: Annual limit
Endpoints
Tariff Management
Create Tariff
POST /v1/tariffs
Create a new tariff.
Request Body:
{
"name": "standard",
"description": "Standard pricing tier",
"fallback_tariff_id": "uuid",
"subscription_product_id": "uuid",
"active": true,
"default": false
}Response:
{
"id": "uuid",
"name": "standard",
"description": "Standard pricing tier",
"fallback_tariff_id": "uuid",
"subscription_product_id": "uuid",
"active": true,
"default": false,
"created_at": "timestamp",
"created_by": "uuid"
}Get Tariff
GET /v1/tariffs/{id}
Get a specific tariff.
Response:
{
"id": "uuid",
"name": "standard",
"description": "Standard pricing tier",
"fallback_tariff_id": "uuid",
"subscription_product_id": "uuid",
"active": true,
"default": true,
"fee_ranges": [
{
"id": "uuid",
"range_start": 0,
"range_end": 1000,
"fixed_fee": 2.5,
"percent_fee": 1.0,
"method": "sum",
"min_fee": 2.0,
"max_fee": 20.0,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z"
}
],
"velocity_rules": [
{
"id": "uuid",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"transaction_type": "any",
"active": true
}
]
}Update Tariff
PUT /v1/tariffs/{id}
Update an existing tariff.
Request Body:
{
"name": "standard",
"description": "Updated standard pricing tier",
"fallback_tariff_id": "uuid",
"subscription_product_id": "uuid",
"active": true,
"default": false
}Delete Tariff
DELETE /v1/tariffs/{id}
Delete a tariff and all associated fee ranges and velocity rules.
Get All Tariffs
GET /v1/tariffs
Retrieve all tariffs.
Query Parameters:
- active: Filter by active status
- name: Filter by name
- search: Search in name/description
Response:
[
{
"id": "uuid",
"name": "standard",
"description": "Standard pricing tier",
"fallback_tariff_id": "uuid",
"active": true,
"default": true,
"fee_ranges": [
{
"id": "uuid",
"range_start": 0,
"range_end": 1000,
"fixed_fee": 2.5,
"percent_fee": 1.0,
"method": "sum",
"min_fee": 2.0,
"max_fee": 20.0,
"transaction_type": "iwt"
}
],
"velocity_rules": [
{
"id": "uuid",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"transaction_type": "any"
}
]
}
]Clone Tariff
POST /v1/tariffs/{id}/clone
Duplicates a tariff with a new ID, copying all fee ranges and velocity rules. The clone is never marked as default.
Request Body (optional):
{
"name": "Copy of Standard"
}When name is omitted, the server uses Copy of {source name} and appends (n) if that name already exists.
Response: 201 Created with the full tariff (including fee_ranges and velocity_rules).
Export Tariff Seeds to Server DATA_DIR
POST /v1/tariffs/export-seed
Writes one .import.json file per tariff under DATA_DIR/tariffs on the API host.
If tariff_ids is omitted or empty, all tariffs are exported.
Request Body:
{
"tariff_ids": ["uuid"]
}Response:
{
"dir": "/abs/path/to/DATA_DIR/tariffs",
"written": [
{
"tariff_id": "uuid",
"name": "standard",
"filename": "standard-2f4c8b1d.import.json",
"path": "/abs/path/to/DATA_DIR/tariffs/standard-2f4c8b1d.import.json"
}
]
}Fee Range Management
Create Fee Range
POST /v1/tariffs/ranges
Create a new fee range.
Request Body:
{
"tariff_id": "uuid",
"range_start": 0,
"range_end": 1000,
"fixed_fee": 2.5,
"percent_fee": 1.0,
"method": "sum",
"min_fee": 2.0,
"max_fee": 20.0,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"active": true
}Get Fee Range
GET /v1/tariffs/ranges/{range_id}
Get a specific fee range.
Response:
{
"id": "uuid",
"tariff_id": "uuid",
"range_start": 0,
"range_end": 1000,
"fixed_fee": 2.5,
"percent_fee": 1.0,
"method": "sum",
"min_fee": 2.0,
"max_fee": 20.0,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"active": true
}Update Fee Range
PUT /v1/tariffs/ranges/{range_id}
Update an existing fee range.
Request Body:
{
"tariff_id": "uuid",
"range_start": 0,
"range_end": 2000,
"fixed_fee": 3.0,
"percent_fee": 1.5,
"method": "sum",
"min_fee": 3.0,
"max_fee": 25.0,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"active": true
}Delete Fee Range
DELETE /v1/tariffs/ranges/{range_id}
Delete a fee range.
Velocity Rule Management
Create Velocity Rule
POST /v1/tariffs/velocity-rules
Create a new velocity rule.
Request Body:
{
"tariff_id": "uuid",
"reference_id": "customer_12345",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 10,
"active": true
}Response:
{
"id": "uuid",
"tariff_id": "uuid",
"reference_id": "customer_12345",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 10,
"active": true,
"created_at": "timestamp",
"created_by": "uuid",
"modified_at": "timestamp",
"modified_by": "uuid",
"metadata": {}
}Get Velocity Rule
GET /v1/tariffs/velocity-rules/{rule_id}
Get a specific velocity rule.
Response:
{
"id": "uuid",
"tariff_id": "uuid",
"reference_id": "customer_12345",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 10,
"active": true,
"created_at": "timestamp",
"created_by": "uuid",
"modified_at": "timestamp",
"modified_by": "uuid",
"metadata": {}
}Update Velocity Rule
PUT /v1/tariffs/velocity-rules/{rule_id}
Update an existing velocity rule.
Request Body:
{
"reference_id": "customer_12345",
"type": "amount",
"interval": "monthly",
"limit": 50000,
"media": "any",
"asset": "EUR",
"from_channel": "any",
"to_channel": "any",
"transaction_type": "any",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 5,
"active": true
}Note: The reference_id field is optional in update requests. If not provided, the existing value will be preserved.
Response:
{
"id": "uuid",
"tariff_id": "uuid",
"reference_id": "customer_12345",
"type": "amount",
"interval": "monthly",
"limit": 50000,
"media": "any",
"asset": "EUR",
"from_channel": "any",
"to_channel": "any",
"transaction_type": "any",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 5,
"active": true,
"created_at": "timestamp",
"created_by": "uuid",
"modified_at": "timestamp",
"modified_by": "uuid",
"metadata": {}
}Delete Velocity Rule
DELETE /v1/tariffs/velocity-rules/{rule_id}
Delete a velocity rule.
Get Tariff Velocity Rules
GET /v1/tariffs/{id}/velocity-rules
Get all velocity rules for a specific tariff.
Response:
[
{
"id": "uuid",
"tariff_id": "uuid",
"reference_id": "customer_12345",
"type": "transaction_count",
"interval": "daily",
"limit": 1000,
"media": "card",
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"transaction_type": "iwt",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 10,
"active": true,
"created_at": "timestamp",
"created_by": "uuid",
"modified_at": "timestamp",
"modified_by": "uuid",
"metadata": {}
},
{
"id": "uuid",
"tariff_id": "uuid",
"reference_id": "global",
"type": "amount",
"interval": "monthly",
"limit": 50000,
"media": "any",
"asset": "EUR",
"from_channel": "any",
"to_channel": "any",
"transaction_type": "any",
"valid_from": "2024-01-01T00:00:00Z",
"valid_to": "2024-12-31T23:59:59Z",
"priority": 5,
"active": true,
"created_at": "timestamp",
"created_by": "uuid",
"modified_at": "timestamp",
"modified_by": "uuid",
"metadata": {}
}
]Fee Calculation
Calculate Fee
POST /v1/tariffs/calculate-fee
Calculate transaction fee.
Parameters:
value_date(optional): Future date for tariff calculation. If not provided, current date is used. Useful for calculating fees for future transactions based on scheduled tariff changes.
Velocity reference: Provide either customer_id (recommended) or reference_id. When customer_id is set, the API resolves reference_id from customer metadata as {customer_id}:{package_year} (customers.metadata.package_year, default year 1). With commit: true, pass transfer_id so velocity commits are idempotent per transfer and matched rule (transfer_id:rule_id).
Channel filters: from_channel and to_channel are optional. When omitted or sent as an empty string, the server defaults both to any (wildcard matching against fee ranges and velocity rules that use any).
Request Body:
The amount field uses the CcyAmt format — a monetary amount in minor/atomic units with its currency code. See Currencies - CcyAmt for details.
{
"customer_id": "uuid",
"transfer_id": "uuid",
"amount": {
"amount": "10000",
"currency": "EUR"
},
"asset": "EUR",
"from_channel": "pos",
"to_channel": "bank_account",
"media": "card",
"transaction_type": "iwt",
"value_date": "2024-12-25T10:00:00Z",
"commit": true
}| Field | Type | Description |
|---|---|---|
customer_id | uuid | Optional. Resolves velocity reference_id from customer metadata; use instead of sending reference_id manually |
reference_id | string | Optional when customer_id is set. Required when customer_id is omitted (explicit velocity bucket key) |
transfer_id | uuid | Optional. When commit is true, scopes velocity commit idempotency per transfer and rule |
amount.amount | string | Amount in minor units (e.g., "10000" = 100.00 EUR with precision 2) |
amount.currency | string | ISO currency code or asset code (e.g., "EUR", "USDC") |
asset | string | Asset filter for fee range matching. If omitted or "any", the currency from amount is used for fee denomination |
from_channel | string | Optional. Origin channel filter; defaults to any when omitted or empty |
to_channel | string | Optional. Destination channel filter; defaults to any when omitted or empty |
media | string | Required. Payment media filter (e.g. card, any) |
commit | boolean | When true, records velocity usage for matched rules |
Response:
Monetary fee fields (fixed_fee, total_fee, min_fee, max_fee) are returned in CcyAmtWithPrecision format — minor units with currency and precision metadata. The percent_fee remains a numeric rate.
{
"tariff_id": "uuid",
"tariff_name": "standard",
"fee_range_id": "uuid",
"fixed_fee": {
"amount": "250",
"currency": "EUR",
"precision": 2
},
"percent_fee": 1.0,
"total_fee": {
"amount": "350",
"currency": "EUR",
"precision": 2
},
"min_fee": {
"amount": "200",
"currency": "EUR",
"precision": 2
},
"max_fee": {
"amount": "2000",
"currency": "EUR",
"precision": 2
},
"method": "sum",
"is_fallback": false
}| Field | Type | Description |
|---|---|---|
fixed_fee | CcyAmtWithPrecision | Fixed component of the fee in minor units |
percent_fee | number | Percentage rate applied to the amount (e.g., 1.0 = 1%) |
total_fee | CcyAmtWithPrecision | Total calculated fee in minor units |
min_fee | CcyAmtWithPrecision | Minimum fee cap in minor units |
max_fee | CcyAmtWithPrecision | Maximum fee cap in minor units |
:::tip Converting to major units
To convert any CcyAmtWithPrecision value to major units: major = parseInt(amount) / 10^precision.
For example, {"amount": "350", "currency": "EUR", "precision": 2} = 3.50 EUR.
:::
Get customer velocity usage
GET /v1/tariffs/customers/{customer_id}/velocity-usage
Returns RAM-backed velocity usage for the customer's transfer and FX tariffs: per-rule used, limit, remaining, exceeded, ttl_seconds, and bucket_resets_at. Requires read permission on the customer record. transfer lists payment-tariff rules; fx lists FX spread rules (empty array when not applicable).
{
"customer_id": "uuid",
"package_year": 2,
"reference_id": "uuid:2",
"transfer": [
{
"velocity_rule_id": "uuid",
"tariff_id": "uuid",
"tariff_kind": "transfer",
"type": "amount",
"interval": "annual",
"limit": 100000,
"used": 2500,
"remaining": 97500,
"exceeded": false,
"package_year": 2,
"reference_id": "uuid:2",
"ttl_seconds": 86400,
"bucket_resets_at": "2026-12-31T23:59:59Z",
"asset": "EUR",
"media": "any",
"transaction_type": "any"
}
],
"fx": []
}Subscription billing charges (fee queue)
GET /v1/tariffs/subscription-charges
Returns the organisation-wide queue of recurring subscription fee charges — the operator view for chasing unpaid fees. Each row carries the customer's customer_name for display alongside the id. The response also includes a summary block with the count and total amount per status and currency, so the queue header can show how many fees need attention and the outstanding exposure. Requires the org-wide tariff read permission.
Charges are recorded by the subscription_billing_daily job in the Ops module; the same module exposes the per-charge attempt history and the manual "Try again" action. The charge status follows the lifecycle documented under Recurring Subscription Fees.
Query Parameters:
status: comma-separated status filter (defaults to the attention set — everything except settled)currency: ISO 4217 filterperiod_key: billing period, e.g.2026-06customer_id: restrict to one customerlimit,offset: pagination
{
"charges": [
{
"id": "uuid",
"product_code": "MFEE",
"customer_id": "uuid",
"customer_name": "Demo Company",
"account_id": "uuid",
"business_date": "2026-06-01",
"period_key": "2026-06",
"amount": "9.9000",
"currency": "EUR",
"txn_id": "uuid",
"status": "open_receivable",
"attempts": 2,
"last_error": "settle: no eligible account in currency"
}
],
"summary": [
{ "status": "open_receivable", "currency": "EUR", "count": 32, "total_amount": "316.8000" },
{ "status": "settled", "currency": "EUR", "count": 13, "total_amount": "128.7000" }
],
"total_count": 45,
"limit": 200,
"offset": 0
}List customer subscription charges
GET /v1/tariffs/customers/{customer_id}/subscription-charges
Returns recurring subscription billing charge rows from tariffs.subscription_charges for one customer, newest periods first, each including the customer's customer_name. Requires read permission on the customer record. Use txn_id with GET /v1/ledgers/entries?search.transaction_id= to inspect journal legs, or the Ops attempt-history endpoint for the full collection trail.
{
"customer_id": "uuid",
"charges": [
{
"id": "uuid",
"product_code": "MFEE",
"customer_id": "uuid",
"customer_name": "Demo Company",
"account_id": "uuid",
"business_date": "2026-06-01",
"period_key": "2026-06",
"amount": "9.9000",
"currency": "EUR",
"txn_id": "uuid",
"status": "settled",
"attempts": 1,
"last_error": null
}
]
}DSL Action Output
When fee calculation is invoked via the DSL transfer action, the output map provides both major units (backwards-compatible) and minor units for monetary amounts. The transaction amount / amount_minor is included for consistency with the exchange action, enabling uniform access to $state.*.amount_minor:
{
"amount": 200.00,
"amount_minor": 20000,
"tariff_id": "uuid",
"tariff_name": "standard",
"fee_range_id": "uuid",
"fixed_fee": 2.50,
"percent_fee": 1.0,
"total_fee": 3.50,
"min_fee": 2.00,
"max_fee": 20.00,
"method": "sum",
"is_fallback": false,
"currency": "EUR",
"precision": 2,
"total_fee_minor": 350,
"fixed_fee_minor": 250,
"percent_fee_minor": 1.0,
"min_fee_minor": 200,
"max_fee_minor": 2000
}| Field | Type | Description |
|---|---|---|
amount | number | Transaction amount in major units (e.g., 200.00) |
amount_minor | integer | Transaction amount in minor/atomic units (e.g., 20000) |
total_fee | number | Total calculated fee in major units (backwards-compatible) |
total_fee_minor | integer | Total calculated fee in minor/atomic units |
fixed_fee | number | Fixed fee component in major units |
fixed_fee_minor | integer | Fixed fee component in minor units |
percent_fee | number | Percentage rate (not a monetary amount, same in both) |
percent_fee_minor | number | Same as percent_fee (rate, not minor units) |
min_fee / min_fee_minor | number / integer | Minimum fee cap in major / minor units |
max_fee / max_fee_minor | number / integer | Maximum fee cap in major / minor units |
currency | string | Fee currency code |
precision | integer | Number of decimal places for the currency |
:::tip DSL path references
Use $state.tariff.amount_minor for the transaction amount and $state.tariff.total_fee_minor for fees in DSL expressions. Both transfer and exchange actions provide amount_minor, so you can access $state.*.amount_minor uniformly.
:::
Dictionary Endpoints
Get Asset Types
GET /v1/tariffs/asset-types
Get available asset types.
Response:
[
{
"name": "EUR",
"description": "Euro"
},
{
"name": "USD",
"description": "US Dollar"
}
]Get Channels
GET /v1/tariffs/channels
Get available channels.
Response:
[
{
"name": "pos",
"description": "Point of Sale"
},
{
"name": "bank_account",
"description": "Bank Account"
}
]Get Media Types
GET /v1/tariffs/media-types
Get available media types.
Response:
[
{
"name": "card",
"description": "Payment Card"
},
{
"name": "bank_transfer",
"description": "Bank Transfer"
}
]Get Fee Methods
GET /v1/tariffs/fee-methods
Get available fee calculation methods.
Response:
[
"fixed",
"percentage",
"greater",
"lesser",
"sum"
]Get Velocity Types
GET /v1/tariffs/velocity-types
Get available velocity types.
Response:
[
"transaction_count",
"amount"
]Get Velocity Intervals
GET /v1/tariffs/velocity-intervals
Get available velocity intervals.
Response:
[
"once",
"daily",
"weekly",
"monthly",
"quarterly",
"annually"
]Error Handling
All errors follow a standard format:
{
"code": "error_code",
"message": "Error description"
}Error Codes
General Errors
| Code | Description |
|---|---|
| tariffs_m.tariff_not_found | Tariff not found |
| tariffs_m.tariff_already_exists | Tariff with this name already exists |
| tariffs_m.invalid_tariff_data | Invalid tariff data provided |
| tariffs_m.insufficient_rights | Insufficient permissions |
| tariffs_m.default_tariff_exists | Another default tariff already exists |
Fee Range Errors
| Code | Description |
|---|---|
| tariffs_m.fee_range_not_found | Fee range not found |
| tariffs_m.invalid_fee_range | Invalid fee range data |
| tariffs_m.overlapping_ranges | Fee ranges overlap |
| tariffs_m.invalid_date_range | Invalid date range |
| tariffs_m.invalid_calculation_method | Invalid fee calculation method |
Velocity Rule Errors
| Code | Description |
|---|---|
| tariffs_m.velocity_rule_not_found | Velocity rule not found |
| tariffs_m.invalid_velocity_rule | Invalid velocity rule data |
| tariffs_m.velocity_limit_exceeded | Velocity limit exceeded |
| tariffs_m.invalid_velocity_interval | Invalid velocity interval |
| tariffs_m.invalid_velocity_type | Invalid velocity type |
| tariffs_m.failed_to_create_velocity_rule | Failed to create velocity rule |
| tariffs_m.failed_to_update_velocity_rule | Failed to update velocity rule |
| tariffs_m.failed_to_delete_velocity_rule | Failed to delete velocity rule |
| tariffs_m.failed_to_retrieve_velocity_rule | Failed to retrieve velocity rule |
Fee Calculation Errors
| Code | Description |
|---|---|
| tariffs_m.failed_to_calculate_fee | Fee calculation failed |
| tariffs_m.no_valid_tariff_entry | No valid tariff entry found |
| tariffs_m.invalid_transaction_data | Invalid transaction data |
| tariffs_m.unsupported_asset_type | Unsupported asset type |
| tariffs_m.unsupported_channel | Unsupported channel |
| tariffs_m.invalid_amount | Invalid amount |