This page covers changes in the v3 API in detail. We recommend importing
this directly into your LLM / coding agent as a
SKILL.MD.- TypeScript SDK
- Python SDK (coming soon)
- Rust SDK (coming soon)
openapi.json spec (simply add /openapi.json to the base URL).
1
Collapse to a single wallet
There is no longer a distinction between your “wallet” and a separate “Derive Wallet” or “Smart Contract Wallet”.
Every
wallet / owner field is your own EOA or multisig. A subaccount carries a single
wallet address, and an action is authorised only when the subaccount’s wallet equals
the action owner (the signer is either that owner or one of its session keys).Note that during the v2 -> v3 state migration all “Derive Wallets” will be automatically transferred to the user’s EOA or multisig.2
Update action signing flow
Account creation is fully permissionless. Deposits move onchain. Some minor changes to certain actions.
3
Repoint hosts and constants
Switch base URLs to the v3 hosts (
api.derive.xyz/v3 / testnet.api.derive.xyz/v3), recompute the EIP-712 domain
separator for the target chain, and use the shared module addresses. See Constants.4
API endpoint changes
Apply the mapping table. Note that several v3 targets (
get_all_instruments, get_open_orders,
get_order_history, get_ticker/get_tickers, order_quote) already existed in v2 — the corresponding v2 name
was removed, not aliased.Signed-action changes
Deposits are on-chain
There is no deposit action orprivate/deposit RPC or action in v3. Deposit directly on-chain via one of the three supported flows. Refer to the Depositing guide or SDKs for more information.
Withdrawals
The withdrawal action gainsmax_fee_usd, force_batch, and a recipient (in the signed
action data), and the amount stays in the asset’s underlying decimals:
private/withdraw response: { operation_id, op_uuid } — no status.
Transfers
private/transfer_spot— one spot asset at a time, between subaccounts of the same owner (existing, or a newly-created sender-owned subaccount via a non-zeronew_subaccount_manager). Request carriesmax_fee_usd(the exchange may charge for creating a subaccount). It cannot send to a different owner.private/transfer_positions— replacestransfer_position; handles one or many legs (position transfers still use the RFQ-style signed-quote envelope).
{ operation_id, op_uuid } (no status).
Refer to Transfers & Withdrawals or SDKs for more information.
Session keys
v3 splits session-key management into a signed protocol action and an off-chain edit:private/create_session_key— signs aCreateSessionKeyaction containing{ session_key, expiry_sec, scopes, subaccounts }. Only protocol scopes are part of the signed action; off-chain scopes travel as unsigned metadata. Signed againstsubaccount_id = 0. This is the replacement forregister_scoped_session_keyand the old admin-key register/deregister/build-tx flow.private/edit_session_key— existed in v2, but now edits only off-chain fields (label,ip_whitelist,offchain_scopes). Its v2disableflag was removed.- To retire a key, re-register the same key via
private/create_session_keywith a past or zero expiry. v2’sderegister_session_keyand thedisableflag onedit_session_keyhave no direct replacement in v3.
edit_session_key. See Access scopes.
RFQ quote signatures
Maker and taker quote (send_quote) signature_expiry_sec must satisfy roughly:
Nonces: nanoseconds, and increasing
In v2, nonces were treated as milliseconds. In v3 a nonce is a UTC timestamp in nanoseconds (~19 digits for 2025, e.g.1751558400000000000). A millisecond- or microsecond-scale nonce will be rejected.
Beyond the unit change, nonces on most signed actions must be strictly increasing per
subaccount, and must fall inside a validity window. The window differs by action type:
Orders and RFQs are exempt from the increasing requirement. A coarser ± 5-minute check is also applied
when the request is first received. Exact bounds may vary by environment.
Constants
The hosts and signing constants that changed between v2 and v3.Endpoints
See Endpoints for the canonical host/path list and Connecting
for the WebSocket handshake.
Module addresses are now shared across deployments
The per-action module contracts — themodule field selected inside a signed Action — are
identical on testnet and mainnet in v3. They are fixed protocol constants; hardcoding them
is safe.
Domain separator is recomputed per chain
Unlike the module addresses, the EIP-712 domain separator is not shared: v3 recomputes it per chain. Note, since v3 is now on Ethereum Sepolia & Mainnet, the domain separators must be recomputer. TheverifyingContract is constant across every network (the v2 mainnet Matching,
0xeB8d770ec18DB98Db922E9D83260A585b9F0DeAD); only chainId differs, so testnet and mainnet
resolve to different separators.
Auth headers: X-Lyra* → X-Derive*
See Action signing or SDKs for more information.
Transaction ids → operation uuids
v2 returned atransaction_id (and a status) on state-changing calls. v3 removes
status and returns an operation identifier instead:
op_uuid(string) — the stable UUID of the operation.operation_id(integer) — a numeric operation id, returned alongsideop_uuidon action responses (withdraw, transfers, whitelist).
tx_hash still exists but now points to the Ethereum L1 transaction in which the zk
batch settled, not a Derive-chain transaction. The full lineage is
op_uuid → operation → batch → L1 tx, and tx_hash (plus l1_block) is populated from
the batch once it settles.
Settlement status: TxStatus → BatchStatus
v2 tracked settlement with a single per-transaction status. Because v3 settles operations in
zk-batches, the settlement lifecycle is a BatchStatus on the batch — the single source of
truth for every operation in it (individual operations carry no status of their own). Each stage
has a healthy value and a matching …Error value if that stage fails:
Values serialize as the PascalCase variant name and are
null until the operation is picked
up into a batch. Deposit / withdrawal / erc20-transfer history expose this as batch_status;
trade history keeps the field name tx_status but returns these same BatchStatus values
(see History routes).
Renamed / consolidated methods
First, download the fullopenapi.json spec by adding /openapi.json to the base URL. Use that as the reference as you migrate the routes.
Response reshapes
public/get_currency & public/get_all_currencies
Both keep their names and params and return the same per-currency object shape
(get_all_currencies returns an array of it). The object was completely restructured:
v2 was flat, keyed risk by manager contract address, and assumed USDC collateral. v3
nests everything around two grouping keys — risk_universe_id and per-asset
(option / perp / spot, where spot is now a list).
New fields with no v2 equivalent:
risk_universe_id on every managers[] / *.universes[]
entry; option/perp become {name, address, universes[]}; spot[] gains name,
min_deposit_usd, and erc20.decimals.
Type notes:
sm / pm / all risk_universe_id are numbers (u32); erc20.decimals is a number; all
money/discount/OI fields are decimal strings. In v2 the manager identifier was a hex address string — it is now a
numeric id. managers[].pm is the PMRM2 manager (v2 margin_type: "PM2"), not the legacy v2 "PM".spot_price_24h, top-level option,
top-level perp, managers[].sm, managers[].pm, spot[].universes[].lending,
spot[].erc20.underlying_erc20. spot and every universes array are always present (may
be empty).
To resolve a spot collateral’s discount/lending, iterate
spot[] → universes[] and match on risk_universe_id (from
private/get_subaccount’s new risk_universe_id) instead of reading a single top-level value. spot being an
array is the most likely thing to break clients that assumed one spot asset per currency. Perp margin requirements
are no longer in this response.private/get_subaccount
New fields: manager_id (u32) and risk_universe_id (u32, the key into the
per-universe currency data above). Also:
currencychanged from a single string to astring[]— a breaking shape change.margin_typewidened from a fixed enum (PM|SM|PM2) to an open string.- New
failed_to_fetch(bool) andvault_deposit_holds(array) fields. projected_margin_changeis currently always"0"(not yet implemented in v3, unlike v2 which computed a real value).
History routes
public/get_trade_historyno longer accepts atx_hashquery param.- Status field: deposit / withdrawal / erc20-transfer history now expose
batch_status; trade history (public and private) keepstx_status. Both use the same set of status values (PascalCase strings) — success stagesBatching,Executing,Proving,Settling,Settled, and their errored counterpartsBatchingError,ExecutingError,ProvingError,SettlingError,SettledError. The value isnulluntil the operation is picked up for settlement. - Deposit / withdrawal / erc20-transfer history: each row now carries
operation_id(string UUID),batch_uuid, andbatch_status.tx_hashis still present (nullable) — it is not removed, but it now resolves to the settling L1 transaction.
The status field name differs by endpoint: trade history uses
tx_status; the deposit/withdrawal/erc20-transfer
history endpoints use batch_status. The operation-uuid field is op_uuid on the private trade-history row and
operation_id on the deposit/withdrawal/erc20 rows. Public trade history exposes no operation-uuid field.