# private/get_account Source: https://docs.derive.xyz/api-reference/account/privateget_account /openapi.json post /private/get_account Returns account-level information for a given wallet address: the list of its subaccount IDs, WebSocket rate limits (matching, non-matching, perp, and option messages per second) and any per-endpoint overrides, cancel-on-disconnect and RFQ-maker flags, the account creation timestamp, and a fee_info block with fee discounts and per-instrument maker/taker fee overrides expressed as decimals. Returns an account-not-found error if the wallet has never been registered. # Auctions watch Source: https://docs.derive.xyz/api-reference/channels/auctionswatch Channel name: `auctions.watch`. Public channel that streams the live state of all ongoing liquidation and insolvency auctions across the exchange. Takes no parameters. # Margin watch Source: https://docs.derive.xyz/api-reference/channels/marginwatch Channel name: `margin.watch`. Public channel that streams margin and mark-to-market state for all accounts, letting you monitor collateralization and liquidation risk across the exchange. Takes no parameters. # Orderbook Source: https://docs.derive.xyz/api-reference/channels/orderbook Channel name: `orderbook.{instrument_name}.{group}.{depth}`. Public channel that periodically publishes aggregated bids and asks for a single instrument, where {instrument_name} is the market (e.g. ETH-PERP), {group} is the price-level aggregation size (1, 10, or 100), and {depth} is the number of levels returned per side (1, 10, 20, or 100). # Spot feed Source: https://docs.derive.xyz/api-reference/channels/spotfeed Channel name: `spot_feed.{currency}`. Public channel that streams the spot price feed for a given underlying, where {currency} selects the asset (e.g. ETH or BTC). # Subaccount balances Source: https://docs.derive.xyz/api-reference/channels/subaccountbalances Channel name: `{subaccount_id}.balances`. Private channel (requires authentication) that streams balance and position updates for one of your subaccounts, where {subaccount_id} is the numeric subaccount identifier. # Subaccount best quotes Source: https://docs.derive.xyz/api-reference/channels/subaccountbestquotes Channel name: `{subaccount_id}.best.quotes`. Private channel (requires authentication) that streams best-quote updates relevant to your subaccount, where {subaccount_id} is the numeric subaccount identifier. # Subaccount orders Source: https://docs.derive.xyz/api-reference/channels/subaccountorders Channel name: `{subaccount_id}.orders`. Private channel (requires authentication) that streams live order updates (opens, fills, cancels, and status changes) for your subaccount, where {subaccount_id} is the numeric subaccount identifier. # Subaccount quotes Source: https://docs.derive.xyz/api-reference/channels/subaccountquotes Channel name: `{subaccount_id}.quotes`. Private channel (requires authentication) that streams updates to your RFQ quotes for a subaccount, where {subaccount_id} is the numeric subaccount identifier. # Subaccount trades Source: https://docs.derive.xyz/api-reference/channels/subaccounttrades Channel name: `{subaccount_id}.trades`. Private channel (requires authentication) that streams your subaccount's own trade fills as they occur, where {subaccount_id} is the numeric subaccount identifier. # Ticker slim Source: https://docs.derive.xyz/api-reference/channels/tickerslim Channel name: `ticker_slim.{instrument_name}.{interval}`. Public channel that streams a lightweight ticker (prices and key stats) for an instrument, where {instrument_name} is the market and {interval} is the update cadence in milliseconds (100 or 1000). # Trades by instrument Source: https://docs.derive.xyz/api-reference/channels/tradesbyinstrument Channel name: `trades.{instrument_name}`. Public channel that streams the public trade tape for a single instrument, where {instrument_name} is the market whose fills you want to follow. # Trades by instrument type currency Source: https://docs.derive.xyz/api-reference/channels/tradesbyinstrumenttypecurrency Channel name: `trades.{instrument_type}.{currency}`. Public channel that streams public trades filtered by market category, where {instrument_type} is the kind of instrument (erc20, option, or perp) and {currency} is the underlying asset (e.g. ETH or BTC). # Wallet rfqs Source: https://docs.derive.xyz/api-reference/channels/walletrfqs Channel name: `{wallet}.rfqs`. Private channel (requires authentication) that streams RFQ (request-for-quote) updates for all subaccounts under a wallet, where {wallet} is your wallet address. # private/get_deposit_history Source: https://docs.derive.xyz/api-reference/history/privateget_deposit_history /openapi.json post /private/get_deposit_history Returns settled deposits for a single subaccount or an entire wallet (specify exactly one), optionally bounded by a start/end timestamp window. Each entry reports the deposited amount as a decimal, the fee routed to the security module (the net credited amount is amount minus fee), and the resolved settlement batch and status. # private/get_erc20_transfer_history Source: https://docs.derive.xyz/api-reference/history/privateget_erc20_transfer_history /openapi.json post /private/get_erc20_transfer_history Returns settled spot (ERC-20) transfers involving a single subaccount or an entire wallet (specify exactly one), optionally bounded by a start/end timestamp window. Each entry reports the transfer amount and fee as decimals along with direction — the sender sees the gross amount plus fee, the receiver sees the net credit — plus the resolved settlement batch and status. # private/get_funding_history Source: https://docs.derive.xyz/api-reference/history/privateget_funding_history /openapi.json post /private/get_funding_history Returns a paginated history of perpetual funding (settlement) events for a single subaccount or an entire wallet (specify exactly one), optionally bounded by a start/end timestamp window and filtered by perpetual instrument name. The response includes the funding events with their instrument name and settled amounts, plus pagination info with total count and page count. # private/get_interest_history Source: https://docs.derive.xyz/api-reference/history/privateget_interest_history /openapi.json post /private/get_interest_history Returns realized interest settlements per subaccount, for a single subaccount or an entire wallet (specify exactly one), optionally bounded by a start/end timestamp window. Each entry gives the settled interest as a decimal, where a negative value was paid (borrowed) and a positive value was received (supplied). # private/get_option_settlement_history Source: https://docs.derive.xyz/api-reference/history/privateget_option_settlement_history /openapi.json post /private/get_option_settlement_history Returns option settlement (expiry) events for a single subaccount or an entire wallet (specify exactly one). Each settlement includes the reconstructed option instrument name, the settled amount and value for that expired position, and the realized settlement PnL against the position's cost basis, with and without trading fees. # private/get_order_history Source: https://docs.derive.xyz/api-reference/history/privateget_order_history /openapi.json post /private/get_order_history Returns a paginated history of orders for a single subaccount or for an entire wallet (specify exactly one), optionally bounded by a from/to timestamp window. Only orders in a terminal state (filled, cancelled, expired) are recorded here, one record per order. Trigger and algo orders appear once they terminate, carrying their accumulated filled amount; while they are still live — including partially executed algo orders and trigger orders that have not yet fired — read them from get_trigger_orders and get_algo_orders instead. Each page includes the order records plus pagination info with the total count and number of pages. # private/get_trade_history Source: https://docs.derive.xyz/api-reference/history/privateget_trade_history /openapi.json post /private/get_trade_history Returns a paginated history of executed trades for a single subaccount or an entire wallet (specify exactly one), with optional filters for a time window, order id, instrument name, or quote id. The response includes the trade records and pagination info with total count and page count. # private/get_withdrawal_history Source: https://docs.derive.xyz/api-reference/history/privateget_withdrawal_history /openapi.json post /private/get_withdrawal_history Returns settled withdrawals for a single subaccount or an entire wallet (specify exactly one), optionally bounded by a start/end timestamp window. Each entry reports the withdrawn amount as a decimal, the fee routed to the security module (the net amount sent to the recipient is amount minus fee), and the resolved settlement batch and status. # public/get_liquidation_history Source: https://docs.derive.xyz/api-reference/history/publicget_liquidation_history /openapi.json post /public/get_liquidation_history Returns a paginated history of liquidation auctions, newest first, for a single subaccount or across all of them, optionally bounded by a start/end timestamp window over the auction start. Each entry gives the auctioned subaccount, whether the auction was solvent or insolvent, the fee charged at start, the start and end timestamps, and the bids that filled it with the fraction of the account each absorbed and the cash it moved. # public/get_maker_program_scores Source: https://docs.derive.xyz/api-reference/maker-scoring/publicget_maker_program_scores /openapi.json post /public/get_maker_program_scores Returns the wallet-level score breakdown for one maker-program epoch, including square-rooted coverage and quality components, traded-volume multiplier, holder boost, volume, and epoch totals. Scores, trades, program configuration, and the temporary enrolled-wallet set are read from ClickHouse. # public/get_maker_programs Source: https://docs.derive.xyz/api-reference/maker-scoring/publicget_maker_programs /openapi.json post /public/get_maker_programs Returns every maker-program epoch, including historical epochs, with its asset and currency scope, minimum eligible notional, timestamps, and reward-token amounts. Takes no parameters and reads the configuration from ClickHouse. # public/get_all_currencies Source: https://docs.derive.xyz/api-reference/market-data/publicget_all_currencies /openapi.json post /public/get_all_currencies Returns detailed metadata for every configured currency, including supported instrument types, risk managers, spot price and 24h-ago price, SRM/portfolio margin collateral discounts, lending borrow/supply APYs, open-interest caps and utilization per manager, and protocol asset addresses. Currencies that exist but have no configured assets are omitted. Public endpoint, no authentication required. # public/get_all_instruments Source: https://docs.derive.xyz/api-reference/market-data/publicget_all_instruments /openapi.json post /public/get_all_instruments Returns a paginated list of full instrument definitions filtered by `instrument_type`, optional `currency`, and `expired` flag, with `page`/`page_size` (max 1000) controls and pagination metadata. Expired options are included when the `expired` flag is set. Public endpoint. # public/get_all_live_instruments Source: https://docs.derive.xyz/api-reference/market-data/publicget_all_live_instruments /openapi.json post /public/get_all_live_instruments Returns a sorted list of the names of every currently live instrument (active and within its scheduled activation window). Takes no parameters and returns names only, not full definitions. Public endpoint. # public/get_assets Source: https://docs.derive.xyz/api-reference/market-data/publicget_assets /openapi.json post /public/get_assets Returns the assets of a given `asset_type` (option, perp, or erc20) for a `currency`, with `expired` controlling whether past-expiry options are included. Each entry includes the asset id, name, on-chain address and sub_id, collateral/position flags, and type-specific details (option strike/expiry/settlement price, perp funding config, or ERC20 lending indices). Public endpoint. # public/get_currency Source: https://docs.derive.xyz/api-reference/market-data/publicget_currency /openapi.json post /public/get_currency Returns the full detail record for one currency named by the required `currency` parameter: supported instrument types, risk managers, current and 24h spot price, margin collateral discounts, lending APYs and totals, per-manager open-interest caps and current OI, and protocol asset addresses. Returns a not-found error for unknown currencies. Public endpoint. # public/get_funding_rate_history Source: https://docs.derive.xyz/api-reference/market-data/publicget_funding_rate_history /openapi.json post /public/get_funding_rate_history Returns funding-rate OHLC candles for a perpetual instrument (`instrument_name`) over an optional `start_timestamp`/`end_timestamp` window (UTC milliseconds) at a `period` granularity (defaults to 1h; 60s up to 1w supported). Each candle carries open/high/low/close per-hour rates plus a `funding_rate` mirroring the close. Public endpoint. # public/get_index_chart_data Source: https://docs.derive.xyz/api-reference/market-data/publicget_index_chart_data /openapi.json post /public/get_index_chart_data Returns spot index OHLC candles for a `currency` between required `start_timestamp` and `end_timestamp` (UTC seconds) at the given `period` granularity (1m up to 1w). Each candle includes open/high/low/close and a `price` mirroring the close. The requested range is clamped to a maximum number of buckets. Public endpoint. # public/get_instrument Source: https://docs.derive.xyz/api-reference/market-data/publicget_instrument /openapi.json post /public/get_instrument Returns the full public definition of one instrument named by `instrument_name`: type, activation window and active status, tick size, min/max/step amounts, maker/taker/base fee rates, pro-rata matching parameters, base/quote currencies, and type-specific details (option, perp, or spot). Expired options remain queryable by `instrument_name`. Public endpoint. # public/get_interest_rate_history Source: https://docs.derive.xyz/api-reference/market-data/publicget_interest_rate_history /openapi.json post /public/get_interest_rate_history Returns lending borrow/supply APY OHLC candles for a `currency`'s pool over an optional time window (UTC milliseconds) at a `period` of 1h or larger (defaults to 1h; sub-hour periods are rejected). An optional `risk_universe_id` restricts to one pool; otherwise every universe's candles are returned, each tagged and carrying pool total supply/borrow. Public endpoint. # public/get_latest_signed_feeds Source: https://docs.derive.xyz/api-reference/market-data/publicget_latest_signed_feeds /openapi.json post /public/get_latest_signed_feeds Returns the most recent oracle-signed feed data — spot, forward, volatility (SVI), rate, perp, and perpetual funding-rate feeds — each with signer addresses and signatures for on-chain submission. Optional `currency` and `expiry` filters narrow the result (use `expiry` 0 for the non-expiry feeds: spot, perp and funding); both default to all. Public endpoint. # public/get_option_settlement_prices Source: https://docs.derive.xyz/api-reference/market-data/publicget_option_settlement_prices /openapi.json post /public/get_option_settlement_prices Returns the settlement price for each settled option expiry of a `currency`, with the expiry date (YYYYMMDD), unix expiry timestamp, and price. Only already-settled expiries are returned. Unknown currencies return a not-found error. Public endpoint. # public/get_risk_universes Source: https://docs.derive.xyz/api-reference/market-data/publicget_risk_universes /openapi.json post /public/get_risk_universes Returns every risk universe (the isolated risk-containment boundaries trades never cross), each with its display name and description, its managers — at most one Standard (`SM`) and one Portfolio (`PM2`), each listing the derivative instruments it risk-prices and the collateral assets it accepts with their margin discounts and deposit metadata — and its Security Module. The one call that answers which universe and manager to choose: pick a universe, pick a manager in it, and pass its `manager_id` when depositing to a new subaccount (the accepted collateral entries carry the `asset` address for the deposit). The no-margin fallback universe (id 0) is included; it custodies dead-lettered deposits and lists no tradeable instruments. Public endpoint, no authentication required. # public/get_ticker Source: https://docs.derive.xyz/api-reference/market-data/publicget_ticker /openapi.json post /public/get_ticker Returns the latest ticker snapshot for a single instrument named by `instrument_name`. The payload matches the data of a `ticker_slim.{instrument}.{interval}` subscription update (mark/index prices, best bid/ask, sizes, greeks, and related fields). Returns an error if no ticker is available. Public endpoint. # public/get_tickers Source: https://docs.derive.xyz/api-reference/market-data/publicget_tickers /openapi.json post /public/get_tickers Returns a map of instrument name to latest ticker snapshot for an `instrument_type`. Options require both `currency` and an 8-digit `expiry_date` (YYYYMMDD); perps and spot accept an optional `currency` (omit to fetch all currencies) and reject `expiry_date`. Public endpoint. # public/get_trade_history Source: https://docs.derive.xyz/api-reference/market-data/publicget_trade_history /openapi.json post /public/get_trade_history Returns paginated, anonymized trades with optional filters: `trade_id` (a UUID, which overrides all other filters), `instrument_name`, `instrument_type` (erc20/option/perp), `currency`, `subaccount_id`, `batch_status` (a batch-status name; unset returns trades in every batch state), and `from_timestamp`/`to_timestamp`. Each trade is enriched with its settlement status and transaction hash. Public endpoint. # public/get_tradingview_chart_data Source: https://docs.derive.xyz/api-reference/market-data/publicget_tradingview_chart_data /openapi.json post /public/get_tradingview_chart_data Returns traded-price OHLCV candles for an `instrument_name` between required `start_timestamp` and `end_timestamp` (UTC seconds) at the given `period` (1m up to 1w), aggregated from executed trade history. Each candle includes open/high/low/close prices plus USD and contract volume, formatted for charting. Public endpoint. # private/get_mmp_config Source: https://docs.derive.xyz/api-reference/market-maker-protection/privateget_mmp_config /openapi.json post /private/get_mmp_config Returns the market maker protection (MMP) settings for a subaccount, optionally filtered to a single currency. Each entry reports the amount and delta limits (as decimal strings), the rolling interval and freeze duration (in milliseconds), and the current freeze state, including whether MMP is currently frozen and the timestamp at which it unfreezes. # private/reset_mmp Source: https://docs.derive.xyz/api-reference/market-maker-protection/privatereset_mmp /openapi.json post /private/reset_mmp Clears an active market maker protection freeze and resets the rolling MMP window for a subaccount, optionally scoped to a single currency. Use this to resume quoting after MMP has frozen a subaccount. Requires a trading-scoped session key and returns "ok" on success. # private/set_mmp_config Source: https://docs.derive.xyz/api-reference/market-maker-protection/privateset_mmp_config /openapi.json post /private/set_mmp_config Creates or fully replaces the market maker protection settings for a subaccount and currency. Accepts an amount limit and delta limit (as decimals) plus the rolling interval and freeze duration in milliseconds; omitted limits default to zero. Requires a trading-scoped session key and echoes the applied configuration back. # Authenticate a WebSocket connection Source: https://docs.derive.xyz/api-reference/methods/authenticate-a-websocket-connection WebSocket-only method that authenticates the connection so subsequent private/* calls and private channel subscriptions act on your wallet without per-request headers. Pass the wallet address, a millisecond UTC timestamp, and an EIP-191 signature of that timestamp made by either the wallet itself or one of its registered session keys. Returns the subaccount ids the authenticated key can access. # Enable or disable cancel on disconnect Source: https://docs.derive.xyz/api-reference/methods/enable-or-disable-cancel-on-disconnect WebSocket-only method that toggles cancel on disconnect for the authenticated wallet. While enabled, dropping the WebSocket connection cancels the wallet's open orders, trigger and algo orders, and quotes. Pass enabled true or false; omitting it leaves the setting unchanged. Requires a session key with full trade scope; returns "ok". # Subscribe to notification channels Source: https://docs.derive.xyz/api-reference/methods/subscribe-to-notification-channels WebSocket-only method that subscribes the connection to one or more notification channels by name — see the Subscriptions section for the available channels and their address formats (e.g. orderbook.ETH-PERP.10.20). Duplicate names are deduplicated; private channels require a logged-in connection with access to the referenced subaccount. Returns a per-channel status map ("ok" or an error string) plus the connection's full set of subscriptions after the call. # Unsubscribe from notification channels Source: https://docs.derive.xyz/api-reference/methods/unsubscribe-from-notification-channels WebSocket-only method that removes the connection's subscriptions for the given channel names; omit channels (or pass null) to unsubscribe from everything. Returns a per-channel status map ("ok" or an error string) plus the subscriptions that remain after the call. # public/get_onchain_action_history Source: https://docs.derive.xyz/api-reference/onchain-actions/publicget_onchain_action_history /openapi.json post /public/get_onchain_action_history Returns the lifecycle of onchain actions submitted via `OnchainActionManager.sol`. Public endpoint. Onchain actions are either applied directly or with a `fallback` flag. Each onchain action is affected by the `fallback` flag uniquely: - Deposits: sequencer moved the deposit into the `fallback_subaccount` - SetSessionKey: no-op - Admin actions: no-op The onchain action can have several states: - `applied`: action was successfully applied by the sequencer - `applied_with_fallback`: action was applied by the sequencer with fallback=true - `instant_fallback`: action failed its initial attempt, will be re-submitted with fallback=true - `retry_then_fallback`: action failed its initial attempt, will be re-submitted with fallback=false until the retry budget is exceeded, then re-submitted with fallback=true - `never_escalate`: action failed its initial attempt but will retry until successful, never with fallback=true # public/get_pending_deposits Source: https://docs.derive.xyz/api-reference/onchain-actions/publicget_pending_deposits /openapi.json post /public/get_pending_deposits Returns deposits queued on L1 but not yet applied by the sequencer, indexed for a wallet and keyed by (tx_hash, log_index). Once a deposit is applied it moves to private/get_deposit_history; this endpoint covers the window in between. Public because a first-time depositor has no account (and thus no session key) until their deposit is applied. # public/register_deposit_address Source: https://docs.derive.xyz/api-reference/onchain-actions/publicregister_deposit_address /openapi.json post /public/register_deposit_address Returns the deterministic on-chain deposit address for a wallet and records it so incoming deposits are watched and credited. Pass the wallet and optionally an existing subaccount id; when creating a new subaccount (subaccount omitted or 0) a non-zero manager_id is required. Repeated calls return the same cached address and keep it alive, while new registrations are rate limited per rolling window; unused addresses are dropped after 7 days. # private/cancel Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel /openapi.json post /private/cancel Cancels one resting order identified by order_id, subaccount_id and instrument_name. Requires any trade scope on the session; returns the cancelled order with its final status. # private/cancel_algo_order Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_algo_order /openapi.json post /private/cancel_algo_order Cancels a single active algo order identified by order_id and subaccount_id, stopping any further child-order execution. Requires any trade scope; returns the cancelled order. # private/cancel_all Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_all /openapi.json post /private/cancel_all Cancels every open order on the given subaccount. Optional cancel_trigger_orders and cancel_algo_orders flags additionally clear the subaccount's trigger and algo orders in the same call. Requires any trade scope; returns "ok" on success. # private/cancel_all_algo_orders Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_all_algo_orders /openapi.json post /private/cancel_all_algo_orders Cancels every active algo order on the given subaccount. Requires any trade scope; returns "ok" on success. # private/cancel_all_trigger_orders Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_all_trigger_orders /openapi.json post /private/cancel_all_trigger_orders Cancels every pending trigger order on the given subaccount. Requires any trade scope; returns "ok" on success. # private/cancel_by_instrument Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_by_instrument /openapi.json post /private/cancel_by_instrument Cancels every open order for a subaccount on a single instrument. Requires any trade scope; returns the number of orders cancelled. # private/cancel_by_label Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_by_label /openapi.json post /private/cancel_by_label Cancels a subaccount's open orders that carry the given client label, optionally scoped to a single instrument_name. When an instrument is supplied the call is acknowledged with the number of orders cancelled; without one it runs as a bulk fire-and-forget cancel and returns a -1 sentinel count. Requires any trade scope. # private/cancel_by_nonce Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_by_nonce /openapi.json post /private/cancel_by_nonce Cancels a single order identified by its signing nonce, instrument_name and subaccount_id. The owning wallet is taken from the authenticated session rather than the request. Requires any trade scope; returns the cancelled order. # private/cancel_trigger_order Source: https://docs.derive.xyz/api-reference/orderbook/privatecancel_trigger_order /openapi.json post /private/cancel_trigger_order Cancels a single pending trigger order identified by order_id and subaccount_id. Requires any trade scope; returns the cancelled order. # private/get_algo_orders Source: https://docs.derive.xyz/api-reference/orderbook/privateget_algo_orders /openapi.json post /private/get_algo_orders Returns all active algo orders (e.g. time-sliced execution orders) for the given subaccount as a flat list. Read-only query. # private/get_open_orders Source: https://docs.derive.xyz/api-reference/orderbook/privateget_open_orders /openapi.json post /private/get_open_orders Returns all currently open orders for the given subaccount, including each order's instrument, direction, prices, amounts and status. Only orders resting on the order book are returned: pending trigger orders that have not yet fired are available from get_trigger_orders, and active algo orders from get_algo_orders. Once a trigger order fires, the resulting order appears here if it rests on the book rather than filling immediately; algo orders execute as IOC slices and so never rest here. Read-only query. # private/get_order Source: https://docs.derive.xyz/api-reference/orderbook/privateget_order /openapi.json post /private/get_order Returns one order (active or completed) by order_id and subaccount_id; the subaccount filter enforces ownership. Returns the order or an order-does-not-exist error. # private/get_trigger_orders Source: https://docs.derive.xyz/api-reference/orderbook/privateget_trigger_orders /openapi.json post /private/get_trigger_orders Returns all pending trigger (conditional) orders for the given subaccount that have not yet fired. Read-only query. # private/order Source: https://docs.derive.xyz/api-reference/orderbook/privateorder /openapi.json post /private/order Submits a signed limit or market order for a subaccount, specifying instrument, direction, amount, limit price and max fee, plus optional flags like time-in-force, reduce-only, post-only and MMP. The same endpoint also creates trigger orders (via trigger_type/trigger_price) and algo orders (via algo_type), though the two cannot be combined in one request. Requires a signed order payload and Orderbook trade scope for the instrument's asset; returns the created order with its assigned id and current status. # private/order_debug Source: https://docs.derive.xyz/api-reference/orderbook/privateorder_debug /openapi.json post /private/order_debug Takes the same params as private/order and rebuilds the order Action without executing anything, returning the EIP-712 encoded_data, encoded_data_hashed, action_hash, typed_data_hash, domain_separator, action_typehash, module, owner and expected_signer, plus the decoded order action data. Byte-compare these against your local computation to find why a signature is rejected. Requires a logged-in session; no trade scope needed. # private/order_quote Source: https://docs.derive.xyz/api-reference/orderbook/privateorder_quote /openapi.json post /private/order_quote Dry-run pricing helper that estimates the outcome of a prospective order without placing it, returning projected fill price and amount, fees, pre/post initial margin, realized PnL, resulting order status and (where relevant) liquidation price and max tradable amount. All monetary values are returned as decimal strings. The private variant requires account-level authentication. # private/replace Source: https://docs.derive.xyz/api-reference/orderbook/privatereplace /openapi.json post /private/replace Atomically cancels a resting order (identified by order_id_to_cancel or nonce_to_cancel) and submits a replacement order in a single request. The payload is a full new-order specification plus the cancel target and an optional expected_filled_amount guard. Requires a signed order payload and Orderbook trade scope; returns both the cancelled and the newly created order. # public/order_quote Source: https://docs.derive.xyz/api-reference/orderbook/publicorder_quote /openapi.json post /public/order_quote Public dry-run variant of order_quote that estimates fill price, fees, margin impact, projected order status and realized PnL for a prospective order without placing it, returning all values as decimal strings. Unlike private/order_quote it bypasses session scope checks, so it can be used to preview costs without account authentication. # private/get_subaccount_value_history Source: https://docs.derive.xyz/api-reference/other/privateget_subaccount_value_history /openapi.json post /private/get_subaccount_value_history # private/liquidate Source: https://docs.derive.xyz/api-reference/other/privateliquidate /openapi.json post /private/liquidate # public/get_margin Source: https://docs.derive.xyz/api-reference/other/publicget_margin /openapi.json post /public/get_margin # public/set_socialization_feed_data Source: https://docs.derive.xyz/api-reference/other/publicset_socialization_feed_data /openapi.json post /public/set_socialization_feed_data # public/start_auction Source: https://docs.derive.xyz/api-reference/other/publicstart_auction /openapi.json post /public/start_auction # public/get_all_referral_codes Source: https://docs.derive.xyz/api-reference/referrals/publicget_all_referral_codes /openapi.json post /public/get_all_referral_codes Returns every registered referral code along with the owner's wallet address and, when configured, the wallet that receives referral rewards. Takes no parameters. # public/get_referral_performance Source: https://docs.derive.xyz/api-reference/referrals/publicget_referral_performance /openapi.json post /public/get_referral_performance Returns broker-program referral performance for a referral code, identified either by the code itself or by the referrer's wallet, over a requested time window (start_ms to end_ms, capped at 28 days). The response includes total notional volume, referred fees, fee rewards, builder fees collected, the applicable fee-share percentage, and a per-role/currency/instrument-type breakdown, all as decimal strings. # private/cancel_batch_quotes Source: https://docs.derive.xyz/api-reference/rfq/privatecancel_batch_quotes /openapi.json post /private/cancel_batch_quotes Cancels all open quotes on a subaccount that match the supplied filters (any combination of quote_id, rfq_id, label, and nonce, applied together) in a single atomic operation. Omitting the optional filters targets all of the subaccount's open quotes. Returns the list of cancelled quote ids. Requires an RFQ trade scope. # private/cancel_batch_rfqs Source: https://docs.derive.xyz/api-reference/rfq/privatecancel_batch_rfqs /openapi.json post /private/cancel_batch_rfqs Cancels all open RFQs on a subaccount that match the supplied filters (any combination of rfq_id, label, and nonce, applied together), cascade-cancelling their quotes. Omitting the optional filters targets all of the subaccount's open RFQs. Returns the set of cancelled RFQs. Requires an RFQ trade scope. # private/cancel_quote Source: https://docs.derive.xyz/api-reference/rfq/privatecancel_quote /openapi.json post /private/cancel_quote Cancels one open quote owned by the subaccount, identified by quote_id (optionally further constrained by rfq_id, label, or nonce). Returns the cancelled quote, or a quote-not-found error if it does not exist or is no longer open. Requires an RFQ trade scope. # private/cancel_rfq Source: https://docs.derive.xyz/api-reference/rfq/privatecancel_rfq /openapi.json post /private/cancel_rfq Cancels one open RFQ owned by the given subaccount and cascade-cancels any quotes makers have submitted against it, notifying those makers. Identify the RFQ by its rfq_id. Returns "ok" on success. Requires an RFQ trade scope. # private/execute_quote Source: https://docs.derive.xyz/api-reference/rfq/privateexecute_quote /openapi.json post /private/execute_quote Taker-side call that accepts a specific maker quote and settles the trade atomically. You reference the rfq_id and quote_id and supply the taker's priced legs, direction, max fee, and EIP-712 signature (signer, signature, nonce, expiry), with an optional taker-protection flag. The signature is verified and the taker fill, maker quote, and RFQ are updated together in one atomic step. Requires trade scope for every instrument in the legs. # private/get_quotes Source: https://docs.derive.xyz/api-reference/rfq/privateget_quotes /openapi.json post /private/get_quotes Returns a paginated, merged view of a subaccount's quotes, combining currently open quotes with the archived history of filled, cancelled, and expired ones. Supports filtering by quote_id, rfq_id, status, and a from/to timestamp window, with page and page_size controls. Each entry includes priced legs, direction, fees, liquidity role, and fill percentage. # private/get_rfqs Source: https://docs.derive.xyz/api-reference/rfq/privateget_rfqs /openapi.json post /private/get_rfqs Returns a paginated, merged view of a subaccount's RFQs, combining currently open RFQs with the archived history of filled, cancelled, and expired ones. Supports filtering by rfq_id, status, and a from/to timestamp window, with page and page_size controls. Each entry includes legs, status, timestamps, cost bounds, and fill percentage. # private/poll_quotes Source: https://docs.derive.xyz/api-reference/rfq/privatepoll_quotes /openapi.json post /private/poll_quotes Taker-side call that lists the maker quotes received against RFQs owned by the subaccount. Supports filtering by quote_id, rfq_id, status, and a timestamp window, with pagination. Returns public quote views (including the maker's wallet) without any signing material. # private/poll_rfqs Source: https://docs.derive.xyz/api-reference/rfq/privatepoll_rfqs /openapi.json post /private/poll_rfqs Maker-side call that lists the RFQs a subaccount is eligible to quote, i.e. RFQs that are open to all makers or that name the maker's wallet as a counterparty. Supports filtering by rfq_id, status, an originating RFQ subaccount, and a timestamp window, with pagination. Returns public RFQ views without taker-private signing material. # private/replace_quote Source: https://docs.derive.xyz/api-reference/rfq/privatereplace_quote /openapi.json post /private/replace_quote Maker-side call that atomically cancels an existing quote and submits a new signed quote for the same RFQ in one operation. You provide the new priced legs, direction, max fee, and EIP-712 signature, plus the quote to cancel (by quote_id or nonce_to_cancel). Returns the cancellation result together with the newly created quote. Requires trade scope for every instrument quoted. # private/rfq_get_best_quote Source: https://docs.derive.xyz/api-reference/rfq/privaterfq_get_best_quote /openapi.json post /private/rfq_get_best_quote Taker-side dry run that evaluates a prospective RFQ (its legs and intended direction) without creating anything. Returns the best available maker quote if one exists, along with the expected fee, validity, projected liquidation prices, and realized-PnL estimate for taking it. # private/send_quote Source: https://docs.derive.xyz/api-reference/rfq/privatesend_quote /openapi.json post /private/send_quote Maker-side call that submits a priced, signed quote in response to an existing RFQ. You provide the priced legs, direction, max fee, an EIP-712 signature (signer, signature, nonce, expiry), and optional label or MMP flag. The quote's signature is verified and validated against the referenced RFQ before it is stored. Returns the created quote. Requires trade scope for every instrument quoted. # private/send_rfq Source: https://docs.derive.xyz/api-reference/rfq/privatesend_rfq /openapi.json post /private/send_rfq Taker-side call that opens a new RFQ for a multi-leg structure on a subaccount, inviting makers to quote it. You supply the legs (instrument, amount, and direction per leg) plus optional limits such as min/max total cost, a partial-fill step, a label, and a list of specific counterparties to restrict who can see it. Returns the created RFQ including its assigned id and validity window. Requires trade scope for every instrument in the legs. # public/execute_quote_debug Source: https://docs.derive.xyz/api-reference/rfq/publicexecute_quote_debug /openapi.json post /public/execute_quote_debug Public signing-preview helper that returns the taker's EIP-712 encoding artifacts (encoded data and hashes) plus the maker-side encoded legs and legs hash for a quote execution a client is about to sign. It does not verify signatures, persist anything, or execute the trade; use it to construct and check the exact payload before calling private/execute_quote. # public/send_quote_debug Source: https://docs.derive.xyz/api-reference/rfq/publicsend_quote_debug /openapi.json post /public/send_quote_debug Public signing-preview helper that returns the EIP-712 encoding artifacts (encoded data, its hash, the action hash, and the typed-data hash) for a maker quote a client is about to sign. It does not verify signatures, persist anything, or place a quote; use it to construct and check the exact payload to sign before calling private/send_quote. # private/edit_session_key Source: https://docs.derive.xyz/api-reference/session-keys/privateedit_session_key /openapi.json post /private/edit_session_key Updates an existing session key's label, IP allowlist, and/or off-chain scopes for a given wallet; it cannot change on-chain (protocol) scopes, for which you must re-register with private/set_session_key. Editing only the label needs account-info permission, while changing the IP allowlist or off-chain scopes requires admin/owner authorization. Returns the updated session key details. # private/session_keys Source: https://docs.derive.xyz/api-reference/session-keys/privatesession_keys /openapi.json post /private/session_keys Returns every session key registered to the given wallet, including expired and not-yet-activated keys, each with its public address, scopes, expiry, label, IP allowlist, and permitted subaccounts. # private/set_session_key Source: https://docs.derive.xyz/api-reference/session-keys/privateset_session_key /openapi.json post /private/set_session_key Authorizes a new session key for a wallet from a signed action, granting it a set of on-chain (protocol) scopes and off-chain scopes with an expiry, an optional label, an optional IP allowlist, and an optional list of subaccounts it may act on (defaults to all of the wallet's subaccounts). Send the signed action fields (nonce, signer, signature, signature expiry, and module) alongside the requested scopes; the endpoint returns the registered key's public address and its granted scopes, expiry, allowlist, label, and subaccounts. # public/get_wallets_from_session_key Source: https://docs.derive.xyz/api-reference/session-keys/publicget_wallets_from_session_key /openapi.json post /public/get_wallets_from_session_key Public lookup that returns the wallet addresses a given session key is registered to, sorted by expiry, with expired keys omitted. An optional scope filter narrows results to keys holding that off-chain scope; returns an error if no matching, unexpired keys exist. # private/change_subaccount_label Source: https://docs.derive.xyz/api-reference/subaccounts/privatechange_subaccount_label /openapi.json post /private/change_subaccount_label Updates the human-readable label for a subaccount. The label must be at most 16 characters, otherwise an invalid-params error is returned. Requires a signed session key with account-info scope; on success it echoes back the subaccount ID and the new label. # private/delete_subaccount Source: https://docs.derive.xyz/api-reference/subaccounts/privatedelete_subaccount /openapi.json post /private/delete_subaccount Permanently deletes one of the caller's subaccounts via a signed action. The subaccount must be fully wound down: no balances or debt, no open orders, RFQs, or quotes, not a vault, not under auction, and not the wallet's fallback subaccount. Deletion never moves value; subaccount ids are never reused. This is an owner-or-admin operation (session keys need the admin scope). # private/get_all_portfolios Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_all_portfolios /openapi.json post /private/get_all_portfolios Returns the complete portfolio (same shape as get_subaccount) for every subaccount owned by the given wallet, including valuations, margin figures, open orders, positions, and collaterals. If an individual subaccount's portfolio cannot be built, it is returned as a placeholder entry with failed_to_fetch set to true rather than failing the whole request. # private/get_collaterals Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_collaterals /openapi.json post /private/get_collaterals Returns the subaccount ID and the list of its collateral balances, including each asset's amount and current value valued against live feed data. A lighter alternative to get_subaccount when only collateral holdings are needed. # private/get_margin Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_margin /openapi.json post /private/get_margin Calculates net initial and maintenance margin for a subaccount before and after an optional simulated state change (position and/or collateral deltas), and whether that change would pass the margin requirement. Values are net margin — mark-to-market value minus the requirement — so positive means healthy. Does not take open-order margin into account. # private/get_positions Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_positions /openapi.json post /private/get_positions Returns the subaccount ID and the list of its active positions, including size, mark price, average price, unrealized and realized PnL, and Greeks where applicable, valued against live feed data at request time. A lighter alternative to get_subaccount when only positions are needed. # private/get_subaccount Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_subaccount /openapi.json post /private/get_subaccount Returns the complete portfolio for a single subaccount: its label, manager and risk-universe identifiers, margin type and liquidation status, aggregate valuation and margin figures (positions, collaterals, initial and maintenance margin, open-order margin) as decimal strings, plus the full lists of open orders, positions, and collateral balances. Margin, mark price, and Greek values are computed from live feed data at request time. # private/get_subaccounts Source: https://docs.derive.xyz/api-reference/subaccounts/privateget_subaccounts /openapi.json post /private/get_subaccounts Returns the wallet address and the sorted list of subaccount IDs owned by that wallet. Yields an empty list if the wallet has no subaccounts. # public/margin_watch Source: https://docs.derive.xyz/api-reference/subaccounts/publicmargin_watch /openapi.json post /public/margin_watch Calculates the mark-to-market value and initial/maintenance margin for a given subaccount, with per-position and per-collateral breakdowns, computed from live feed data at request time. Margins are reported on the margin basis actually in effect: when a delayed-liquidation override is active the response's is_delayed_liquidation flag is true and the reported margins use the temporarily lowered requirements. A maintenance margin below zero means the subaccount is flagged for liquidation. # public/decode_action Source: https://docs.derive.xyz/api-reference/system/publicdecode_action /openapi.json post /public/decode_action Runs a hex-encoded Action.data payload through the exchange's own ABI decoder and returns either the decoded fields or the exact reason the bytes were rejected, naming the 32-byte word and the field that broke. Reach for this first when a request fails signature verification (14014): every other route encodes Action.data server-side, so a client whose encoder emits a bad word never sees a decode error - the hashes simply disagree. This is the only route a client's own bytes reach the decoder. Needs no auth and executes nothing. # public/get_time Source: https://docs.derive.xyz/api-reference/system/publicget_time /openapi.json post /public/get_time Returns the current server time in milliseconds since the UNIX epoch. Takes no parameters and requires no authentication. Use it to align the timestamps and nonces bound into signed actions with the server clock. # public/get_transaction Source: https://docs.derive.xyz/api-reference/system/publicget_transaction /openapi.json post /public/get_transaction Looks up the settlement lifecycle of a previously submitted operation by its operation UUID (op_uuid). Returns the operation's serialized data along with its batch status, settlement transaction hash, and any error log; the status is null until the operation has been picked up for settlement. # public/getRateLimits Source: https://docs.derive.xyz/api-reference/system/publicgetratelimits /openapi.json post /public/getRateLimits Returns the caller's current remaining rate-limit allowances for the active connection, broken out into matching-request, non-matching-request, and per-endpoint buckets. On WebSocket connections it also includes the remaining connection allowance. # private/transfer_positions Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/privatetransfer_positions /openapi.json post /private/transfer_positions Atomically transfers one or more derivative positions between a maker and a taker subaccount using matched, signed transfer quotes. Each side supplies its subaccount, signer, nonce, signature, expiry, max fee, direction, and priced legs (instrument, amount, price); the two quotes must mirror each other. Requires a session key with transfer permission and returns the resulting operation details. # private/transfer_spot Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/privatetransfer_spot /openapi.json post /private/transfer_spot Submits a signed transfer of a single spot asset from one subaccount to another subaccount you own. You specify the source and destination subaccounts (or set new_subaccount_manager to create a new destination subaccount under a manager), the asset and its sub_id, the amount, a nonce, signer, signature with expiry, and the maximum USD sequencer fee. Requires a session key with a transfer permission and returns the operation id and uuid. # private/transfer_spot_external Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/privatetransfer_spot_external /openapi.json post /private/transfer_spot_external Submits a signed transfer of a single spot asset to a subaccount belonging to a different owner. Alongside the standard transfer fields (asset, sub_id, amount, nonce, signer, signature, expiry, max USD fee) you give the recipient's wallet address and either an existing destination subaccount or 0 to create a new one under new_subaccount_manager; the max fee must cover both the transfer and any subaccount-creation cost. Requires a session key permitted to transfer to a different owner and returns the operation id and uuid. # private/update_whitelisted_recipients Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/privateupdate_whitelisted_recipients /openapi.json post /private/update_whitelisted_recipients Adds and/or removes recipient wallet addresses on an account's transfer whitelist via a signed, wallet-level action. Provide the owner wallet, signer, nonce, signature with expiry, and the add and remove address lists. This is an owner-or-admin operation (session keys need the admin scope); it returns the operation id, uuid, and the full whitelist after the update is applied. # private/withdraw Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/privatewithdraw /openapi.json post /private/withdraw Submits a signed request to withdraw a spot asset out of a subaccount. You provide the subaccount id, asset name, amount in underlying units, a nonce, the signer, an EIP-712 signature with its expiry, and the maximum sequencer fee (in USD) you authorise; setting force_batch controls whether the withdrawal is batched. Requires a session key with withdraw permission, and returns the accepted operation id and its uuid for tracking. # public/withdraw_debug Source: https://docs.derive.xyz/api-reference/transfers-&-withdrawals/publicwithdraw_debug /openapi.json post /public/withdraw_debug Dry-run helper that returns the EIP-712 typed data and hashes that would be computed for the given withdrawal parameters, so clients can verify their own signing and hashing before submitting. Takes the same inputs as private/withdraw (subaccount, signer, nonce, amount, max fee, expiry, asset) but performs no state change and needs no signature. # private/burn_vault_shares Source: https://docs.derive.xyz/api-reference/vault-curators/privateburn_vault_shares /openapi.json post /private/burn_vault_shares Curator-only endpoint that settles a pending withdraw request by signing a burn approval at a quoted share price (USD per share). Takes the request id and the user's withdraw-action hash, burns the shares, and returns the settlement result. Requires the curator mint-and-burn permission. # private/create_vault Source: https://docs.derive.xyz/api-reference/vault-curators/privatecreate_vault /openapi.json post /private/create_vault Registers a new vault on-chain from a signed action; the signing wallet becomes the vault's curator and seeds the initial deposit from its funding subaccount. Inputs set the deposit asset, initial deposit amount, initial share price, management/performance fee rates (in basis points), max slippage, redemption cooldown, an optional benchmark asset for the high-water mark, and the max sequencer fee authorized. Requires the vault curator-create permission. # private/force_burn Source: https://docs.derive.xyz/api-reference/vault-curators/privateforce_burn /openapi.json post /private/force_burn Curator-signed endpoint that builds an on-chain action to forcibly redeem a given holder's entire share balance at the current mark-to-market share price. Inputs are the vault subaccount the curator signs on, the EIP-712 envelope (nonce, signer, signature, expiry), and the holder's wallet address. Requires the curator mint-and-burn permission; the protocol re-verifies the curator signature. # private/get_curated_vaults Source: https://docs.derive.xyz/api-reference/vault-curators/privateget_curated_vaults /openapi.json post /private/get_curated_vaults Returns the subaccount ids of the vaults curated by the given wallet. The wallet parameter must match the authenticated connection. Read-only. # private/get_live_burn_requests Source: https://docs.derive.xyz/api-reference/vault-curators/privateget_live_burn_requests /openapi.json post /private/get_live_burn_requests Curator-only endpoint that returns a FIFO page of a vault's pending withdraw (burn) requests. Inputs are the vault subaccount and a page limit. Requires the curator mint-and-burn permission. # private/get_live_mint_requests Source: https://docs.derive.xyz/api-reference/vault-curators/privateget_live_mint_requests /openapi.json post /private/get_live_mint_requests Curator-only endpoint that returns a FIFO page of a vault's pending deposit (mint) requests. Inputs are the vault subaccount and a page limit. Requires the curator mint-and-burn permission. # private/mint_vault_shares Source: https://docs.derive.xyz/api-reference/vault-curators/privatemint_vault_shares /openapi.json post /private/mint_vault_shares Curator-only endpoint that settles a pending deposit request by signing a mint approval at a quoted share price (USD per share). Takes the request id and the user's deposit-action hash, mints the corresponding shares, and returns the settlement result. Requires the curator mint-and-burn permission. # private/reject_deposit_request Source: https://docs.derive.xyz/api-reference/vault-curators/privatereject_deposit_request /openapi.json post /private/reject_deposit_request Curator-only endpoint that removes a queued deposit request off-chain (no on-chain settlement), recording the rejection with an optional short reason. Takes the request id and returns an acknowledgement. Requires the curator mint-and-burn permission. # private/update_vault_info Source: https://docs.derive.xyz/api-reference/vault-curators/privateupdate_vault_info /openapi.json post /private/update_vault_info Applies an off-chain patch to a vault the caller curates, updating any of its display name, description, advisory mark-to-market cap (a USD decimal), or whitelist-only flag. Only the fields supplied are changed. The caller must own the vault's subaccount (i.e. be its curator); description length is capped. # private/cancel_all_vault_requests Source: https://docs.derive.xyz/api-reference/vault-shareholders/privatecancel_all_vault_requests /openapi.json post /private/cancel_all_vault_requests Submits a signed cancel action that drains all of the caller's pending deposit and withdraw requests for a given vault and posts the corresponding on-chain operation. Input is the vault subaccount; any subaccount the caller owns may sign. Requires the user-cancel permission. # private/get_live_vault_requests Source: https://docs.derive.xyz/api-reference/vault-shareholders/privateget_live_vault_requests /openapi.json post /private/get_live_vault_requests Returns the caller's currently-pending vault deposit and withdraw requests, read live from the vault queue. Not paginated (the live queue is bounded); settled and terminal history is served by get_vault_request_history. The wallet parameter must match the authenticated connection. # private/get_shareholder_vaults Source: https://docs.derive.xyz/api-reference/vault-shareholders/privateget_shareholder_vaults /openapi.json post /private/get_shareholder_vaults Returns the subaccount ids of the vaults in which the given wallet holds shares. The wallet parameter must match the authenticated connection. Read-only. # private/get_vault_request_history Source: https://docs.derive.xyz/api-reference/vault-shareholders/privateget_vault_request_history /openapi.json post /private/get_vault_request_history Returns the caller's full vault action history (deposits, withdrawals, force-withdrawals, and cancels) across every status — enqueued, requested, applied, cancelled, rejected, or expired — with one row per action at its latest state. Paginated by page and page_size; monetary amounts, prices, and share counts are decimal strings. The wallet parameter must match the authenticated connection. # private/get_vault_shares Source: https://docs.derive.xyz/api-reference/vault-shareholders/privateget_vault_shares /openapi.json post /private/get_vault_shares Returns the caller's share balance for every vault it holds shares in, each paired with the full enriched vault row (the same shape as public/get_vault). The wallet parameter must match the authenticated connection. Read-only. # private/request_vault_deposit Source: https://docs.derive.xyz/api-reference/vault-shareholders/privaterequest_vault_deposit /openapi.json post /private/request_vault_deposit Submits a signed deposit action from the user's source subaccount and enqueues it in the vault's pending-deposit queue for the curator to settle. Inputs are the target vault subaccount, the deposit asset, and the amount as a decimal string. Returns an acknowledgement with the queued request; requires the user-deposit permission. # private/request_vault_withdraw Source: https://docs.derive.xyz/api-reference/vault-shareholders/privaterequest_vault_withdraw /openapi.json post /private/request_vault_withdraw Submits a signed withdraw action that enqueues a request to burn a given number of vault shares and redeem the proceeds to the user's subaccount. Inputs are the vault subaccount and the share quantity to burn (decimal string). Returns an acknowledgement with the queued request; requires the user-withdraw permission. # public/get_vault Source: https://docs.derive.xyz/api-reference/vault-shareholders/publicget_vault /openapi.json post /public/get_vault Returns the full vault record — on-chain state plus curator metadata — for one vault subaccount id. Unauthenticated and read-only. # public/get_vault_action_history Source: https://docs.derive.xyz/api-reference/vault-shareholders/publicget_vault_action_history /openapi.json post /public/get_vault_action_history Returns a vault's finalized deposit, withdrawal, fee-accrual, and cancel events — including NAV, share price, high-water mark, and the fee-share split across management, performance, curator, and protocol — at the vault level (per-holder position details are omitted). Inputs are the vault subaccount, an optional event_type filter, and pagination. Amounts and prices are decimal strings. Unauthenticated. # public/get_vault_performance_history Source: https://docs.derive.xyz/api-reference/vault-shareholders/publicget_vault_performance_history /openapi.json post /public/get_vault_performance_history Returns a time series of a vault's mark-to-market performance (NAV, share price, total and curator shares, high-water mark, optional benchmark) sampled hourly and downsampled to the requested resolution (1h, 8h, 24h, or 1wk). Supports optional from/to unix-second bounds and a limit (newest first, default 1000, capped at 10000). Values are decimal strings, with live prices nullable. Unauthenticated. # public/get_vaults Source: https://docs.derive.xyz/api-reference/vault-shareholders/publicget_vaults /openapi.json post /public/get_vaults Returns every vault in the system, each paired with its subaccount id, paginated by page and page_size. Unauthenticated and read-only. # Access Scopes Source: https://docs.derive.xyz/authentication/access-scopes The two scope sets that bound what a session key may do. Every [session key](/authentication/session-keys) carries **two independent scope sets**, enforced by two different layers: * **Protocol scopes** — on-chain authority. Signed into each action and re-validated by the protocol state machine. These decide what state-changing actions the key can authorize. * **Off-chain scopes** — server-side capabilities. Never signed, never seen by the protocol; enforced only by the server before a request is processed. All of the steps in this guide can be done through the UX or SDKs. ## Protocol scopes Protocol scopes form a **tree**. A grant for a branch covers everything under it, so `trade:all` covers `trade:orderbook:all` and `trade:rfq:option` alike, and `all` at any level covers its children. A request is checked by asking whether one of the key's grants *allows* the specific scope the action requires. Diagram of the Derive protocol scope tree. The root grant `admin` sits above the branches trade, transfer, withdraw, liquidate, set_session_key, and vault; a grant on any node implicitly covers all of its descendants (for example `trade:all` covers `trade:orderbook:all` and `trade:rfq:option`, and `all` at any level covers its children). The trade branch nests by venue (orderbook, rfq) and then instrument (perp, option, spot), and each node is labeled with the exact wire string a key is granted (e.g. `trade:orderbook:all`, `transfer:existing_subaccount`). A session key with no protocol scopes is read-only. A request is authorized by checking whether one of the key's grants allows the specific scope the action requires. Diagram of the Derive protocol scope tree. The root grant `admin` sits above the branches trade, transfer, withdraw, liquidate, set_session_key, and vault; a grant on any node implicitly covers all of its descendants (for example `trade:all` covers `trade:orderbook:all` and `trade:rfq:option`, and `all` at any level covers its children). The trade branch nests by venue (orderbook, rfq) and then instrument (perp, option, spot), and each node is labeled with the exact wire string a key is granted (e.g. `trade:orderbook:all`, `transfer:existing_subaccount`). A session key with no protocol scopes is read-only. A request is authorized by checking whether one of the key's grants allows the specific scope the action requires. ## `admin` and `owner` special powers These are actions only the `admin` or `owner` wallet can do: * can call `private/update_whitelisted_recipients` to add or remove whitelisted recipients for external transfers and withdrawals. * can withdraw and transfer to any recipient without setting a whitelist recipient. * can modify the ip whitelist for a session key ## Off-chain scopes Off-chain scopes are exact-match only — no tree, no hierarchy. | Wire string | Grants | | -------------- | ------------------------------------------------------------------------------------------------------------------ | | `account_info` | Read-tier account capability. Gates `private/change_subaccount_label` and a label-only `private/edit_session_key`. | ## How scopes are set Creator must specify the exact scopes for a session key. A session key with no scopes is a purely read-only session key. The "owner" wallet by default has all scopes. See [Session keys](/authentication/session-keys) for the full create / edit / list lifecycle and [Authentication](/authentication/session-login) for how a session resolves to protocol and off-chain scopes. Amounts and constants referenced by signed actions live on [Action signing](/authentication/action-signing). ```typescript TypeScript (SDK) theme={null} import { Wallet } from 'ethers'; import { DeriveClient, OffchainScope, ProtocolScopeCode, } from '@derivexyz/derive-ts'; const client = new DeriveClient({ network: 'testnet', wallet: process.env.PRIVATE_KEY!, }); // set_session_key is an action-signed private call: open and authenticate first. await client.connect(); await client.login(); // Generate the key locally — only its address is registered. const sessionKey = Wallet.createRandom(); const created = await client.sessionKeys.set({ publicSessionKey: sessionKey, expirySec: Math.floor(Date.now() / 1000) + 30 * 24 * 3600, // the KEY's lifetime: 30 days protocolScopes: [ ProtocolScopeCode.TradeOrderbookAll, ProtocolScopeCode.TradeRfqOption, ], offchainScopes: [OffchainScope.AccountInfo], label: 'trading-bot', }); // The ack echoes exactly what the key holds — there is no implicit default: console.log(created.protocol_scopes); // ['trade:orderbook:all', 'trade:rfq:option'] console.log(created.offchain_scopes); // ['account_info'] ``` ```python Python (SDK) theme={null} import asyncio import time from eth_account import Account from derive_py import WebSocketClient from derive_py.data_types import OffchainScope, ProtocolScope async def main(): client = WebSocketClient.from_env() # set_session_key is an action-signed private call: connect first. await client.connect() # Generate the key locally — only its address is registered. session_wallet = Account.create() created = await client.account.set_session_key( public_session_key=session_wallet.address, expiry_sec=int(time.time()) + 30 * 24 * 3600, # the KEY's lifetime: 30 days protocol_scopes=[ ProtocolScope.TRADE_ORDERBOOK_ALL, ProtocolScope.TRADE_RFQ_OPTION, ], offchain_scopes=[OffchainScope.ACCOUNT_INFO], label="trading-bot", ) # The ack echoes exactly what the key holds — there is no implicit default: print(created.protocol_scopes) # ['trade:orderbook:all', 'trade:rfq:option'] print(created.offchain_scopes) # ['account_info'] await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use alloy::signers::local::PrivateKeySigner; use derive_rs::{ Environment, WsClient, actions::session_key::{OffChainScope, ProtocolScope, SetSessionKeyArgs}, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = WsClient::new( Environment::Testnet, Some(std::env::var("DERIVE_PRIVATE_KEY")?), Some(std::env::var("DERIVE_WALLET")?), Some(9), ) .await?; // set_session_key is an action-signed private call: authenticate first. client.login().await?; // Generate the key locally — only its address is registered. let session_signer = PrivateKeySigner::random(); let expiry_sec = chrono::Utc::now().timestamp() as u64 + 30 * 24 * 3600; let args = SetSessionKeyArgs::builder() .public_session_key(session_signer.address().to_string()) .expiry_second(expiry_sec) // the KEY's lifetime: 30 days .protocol_scopes(vec![ ProtocolScope::TradeOrderbookAll, ProtocolScope::TradeRfqOption, ]) .off_chain_scopes(vec![OffChainScope::AccountInfo]) .label("trading-bot".to_string()) .subaccount_ids(vec![9]) .build(); let created = client.session_keys().create(args).await?; // The ack echoes exactly what the key holds — there is no implicit default: println!("{:?}", created.protocol_scopes); // ["trade:orderbook:all", "trade:rfq:option"] println!("{:?}", created.offchain_scopes); // ["account_info"] Ok(()) } ``` ```bash cURL theme={null} # Body nonce/signature: EIP-712 action signing under the set-session-key # module (see /action-signing). X-Derive* headers: session auth (see /json-rpc). curl -X POST https://api.derive.xyz/v3/private/set_session_key \ -H "Content-Type: application/json" \ -H "X-DeriveWallet: $WALLET_ADDRESS" \ -H "X-DeriveTimestamp: $TS" \ -H "X-DeriveSignature: $SIG" \ -d '{ "wallet": "0xYourWallet", "public_session_key": "0xSessionKeyAddress", "expiry_sec": 1733592000, "subaccount_ids": null, "nonce": "1730999700000123000", "signature_expiry_sec": 1731000600, "signer": "0xYourWallet", "signature": "0x…", "protocol_scopes": ["trade:orderbook:all", "trade:rfq:option"], "offchain_scopes": ["account_info"], "label": "trading-bot" }' ``` # Action Signing Source: https://docs.derive.xyz/authentication/action-signing The EIP-712 scheme every state-changing action must carry. The easiest way to begin signing is to either use the SDKs directly. * [TypeScript SDK](github.com/derivexyz/derive-ts) * [Rust SDK](github.com/derivexyz/derive-rs) * [Python SDK](github.com/derivexyz/derive-py) Continue reading for a deep dive into the signing scheme, its parameters, and how to debug signature issues. ### Why sign actions? Every state-changing action — placing an order, transferring spot, withdrawing, creating a session key — carries its own **EIP-712** signature over an `Action` struct. This ensures the protocol is self-custodial. Action signing is **not** [session login](/authentication/session-login). Login (EIP-191) authenticates a connection; it does not authorize actions. Each action is signed and verified on its own. Keep the two layers distinct. ```jsonc theme={null} { "subaccount_id": 9, "nonce": 1720512000000123456, // ms timestamp * 1e6 + random suffix "module": "0xB8D2...5e7b", // TRADE_MODULE "expiry": 1720512300, // unix seconds "owner": "0xYourWallet", "signer": "0xYourWallet", // or a session key "data": { // ABI-encoded into `encoded_data` "asset": "0xETHOptionAsset", // base_asset_address of the instrument "sub_id": "39614082545622357598554435968", // packed expiry/strike/is_call; 0 for perps "limit_price": "310e18", "amount": "1e18", "max_fee": "0.01e18", "recipient_id": 9, "is_bid": true } } ``` The seven `data` words are hashed into the `Action` struct, which is hashed with the domain separator into the digest `signer` signs. ## Worked example: a buy order An option buy order signed by the wallet directly (`owner == signer`). The SDK ABI-encodes the payload, builds and signs the EIP-712 digest, and submits it in a single call — amounts and prices are plain decimals, with e18 scaling handled internally. The last tab does the same thing without an SDK, one step at a time: ```typescript TypeScript (SDK) theme={null} import { DeriveClient } from '@derivexyz/derive-ts'; const client = new DeriveClient({ network: 'mainnet', wallet: process.env.PRIVATE_KEY!, }); await client.connect(); await client.login(); // Signed by the wallet key held in the client (owner == signer). The nonce, // signature, and signature_expiry_sec are generated and signed for you. const { order } = await client.orders.place({ subaccountId: 9, instrumentName: 'ETH-20260626-3000-C', direction: 'buy', amount: '1', limitPrice: '310', maxFee: '0.01', }); console.log(order.order_id); ``` ```python Python (SDK) theme={null} import asyncio from decimal import Decimal from derive_py import WebSocketClient from derive_py.data_types import Direction async def main(): client = WebSocketClient.from_env() await client.connect() # Signed by the key held in the client. The nonce, signature, and # signature_expiry_sec are generated and signed for you. response = await client.orders.create( instrument_name="ETH-20260626-3000-C", direction=Direction.buy, amount=Decimal("1"), limit_price=Decimal("310"), max_fee=Decimal("0.01"), ) print(response.order.order_id) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use bigdecimal::BigDecimal; use derive_rs::{ Environment, WsClient, actions::OrderArgs, models::{Direction, OrderType, TimeInForce}, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = WsClient::new( Environment::Mainnet, Some(std::env::var("DERIVE_PRIVATE_KEY")?), Some(std::env::var("DERIVE_WALLET")?), Some(9), ) .await?; client.login().await?; // Signed by the wallet key held in the client (owner == signer). The nonce, // signature, and signature_expiry_sec are generated and signed for you. let order = OrderArgs::builder() .instrument_name("ETH-20260626-3000-C".to_string()) .direction(Direction::Buy) .order_type(OrderType::Limit) .time_in_force(TimeInForce::Gtc) .amount(BigDecimal::from(1)) .limit_price(BigDecimal::from(310)) .build(); let response = client.orders().place(order).await?; println!("{}", response.order.order_id); Ok(()) } ``` ```python Python (manual) theme={null} # No SDK: every step done by hand. pip install eth-abi eth-account requests import os, random, time from decimal import Decimal import requests from eth_abi import encode as abi_encode from eth_account import Account from eth_account.messages import encode_defunct from eth_utils import keccak, to_checksum_address BASE_URL = "https://api.derive.xyz/v3" CHAIN_ID = 1 # Sepolia (11155111) on testnet MATCHING = "0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD" TRADE_MODULE = "0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b" # keccak("Action(uint256 subaccountId,uint256 nonce,address module,bytes data, # uint256 expiry,address owner,address signer)") — same on every deployment. ACTION_TYPEHASH = bytes.fromhex("4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17") session_key = Account.from_key(os.environ["PRIVATE_KEY"]) # signer owner = to_checksum_address(os.environ["OWNER"]) subaccount_id = 9 def e18(value: str) -> int: """The exchange rejects, not truncates, sub-1e12 precision.""" scaled = int(Decimal(value) * 10**18) if scaled % 10**6: raise ValueError("more than 12 decimal places can never verify") return scaled # 1. The action signs the asset, not the instrument name. instrument = requests.post( f"{BASE_URL}/public/get_instrument", json={"instrument_name": "ETH-20260626-3000-C"}, ).json()["result"] # 2. Action data: seven static words, decimals e18-scaled. encoded_data = abi_encode( ["address", "uint256", "int256", "int256", "uint256", "uint256", "bool"], [ to_checksum_address(instrument["base_asset_address"]), int(instrument["base_asset_sub_id"]), e18("310"), # limit_price e18("1"), # amount e18("0.01"), # max_fee subaccount_id, # recipient_id True, # is_bid ], ) # 3. EIP-712 struct hash of the Action envelope. Step 5 resends these two # values verbatim, so they have to be bound before signing. nonce = int(time.time() * 1000) * 10**6 + random.randrange(10**6) # nonce expiry_sec = int(time.time()) + 300 # expiry_sec action_hash = keccak( abi_encode( ["bytes32", "uint256", "uint256", "address", "bytes32", "uint256", "address", "address"], [ ACTION_TYPEHASH, subaccount_id, nonce, to_checksum_address(TRADE_MODULE), keccak(encoded_data), expiry_sec, owner, session_key.address, # signer; pass owner here if the wallet signs directly ], ) ) # 4. Domain separator, then the digest to sign. domain_separator = keccak( abi_encode( ["bytes32", "bytes32", "bytes32", "uint256", "address"], [ keccak(b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak(b"Matching"), keccak(b"1.0"), CHAIN_ID, to_checksum_address(MATCHING), ], ) ) digest = keccak(b"\x19\x01" + domain_separator + action_hash) signature = "0x" + Account.unsafe_sign_hash(digest, session_key.key).signature.hex() # 5. Submit. Wire decimals are formatted from the signed e18 words so the # exchange re-derives the exact bytes the signature commits to. login_ts = str(int(time.time() * 1000)) login_sig = Account.sign_message(encode_defunct(text=login_ts), session_key.key).signature response = requests.post( f"{BASE_URL}/private/order", headers={ "X-DeriveWallet": owner, "X-DeriveTimestamp": login_ts, "X-DeriveSignature": "0x" + login_sig.hex(), }, json={ "subaccount_id": subaccount_id, "instrument_name": "ETH-20260626-3000-C", "direction": "buy", "limit_price": "310", "amount": "1", "max_fee": "0.01", "nonce": str(nonce), # decimal string on the wire "signer": session_key.address, "signature": signature, "signature_expiry_sec": expiry_sec, "order_type": "limit", "time_in_force": "gtc", }, ).json() print(response["result"]["order"]["order_id"]) ``` The instrument name above is an option, named `---` (perps are `-PERP`). See [Instrument names](/trading/instrument-names) for the full grammar. ## Nonce and expiry The `nonce` param (decoded as a UTC timestamp in nanoseconds \* 6-digit suffix) has some special rules depending on the action. | Action type | Nonce window (relative to server clock) | Must increase? | | ------------------------------------------------------------------------ | --------------------------------------- | -------------- | | Signed actions — withdraw, transfer, session key, whitelist, liquidation | ± 1 hour | Yes | | Orders | 120 days before → 1 hour after | No | | RFQs | ± 1 hour | No | | Vault actions | 60 days before → 1 hour after | Yes | The Action's `expiry`, in unix seconds; rejected once `now > expiry`. The exchange enforces certain minimums depending on the action type to ensure the action is valid long enough. | Action type | Minimum expiry (from server clock) | Maximum expiry (from server clock) | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------- | -------------------------------------------- | | Orders | 10 seconds | 120 days — or 15 minutes if the order is MMP | | RFQ quotes — maker quote and taker execute | 60 seconds past the RFQ's expiry (an RFQ is valid for 10 minutes from creation) | 1 day | | Position transfers | 60 seconds (both sides; the taker's expiry must also cover the maker's) | — | | Withdraw, spot transfer, external transfer, liquidation, whitelist | None — must simply be unexpired when verified | — | | Vault actions | None — must simply be unexpired when verified | 30 days | | Set session key | None on the signature — but the key's own `expiry_sec` must be ≥ 5 minutes | — | ## Debugging signature issues A rejected signature is almost always one of three things: * **Bad action data encoding** — your ABI-encoded `encoded_data` bytes are wrong. Check them with [`public/decode_action`](/api-reference/system/publicdecode_action). * **Bad action envelope encoding** — the action data is fine, but something the signature also commits to (module, nonce, expiry, owner, signer, domain separator) differs from what the exchange rebuilt. Byte-compare each stage against the relevant `private/*_debug` routes (e.g. [`private/order_debug`](/api-reference/orderbook/privateorder_debug)). * **Wallet issues** — the encoding is right on both counts, but the wrong key signed, or `signer` does not match the recovered address. This is often because you passed in the `owner` instead of the `signer`. ## Constants The `module` field names the contract whose ABI layout `data` follows. Module addresses are fixed protocol constants — identical across every deployment, safe to hardcode: | Action | Module | Address | Purpose | | ----------------------- | ------------------------------ | -------------------------------------------- | ------------------------------------------------- | | Order / trade | `TRADE_MODULE` | `0xB8D20c2B7a1Ad2EE33Bc50eF10876eD3035b5e7b` | Place an order | | Spot transfer | `TRANSFER_MODULE` | `0x01259207A40925b794C8ac320456F7F6c8FE2636` | Move a spot ERC-20 between subaccounts | | Withdraw | `WITHDRAW_MODULE` | `0x9d0E8f5b25384C7310CB8C6aE32C8fbeb645d083` | Withdraw to L1 | | RFQ / position transfer | `RFQ_MODULE` | `0x9371352CCef6f5b36EfDFE90942fFE622Ab77F1D` | RFQ quotes; position transfers book as RFQ trades | | External transfer | `EXTERNAL_TRANSFER_MODULE` | `0x8F9B8f12ddA05FB1F0DDDDe8f5af8cECF54f8aC9` | External spot transfer | | Whitelisted recipient | `WHITELISTED_RECIPIENT_MODULE` | `0xB86D6DE1b76c9839e4BA860848CD98A1dABd6B54` | Recipient allow-list | | Liquidation | `LIQUIDATION_MODULE` | `0x66d23e59DaEEF13904eFA2D4B8658aeD05f59a92` | Bid into a Dutch auction | | Vault | `VAULT_MODULE` | `0x2885c174ebf5524aED9c721d60c12b1537685186` | Vault actions | | Set session key | `SET_SESSION_KEY_MODULE` | `0xe330CF64ff6EbF41699aad344Cb21d78db1D2bb6` | Register a delegated session key | ### owner vs signer `owner` is always the wallet that owns the subaccount. `signer` is whoever produced the signature — the wallet itself (`owner == signer`) or a delegated [session key](/authentication/session-keys), in which case `signer` is the key's address and `owner` stays the wallet. The protocol recovers the ECDSA signer and requires it to equal `signer`; if `signer != owner`, it loads the session key and checks its scopes cover the action. # Smart Contracts & Multi-sigs Source: https://docs.derive.xyz/authentication/contract-owned-accounts Own and operate a Derive account from a multi-sig or smart contract on the Ethereum L1. A Derive account is owned by a single Ethereum L1 address — and that address can be a **multi-sig or any smart contract**, not just an EOA. The contract keeps custody and control on L1, while a delegated [session key](/authentication/session-keys) does the day-to-day trading. All of the steps in this guide can be done through the UX or programmatically. ## Why it works differently Most of the API is driven by **signed actions**: your wallet produces a single-key EIP-712 signature and the protocol verifies it (see [Action signing](/authentication/action-signing)). A multi-sig or smart contract has no single private key, so it cannot produce that signature directly. Instead, a contract owner operates entirely through **L1 onchain actions** — transactions it sends to the `OnchainActionManager` contract. The exchange's L1 listener picks each one up and applies it, authorized by the transaction's `msg.sender`. Two building blocks are enough to run an account end to end: Creating and funding the account is already an L1 call — the same for every owner. See [Programmatic Onboarding](/getting-started/depositing). Authorize an EOA session key from L1, then let that key sign trades, transfers, and vault operations offchain. The account owner is whatever address you pass as `owner` when depositing, and the L1 listener authorizes onchain actions by the transaction's `msg.sender`. So the **same contract that owns the account must send the `OnchainActionManager` transactions** — set the session key from the multi-sig itself, not from an unrelated EOA. ## Set up and operate The first deposit creates the account and its subaccounts automatically. Make sure that either * `owner` address you pass into `OnchainActionMAnager.depositToNewSubaccount()` is the multi-sig / smart contract, or * the `wallet` you send to `public/register_deposit_address` is the multi-sig / smart contract. Deposit through the UX or see [Programmatic Onboarding](/getting-started/depositing) for more information. An onchain Set Session Key is ignored if the account does not exist yet. Submit a Set Session Key onchain action from the owner contract. This registers an EOA session key with the protocol scopes you choose (see [Access scopes](/authentication/access-scopes)). ```solidity Solidity theme={null} interface IOnchainActionManager { function submit(uint256 actionType, bytes calldata data) external payable returns (uint256 actionId); } uint256 constant SET_SESSION_KEY = 51; // Protocol scope codes — see /authentication/access-scopes. uint256 constant SCOPE_TRADE_ALL = 2; uint256 constant SCOPE_TRANSFER_EXISTING_SUBACCOUNT = 12; /// Called by the owner contract itself, so msg.sender is the account owner. function authorizeSessionKey(IOnchainActionManager manager, address sessionKey, uint256 expirySec) external { // SetSessionKeyActionData — standard `abi.encode(address, uint256, // uint256[] scopes, uint256[] subaccountIds)`. uint256[] memory scopes = new uint256[](2); scopes[0] = SCOPE_TRADE_ALL; scopes[1] = SCOPE_TRANSFER_EXISTING_SUBACCOUNT; uint256[] memory subaccountIds = new uint256[](0); // empty = all subaccounts bytes memory data = abi.encode(sessionKey, expirySec, scopes, subaccountIds); manager.submit(SET_SESSION_KEY, data); } ``` ```typescript TypeScript (SDK) theme={null} import { DeriveClient, ProtocolScopeCode } from '@derivexyz/derive-ts'; import { Wallet } from 'ethers'; const client = new DeriveClient({ network: 'mainnet', ownerAddress: MULTISIG_ADDRESS }); // A signer connected to the chain RPC that executes the OnchainActionManager // transaction as the account owner (an owner EOA, a Safe module, etc.). const signer = new Wallet(process.env.OWNER_KEY!, provider); // Register a trading session key via L1. await client.onchainActions.setSessionKey({ signer, sessionKey: SESSION_KEY_ADDRESS, expirySec: Math.floor(Date.now() / 1000) + 30 * 24 * 3600, scopes: [ProtocolScopeCode.TradeAll, ProtocolScopeCode.TransferExistingSubaccount], }); ``` ```python Python (SDK) theme={null} # Open an issue in derivexyz/derive-py if you'd like this implemented in the SDK. ``` ```rust Rust (SDK) theme={null} // Open an issue in derivexyz/derive-rs if you'd like this implemented in the SDK. ``` From here everything is normal: the session key [logs in](/authentication/session-login) and signs actions — trading, transfers, and [creating or curating a vault](/vaults/create-a-vault) — with no further L1 transactions. When connecting to the UX with the session key you will be prompted to choose between signing in as the session key or the smart contract. Remove a key by sending the same action with expiry `0` — the only way to delete a key (the offchain API has no revoke). ```typescript TypeScript (SDK) theme={null} // Revoke: a Set Session Key with expiry 0. await client.onchainActions.revokeSessionKey({ signer, sessionKey: SESSION_KEY_ADDRESS }); ``` ```solidity Solidity theme={null} // Revoke = Set Session Key with expiry 0 (deletes the key). // IOnchainActionManager as declared in the previous step. function revokeSessionKey(IOnchainActionManager manager, address sessionKey) external { uint256[] memory empty = new uint256[](0); bytes memory data = abi.encode(sessionKey, uint256(0), empty, empty); // expiry 0 = delete manager.submit(51, data); } ``` ## Related What a session key is, its scopes, and its lifecycle. The full protocol and off-chain scope catalog to scope a key tightly. Create and fund the account with an L1 deposit. Run a vault from the account with the session key. # MPC Wallets Source: https://docs.derive.xyz/authentication/mpc-wallets Restrict what an MPC wallet can do on Derive Most MPC services (Fordefi, Fireblocks, and similar) can permission over three types of actions: * **Smart contract calls** — which contract, which function, and often which argument values. Fordefi expresses these as [ABI conditions](https://docs.fordefi.com/user-guide/policies/policy-rules-conditions-and-actions#abi), matching on `address`, integer, boolean, `bytes`, array, and tuple arguments. * **Personal messages** (`personal_sign`, e.g. EIP-191) — see [personal messages and messages on non-EVM chains](https://docs.fordefi.com/user-guide/policies/message-policy#personal-messages-and-messages-on-non-evm-chains). * **Typed data** (EIP-712) — see [typed data messages](https://docs.fordefi.com/user-guide/policies/message-policy#typed-data-messages), where you can deny signatures that match certain fields of the EIP-712 domain or message. Derive uses all three paths for different purposes: | Path | Medium | Scheme | Reaches | | ---------------------------------------------------- | --------- | ----------- | --------------------------------------------------------------------- | | [`OnchainActionManager`](/getting-started/contracts) | Onchain | transaction | deposit, withdraw, add/remove session keys | | [Signed actions](/authentication/action-signing) | HTTP / WS | EIP-712 | all of the above, **and trading** | | [Session login](/authentication/session-login) | HTTP / WS | EIP-191 | **read-only** — authenticates an HTTP request or WebSocket connection | Because the EIP-712 path can do strictly more than the contract-call path, the tightest setup is to close it entirely and operate through contract calls only. ## Recommended policy Every state-changing Derive action taken over the API is an EIP-712 signed action. Denying EIP-712 outright means the MPC wallet can never trade, transfer, or withdraw through the API — no allow-list to maintain and no new action type can slip through later. In Fordefi this is a [typed data message](https://docs.fordefi.com/user-guide/policies/message-policy#typed-data-messages) rule. Fordefi surfaces an EIP-712 message's **recipient as its `verifyingContract`**, so target the rule at Derive's action domain: | Domain field | Value | | ------------------- | ----------------------------------------------------------------- | | `name` | `Matching` | | `version` | `1.0` | | `verifyingContract` | `0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD` | | `chainId` | the settlement chain's id — the only field that varies by network | Every Derive signed action uses this same `verifyingContract` on every network, so one deny rule on that address covers mainnet and testnet alike. On `OnchainActionManager`, allow the two deposit entrypoints: ```solidity theme={null} function deposit(address asset, uint256 amount, uint64 subaccountId, address fallbackRecipient); function depositToNewSubaccount(address asset, uint256 amount, uint32 managerId, address owner); ``` Pin `owner` and `fallbackRecipient` to the MPC wallet address. `owner` is what makes the MPC wallet the account owner; `fallbackRecipient` is where a deposit lands if it cannot be applied to its intended subaccount. Session keys are registered from L1 through the generic entrypoint: ```solidity theme={null} function submit(uint256 actionType, bytes calldata data) ``` ### Example (trade and withdraw only session key) In this example we're going to create a policy that will only allow your MPC wallet to create Session keys with the `trade:orderbook:perp` and `withdraw` protocol scopes. Because only `admin` scoped keys can withdraw to any recipient, this policy will only allow withdrawals to the MPC wallet's own subaccounts. Use the TypeScript SDK's [session-key codec](https://github.com/derivexyz/derive-ts/blob/master/src/codecs/sessionKey.ts), to produce the bytes you'll use to create an MPC policy. ```typescript theme={null} import { encodeSetSessionKeyActionData } from '@derivexyz/derive-ts/codecs'; import { ProtocolScopeCode } from '@derivexyz/derive-ts'; import { Interface } from 'ethers'; const data = encodeSetSessionKeyActionData({ sessionKey: '0x9f000000000000000000000000000000000000be', expirySec: 1793491200, // the KEY's lifetime; 0 deletes it // Exactly two scopes, in this order — the policy pins both. scopes: [ProtocolScopeCode.Withdraw, ProtocolScopeCode.TradeOrderbookPerp], // [1, 5] subaccountIds: [], // empty = all current and future subaccounts }); const manager = new Interface([ 'function submit(uint256 actionType, bytes data) payable returns (uint256)', ]); // 51 = Set Session Key. `data` is the 256 bytes broken down below. console.log(manager.encodeFunctionData('submit', [51, data])); ``` ### Which bytes to pin in your MPC policy | Param | Contents | Pin to | | --------------- | ------------------------------------ | ---------------- | | `actionType` | Set Session Key action type | `51` | | `data[0:32]` | `sessionKey` address | free — rotates | | `data[32:64]` | `expirySec` | free — rotates | | `data[64:96]` | offset → `scopes` | from console.log | | `data[96:128]` | offset → `subaccountIds` | from console.log | | `data[128:160]` | `scopes.length` | from console.log | | `data[160:192]` | `scopes[0]` = `withdraw` | from console.log | | `data[192:224]` | `scopes[1]` = `trade:orderbook:perp` | from console.log | | `data[224:256]` | `subaccountIds.length` = all | from console.log | You can modify what you pin depending on your needs. Leave [personal messages (EIP191)](https://docs.fordefi.com/user-guide/policies/message-policy#personal-messages-and-messages-on-non-evm-chains) allowed so [session login](/authentication/session-login) keeps working and the MPC wallet retains read-only account access through UX and API. ## Related The full protocol and off-chain scope catalog. What a session key is and how its lifecycle works. The withdrawal action and its recipient semantics. # Session Keys Source: https://docs.derive.xyz/authentication/session-keys Delegated signing keys: create, edit, list, and their lifecycle. A **session key** is a delegated EVM address (an EOA your client controls) that your wallet authorizes to sign [actions](/authentication/action-signing) on its behalf. Instead of exposing your wallet's private key to a trading service, you mint a scoped, expiring key that can sign only the actions you permit, on only the subaccounts you allow. Every session key carries **two independent scope sets** on one record: * **Protocol scopes** — on-chain authority (trade, transfer, withdraw, …). Signed into each action and re-validated by the protocol. * **Off-chain scopes** — server-side capabilities (`account_info`). Never signed; enforced by the server. See [Access scopes](/authentication/access-scopes) for the full scope catalog and per-route gating. ## How session keys are used Diagram showing the two distinct ways a session key signs. First, signed actions: the key produces EIP-712 typed-data signatures over an Action struct for state-changing operations such as trading, RFQs, withdrawals, and transfers, and the protocol re-validates the key's protocol scopes against each action. Second, login headers: the key produces EIP-191 signatures over the login payload to authenticate HTTP and WebSocket sessions, and the server enforces the key's off-chain scopes, IP whitelist, and expiry. The wallet delegates authority to the session key, which then performs both signing roles on the wallet's behalf without ever exposing the wallet's own private key. Diagram showing the two distinct ways a session key signs. First, signed actions: the key produces EIP-712 typed-data signatures over an Action struct for state-changing operations such as trading, RFQs, withdrawals, and transfers, and the protocol re-validates the key's protocol scopes against each action. Second, login headers: the key produces EIP-191 signatures over the login payload to authenticate HTTP and WebSocket sessions, and the server enforces the key's off-chain scopes, IP whitelist, and expiry. The wallet delegates authority to the session key, which then performs both signing roles on the wallet's behalf without ever exposing the wallet's own private key. ## Delegated attenuation The key you create can never exceed the authority of the key that signs the creation action. A child key's **scopes, expiry, and subaccounts must each be a subset of its parent's**. A key without `admin` cannot mint an `admin` child; a key expiring next week cannot mint a child expiring next month. The protocol enforces this when the create action is applied. The signing parent must itself hold the `set_session_key` protocol scope. ## Create a session key 1. API -> call `private/set_session_key` with the owner or any session key with stronger scopes. 2. Onchain -> see [Smart Contract & Multi-sig Accounts](/authentication/contract-owned-accounts). ```typescript TypeScript (SDK) theme={null} import { DeriveClient, ProtocolScopeCode, OffchainScope, } from '@derivexyz/derive-ts'; import { Wallet } from 'ethers'; const client = new DeriveClient({ network: 'mainnet', wallet: process.env.PRIVATE_KEY!, }); await client.connect(); await client.login(); // A fresh keypair for the delegated key; persist its private key for the bot. const sessionWallet = Wallet.createRandom(); // The SDK builds and EIP-712-signs the create action for you (nonce, signature, // signature_expiry_sec, module) using the client's signer. const created = await client.sessionKeys.set({ publicSessionKey: sessionWallet, expirySec: 1793491200, protocolScopes: [ ProtocolScopeCode.TradeOrderbookAll, ProtocolScopeCode.TransferExistingSubaccount, ], offchainScopes: [OffchainScope.AccountInfo], subaccountIds: [9], label: 'market-maker-1', }); console.log(created.public_session_key, created.subaccount_ids); await client.close(); ``` ```python Python (SDK) theme={null} import asyncio from eth_account import Account from derive_py import WebSocketClient from derive_py.data_types import OffchainScope, ProtocolScope async def main(): client = WebSocketClient.from_env() await client.connect() # A fresh keypair for the delegated key; persist its private key for the bot. session_wallet = Account.create() # The SDK builds and EIP-712-signs the create action for you (nonce, # signature, signature_expiry_sec, module) using the client's signer. created = await client.account.set_session_key( public_session_key=session_wallet.address, expiry_sec=1793491200, protocol_scopes=[ ProtocolScope.TRADE_ORDERBOOK_ALL, ProtocolScope.TRANSFER_EXISTING_SUBACCOUNT, ], offchain_scopes=[OffchainScope.ACCOUNT_INFO], subaccount_ids=[9], label="market-maker-1", ) print(created.public_session_key, created.subaccount_ids) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use alloy::signers::local::PrivateKeySigner; use derive_rs::{ Environment, WsClient, actions::session_key::{OffChainScope, ProtocolScope, SetSessionKeyArgs}, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = WsClient::new( Environment::Mainnet, Some(std::env::var("DERIVE_PRIVATE_KEY")?), Some(std::env::var("DERIVE_WALLET")?), Some(9), ) .await?; client.login().await?; // A fresh keypair for the delegated key; persist its private key for the bot. let session_signer = PrivateKeySigner::random(); // The SDK builds and EIP-712-signs the create action for you (nonce, // signature, signature_expiry_sec, module) using the client's signer. let args = SetSessionKeyArgs::builder() .public_session_key(session_signer.address().to_string()) .expiry_second(1793491200) .protocol_scopes(vec![ ProtocolScope::TradeOrderbookAll, ProtocolScope::TransferExistingSubaccount, ]) .off_chain_scopes(vec![OffChainScope::AccountInfo]) .subaccount_ids(vec![9]) .label("market-maker-1".to_string()) .build(); let created = client.session_keys().create(args).await?; println!("{} {:?}", created.public_session_key, created.subaccount_ids); Ok(()) } ``` ```bash cURL theme={null} # Signed action: build nonce/signature/signature_expiry_sec per /authentication/action-signing. # X-Derive* headers authenticate the HTTP session (see /json-rpc). curl -X POST https://api.derive.xyz/v3/private/set_session_key \ -H "Content-Type: application/json" \ -H "X-DeriveWallet: $WALLET_ADDRESS" \ -H "X-DeriveTimestamp: $TS" \ -H "X-DeriveSignature: $SIG" \ -d '{ "wallet": "0x1234...abcd", "public_session_key": "0x9f00...beef", "expiry_sec": 1793491200, "subaccount_ids": [9], "protocol_scopes": ["trade:orderbook:all", "transfer:existing_subaccount"], "offchain_scopes": ["account_info"], "label": "market-maker-1", "ip_whitelist": [], "nonce": "1710000000000123000", "signer": "0x1234...abcd", "signature": "0xabcd...1b", "signature_expiry_sec": 1710000600, "module": "0x0000000000000000000000000000000000000000" }' ``` ```json Request theme={null} { "jsonrpc": "2.0", "id": 1, "method": "private/set_session_key", "params": { "wallet": "0x1234...abcd", "public_session_key": "0x9f00...beef", "expiry_sec": 1793491200, "subaccount_ids": [9], "protocol_scopes": ["trade:orderbook:all", "transfer:existing_subaccount"], "offchain_scopes": ["account_info"], "label": "market-maker-1", "ip_whitelist": [], "nonce": "1710000000000123000", "signer": "0x1234...abcd", "signature": "0xabcd...1b", "signature_expiry_sec": 1710000600, "module": "0x0000000000000000000000000000000000000000" } } ``` ```json Response theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "public_session_key": "0x9f00...beef", "protocol_scopes": ["trade:orderbook:all", "transfer:existing_subaccount"], "offchain_scopes": ["account_info"], "label": "market-maker-1", "ip_whitelist": [], "expiry_sec": 1793491200, "subaccount_ids": [9] } } ``` ## IP whitelist If a key's whitelist is non-empty, [login](/authentication/session-login) from any other IP is rejected. An empty whitelist imposes no IP restriction. Adding or removing IP Whitelists requires the owner or an admin-scoped key. ## Editing a session key * Protocol Scopes: call the same `private/set_session_key` endpoint or onchain `Set Session Key` (see [Smart Contract & Multi-sig Accounts](/authentication/contract-owned-accounts)) action again for the session key you want to modify. * Offchain Scopes and metadata: call `private/edit_session_key` to update the label, IP whitelist, or off-chain scopes. This does not require a signed action. **IP whitelist or off-chain scopes** requires protocol `admin` scope or the owner. ```typescript TypeScript (SDK) theme={null} import { DeriveClient } from '@derivexyz/derive-ts'; const client = new DeriveClient({ network: 'mainnet', wallet: process.env.PRIVATE_KEY!, }); await client.connect(); await client.login(); // Off-chain patch: omitted fields are left unchanged. Editing ip_whitelist or // offchain_scopes requires the owner or an admin-scoped key. const updated = await client.sessionKeys.edit({ publicSessionKey: '0x9f00...beef', label: 'market-maker-primary', ipWhitelist: ['203.0.113.7'], }); console.log(updated.label, updated.ip_whitelist); await client.close(); ``` ```python Python (SDK) theme={null} import asyncio from derive_py import WebSocketClient async def main(): client = WebSocketClient.from_env() await client.connect() # Off-chain patch: omitted fields are left unchanged. Editing ip_whitelist or # offchain_scopes requires the owner or an admin-scoped key. updated = await client.account.edit_session_key( public_session_key="0x9f00...beef", label="market-maker-primary", ip_whitelist=["203.0.113.7"], ) print(updated.label, updated.ip_whitelist) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use derive_rs::{Environment, WsClient, models::EditSessionKeyRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let client = WsClient::new( Environment::Mainnet, Some(std::env::var("DERIVE_PRIVATE_KEY")?), Some(std::env::var("DERIVE_WALLET")?), Some(9), ) .await?; client.login().await?; // Off-chain patch, no signing: omitted fields are left unchanged. Editing // ip_whitelist or offchain_scopes requires the owner or an admin-scoped key. let params = EditSessionKeyRequest::builder() .wallet(std::env::var("DERIVE_WALLET")?) .public_session_key("0x9f00...beef") .label("market-maker-primary".to_string()) .ip_whitelist(vec!["203.0.113.7".to_string()]) .try_into()?; let updated = client.rpc().session_keys().edit_session_key(params).await?; println!("{:?} {:?}", updated.label, updated.ip_whitelist); Ok(()) } ``` ```bash cURL theme={null} # Off-chain patch (no signed action); X-Derive* headers: session auth (see /json-rpc). curl -X POST https://api.derive.xyz/v3/private/edit_session_key \ -H "Content-Type: application/json" \ -H "X-DeriveWallet: $WALLET_ADDRESS" \ -H "X-DeriveTimestamp: $TS" \ -H "X-DeriveSignature: $SIG" \ -d '{ "wallet": "0x1234...abcd", "public_session_key": "0x9f00...beef", "label": "market-maker-primary", "ip_whitelist": ["203.0.113.7"] }' ``` ```json Request theme={null} { "jsonrpc": "2.0", "id": 2, "method": "private/edit_session_key", "params": { "wallet": "0x1234...abcd", "public_session_key": "0x9f00...beef", "label": "market-maker-primary", "ip_whitelist": ["203.0.113.7"] } } ``` ## Lifecycle at a glance Wallet (or a parent key with `set_session_key` scope) signs a create action granting a subset of its own scopes, expiry, and subaccounts. Can be done both through API via `private/set_session_key` or on-chain via the `Set Session Key` action (see [Smart Contract & Multi-sig Accounts](/authentication/contract-owned-accounts)). The session key logs in via EIP-191 [session login](/authentication/session-login); its IP whitelist and expiry are enforced at this step. The key EIP-712-signs each action; the protocol re-checks that the key's scopes cover the action (see [Action signing](/authentication/action-signing)). Adjust label, IP whitelist, or off-chain scopes with `edit_session_key`. Protocol scopes require a fresh `set_session_key`. The key stops working once `expiry_sec` passes. You can "revoke" session keys by bringing the expiry closer to now. However, there is a minimum cooldown of 5-15min. ## Related The full protocol and off-chain scope catalog and per-route gating. How the create action (and every trading action) is signed with EIP-712. Authorize a session key from L1 when the account owner is a contract or multi-sig. # Session Login Source: https://docs.derive.xyz/authentication/session-login Authenticate a connection or request on behalf of a wallet. Session login proves that a connection (WebSocket) or a request (HTTP) may act on behalf of a wallet. It works over both transports and returns the list of subaccount IDs the caller is authenticated for. Logging in does **not** authorize trading. Every state-changing action carries its own EIP-712 signature that the protocol re-verifies independently — see [Action signing](/authentication/action-signing). ## What you sign The signed message is the **current time in milliseconds as a decimal string** (`Date.now().toString()`), signed with [EIP-191](https://eips.ethereum.org/EIPS/eip-191) `personal_sign`. There is no separate challenge or login nonce — the timestamp is the anti-replay token. The signer may be the wallet itself or a delegated [session key](/authentication/session-keys). The three values — `wallet`, `timestamp`, `signature` — are the same on both transports. WebSocket sends them as `public/login` params; HTTP sends them as `X-Derive*` headers. The server reads each value from the header **or** the request params interchangeably. ## WebSocket Open the socket, then call `public/login` with `{wallet, timestamp, signature}`. The `result` is the array of subaccount IDs the connection is now authenticated for (an empty session-key subaccount list resolves to all of the wallet's current subaccounts). ```typescript TypeScript (SDK) theme={null} import { DeriveClient } from '@derivexyz/derive-ts'; const client = new DeriveClient({ network: 'mainnet', wallet: process.env.PRIVATE_KEY!, }); await client.connect(); // open the websocket const subaccountIds = await client.login(); // EIP-191 login over public/login // The client re-logs in automatically after a reconnect. ``` ```python Python (SDK) theme={null} import asyncio from derive_py import WebSocketClient async def main(): client = WebSocketClient.from_env() # connect() opens the websocket AND performs the EIP-191 public/login; # it re-authenticates automatically after a reconnect. await client.connect() subaccounts = await client.account.get_subaccounts() print(subaccounts.subaccount_ids) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use derive_rs::{Environment, WsClient}; #[tokio::main] async fn main() -> Result<(), Box> { // new() opens the websocket. let client = WsClient::new( Environment::Mainnet, Some(std::env::var("DERIVE_PRIVATE_KEY")?), Some(std::env::var("DERIVE_WALLET")?), Some(9), ) .await?; // EIP-191 login over public/login; returns the wallet's subaccount ids. let subaccount_ids = client.login().await?; println!("{:?}", subaccount_ids); Ok(()) } ``` The raw frame and response: ```json Request theme={null} { "id": "1", "jsonrpc": "2.0", "method": "public/login", "params": { "wallet": "0xYourWalletAddress", "timestamp": "1720512000000", "signature": "0x…65-byte personal_sign over the timestamp string" } } ``` ```json Response theme={null} { "id": "1", "jsonrpc": "2.0", "result": [9, 42] } ``` ## HTTP REST requests authenticate **per request** — there is no separate login call. Attach the same three values as headers to each private request: `X-DeriveWallet`, `X-DeriveTimestamp`, `X-DeriveSignature`. ```typescript TypeScript (SDK) theme={null} import { DeriveClient } from '@derivexyz/derive-ts'; // No connect()/login(): the client signs and attaches X-Derive\* headers on // every private REST call automatically. const client = new DeriveClient({ network: 'mainnet', wallet: process.env.PRIVATE_KEY!, }); const subaccountIds = await client.subaccounts.list(); // authenticated over HTTP ``` ```python Python (SDK) theme={null} import asyncio from derive_py import AsyncHTTPClient async def main(): # No login step: the client signs and attaches X-Derive* headers on # every private REST call automatically. client = AsyncHTTPClient.from_env() await client.connect() subaccounts = await client.account.get_subaccounts() # authenticated over HTTP print(subaccounts.subaccount_ids) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} // Only WebSocket as it is lower latency. // Leave an issue in derivexyz/derive-rs if you'd like this feature in the SDK. ``` ```bash cURL theme={null} # timestamp = current time in milliseconds TS=$(($(date +%s) * 1000)) # SIG = EIP-191 personal_sign over "$TS" (wallet or session key) SIG="0x…65-byte signature" curl -X POST https://api.derive.xyz/v3/private/get_subaccounts \ -H "Content-Type: application/json" \ -H "X-DeriveWallet: 0xYourWalletAddress" \ -H "X-DeriveTimestamp: $TS" \ -H "X-DeriveSignature: $SIG" \ -d '{ "wallet": "0xYourWalletAddress" }' ``` ## Who may sign the login The `signature` may come from the wallet itself **or** from a [session key](/authentication/session-keys) the wallet has delegated: The recovered signer equals `wallet`. The connection receives full authority, and the **timestamp freshness window is enforced** — sign immediately before connecting. The recovered signer is a registered session-key address whose `wallet` matches and which is not expired. The key's authority is bounded by its [access scopes](/authentication/access-scopes), and if it declares an IP whitelist the request IP must be on it. **Freshness is not enforced** on this path. The exact timestamp validity window for direct-wallet auth is deployment-specific; allow for it (and a little clock skew) when signing the login timestamp. ## Re-authentication Long-lived WebSocket sessions periodically re-verify the stored login signature as part of the heartbeat, so a connection stays authenticated without a fresh `public/login`. See [Connecting over WebSocket](/connecting) for heartbeat and reconnection guidance. # Changelog Source: https://docs.derive.xyz/changelog Release notes for the Derive API. Release notes for the Derive v3 API, newest first. Subscribe to the [RSS feed](/changelog/rss.xml) to be notified of new entries. Migrating from v2? See [Migrating from V2](/migrating/v3-improvements). ## Proven operations share one per-wallet rate-limit budget Every method that submits a proven operation outside order matching now carries the `proven` rate-limit class and decrements a single per-wallet budget of **1 TPS** by default (5 points per 5s window on mainnet). Previously most of these methods drew from the far larger `non_matching` budget, and a few had their own per-method budget. Affected methods: `private/transfer_spot`, `private/transfer_spot_external`, `private/transfer_positions`, `private/withdraw`, `private/liquidate`, `private/set_session_key`, `private/delete_subaccount`, `private/update_whitelisted_recipients`, `private/create_vault`, `private/mint_vault_shares`, `private/burn_vault_shares`, `private/request_vault_deposit`, `private/request_vault_withdraw`, `private/cancel_all_vault_requests`, `private/force_burn`. * `public/getRateLimits` reports the budget under `remaining_per_endpoint.proven`. * Rejections use the usual `-32000` code with message `Rate limit exceeded: {wallet}-proven`. * Order, quote, and RFQ execution methods are unaffected. See [Rate Limits](/rate-limits). ## `private/create_session_key` renamed to `private/set_session_key` The route now matches what the action does (register, refresh, **or** retire a key by writing a nearer expiry) and its L1 counterpart, the `SetSessionKey` onchain action. Everything about the rename is mechanical: * Route: `private/create_session_key` → **`private/set_session_key`** (and the debug variant `private/create_session_key_debug` → `private/set_session_key_debug`). The old route names are gone. * Protocol scope: `create_session_key` → **`set_session_key`** in `protocol_scopes` on registration requests and in `private/session_keys` responses. * TypeScript SDK: `client.sessionKeys.create(...)` → **`client.sessionKeys.set(...)`**; `ProtocolScopeCode.CreateSessionKey` → `ProtocolScopeCode.SetSessionKey`. ## Signed action payloads now use canonical Solidity ABI encoding Every signed action's `data` bytes are now exactly what Solidity `abi.encode` produces — if you sign with a standard ABI coder (ethers `AbiCoder`, viem `encodeAbiParameters`, `abi.encode` in a contract), nothing changes for you. Hand-rolled encoders must drop the previous non-standard layouts, which are **no longer accepted**: **`private/set_session_key` and the L1 `SetSessionKey` action (type 51)** The payload is canonical `abi.encode(address sessionKey, uint256 expirySec, uint256[] scopes, uint256[] subaccountIds)` — dynamic arrays sit behind standard offset words, each prefixed by its length word. The old hand-packed layout (`[key][expiry][scopeCount][subaccountCount][scopes…] [subaccounts…]`, no offsets) is rejected. In Solidity, build the L1 payload with a plain `abi.encode` call instead of packing words manually. **`private/update_whitelisted_recipients`** The payload is canonical `abi.encode(address[] add, address[] remove)`. The old hand-packed layout (`[addCount][removeCount][adds…][removes…]`, no offsets) is rejected. **Orders (`private/order`) and liquidations (`private/liquidate`)** Negative `int256` values (`limit_price`, `amount`, `price_limit`) are now full 32-byte two's-complement words, sign-extended through the high 16 bytes — standard ABI coder output. Previously the server produced and expected a non-standard form with the value packed into the low 16 bytes only, which broke standard-signing clients on negative liquidation price limits. Non-negative values encode identically, so most existing signatures are unaffected. Decoding is also strict across these payloads: offset words must point at their canonical positions, lengths must match the byte count, and value words must fit their field's range — dirty high bytes are rejected instead of silently truncated, matching Solidity `abi.decode`. See [Action signing](/authentication/action-signing) for the per-module payload layouts. ## Settlement status unified under `batch_status` The `tx_status` field and the `TxStatus` type are removed. Every route and channel that reported settlement status now uses **`batch_status`**, typed as the shared `BatchStatus` enum — `Batching`, `Executing`, `Proving`, `Settling`, `Settled` and their `…Error` counterparts — and `null` until the operation is picked up into a batch. This replaces the old granular transaction states (`applied`, `in_batch`, `proving`, `submitted`) **and** the lowercase matching-engine values (`requested`, `pending`, `settled`, `reverted`, `ignored`, `timed_out`), which are all **gone** across the entire API. There is now one status field, one set of values. **REST methods** * `public/get_trade_history` — optional `batch_status` request filter (unset returns trades in every batch state) and on each trade * `private/get_trade_history` — `batch_status` on each trade * `private/order`, `private/replace` — `batch_status` on each `trades[]` entry (always `null` at placement) * `private/send_quote`, `private/cancel_quote`, `private/replace_quote`, `private/get_quotes`, `private/transfer_positions` — `batch_status` on each quote (present only for executed quotes) * `private/get_deposit_history`, `private/get_withdrawal_history`, `private/get_erc20_transfer_history`, `private/get_funding_history` — `batch_status` on each row * `public/get_transaction` — `status` uses the same `BatchStatus` values **WebSocket channels** * `trades.{instrument_type}.{currency}.{tx_status}` and `{subaccount_id}.trades.{tx_status}` — removed; settlement is no longer streamed. Poll `public/get_trade_history` for `batch_status` / `tx_hash` instead * `trades.{instrument_name}`, `trades.{instrument_type}.{currency}` — each event now carries the full trade detail (`wallet`, `subaccount_id`, `liquidity_role`, fees, realized PnL) * `{subaccount_id}.trades`, `{subaccount_id}.quotes` — `batch_status` on each payload item ## `instrument_type` is now a typed enum On the trade-history schemas, `instrument_type` is typed as the `AssetType` enum (`erc20` / `option` / `perp`) instead of a free-form string. See [Breaking changes → Settlement status](/migrating/breaking-changes) for the full route table and value reference. # Connecting over WebSocket Source: https://docs.derive.xyz/connecting Open a session, send JSON-RPC frames, and keep the connection alive with heartbeats. The Derive v3 API is **JSON-RPC 2.0** served over WebSocket. Every method also works over [HTTP POST](/getting-started/introduction#endpoints), but real-time streams and [cancel-on-disconnect](/trading/cancel-on-disconnect) are WebSocket-only. ## Connect Open a WebSocket to the `/v3/ws` endpoint for your environment: ```text Production theme={null} wss://api.derive.xyz/v3/ws ``` ```text Testnet theme={null} wss://testnet.api.derive.xyz/v3/ws ``` Authentication is a separate layer from the transport. You can either log in in-band after connecting (`public/login`) or attach a read-only JWT to the URL. See [Authentication](/authentication/session-login) for the full model. Connect unauthenticated, then send `public/login` with an EIP-191 signature. This is the only path that can authorize trading actions. Pass a bearer token as a query parameter: ```text theme={null} wss://api.derive.xyz/v3/ws?token= ``` A JWT session carries only the off-chain `account_info` capability — it can read account data but cannot authorize actions. Action signing is still required for anything state-changing. ## Send JSON-RPC frames Send each request as a JSON **text frame**. Responses correlate by `id`; subscription updates arrive as separate notification frames (see [Subscriptions](/subscriptions)). ```typescript TypeScript (SDK) theme={null} import { DeriveClient } from '@derivexyz/derive-ts'; // public/get_time takes no params and returns the server time in ms. const client = new DeriveClient({ network: 'mainnet' }); const serverTime = await client.send('public/get_time', null); console.log(serverTime); // 1720483200000 ``` ```python Python (SDK) theme={null} import asyncio from derive_py import WebSocketClient async def main(): # public/get_time takes no params and returns the server time in ms. client = WebSocketClient.from_env() await client.connect() server_time = await client.system.get_time() print(server_time) # 1720483200000 # public_api.rpc is the escape hatch for any method without a namespace # wrapper; every method there takes its generated params struct. raw = await client.public_api.rpc.get_time(None) print(raw) await client.disconnect() asyncio.run(main()) ``` ```rust Rust (SDK) theme={null} use derive_rs::{Environment, WsClient}; #[tokio::main] async fn main() -> Result<(), Box> { // public/get_time takes no params and returns the server time in ms. let client = WsClient::new_public(Environment::Mainnet).await?; let server_time = client.rpc().system().get_time().await?; println!("{server_time}"); // 1720483200000 // send_rpc is the escape hatch for any method without a typed wrapper. let raw: serde_json::Value = client .send_rpc("public/get_time", serde_json::json!({})) .await?; println!("{raw}"); Ok(()) } ``` ```bash cURL theme={null} curl -X POST https://api.derive.xyz/v3/public/get_time \ -H "Content-Type: application/json" \ -d '{}' ``` ```json Request theme={null} { "id": 1, "method": "public/get_time", "params": {} } ``` ```json Response theme={null} { "id": 1, "result": 1720483200000 } ``` Assign a unique `id` per in-flight request and match responses back to it — frames are not guaranteed to return in send order. ## Heartbeat The server drives a heartbeat loop and expects the client to stay responsive. On each heartbeat interval the server sends a WebSocket **Ping** frame. Your client library normally answers with a **Pong** automatically. Any inbound text frame from you also counts as liveness. You may send the text frame `ping`; the server replies with `pong`. If no activity is seen within the heartbeat window, the server closes the connection. The heartbeat interval is deployment-configured (currently \~180 seconds). ### Heartbeat re-authentication On each heartbeat tick, a **logged-in** connection is silently **re-authenticated** — the server re-runs your stored login signature check against current account state. If the session key has expired, been revoked, or falls outside its IP allow-list, the connection is closed. Long-lived connections therefore pick up permission changes without you reconnecting. ## Connection limits Concurrent WebSocket connections are capped **per source IP** (deployment-configured, currently 4 per IP). When the cap is exceeded the server sends a text frame and then closes the socket: ```text theme={null} connectionLimitExceeded: ``` This maps to error code **`-32100` "Number of concurrent websocket clients limit exceeded"**. The exact heartbeat window and per-IP connection cap are deployment-specific config values. The values above reflect current deployments; see [Rate limits](/rate-limits) for the per-IP connection cap and read your live budget from [`public/getRateLimits`](/rate-limits#runtime-introspection). Per-request rate limits are separate and shared across all of a wallet's connections — see [Rate limits](/rate-limits). ## Reconnection guidance Sessions do not survive a dropped socket. On reconnect: Dial the same `/v3/ws` endpoint, with backoff (exponential + jitter) so a server restart doesn't produce a reconnect storm that trips the per-IP cap. Re-send `public/login` (or reconnect with a fresh `?token=`). Nothing from the previous session — auth, subscriptions — is retained. Re-establish every channel from [Subscriptions](/subscriptions), then reload state (open orders, positions) over REST or private reads, since you may have missed updates while disconnected. ## Cancel on disconnect Cancel-on-disconnect automatically cancels your resting orders, quotes, and trigger orders when a connection drops. It is a persisted account setting — see [Cancel on disconnect](/trading/cancel-on-disconnect) for the full behaviour and the `private/set_cancel_on_disconnect` request. # Error Codes Source: https://docs.derive.xyz/error-codes The JSON-RPC error shape and the full Derive v3 application error catalog. Every failed request returns a JSON-RPC 2.0 error object, whether the call was made over WebSocket or HTTP POST. The `error` object always has three fields: ```json theme={null} { "id": "1", "jsonrpc": "2.0", "error": { "code": -32602, "message": "Invalid params", "data": "expiry: value out of range" } } ``` The numeric error code. Negative codes are JSON-RPC / transport-level; positive codes are Derive application errors (see the catalog below). A short, stable human-readable summary. Optional detail (e.g. the offending field or protocol reason). Always present on the wire — it is `null` when there is no detail. ## JSON-RPC base codes The standard JSON-RPC 2.0 codes cover malformed or unroutable requests before any application logic runs. | Code | Name | Message | When it fires | | ------ | ------------------ | --------------------- | --------------------------------------- | | −32700 | `parse_error` | Error parsing message | Request body is not valid JSON | | −32603 | `internal_error` | Internal error | Any uncaught or internal server failure | | −32602 | `invalid_params` | Invalid params | Params fail validation | | −32601 | `method_not_found` | Method not found | Unknown RPC method | | −32600 | `invalid_request` | Invalid Request | Not a valid JSON-RPC request object | **Internal errors are redacted.** For code **−32603**, the server always returns `message: "Internal error"` and `data: null` on the wire — any underlying detail is kept server-side and never leaked to clients. Treat −32603 as "retry or contact support," not as a description of what went wrong. ## Transport & session codes | Code | Name | Message | When it fires | | ------ | ------------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | | −32000 | `rate_limit_exceeded` | Rate limit exceeded | Per-wallet or per-instrument limit hit | | −32000 | `custom_rate_limit_error` | *(caller-supplied)* | A method-specific rate-limit rejection | | −32100 | `concurrent_ws_client_exceeded` | Number of concurrent websocket clients limit exceeded | Too many concurrent WebSocket connections | | 401 | `unauthorized` | Unauthorized | Missing or invalid authentication | | 403 | `forbidden` | Forbidden | Authenticated but not permitted | | 403 | `restricted_region` | You are in a restricted region that violates our terms of service. | Request originates from a geoblocked region | See [Rate limits](/rate-limits) for how the two-tier limiter works and how to read your live limits, and [Authentication](/authentication/session-login) for the session-login and action-signing layers behind the 401/403 and 14xxx codes. ## Application error catalog Application errors use positive codes grouped by numeric range. Codes are stable; gaps within a range are reserved. Internal **8xxx** codes (e.g. counterparty/feed diagnostics) are intentionally omitted from this reference — they are server-side signals, not client-actionable, and are excluded from public docs. ### 9xxx — Confirmation timeouts The order was accepted but a downstream confirmation did not arrive in time. Query order state before resubmitting. | Code | Name | Message | When it fires | | ---- | ----------------------------- | --------------------------- | ----------------------------------------------------- | | 9000 | `order_confirmation_timeout` | Order confirmation timeout | Order confirmation timed out | | 9001 | `engine_confirmation_timeout` | Engine confirmation timeout | Matching-engine confirmation timed out; order dropped | ### 10xxx — Accounts, transfers & margin Rejections from account, session-key, and subaccount-level constraints before an order reaches the book. | Code | Name | Message | When it fires | | ----- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------- | | 10001 | `asset_not_erc20` | Asset is not an ERC20 token | Deposit/withdraw/transfer of a non-ERC20 asset | | 10003 | `same_account_transfer` | Sender and recipient subaccount IDs are the same | Self-transfer | | 10004 | `multiple_currencies_not_supported` | Multiple currencies not supported | Mixed-currency request rejected | | 10006 | `max_session_keys_per_wallet` | Maximum number of session keys per wallet reached | Session-key cap reached | | 10007 | `max_assets_per_subaccount_exceeded` | Maximum number of assets per subaccount reached | Per-subaccount asset cap reached | | 10010 | `pmrm_only_supports_quote_asset` | PMRM only supports USDC asset collateral. Cannot trade spot markets. | PMRM subaccount tried to trade spot | | 10015 | `pm2_only_support_single_currency_options_and_perps` | PortfolioMargin2 supports multiple collaterals but only options and perps of the same currency | PM2 cross-currency options/perps | | 10016 | `pm2_does_not_support_this_collateral` | PortfolioMargin2 does not support this collateral | PM2 unsupported collateral | ### 11000–11039 — Orders, balances & matching Order-lifecycle rejections from the matching engine, plus the balance-level guards that reject a debit before it is applied. | Code | Name | Message | When it fires | | ----- | --------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------- | | 11000 | `insufficient_funds` | Insufficient funds | Insufficient margin/funds for the order | | 11006 | `order_does_not_exist` | Does not exist | Cancel/query of an unknown order | | 11007 | `self_crossing_disallowed` | Self-crossing disallowed | Order would cross your own resting order | | 11008 | `post_only_reject` | Post only order cannot cross the market | Post-only order would match immediately | | 11009 | `zero_liquidity_for_taker_order` | Zero liquidity for market or IOC/FOK order | No liquidity within the limit price | | 11011 | `order_invalid_signature_expiry` | Invalid signature expiry | Expiry outside the allowed validity window | | 11012 | `invalid_amount` | Invalid amount | Order amount invalid | | 11013 | `order_invalid_limit_price` | Invalid limit price | Limit price invalid | | 11014 | `fok_not_filled` | Fill-or-kill not filled | FOK order could not be fully filled | | 11015 | `mmp_frozen` | MMP frozen | Market-maker protection frozen for the subaccount/currency | | 11017 | `non_unique_nonce` | Non unique nonce | Nonce already used | | 11018 | `order_invalid_nonce` | Invalid nonce date | Leading nonce digits are not a valid UTC-second timestamp | | 11019 | `too_many_orders` | Open orders limit exceeded | Open-order cap per subaccount reached | | 11020 | `negative_erc20_balance` | Negative ERC20 balance | Wrapped ERC20 balance would go negative | | 11021 | `instrument_not_live` | Instrument is not live | Instrument deactivated or expired | | 11022 | `reject_timestamp_exceeded` | Reject timestamp exceeded | Order arrived after its `reject_timestamp` | | 11023 | `max_fee_too_low` | Max fee order param is too low | `max_fee` below the required minimum | | 11024 | `reduce_only_not_supported` | Reduce only not supported with this time in force | Reduce-only requires market/IOC/FOK | | 11025 | `reduce_only_reject` | Reduce only reject | Reduce-only order would increase the position | | 11027 | `undergoing_liquidation` | Subaccount undergoing liquidation | Subaccount is being liquidated | | 11028 | `replace_order_filled_amount_mismatch` | Replaced order filled amount does not match expected state. | Replace saw an unexpected filled amount | | 11029 | `oi_cap_exceeded` | Trade or transfer rejected: open interest cap would be exceeded | Open-interest cap for the manager exceeded | | 11030 | `fee_consumes_amount` | Fee consumes entire amount | Fee is ≥ the transfer/withdraw amount | | 11031 | `withdrawal_rounds_to_zero` | Withdrawal rounds to zero | Net withdrawal rounds to zero in native decimals | | 11032 | `transfer_below_min` | Transfer below minimum | New-subaccount funding below the minimum | | 11033 | `transfer_not_whitelisted` | Transfer recipient not whitelisted | Recipient not on the sender's whitelist | | 11034 | `withdrawals_blocked_insolvent_auction` | Withdrawals blocked by insolvent auction | Withdrawals paused during an insolvent auction | | 11035 | `non_increasing_nonce` | Non increasing nonce | Signed-action nonce is not strictly increasing | | 11039 | `lending_borrows_exceed_supply` | Lending pool borrows exceed supply | Pool has lent out more than it holds; debit uncoverable | ### 11050–11055 — Trigger orders Stop / trigger-order specific rejections. | Code | Name | Message | When it fires | | ----- | ------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------- | | 11050 | `trigger_order_already_cancelled_or_expired` | Trigger order was cancelled between the time worker sent order and engine processed order | Trigger cancelled before it could be processed | | 11051 | `invalid_trigger_price` | Trigger price must be higher than current price for stop orders and vice versa | Trigger price on the wrong side | | 11052 | `too_many_trigger_orders` | Trigger order limit exceeded (separate limit from regular orders) | Trigger-order cap reached | | 11053 | `trigger_price_type_not_supported` | Index and last-trade trigger price types not supported yet | Only mark-price triggers are supported | | 11054 | `cannot_replace_or_be_replaced_by_trigger_orders` | Trigger orders cannot replace or be replaced | Replace involving a trigger order | | 11055 | `unfillable_market_trigger_order` | Market order limit\_price is unfillable at the given trigger price | Market trigger would not be crossable | A parallel **11150–11155** code range for trigger-order rejects appears in the reject-message mapping and may surface for some persisted trigger rejections. Handle trigger rejects by `name`/`message` rather than hard-coding `11050` vs `11150`. ### 11100–11113 — RFQs & quotes Request-for-quote and quote-matching rejections. | Code | Name | Message | When it fires | | ----- | ---------------------------------------- | ----------------------------------------------- | ------------------------------------------------- | | 11100 | `leg_instruments_not_unique` | Leg instruments are not unique | Repeated instrument across RFQ/quote legs | | 11101 | `rfq_not_found` | RFQ not found | RFQ query/cancel matched nothing | | 11102 | `quote_not_found` | Quote not found | Quote query/cancel matched nothing | | 11103 | `quote_leg_mismatch_vs_rfq` | Quote leg does not match RFQ leg | Quote legs differ from RFQ legs | | 11104 | `quote_rfq_not_open` | Requested quote or RFQ is not open | RFQ expired, filled, or cancelled | | 11105 | `quote_rfq_id_mismatch` | Quote references a different RFQ than requested | Quote's RFQ id differs from the one provided | | 11106 | `rfq_invalid_counterparty` | Invalid RFQ counterparty | Counterparty unauthorized or nonexistent | | 11107 | `quote_maker_cost_too_high` | Quote maker total cost too high | Maker cost exceeded the price bandwidth | | 11108 | `rfq_partial_fill_pct_too_high` | RFQ partial fill percentage too high | Fill exceeds the requested total size | | 11109 | `rfq_filled_direction_cannot_be_changed` | RFQ filled direction cannot be changed | A later fill would flip the direction | | 11110 | `quote_taker_cost_too_high` | Quote taker total cost too high | Taker cost exceeded the orderbook execution price | | 11111 | `rfq_disabled_for_account` | RFQ functionality is disabled for this account | RFQ disabled for the account | | 11112 | `rfq_too_many_legs` | RFQ has too many legs | Leg count over the maximum | | 11113 | `rfq_too_many_counterparties` | RFQ has too many counterparties | Counterparty count over the maximum | ### 12xxx — Market data | Code | Name | Message | When it fires | | ----- | ---------------------- | -------------------- | ------------------ | | 12001 | `instrument_not_found` | Instrument not found | Unknown instrument | | 12002 | `currency_not_found` | Currency not found | Unknown currency | | 12003 | `asset_not_found` | Asset not found | Unknown asset | ### 13000 — Subscriptions | Code | Name | Message | When it fires | | ----- | ------------------ | ---------------- | ----------------------------------------- | | 13000 | `invalid_channels` | Invalid channels | Unknown or malformed subscription channel | ### 14xxx — Authentication & authorization Session-login, signature, and session-key scope failures. See [Authentication](/authentication/session-login) for the underlying scheme. | Code | Name | Message | When it fires | | ----- | ---------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- | | 14000 | `account_not_found` | Account not found | Account does not exist | | 14001 | `subaccount_not_found` | Subaccount not found | Subaccount not owned by a registered wallet | | 14013 | `string_is_not_ethereum_address` | String is not a valid ethereum address | Malformed address | | 14014 | `invalid_signature` | Signature invalid for message or transaction | Recovered signer does not match the expected signer | | 14020 | `auth_header_mismatch` | The X-DeriveWallet header does not match the requested subaccount\_id or wallet | Auth header subject mismatch | | 14021 | `ip_not_whitelisted` | IP not whitelisted | Request IP not on the whitelist | | 14023 | `invalid_signer` | Signer in on-chain related request is not wallet owner or registered session key | Signer is neither the owner nor a registered session key | | 14026 | `session_key_not_found` | Session key not found | Unknown session key | | 14027 | `unauthorized_as_rfq_maker` | Unauthorized as RFQ maker | Account is not an RFQ maker | | 14028 | `cross_currency_rfq_not_supported` | Cross currency RFQ not supported | RFQ legs span multiple currencies | | 14030 | `session_key_expired` | Session key expired | Session key is past its expiry | | 14031 | `unauthorized_key_scope` | Unauthorized Key Scope | Session-key scope insufficient for the action | | 14033 | `account_not_atomic_whitelisted` | Account not whitelisted for atomic orders | Atomic order from a non-whitelisted account | | 14035 | `session_key_already_registered` | Session key already registered | Session key already belongs to another wallet | **`14014` with signing that looks right is usually bad ABI encoding, not a bad signature.** Routes encode `Action.data` server-side, so your bytes never reach the decoder — the hashes simply disagree, naming no field. Send them to [`public/decode_action`](/api-reference/system/publicdecode_action) (no auth, executes nothing) for the decoded fields or the exact fault: ``` word 2 (limit_price) is not e12-representable: downscale discards a non-zero remainder ``` SDK: `await client.decodeAction('order', encodedData)`. Once the bytes decode, the `*_debug` routes compare EIP-712 hashes. ### 16xxx — Compliance | Code | Name | Message | When it fires | | ----- | ------------------ | ----------------------------------------------------- | ------------------------------------ | | 16001 | `account_disabled` | Account is disabled due to compliance violations | Account disabled by compliance | | 16002 | `ofac_blocked` | Account is blocked due to OFAC compliance violations. | Account blocked under OFAC screening | ### 17xxx — Risk universes | Code | Name | Message | When it fires | | ----- | ------------------------------ | -------------------------------------------------------- | ------------------------------------------------------------------- | | 17000 | `cross_universe_trade` | Instrument not supported by this manager / risk universe | Participants belong to different risk universes | | 17001 | `unknown_risk_universe` | Unknown risk universe | Referenced risk universe does not exist | | 17002 | `manager_cannot_risk_currency` | Risk manager has no configuration for this currency | Trading or holding a currency the subaccount's manager cannot price | | 17003 | `asset_not_in_risk_universe` | Asset is not registered in the destination risk universe | Depositing or transferring an asset the destination cannot hold | ### 18xxx — Vaults | Code | Name | Message | When it fires | | ----- | -------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------- | | 18007 | `vault_not_found` | Vault not found | Subaccount is not a vault | | 18008 | `exceeded_max_user_requests` | Exceeded maximum number of pending vault requests | Too many pending deposit/withdraw requests | | 18009 | `max_shareholder_vaults_reached` | Exceeded maximum number of shareholder vaults | Shareholder-vault cap reached | | 18010 | `vault_closed` | Vault is closed | Vault has been fully withdrawn/closed | | 18011 | `vault_cooldown_active` | Vault withdrawal cooldown active | Deposit→withdraw cooldown not elapsed | | 18012 | `vault_creation_deposit_below_min` | Vault creation deposit below minimum | Curator's initial deposit below the minimum | | 18013 | `vault_curator_stake_below_min` | Curator stake below minimum | Withdrawal would drop curator stake below the floor | | 18014 | `vault_slippage_exceeded` | Vault quote outside slippage band | Share price outside the accepted band | | 18015 | `vault_amount_below_min` | Vault amount below minimum | Deposit/withdraw below the per-asset minimum | | 18016 | `vault_signature_expiry_too_long` | Vault signature expiry too far out | Expiry more than 30 days out | | 18017 | `vault_benchmark_price_unavailable` | Vault HWM benchmark price unavailable | High-water-mark benchmark has no feed | | 18018 | `vault_initial_share_price_too_far_from_benchmark` | Vault initial share price too far from benchmark | Initial price too far from the benchmark | | 18019 | `vault_deposit_exceeds_margin` | Insufficient margin for vault deposit | Source subaccount cannot back the deposit | ### 20000 — Protocol | Code | Name | Message | When it fires | | ----- | ----------------- | --------------------------- | ------------------------------------------------------------------ | | 20000 | `protocol_reject` | Protocol rejected operation | The protocol layer rejected the operation; the reason is in `data` | ## Handling errors Branch on `code` for programmatic handling and surface `message` (and `data` when present) to users. Never parse `message` text — it is stable but treat `code` as the contract. For −32603, retry with backoff rather than inspecting the (redacted) body. # Contracts Source: https://docs.derive.xyz/getting-started/contracts On-chain contract addresses for the Derive v3 testnet (Sepolia) deployment. The on-chain contracts for the Derive v3 **testnet** deployment on **Ethereum Sepolia**. These are the settlement and custody contracts your own wallet interacts with for deposits, withdrawals, and the L1 escape hatch. **Chain ID:** `11155111` For the **signing** constants — `ACTION_TYPEHASH`, the EIP-712 domain, and the per-action module addresses — see [Action signing](/authentication/action-signing). ## `ACTION_MANAGER` ```text theme={null} 0x5e6D8BE71FD6838f045E08c60663C9cC36590c33 ``` Direct L1 calls to the Derive exchange can be made for [deposits](/getting-started/depositing), or for setting session keys from [MPC wallets](/authentication/mpc-wallets) and [multi-sigs or contracts](/authentication/contract-owned-accounts). ## `VAPP` ```text theme={null} 0x3012BA2755B2fB921230407A89c91e86914Bfc53 ``` The zk-vApp settlement contract. It anchors the protocol's on-chain state root and verifies the validity (zero-knowledge) proofs that settle every state transition — the root of custody and settlement for the deployment. ## `WITHDRAWAL_OUTBOX` ```text theme={null} 0xf0894158A309c7B11A0b858B8c62eB88fB65F87f ``` The L1 withdrawal outbox. Finalized withdrawals — and the **escape-hatch** exit if the operator goes offline — are claimed here; payouts are pulled from the spot vault through this contract. ## `SPOT_VAULT` ```text theme={null} 0x08bb6253302967ABca91541B0707f5DBc045D271 ``` Mainnet contract addresses are not published in this repository — read them from the mainnet deployment. # Programmatic Onboarding Source: https://docs.derive.xyz/getting-started/depositing Integrate your app within minutes. User onboarding on Derive is **fully programmatic** - no humans in the loop. You can integrate the Derive Exchange for your users within minutes.