CosignCreate and register a wallet
Create and register a wallet
Create a guarded wallet on Cardano or an EVM chain, then register it and its mandate with Exchain.
Create a guarded wallet the owner holds, then register it with your Node Token. Registration attaches the mandate Exchain enforces on every payment.
What every guarded wallet has
| Part | Cardano | EVM chains |
|---|---|---|
| Wallet | Masumi smart wallet | Stock Safe 1.4.1 |
| Agent | One agent key | One agent address |
| Attested quorum | Every quorum key hash | One owner address per quorum member |
| Owner | Owner key. Never counts toward the quorum | Owner address, the only module. Never a Safe owner |
| Mandate | One per asset, up to 16 assets | One per asset, up to 16 listed assets on the Safe's chain |
| Register with | POST /v1/wallets | POST /v1/x402/wallets |
Cardano
Mint parameters
These are part of the address. Changing any of them means a new wallet.
| Parameter | Value |
|---|---|
owner | The owner's cold key. It never counts toward the quorum |
quorum_vks | Every attested quorum key hash, from the console |
quorum_threshold | 3: every quorum member must sign |
stake | The stake part of the address, frozen for life |
Use exactly Exchain's keys and 3. The contract does not check quorum_threshold: 0 or below removes Exchain from the wallet permanently, and a value above the quorum's weight blocks agent spends (the owner can still sweep). Registration does not read the chain, so Exchain cannot catch either mistake for you.
The datum
The wallet UTxO carries an inline datum. The owner can rewrite it later: Owner controls.
| Field | Meaning |
|---|---|
agent | The one hot agent key. The owner can rotate it |
limit | Maximum outflow per asset per period. Unlisted assets are frozen. ADA must be listed |
period_length, period_start | Accounting window, in milliseconds |
spent_in_period | Outflow counted so far in the period |
min_balance_lovelace | ADA an agent spend may not go below |
Register a Cardano wallet
POST /v1/wallets with your Node Token. Every address must carry the network's prefix (addr or addr_test). A full example is in the Quickstart.
Prop
Type
Send exactly one of governedAsset with constitution, or governedAssets[]. Hydra keys: Head trust.
Registration does not check that the state token exists at the address, the datum, or that the address's script parameters contain the declared owner, quorum and threshold. These surface at the first co-sign, as utxo_unknown or a transaction-rule refusal.
Deposits use the Deposit redeemer, signed by the agent or the owner, with no Exchain call. Withdrawals: Owner controls.
Seller gates
| Field | Default | Rule | Refusal |
|---|---|---|---|
registryGate | true | The intent's agentIdentifier must be a live Masumi registry NFT (V1 or V2 policy, quantity 1). Registry unreachable means refused | registry_gate |
priceGate | Same as registryGate | Amount ≤ Fixed price × (100 + priceTolerancePct) / 100. Dynamic and Free pricing pass | price_above_published |
sellerAllowList | Empty | When non-empty, only listed sellers | counterparty_blocked |
sellerDenyList | Empty | Listed sellers are refused | counterparty_blocked |
List entries are sellerVkeyHash:<56 hex> or agent identifiers.
Several assets
Send governedAssets[] (1 to 16) instead of governedAsset and constitution. Each asset has its own mandate, and each payment counts only against its asset.
{
"governedAssets": [
{ "id": "lovelace", "decimals": 6,
"constitution": { "params": { "perTxCap": "50000000", "daily": "1000000000", "perSeller": "200000000",
"perAgent": "500000000", "envelope": "50000000000", "burstPerMinute": 10 } } },
{ "id": "6ba199f81440944474fb29139bc7a0f736f70615bb26cb7b20d3d6fd.0014df10745553444378", "decimals": 6,
"constitution": { "params": { "perTxCap": "50000000", "daily": "1000000000", "perSeller": "200000000",
"perAgent": "500000000", "envelope": "50000000000", "burstPerMinute": 10 } } }
]
}A token output may carry up to 6 ADA. When the wallet also governs ADA, that ADA counts against the ADA mandate's daily, perSeller, perAgent and envelope, not against perTxCap or burstPerMinute.
EVM chains
The guarded wallet is a stock SafeL2 1.4.1 holding one or more of its chain's listed stablecoins: Assets. Exchain deploys it and pays the gas. Its owners, module and contracts: The guarded Safe. One registration serves both direct and x402 payments.
Bind the owner
If your node belongs to a console account, bind the owner address in the console first, with an EIP-191 signature from that address. Otherwise registration returns 403 owner_unbound.
Register a Safe
Ask for the consent text. Send POST /v1/x402/wallets with your Node Token.
{
"nodeId": "node_1",
"orgId": "org_acme",
"agentAddress": "0xAgent…",
"ownerAddress": "0xOwner…",
"governedAsset": { "id": "eip155:84532/erc20:0x036cbd53842c5426634e7929541ec2318f3dcf7e", "decimals": 6 },
"constitution": {
"params": { "perTxCap": "1000000", "daily": "500000000", "perSeller": "50000000",
"perAgent": "250000000", "envelope": "20000000000", "burstPerMinute": 60 }
}
}1000000 = 1 USDC (6 decimals). The asset id sets the chain: here Base Sepolia (84532).
The answer is 400 owner_signature_required, with the exact consent text in detail:
Exchain guarded Safe
Safe: 0x…
Chain: eip155:84532
Agent: 0xAgent…
Owner module: 0xOwner…Send it again with the owner's signature. Resend the identical body plus ownerSignature: an EIP-191 personal_sign of that text by ownerAddress. ownerSignature is not part of the request hash, so the Safe address does not change.
Exchain deploys the Safe and verifies the singleton, fallback handler, threshold, owners, single module and empty guard. The answer is 201 with walletAddress: Registration response.
Fund it. Send the governed tokens to walletAddress.
Prop
Type
sellers and unsignedSellers bind payments to signed seller offers: Seller binding.
Fund only the registered Safe. Fund only the walletAddress returned at registration. The Safe balance is the most the agent can ever spend, and only the owner module can withdraw it.
Shadow mode
shadow: true registers the agent's own address, with no Safe and no owner. Exchain decides and journals each payment, but the agent signs alone and pays no fees. Use it as a dry run to evaluate a mandate.
Registration errors
| Answer | Rail | Cause |
|---|---|---|
401 unauthorized | Both | Missing or invalid Node Token |
429 rate_limited | Both | Over the node's rate limit |
400 bad_request | Both | Schema. On Cardano, a threshold of 0 or above the key count. On EVM, an owner address that is also a Safe owner |
400 network_mismatch | Both | The network, an address prefix or the asset's chain does not match the token's network |
400 node_mismatch | Both | nodeId differs from the token's node |
400 asset_not_allowed | Both | Asset not enabled on this network |
400 duplicate_asset | Both | An asset listed twice |
400 chain_mismatch | EVM | The governed assets are on different chains |
400 invalid_constitution | Both | The mandate does not compile |
400 quorum_mismatch | Cardano | quorumKeyHashes misses an attested quorum key, or the threshold does not fit |
400 quorum_mismatch | EVM | Not every quorum member's address is known yet. Retry once every member answers |
400 owner_signature_required | EVM | No valid owner signature yet. detail holds the consent text |
400 safe_mismatch | EVM | The Safe is not exactly the expected setup, or walletAddress differs |
400 not_deployed | EVM | Exchain cannot deploy on this chain. detail holds the factory call to send yourself; then register again |
403 owner_unbound | EVM | The owner address is not bound in the console |
409 wallet_exists | Both | Same wallet, different body |
502 rpc_failed | EVM | The chain's RPC failed |
502 deploy_failed | EVM | The deployment transaction failed |
503 x402_unavailable | EVM | Exchain has no RPC for this chain |
Registration response
201 Created returns:
| Field | Rail | Meaning |
|---|---|---|
walletId | Both | wal_ plus 26 characters. Use it in every later call |
constitutionId, mandateEnglish, mandateRules[] | Both | The compiled mandate, in plain English and as rules |
governedAssets[] | Both | One entry per governed asset: id, decimals, symbol, constitutionId, mandateEnglish, mandateRules |
walletAddress, chainId | EVM | The Safe and its chain |
owners[] | EVM | The agent address, then one address per quorum member |
shadow | EVM | Whether the wallet is in shadow mode |
The top-level constitutionId, mandateEnglish and mandateRules are those of the first governed asset. An identical repeat returns 200 with the same walletId. A different body for the same wallet returns 409 wallet_exists. To change a mandate: Changing a mandate. What the owner can do without Exchain: Owner controls.