Fee Ledger

Read the fee calculations recorded for a user and reconcile the quote against what settlement charged.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain access tokens

Overview

The fee ledger returns the fee calculations Wirex recorded for one user, newest first. It prices nothing
and derives nothing — what it returns is exactly what the fee service already quoted or charged, so a
partner can audit charging per operation without re-deriving fee math itself.

Requires a partner token. This endpoint is registered for the Self-Signed Partner JWT provider only.
The subject is the user named by X-User-Wallet, never a request parameter — a partner reads one of its
own users at a time and nothing else.

Calculation Lifecycle

A calculation moves forward through three stages as the operation it prices progresses, and is never moved
backward:

CALCULATION_STATUS_PENDING → CALCULATION_STATUS_CONSUMED → CALCULATION_STATUS_FINALIZED
StatusMeaning
CALCULATION_STATUS_PENDINGPriced, not yet bound to anything. A quote the user never executed stays here
CALCULATION_STATUS_CONSUMEDBound to a transaction accepted for execution
CALCULATION_STATUS_FINALIZEDSettlement amounts recorded. Terminal

src_amount_fee_charged and dst_amount_fee_charged are what settlement produced — present only once the
record is CALCULATION_STATUS_FINALIZED. An amount the platform has not settled yet is absent from the
record rather than reported as zero. The quote the user was shown at the originating endpoint is not part of
this record; compare the finalized record against that estimate to reconcile.

Rate markup is not a fee amount. It is the distance between operation_rate_precise and
user_rate_precise, summarized by effective_fx_markup_bps, and applies even where the fee amounts
themselves are zero.


Get Fee Ledger

GET /api/v1/fees/ledger

Query Parameters

ParameterTypeRequiredDescription
op_typestringNoFilter by operation type, e.g. OP_TYPE_TRADE_CRYPTO. An unrecognized value returns 400
statusstringNoFilter by calculation status — one of the three lifecycle values. An unrecognized value returns 400
transaction_idstring (UUID)NoFilter to the calculation bound to this transaction on Consume
consumed_from_unix_msintegerNoLower bound of the consumed-at window, Unix milliseconds. 0 (default) is unbounded
consumed_to_unix_msintegerNoUpper bound of the consumed-at window, Unix milliseconds. 0 (default) is unbounded. Must not be before consumed_from_unix_ms
page_sizeintegerNoPage size. Default 25, capped at 200
page_tokenstringNoOpaque continuation token from a previous page's next_page_token

op_type, status, client_class, quote_mode and pair_class are the fee service's own enum
constants (e.g. OP_TYPE_TRADE_CRYPTO, CLIENT_CLASS_RETAIL, QUOTE_MODE_SOURCE_FIXED). This API does
not publish a complete list — pass the value exactly as returned by a record you have already read, or as
named by your Wirex account contact, and treat an unrecognized filter value as the 400 it returns.

Response

