Usage events
How metrics work, how events are rated, and how to send volume plus a pre-calculated fee.
Usage events are how you report metered activity. Lucius rates each event and updates the matching draft invoice immediately.
POST /v1/billing/usage → rate → draft invoiceMetrics are per contract
There is no global Lucius metric catalog. Each contract’s usage slugs come from that contract’s pricing program. One client may use usd_transaction_volume_processed and another trading_volume — send the exact string on that contract.
Discover the exact strings with:
GET /v1/billing/contractsTo attach a test program to a customer you already created in the dashboard (customer ≠ contract):
curl -X POST https://api-dev.lucius.finance/v1/billing/contracts \
-H "Authorization: Bearer luc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_id":"YOUR_CUSTOMER_UUID"}'Lucius generates {slug}-test as the contract id, a sample CSA, and usage metrics domestic_wire / international_wire. Use that id as external_contract_id.
There is no usage screen in the dashboard. List accepted events with GET /v1/billing/usage (optional ?contract=). They also land as line items on the period invoice (GET /v1/billing/invoices, or Receivables → customer → invoice).
Use the contract id as external_contract_id and a value from metrics[] as metric. A mismatch returns Metric '…' not in pricing program.
metrics[] is empty when the program has only implementation, subscription, or minimum fees — no usage rule. You cannot submit events until you add one.
Event fields
POST /v1/billing/usage accepts up to 100 events.
| Field | Required | What it is |
|---|---|---|
external_contract_id | Yes | Contract id from GET /v1/billing/contracts |
event_id | Yes | Your unique idempotency key |
metric | Yes | Exact usage slug from that contract’s metrics[] |
quantity | Yes | Volume / notional the fee is based on (usually USD). ≥ 0 |
timestamp | Yes | When the usage occurred (ISO 8601) |
fee_amount | No | Final fee in the contract currency. Authoritative — this is what lands on the invoice |
applied_rate | No | Decimal rate (0.0001 = 1 bps). Charge = quantity × applied_rate |
counterparty | No | Other party on the trade. Matches counterparty-scoped rules |
side | No | maker or taker |
pair | No | Instrument (e.g. BRL/USDC-DT). Matches pair-scoped usage rules |
window_start / window_end | No | Service period dates. Defaults to the timestamp’s date |
fee_amount is not a metric. Do not create a calculated_fee slug. Send the fee on the event.
How the charge is calculated
Priority, first match wins:
fee_amount— used as-is (USD). Ignores the contract rate andapplied_rate.applied_rate—quantity × applied_rate.applied_rateis a decimal, not integer bps (0.25is 25%, not 0.25 bps).- Contract usage rule — percentage (
quantity × rate), flat (quantity × unit_price), tiered, or threshold.
If you already compute maker/taker exceptions upstream, send quantity (volume) and fee_amount (your fee). Lucius will not re-apply the contract bps.
{
"events": [{
"external_contract_id": "acme-corp-2026",
"event_id": "trade-abc123:taker",
"metric": "usd_transaction_volume_processed",
"quantity": 250000,
"fee_amount": 25.00,
"applied_rate": 0.0001,
"counterparty": "acme-markets",
"side": "taker",
"timestamp": "2026-03-25T12:00:00Z"
}]
}applied_rate here is optional documentation of the rate you used. fee_amount is what gets billed.
Contract has no metrics
Add one usage rule, then submit against that slug.
Recommended default for USD volume:
curl -X POST https://api.lucius.finance/v1/billing/contracts/acme-corp-2026/pricing-rules \
-H "Authorization: Bearer luc_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "USD transaction volume",
"type": "usage",
"model": "percentage",
"metric": "usd_transaction_volume_processed",
"rate": 0,
"currency": "USD"
}'rate: 0 is correct when every event will carry fee_amount. Reuse usd_transaction_volume_processed — do not invent a new slug per client or exception.
Then:
{
"events": [{
"external_contract_id": "acme-corp-2026",
"event_id": "trade-abc:maker",
"metric": "usd_transaction_volume_processed",
"quantity": 1000000,
"fee_amount": 25.00,
"timestamp": "2026-09-01T12:00:00Z"
}]
}Counterparty-scoped rules (optional)
If you want Lucius to pick the rate (instead of sending fee_amount), add usage rules with counterparty and counterparty_side. A matching maker/taker rule beats the generic rule for that metric.
For pair-level exceptions you already compute yourself, prefer fee_amount over modeling every pair in Lucius.
What happens after accept
- Event is stored (
event_idis the idempotency key). - It is rated using the priority above.
- The draft invoice whose billing period covers the timestamp (or
window_*) is updated.
Same event_id again → accepted, no second charge. See Idempotency.