Fees

Market makers, read this first. The feeRateBps you sign into every EIP-712 order MUST equal the market's canonical effective fee, or the order is unusable (see Signing the correct fee). Fetch that value from GET /v1/market-data/:market_id/fee-rate before you sign. Do not sign 0, and do not sign platform_fee_bps.

Current production fee: flat 50 / 50

The live platform charges a flat 50 bps (0.50%) on both the maker and the taker of every trade. GET /v1/market-data/:market_id/fee-rate returns maker_fee_bps: 50, taker_fee_bps: 50 for every market today.

The maker-0 / taker-50 per-role model is staged but not active (gated behind the FEE_PER_ROLE_ENABLED operator flag, default off). Until an operator flips it, maker and taker fees are identical. Do not hard-code either the value or the assumption that maker is cheaper than taker; always read /fee-rate and sign what it returns.

Signing the correct fee

feeRateBps is one of the twelve fields inside the EIP-712 Order struct, so its value is baked into the order hash and the signature. The backend resolves a canonical effective fee for the (market, account, API key) tuple and enforces it in two places:

  1. At placement. If your signed fee_rate_bps does not equal the canonical fee, POST /v1/orders is rejected with 422 and the message fee_rate_bps mismatch: submitted <x> but canonical rate is <y>. The response carries the canonical rate so you can re-sign and retry immediately (#2829).
  2. At match time (#3672). A resting maker order whose signed fee_rate_bps no longer equals the canonical fee is silently skipped by the matcher instead of filled. Placement only validates once, so if the canonical fee changes while your order rests, that order stops matching with no error and no fill event. Re-read /fee-rate and re-sign every resting order whenever your effective fee changes.

The integration rule: read GET /v1/market-data/:market_id/fee-rate, sign that exact maker_fee_bps value (currently 50) into feeRateBps, and re-sign if the canonical fee ever moves. The fee_bps field on GET /v1/markets/:id carries the same value and is a valid fallback source.

Query Your Effective Fee

GET /v1/market-data/:market_id/fee-rate

Scope: read:account (authenticated)

Returns the effective maker and taker fee rates for the authenticated user on a specific market, after the backend resolves the full fee hierarchy.

Response

{
  "market_id": "550e8400-e29b-41d4-a716-446655440000",
  "maker_fee_bps": 50,
  "taker_fee_bps": 50,
  "maker_fee_pct": "0.5%",
  "taker_fee_pct": "0.5%"
}
FieldTypeDescription
maker_fee_bpsintCanonical maker fee in basis points. Sign this into feeRateBps for a resting (maker) order.
taker_fee_bpsintCanonical taker fee in basis points.
maker_fee_pctstringPre-rendered percentage string (maker_fee_bps divided by 100, %-suffixed) for display.
taker_fee_pctstringPre-rendered percentage string for display.

There is no rebate_bps field on this response. Basis points: 1 bps = 0.01%.

Fee Application

Fees are enforced at order-match time:

  • Maker orders (limit orders that rest on the book) pay maker_fee_bps.
  • Taker orders (orders that cross the spread) pay taker_fee_bps.

In the current flat model both are 50.

Fee Hierarchy

The canonical fee is resolved from a policy hierarchy, most specific first. Today every level is unconfigured except the market/global default, so the resolver returns a flat 50 everywhere. The tiers exist for future per-key market-maker agreements:

PriorityScopeDescription
1API KeyPer-key override assigned by admin
2AccountPer-user fee policy
3MarketMarket-specific fee policy
4TemplateTemplate-level default for market categories
5GlobalPlatform-wide default (currently 50)

If an admin negotiates a per-key rate for your market-maker key, /fee-rate reflects it automatically. Whatever it returns is still the exact value you must sign.

Admin Endpoints

Fee-policy management is available through admin endpoints (operator-only):

  • GET /admin/platform/fees/policies - List all fee policies
  • POST /admin/platform/fees/policies - Create a fee policy
  • PUT /admin/platform/fees/policies/:id - Update a fee policy
  • DELETE /admin/platform/fees/policies/:id - Delete a fee policy
  • POST /admin/platform/fees/assignments - Assign a policy to a scope
  • GET /admin/platform/fees/effective - Query resolved fee for a scope
  • GET /admin/platform/fees/audit-logs - Fee-policy change history
  • POST /admin/platform/update-fee - Set global maker=taker fee (convenience)