APIAPI
API
Base URL, authentication, envelope, idempotency and limits, with one page per endpoint.
Everything every Exchain API call shares: one base URL, three credentials, one envelope and one set of limits.
Basics
- Base URL:
https://cosign.exchain.network. - JSON in and out. Amounts are minor-unit strings.
- The OpenAPI document at
/openapi.yamland/openapi.jsonis the one the service validates requests against. - No CORS headers: call the API from a backend.
Network selection
There is no network in the URL. The Node Token's node decides it: a preprod node works on Cardano preprod and the EVM testnets, a mainnet node on Cardano mainnet and the EVM mainnets (Networks). A read token uses its wallet's network. See Networks and assets.
Authentication
Send Authorization: Bearer <credential>.
| Credential | Works on | Lifetime |
|---|---|---|
| Node Token | Every /v1 contract and read route, for wallets this node registered only (others return 404) | Until revoked, which takes effect at once. Rotation keeps the old token valid 10 minutes |
| Read token | GET read routes of one wallet, and /protected/{walletId}?t=. Refused on POST | 15 minutes. An opaque string |
| Partner key | /v1/partner/* | Until reset or revoked. See Partner access |
Envelope
Every JSON answer carries:
| Field | Value |
|---|---|
asOf | UTC time, millisecond precision |
schemaVersion | "1.1" |
chainTip | { slot, blockHash } or null |
Errors add { error, detail? }: Errors and codes. Every response has an X-Request-Id header.
Idempotency
Idempotency-Keyis required onPOST /v1/cosign,POST /v1/evm/cosignandPOST /v1/x402/cosign, and must equalbatchId. On EVM chains,batchIdis the authorization nonce (for aSafeTx, the Safe nonce) as0xand 64 hex characters.- Registrations are idempotent by content: the same body returns
200, a different body for the same wallet409 wallet_exists.
Replays, holds and rebuilds: Refusals and retries.
Limits
| Limit | Value |
|---|---|
| Rate | 120 requests per rolling minute per node on POST /v1/cosign, POST /v1/evm/cosign, POST /v1/wallets, POST /v1/x402/wallets, POST /v1/x402/cosign and POST /v1/x402/fees; partner routes per partner. Over it: 429 rate_limited with Retry-After. Read routes are not rate-limited |
| Request body | 64 KiB, else 413 |
txBodyHex | 32,768 hex characters |
| Purchases per request | 1 to 10 (Hydra: 1) |
| Assets per wallet | 1 to 16 |
| Connections | One request per connection, no keep-alive; 30 s socket timeout |
| Concurrency | 256 connections, then 503 {"error":"busy"} with no envelope |
Measured in production in September to November 2026: Cardano co-sign about 1.3 s typical and under 2.5 s; x402 co-sign about 1 s median.
Endpoints
| Method | Path | Auth | Rate-limited | Purpose | Guide |
|---|---|---|---|---|---|
POST | /v1/wallets | Node | Yes | Register a Cardano guarded wallet and its mandate | Wallets |
POST | /v1/wallets/{walletId}/read-token | Node | No | Mint a 15-minute read token for one wallet | Partner access |
POST | /v1/cosign | Node + Idempotency-Key | Yes | Co-sign a Cardano agent spend (layer 1 or Hydra in-head) | Co-sign |
POST | /v1/evm/cosign | Node + Idempotency-Key | Yes | Co-sign a direct payment from a guarded Safe | Co-sign a payment |
GET | /v1/wallets/{walletId}/mandate | Node or read | No | Read the wallet's mandate | Mandates |
GET | /v1/wallets/{walletId}/allowance | Node or read | No | Remaining limits and the on-chain ceiling | Mandates |
GET | /v1/wallets/{walletId}/bound | Node or read | No | Signed worst-case statement | Bound statement |
GET | /v1/wallets/{walletId}/decisions | Node or read | No | List decisions, newest first | Verify |
GET | /v1/decisions/{decisionId} | Node or read | No | One decision with request and proof | Verify |
GET | /v1/wallets/{walletId}/health | Node or read | No | Quorum reachability, attestation age, PCR0 | Trust model |
GET | /v1/wallets/{walletId}/evidence.zip | Node or read | No | Offline evidence bundle | Evidence bundle |
GET | /v1/x402/signers | Node | No | Enclave addresses and pinned Safe contracts | Guarded Safe |
POST | /v1/x402/wallets | Node | Yes | Register (and deploy) a guarded Safe, one or several assets on one chain | Register a Safe |
POST | /v1/x402/cosign | Node + Idempotency-Key | Yes | Co-sign one x402 payment | Co-sign a payment |
POST | /v1/x402/fees | Node | Yes | Settle fees due | Settle fees |
GET | /v1/x402/wallets/{walletId}/fees | Node | No | Fee status, per token | Fee status |
GET | /v1/partner | Partner | Yes | Partner profile, nodes, webhook | Partner access |
POST | /v1/partner/signup-links | Partner | Yes | Signed 30-day sign-up link for a node operator | Partner access |
POST | /v1/partner/wallets/{walletId}/read-token | Partner | Yes | Read token and embed URL for one wallet | Partner access |
GET | /v1/journal/roots | None | No | Signed journal roots by wallet and time | Journal |
GET | /.well-known/did.json | None | No | DID document with the guard-receipt key | Guard receipt |
GET | /openapi.yaml, /openapi.json | None | No | The OpenAPI document | This page |
GET | /healthz | None | No | Liveness: 200 ok | This page |
GET | /readyz | None | No | Readiness: 503 when the quorum is below threshold | This page |
GET /protected/{walletId}?t= serves the embeddable wallet page: Partner access.
Try it
Each endpoint page has a request playground. Its requests go through the docs' server-side proxy, because the API sends no CORS headers. Use a preprod Node Token.