ExchainDocs
DocsAPI

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

PartCardanoEVM chains
WalletMasumi smart walletStock Safe 1.4.1
AgentOne agent keyOne agent address
Attested quorumEvery quorum key hashOne owner address per quorum member
OwnerOwner key. Never counts toward the quorumOwner address, the only module. Never a Safe owner
MandateOne per asset, up to 16 assetsOne per asset, up to 16 listed assets on the Safe's chain
Register withPOST /v1/walletsPOST /v1/x402/wallets

Cardano

Mint parameters

These are part of the address. Changing any of them means a new wallet.

ParameterValue
ownerThe owner's cold key. It never counts toward the quorum
quorum_vksEvery attested quorum key hash, from the console
quorum_threshold3: every quorum member must sign
stakeThe 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.

FieldMeaning
agentThe one hot agent key. The owner can rotate it
limitMaximum outflow per asset per period. Unlisted assets are frozen. ADA must be listed
period_length, period_startAccounting window, in milliseconds
spent_in_periodOutflow counted so far in the period
min_balance_lovelaceADA 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

FieldDefaultRuleRefusal
registryGatetrueThe intent's agentIdentifier must be a live Masumi registry NFT (V1 or V2 policy, quantity 1). Registry unreachable means refusedregistry_gate
priceGateSame as registryGateAmount ≤ Fixed price × (100 + priceTolerancePct) / 100. Dynamic and Free pricing passprice_above_published
sellerAllowListEmptyWhen non-empty, only listed sellerscounterparty_blocked
sellerDenyListEmptyListed sellers are refusedcounterparty_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
{
  "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.

POST /v1/x402/wallets
{
  "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:

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

AnswerRailCause
401 unauthorizedBothMissing or invalid Node Token
429 rate_limitedBothOver the node's rate limit
400 bad_requestBothSchema. On Cardano, a threshold of 0 or above the key count. On EVM, an owner address that is also a Safe owner
400 network_mismatchBothThe network, an address prefix or the asset's chain does not match the token's network
400 node_mismatchBothnodeId differs from the token's node
400 asset_not_allowedBothAsset not enabled on this network
400 duplicate_assetBothAn asset listed twice
400 chain_mismatchEVMThe governed assets are on different chains
400 invalid_constitutionBothThe mandate does not compile
400 quorum_mismatchCardanoquorumKeyHashes misses an attested quorum key, or the threshold does not fit
400 quorum_mismatchEVMNot every quorum member's address is known yet. Retry once every member answers
400 owner_signature_requiredEVMNo valid owner signature yet. detail holds the consent text
400 safe_mismatchEVMThe Safe is not exactly the expected setup, or walletAddress differs
400 not_deployedEVMExchain cannot deploy on this chain. detail holds the factory call to send yourself; then register again
403 owner_unboundEVMThe owner address is not bound in the console
409 wallet_existsBothSame wallet, different body
502 rpc_failedEVMThe chain's RPC failed
502 deploy_failedEVMThe deployment transaction failed
503 x402_unavailableEVMExchain has no RPC for this chain

Registration response

201 Created returns:

FieldRailMeaning
walletIdBothwal_ plus 26 characters. Use it in every later call
constitutionId, mandateEnglish, mandateRules[]BothThe compiled mandate, in plain English and as rules
governedAssets[]BothOne entry per governed asset: id, decimals, symbol, constitutionId, mandateEnglish, mandateRules
walletAddress, chainIdEVMThe Safe and its chain
owners[]EVMThe agent address, then one address per quorum member
shadowEVMWhether 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.

On this page