{
  "records": [
    {
      "calculation_id": "6f9619ff-8b86-d011-b42d-00c04fc964ff",
      "transaction_id": "1f9619ff-8b86-d011-b42d-00c04fc964aa",
      "status": "CALCULATION_STATUS_FINALIZED",
      "op_type": "OP_TYPE_TRADE_CRYPTO",
      "op_currency": "USD",
      "client_class": "CLIENT_CLASS_RETAIL",
      "quote_mode": "QUOTE_MODE_SOURCE_FIXED",
      "subject_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "src_amount_quoted": { "amount": 100.00, "amount_precise": "100000000", "currency": "USDC", "decimals": 6 },
      "dst_amount_quoted": { "amount": 99.50, "amount_precise": "99500000", "currency": "WEUR", "decimals": 6 },
      "src_amount_debited": { "amount": 100.00, "amount_precise": "100000000", "currency": "USDC", "decimals": 6 },
      "dst_amount_credited": { "amount": 99.48, "amount_precise": "99480000", "currency": "WEUR", "decimals": 6 },
      "src_amount_fee_charged": { "amount": 0.02, "amount_precise": "20000000000000000", "currency": "USDC", "decimals": 18 },
      "dst_amount_fee_charged": { "amount": 0.00, "amount_precise": "0", "currency": "WEUR", "decimals": 18 },
      "mid_rate_calculate_precise": "1000000000000000000",
      "operation_rate_precise": "1000000000000000000",
      "user_rate_precise": "996400000000000000",
      "rate_decimals": 18,
      "effective_fx_markup_bps": 36,
      "pair_class": "MAJOR_MAJOR",
      "config_revision": "41",
      "requested_at_unix_ms": 1735689600000,
      "consumed_at_unix_ms": 1735689601000,
      "finalized_at_unix_ms": 1735689630000
    }
  ],
  "next_page_token": ""
}
FieldDescription
records[].calculation_idIdentifier of the calculation
records[].transaction_idThe transaction the calculation was bound to on Consume. Absent on a PENDING record
records[].statusCalculation lifecycle stage — see Calculation Lifecycle
records[].op_typeOperation the fee was priced for
records[].op_currencyCurrency the operation was denominated in
records[].client_classPricing class the subject was resolved to
records[].quote_modeWhich side of the operation the caller fixed
records[].subject_addressWallet the calculation was priced for
records[].src_amount_quoted / dst_amount_quotedSource / destination amount at quote time
records[].fixed_side_amount_usdUSD value of whichever side was fixed
records[].src_amount_debited / dst_amount_creditedWhat left the source side / reached the destination side at settlement. Present only once FINALIZED
records[].src_amount_trade_in / dst_amount_trade_outThe source slice that entered the trade / the destination slice it produced
records[].src_amount_fee_charged / dst_amount_fee_chargedFee actually charged on each side. Present only once FINALIZED
records[].transport_fee_estimated / transport_fee_charged / transport_fee_actualNetwork cost quoted / billed (a component of src_amount_fee_charged) / actually incurred at Finalize
records[].transport_fee_bpsMarkup applied over the transport estimate
records[].mid_rate_calculate_preciseMid rate held at quote time, in base units scaled by rate_decimals
records[].operation_rate_preciseSettlement rate, in base units scaled by rate_decimals
records[].user_rate_preciseRate the user received after markup, in base units scaled by rate_decimals
records[].rate_decimalsFractional digits the *_rate_precise fields are scaled by
records[].effective_fx_markup_bpsMarkup, in basis points, separating user_rate_precise from operation_rate_precise
records[].pair_classMarkup family the asset pair was classified into
records[].config_revisionFee configuration revision that priced the record
records[].card_format / card_origin_country / card_destination_country / card_delivery_provider / card_delivery_type / card_model_key / card_price_symbolPresent on card operations only
records[].requested_at_unix_msWhen the calculation was quoted
records[].consumed_at_unix_msWhen the calculation was bound to a transaction. Absent on a PENDING record
records[].finalized_at_unix_msWhen settlement amounts were recorded. Absent before FINALIZED
next_page_tokenContinuation token for the next page. Empty on the last page

Every amount object (src_amount_quoted, dst_amount_credited, src_amount_fee_charged, and so on)
carries the same four fields: amount (display value, 6 fractional digits), amount_precise (exact base
units), currency (asset symbol), and decimals (fractional digits amount_precise is scaled by) —
reconcile against amount_precise, not amount.

const response = await fetch(`${baseUrl}/api/v1/fees/ledger?status=CALCULATION_STATUS_FINALIZED&page_size=50`, {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Wallet': userEoaAddress,
    'X-Chain-Id': chainId
  }
});
const { records, next_page_token: nextPageToken } = await response.json();
response = requests.get(
    f"{base_url}/api/v1/fees/ledger",
    params={"status": "CALCULATION_STATUS_FINALIZED", "page_size": 50},
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Wallet": user_eoa_address,
        "X-Chain-Id": chain_id
    }
)
data = response.json()
records = data["records"]
next_page_token = data.get("next_page_token")
req, _ := http.NewRequest("GET", baseURL+"/api/v1/fees/ledger?status=CALCULATION_STATUS_FINALIZED&page_size=50", nil)
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Wallet", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var ledger FeeLedgerResponse
json.NewDecoder(resp.Body).Decode(&ledger)

Error Handling

{
  "error_reason": "ErrorInvalidField",
  "error_description": "Invalid value for op_type",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "field", "details": "op_type" },
    { "key": "issue", "details": "invalid_value" }
  ]
}

Validation Errors (400)

Error ReasonFieldDescription
ErrorInvalidFieldop_typeNot one of the fee service's operation types
ErrorInvalidFieldstatusNot one of the three lifecycle values
ErrorInvalidFieldtransaction_idNot a valid UUID
ErrorInvalidFieldconsumed_from_unix_ms / consumed_to_unix_msNegative, or consumed_from_unix_ms is after consumed_to_unix_ms
ErrorInvalidFieldpage_sizeExceeds 200

Did this page help you?