Account Multisig
Configure an account-level multisig quorum over your account's owner-signed actions, and submit quorum-signed requests.
An account multisig puts a signer quorum in front of your account's owner-signed actions. Once it is active, moving value or changing who can trade for you takes threshold-of-signers approvals instead of one owner signature.
It does not cover trading. Trading actions continue to be signed by a single API wallet key.
What a quorum can sign
Covered — needs the quorum
transfer · withdraw · settle · repay · approveAgent · revokeAgent · activateFor · setAccountMultisig
Not covered — API wallet, single signature
order · cancel · cancelAll · modify · batch
Trading actions can never be quorum-signed. A request that carries auth_account together with a trading action is rejected with AccountAuthNotAllowedForAction.
Setup order
Enabling a multisig locks the owner key out of trading, so the sequence matters:
Approve an API wallet with an owner-signed
approveAgent, and confirm it can place and cancel an order. Do this before step 3 — afterwards the owner key can no longer sign trading actions, andapproveAgentitself will need the quorum.Fund the account with the creation fee — 1,000 USDC or 1,000 USDT — unless a sponsor is paying it.
Submit
setAccountMultisig, owner-signed, withcreation_fee.Read
accountMultisigand recordpolicy_epoch. Every quorum-signed request from here on carries it, and it advances on each configuration change, so re-read it after anysetAccountMultisig.
Calling the API
setAccountMultisig is submitted to POST /trade like any other write, and the configuration is read from POST /info. Examples below use mainnet, API_URL=https://api.native.org; for testnet, substitute https://api-test.native.org and sign with the testnet chain id given on Transaction Signing.
POST /trade is synchronous and answers with the standard envelope — submission_status of accepted, rejected, or timeout, plus tx_hash and an error.code. The full contract, including what timeout means for whether a transaction can still land, is on POST /trade. Every code in Errors below arrives as that error.code.
nonce is a Unix millisecond timestamp and is validated per authority; see Nonces & API Wallets. expires_after_ms is optional on every request here, except a sponsored creation, which requires it.
Reading your configuration
found reports whether the account exists, not whether it has a multisig row — read enabled to decide that. role is non-null only for protocol-operated accounts; for your own account it is always null. policy_epoch is the value every quorum-signed request must carry; it advances on each configuration change.
setAccountMultisig
Creates the account's first configuration, or replaces the current one. The action is the same, but the envelope is not: the first creation is owner-signed with signature, as below; once a configuration is Active, a replacement is a covered action like any other and must be quorum-signed — an owner-signed replacement is rejected with AccountMultisigRequired. A first-ever creation additionally carries creation_fee.
threshold
number
Approvals required. 1 … signers.length. 0 is rejected.
signers
string[]
1–32 addresses. Must be unique, non-zero, and sorted ascending — an unsorted array is rejected even if the set is correct.
creation_fee
object
Only on a first-ever creation. Omit it when replacing an existing configuration.
cloid
string
Required. 16-byte hex. Used only for txStatusByCloid; it is not an idempotency key.
Field names are snake_case and the payload is strict — an unrecognised field fails the request rather than being ignored. asset_id and amount are decimal strings, not JSON numbers, and amount is in raw atoms with no display decimals.
Creation fee
Charged once, on the first configuration only.
asset_id
Asset
amount (atoms)
Value
"1"
USDC
"100000000000"
1,000 USDC
"2"
USDT
"100000000000"
1,000 USDT
The amount must match one of the rows above exactly — a different value is rejected with InvalidAccountMultisigCreationFee, not rounded or topped up.
selfPaid — the account pays its own fee from its available balance:
sponsored — another account pays it. The sponsor signs a separate EIP-712 AccountMultisigSponsor message; its 65-byte signature rides inside the action:
The sponsor signs its own EIP-712 message, under the quorum-signed domain (version: "2" — see EIP-712 typed data):
targetAccount is the account being configured. expiresAfterMs is the value the request's expires_after_ms field carries — which is why that field is required on a sponsored creation; the sponsor's authorization expires with it. The signature is verified by recovering the signer and requiring it to equal payer. It is not part of the outer SetAccountMultisig digest — signing the outer message does not cover it, and it must be produced separately by the sponsor.
Signer-set restriction
A configuration you submit must not list your own account's key among signers. This applies to both a first-ever creation and any replacement set. Violations are rejected with InvalidAccountMultisigConfig before the nonce is consumed.
Submitting a quorum-signed request
Once a configuration is Active, the covered actions are submitted with an account-auth request: drop signature, and send the collected approvals in signatures along with the two account-auth fields.
auth_account
The account the quorum acts for. Must be sent with policy_epoch.
policy_epoch
From the accountMultisig read. A stale value is rejected with AccountMultisigEpochMismatch.
signatures
Array of 65-byte hex signatures, at least threshold of them, each from a distinct configured signer. Order matters — see below.
agent_epoch
Must be absent. Account-auth and agent-auth are mutually exclusive.
Every signature covers the same quorum-signed digest, so the signers can sign independently and in any sequence — but the array you submit must be ordered so that the recovered addresses ascend. Collect the approvals in any order, then sort them by recovered signer address before submitting. An out-of-order array is rejected with DecodeMultisigProofSignaturesNotSorted even when every signature is individually valid, and the same signer appearing twice is rejected with DecodeMultisigProofDuplicateRecoveredSigner rather than counting as two approvals.
EIP-712 typed data
Which scheme you sign follows the envelope, not the action.
Owner-signed (the first creation) — the standard v4 scheme: the domain, the six common fields, and the encoding rules are exactly as specified on Transaction Signing, with authKind: 1 and authScope: 0.
Quorum-signed (every account-auth request) — a v5 variant of that scheme. Every approval in a signatures array must be computed this way, and it differs from the v4 scheme in exactly three places:
The domain
versionis"2". Everything else about the domain — the name, the zeroverifyingContract, the absence ofchainId— is unchanged.The six common fields become seven:
authScopeis replaced byaddress authAccount, uint256 policyEpoch, signed with the same values the envelope'sauth_accountandpolicy_epochfields carry:authKindis2— including an array carrying a single approval.
The action tail after the common fields is identical under both schemes — for this action and for every other covered action. Written out in full, the v4 primary type for setAccountMultisig is below; for the v5 form, replace the six common fields at its head with the seven above and leave everything from uint256 threshold onward unchanged.
Within that tail the fee is flattened, not nested. feeMode is the discriminator — 0 no fee (a replacement), 1 selfPaid, 2 sponsored — and every unused fee field is zero-valued: address zero for feePayer, 0 for the three numerics.
Errors
Every code below is a fixed CamelCase string returned verbatim as error.code. Match on the exact string; the codes are grouped here by how far the request got, because that determines what you do next.
Request-level — the request is malformed and never reaches execution. submission_status is rejected and no tx_hash is returned:
AuthAccountRequiresPolicyEpoch
auth_account sent without policy_epoch.
PolicyEpochRequiresAuthAccount
policy_epoch sent without auth_account.
AgentEpochNotAllowedWithAuthAccount
Both agent-auth and account-auth fields present.
SignaturesNotAllowedForAction
signatures sent without auth_account. This is the usual shape of a forgotten auth_account.
AccountAuthRequiresEip712
auth_account sent without auth_scheme: "eip712".
AccountAuthNotAllowedForAction
Account-auth used with an action a quorum cannot sign — a trading action, most often.
AccountAuthNotSupported
Account multisig is not available on the network you addressed.
InvalidSigner / InvalidPayer / InvalidSponsorSignature
Malformed hex, or a wrong length, in signers, payer, or the sponsor signature.
DecodeMultisigProofSignaturesNotSorted
signatures is not ordered by ascending recovered signer address.
DecodeMultisigProofDuplicateRecoveredSigner
The same signer appears twice in signatures.
InvalidSignaturesLen
signatures is empty, or holds more than 32 entries.
DecodeMultisigProofRecoveryFailed
A signature in the array does not recover to any address.
LegacySignatureNotAccepted
Submitted without auth_scheme:"eip712" — this action accepts no other scheme.
InvalidAuthAccount
auth_account is not a well-formed address.
AmbiguousAuthFields
Both signature and signatures were sent, or neither — a quorum request carries only signatures.
Execution-level — the request was accepted and then rejected on chain. These split at the nonce boundary, and the side decides whether you can retry with the same nonce.
Before the nonce is consumed — the nonce is untouched, so correct the request and resubmit it unchanged:
AccountMultisigAgentRequired
The owner key submitted a trading action while a configuration is Active. Use an API wallet.
AccountMultisigEpochMismatch
policy_epoch is stale. Re-read accountMultisig and re-sign.
AccountMultisigNotConfigured
The account has no configuration.
AccountMultisigNotActive
A configuration exists but is not Active.
AccountMultisigRequired
The account's lifecycle requires a quorum, but a single owner signature was sent.
UnauthorizedMultisig
Fewer than threshold valid signatures, or a signature from an address outside the set.
InvalidAccountMultisigConfig
Structural problem — empty, unsorted, duplicated, zero address, threshold out of range, or the set includes the account's own key.
FeatureDisabled
The network is below the account-multisig activation height, so account-auth frames are not yet accepted.
After the nonce is consumed — the nonce is spent. Fix the cause and resubmit with a fresh nonce:
AccountFrozen
The account is frozen and cannot reconfigure until an admin unfreezes it. A frozen sponsor is rejected the same way.
AccountMultisigCreationFeeRequired
First-ever creation submitted without creation_fee.
UnexpectedAccountMultisigCreationFee
creation_fee sent on a replacement. Only the first-ever creation carries one.
InvalidAccountMultisigCreationFee
asset_id or amount does not exactly match a creation-fee row.
InvalidAccountMultisigFeePayer
The sponsor is ineligible, frozen, or payer_policy_epoch is stale.
AccountNotFound
The account does not exist. Deposit to create it first.
AssetNotFound
The asset_id in creation_fee is not a known asset.
AccountMultisigActiveLimitExceeded
The network's cap of 10,000 active configurations is reached.
AccountMultisigLifecycleLimitExceeded
The network's cap of 100,000 lifecycle entries is reached.
AccountMultisigEpochOverflow
The global policy epoch counter overflowed. Not expected in normal operation.
Last updated