Billing and statement fields
How each call is charged, what the statement fields mean, and how to reconcile them in the console.
How billing works
Synchronous calls: an estimate is held before the request, the exact amount is settled from the upstream usage after the response, and the difference is released immediately. Async tasks: the cap is held at submission and settled at actual tokens after the terminal state. All amounts are recorded in micro-USD and every statement line can be recomputed.
Deduction order: plan quota → resource packs → gift balance → available balance → credit line.
Statement fields
| Field | Type | Description |
|---|---|---|
| request_id | string | Matches the X-Request-Id response header. |
| requested_model / upstream_model | string | The model requested and the upstream model actually routed to. |
| input_tokens / output_tokens / cached_tokens / reasoning_tokens | integer | Token usage by category. |
| charged_micro_usd | integer | Amount charged for this call (micro-USD). |
| pricing_snapshot | object | The price snapshot used at settlement; historical statements are unaffected by later price changes. |
| status_code / error_code | … | HTTP status and error code; failed 4xx/5xx calls are not charged (an interrupted stream is charged for the part already generated). |
Reconciling in the console
The Usage page aggregates by day; the Request logs page lets you search every request by model, status and time; the Ledger page shows account entries (top-ups, consumption, holds, settlements, releases, reversals, rebates).
Export call logs as CSV
GET/api/v1/usage/logs.csv
Downloads your own call logs as CSV using the same filters as the log list. The response is a text/csv; charset=utf-8 attachment named like usage-logs-20260902.csv, stamped with the UTC day it was produced. The body starts with a UTF-8 byte-order mark so spreadsheets open it without mangling the text.
| Field | Type | Description | |
|---|---|---|---|
| model | string | optional | Filter by model name, at most 160 characters. |
| status | integer | optional | Filter by HTTP status code, from 100 to 599. |
curl -OJ "$LINK42_CONSOLE/api/v1/usage/logs.csv?model=seedance-2.0&status=200" \
-b "$LINK42_SESSION_COOKIE"created_at,request_id,api_key_prefix,protocol,requested_model,upstream_model,status_code,error_code,input_tokens,output_tokens,cached_tokens,reasoning_tokens,charged_usd,duration_ms,ttft_ms- One export writes at most 20,000 rows and truncates the rest. Split the range to pull a longer history.
- charged_usd is US dollars with six decimals. ttft_ms is empty for non-streaming requests.
- The file is streamed 500 rows at a time. If a later page fails to load, the download ends at the last complete page instead of turning into an error response.
Export ledger entries as CSV
GET/api/v1/ledger/entries.csv
Downloads your own ledger entries as CSV using the same filters as the ledger list. The file is named like ledger-20260902.csv, also carries the UTF-8 byte-order mark, and is capped at the same 20,000 rows.
| Field | Type | Description | |
|---|---|---|---|
| type | string | optional | Filter by entry type: recharge, gift, consume, hold, capture, release, refund, rebate, withdraw, adjust, expire, migration_opening or reversal. |
| model | string | optional | Filter by model name, at most 160 characters. |
| api_key_id | integer | optional | Only entries produced by one API key. |
| start | string | optional | Start of the window, RFC 3339. |
| end | string | optional | End of the window, RFC 3339; it must be later than start. |
curl -OJ "$LINK42_CONSOLE/api/v1/ledger/entries.csv?type=consume&start=2026-08-01T00:00:00Z&end=2026-09-01T00:00:00Z" \
-b "$LINK42_SESSION_COOKIE"created_at,transaction_id,tx_type,account_type,amount_usd,balance_after_usd,api_key_prefix,model,request_id,order_no,description- amount_usd and balance_after_usd are US dollars with six decimals, and a minus sign marks a debit. account_type is one of available, frozen, gift or credit.
- An invalid window, where start is not before end, answers 422 invalid_time_range; a type outside the enumeration answers 422 invalid_ledger_type.
Agent wholesale quote
POST/api/v1/agent/wholesale/preview
An agent orders at their own price multiplier: credit tops up the agent's own balance, while plan and pack buy a subscription or a resource pack for a bound customer. Preview only prices the request — it creates no order and moves no money.
The payable amount is the list price multiplied by the price multiplier, rounded to micro-USD. multiplier_ppm is parts per million, so 1000000 means no discount, and discount_micro_usd is the difference between the list price and the payable amount.
| kind | What it buys | Required fields | Who receives it |
|---|---|---|---|
| credit | Balance | credit_micro_usd | The agent |
| plan | A subscription plan | plan_id and customer_user_id | The named customer |
| pack | A resource pack | pack_id and customer_user_id | The named customer |
| Field | Type | Description | |
|---|---|---|---|
| kind | string | required | credit, plan or pack. |
| credit_micro_usd | integer | optional | Required when kind is credit: the balance to buy in micro-USD, up to 1000000000000 (one million USD). |
| plan_id | integer | optional | Required when kind is plan: the subscription plan id. |
| pack_id | integer | optional | Required when kind is pack: the resource pack id. |
| customer_user_id | integer | optional | Required when kind is plan or pack: the beneficiary's user id, which must already be bound to this agent. |
| Field | Type | Description |
|---|---|---|
| kind | string | Echoes the kind that was priced. |
| item_name | string | The plan or pack name; empty when kind is credit. |
| list_price_micro_usd | integer | The list price in micro-USD. For credit it equals the requested balance. |
| multiplier_ppm | integer | This agent's price multiplier, in parts per million. |
| payable_micro_usd | integer | The amount actually payable, in micro-USD. |
| discount_micro_usd | integer | The saving: list price minus payable amount. |
curl -X POST "$LINK42_CONSOLE/api/v1/agent/wholesale/preview" \
-H "Content-Type: application/json" \
-b "$LINK42_SESSION_COOKIE" \
-d '{"kind": "plan", "plan_id": 7, "customer_user_id": 3120}'{
"data": {
"kind": "plan",
"plan_id": 7,
"pack_id": null,
"item_name": "Growth Monthly",
"customer_user_id": 3120,
"list_price_micro_usd": 99000000,
"multiplier_ppm": 820000,
"payable_micro_usd": 81180000,
"discount_micro_usd": 17820000
},
"request_id": "req_01J…"
}- The field combination must match the kind exactly: credit cannot carry plan_id, pack_id or customer_user_id; plan and pack cannot carry credit_micro_usd and cannot name the agent as their own customer. Anything else answers 422 wholesale_invalid.
- A customer not bound to this agent answers 422 wholesale_customer_not_bound. A missing agent profile answers 404 agent_not_found and a suspended one answers 403 agent_suspended. A multiplier outside the configured range answers 422 wholesale_multiplier_out_of_policy, which an operator has to fix on the profile.
Agent wholesale orders
POST/api/v1/agent/wholesale/orders
Once the quote looks right, create the order with the same body plus a payment channel. The order belongs to the agent; when the money lands, a plan or pack is delivered to the beneficiary customer, while credit goes to the agent's own balance.
The response is {order, wholesale, replayed}. order is a regular payment order carrying that channel's checkout details (pay_url, a QR code, or a USDT deposit address); wholesale is the record keeping the list price, the multiplier, the payable amount and the beneficiary.
| Field | Type | Description | |
|---|---|---|---|
| provider | string | required | The payment channel: stripe, airwallex, easypay or usdt_trc20. |
| method | string | optional | The method inside that channel, where the channel offers one. |
| idempotency_key | string | optional | The idempotency key; the Idempotency-Key header works too. |
curl -X POST "$LINK42_CONSOLE/api/v1/agent/wholesale/orders" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9d7258b0da1c42df" \
-b "$LINK42_SESSION_COOKIE" \
-d '{"kind": "plan", "plan_id": 7, "customer_user_id": 3120, "provider": "stripe"}'- The first create answers 201. Replaying the same idempotency key answers 200 with replayed set to true, and no second order is opened.
- GET /api/v1/agent/wholesale/orders pages this agent's wholesale orders and accepts status and kind filters.
- If the payment order is created but the wholesale record cannot be stored, the platform closes that order immediately with close_reason wholesale_record_failed, so the purchase can never be delivered to the agent by mistake.