§ 01 — Developers
Build on Ambr
REST API, A2A JSON-RPC, and MCP integration for deploying and verifying Agent Mandates.
/v1/contractsCreate a new Ricardian Contract from a template. Returns a draft that both parties must sign.
const response = await fetch('https://getamber.dev/api/v1/contracts', {
method: 'POST',
headers: {
'X-API-Key': '<API_KEY>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
template: 'd1-general-auth',
parameters: {
principal_name: 'Acme Analytics Ltd.',
principal_type: 'corporation',
principal_registration_number: 'DE-5589021',
principal_address: '1209 Orange St, Wilmington, DE',
agent_id: '0x7a3b...9f2e',
agent_type: 'autonomous',
scope: 'procurement and vendor payments',
categories: ['procurement', 'api-calls'],
spending_limit_per_tx: 2500,
spending_limit_monthly: 10000,
duration_months: 12,
governing_law: 'Delaware',
},
principal_declaration: {
agent_id: '0x7a3b...9f2e',
principal_name: 'Acme Analytics Ltd.',
principal_type: 'company',
},
}),
});
// response.status → 'draft'
// response.sign_url → '/api/v1/contracts/amb-2026-0001/sign'/v1/contracts/:id/signSign a contract with an ECDSA wallet signature. Bilateral templates need both parties; one-sided templates (p2 power of attorney) activate on the principal’s single signature.
const message = [
'I am signing Ambr contract amb-2026-0001',
'',
'Contract SHA-256: <sha256_hash>',
'',
'Timestamp: ' + new Date().toISOString(),
].join('\n');
const response = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001/sign',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet_address: '0x7a3b...9f2e',
signature: await wallet.signMessage(message),
message,
}),
}
);
// Bilateral — first sig: draft → pending_signature
// second sig: pending_signature → active
// One-sided (p2) — principal's sig: draft → active/v1/contracts/:idQuery contract status, metadata, and amendment chain. Full text requires API key or share token.
const contract = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001',
{ headers: { 'X-API-Key': '<API_KEY>' } }
).then(r => r.json());
// contract.status → 'draft' | 'awaiting_principal_approval' | 'handshake'
// | 'pending_signature' | 'active' | 'amended'
// | 'revoked' | 'expired' | 'terminated'
// contract.sha256_hash → 'a1b2c3...'
// contract.amendment_chain → [{ hash, parent_hash, type }]/v1/contracts/:id/statusPublic validity check — no auth required. Answers "is this authority in force right now?" from status, revocation, and expiry together.
const status = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001/status'
).then(r => r.json());
// status.status → 'active'
// status.is_currently_valid → true only while active, not revoked, not expired
// status.is_expired → true once expiry_date is in the past
// status.revoked_at → null | ISO timestamp
// status.expiry_date → null | ISO timestamp/v1/contracts/:id/revokeRevoke an active, pending_signature, or handshake contract. Either party may withdraw — the creator via API key, or any party wallet via signature; revoked_by records who. Irreversible; cascades to child contracts in the delegation chain.
const response = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001/revoke',
{
method: 'POST',
headers: {
'X-API-Key': '<API_KEY>',
'Content-Type': 'application/json',
},
body: JSON.stringify({ reason: 'Engagement ended early' }),
}
);
// Wallet auth instead of API key: send wallet_address + signature + message
// in the body. The signed message must state revocation intent and contain
// the contract SHA-256 hash, e.g.:
// "I revoke Ambr contract amb-2026-0001 with hash <sha256_hash>"
//
// response.status → 'revoked' (terminal — cannot be undone)
// response.cascade.children_revoked → child delegations revoked with the parent/v1/contracts/:id/handshakeSubmit a handshake response — accept, reject, or request changes to a draft contract. Includes visibility preference.
const response = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001/handshake',
{
method: 'POST',
headers: {
'X-API-Key': '<API_KEY>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'accept', // 'accept' | 'reject' | 'request_changes'
visibility: 'private', // 'private' | 'metadata_only' | 'public' | 'encrypted'
message: 'Terms accepted. Proceeding to signature.',
}),
}
);
// action: accept → draft → pending_signature
// action: request_changes → remains draft with change request attached/v1/contracts/:id/wallet-authVerify wallet ownership for contract access. Returns a signed challenge for authenticated reads.
const response = await fetch(
'https://getamber.dev/api/v1/contracts/amb-2026-0001/wallet-auth',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet_address: '0x7a3b...9f2e',
signature: '0xabcd...1234',
message: 'Authenticate wallet for contract amb-2026-0001',
}),
}
);
// response.access_token → short-lived JWT for contract reads/v1/dashboard/wallet-authDashboard login via wallet signature. Returns a session token for the Ambr dashboard.
const response = await fetch(
'https://getamber.dev/api/v1/dashboard/wallet-auth',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet_address: '0x7a3b...9f2e',
signature: '0xabcd...1234',
message: 'Sign in to Ambr Dashboard',
}),
}
);
// response.session_token → use in Authorization header for dashboard API/v1/keysActivate an API key. The developer tier is free (verified email, 25 mandates); credit-pack tiers verify a Base L2 USDC payment by transaction hash.
const response = await fetch(
'https://getamber.dev/api/v1/keys',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'dev@yourco.com',
tier: 'developer', // free — or 'startup' ($49 pack) / 'scale' ($199 pack)
// tx_hash: '0x9876...fedc', // required for paid tiers (Base L2 USDC)
}),
}
);
// response.api_key → 'amb_...' — shown once, save it
// response.credits → 25 (developer) | 200 (startup) | 1000 (scale)Mandate Lifecycle
Mandates are created as drafts. The counterparty reviews and submits a handshake — accepting, rejecting, or requesting changes. Once accepted, the first ECDSA wallet signature moves the contract to pending_signature. When the second party signs, the contract becomes active. One-sided templates (such as the p2 power of attorney) skip this — the principal's single signature activates them immediately. Both signatures are cryptographically verified — no API key needed to sign, just a valid wallet.
An active mandate can end three recorded ways: amended (replaced by a successor contract), revoked (a party's human override), or terminated. Expiry is derived: past its expiry date (taken from the contract's own terms, anchored at signing) the status column may still read active, but is_currently_valid flips to false and is_expired to true. All end states are terminal. For the live answer, always check GET /:id/status → is_currently_valid.
Errors & rate limits
- 400 bad_request — malformed JSON body
- 400 validation_error / invalid_signature / hash_mismatch / message_mismatch — sign & revoke flows: missing fields, unverifiable signature, or a signed message without the required contract hash / intent
- 401 unauthorized / signature_mismatch — missing or invalid credentials (API key, or signature not matching the wallet)
- 403 unauthorized — wallet not associated with this contract
- 404 not_found — unknown contract id or hash
- 409 already_revoked / invalid_state — action was already applied or conflicts with current state
- 422 invalid_status — action not allowed in the contract's current status
- 429 rate_limited — response includes
retry_after_ms
Exact error codes vary slightly per endpoint — the snippets above show each endpoint's behavior. Several write endpoints are rate-limited, per IP or per API key (revocation allows 5 requests per minute per IP). Back off and retry after retry_after_ms on 429.
MCP Integration
Add Ambr to your AI agent with one config block. Works with Claude Code, Cursor, and any MCP-compatible client. Your agent gets 6 tools: list templates, create mandates, get/verify mandates, check status, and agent handshake.
Claude Code — settings.json
{
"mcpServers": {
"ambr": {
"type": "url",
"url": "https://getamber.dev/api/mcp",
"headers": {
"X-API-Key": "YOUR_API_KEY"
}
}
}
}Cursor — .cursor/mcp.json
{
"mcpServers": {
"ambr": {
"url": "https://getamber.dev/api/mcp",
"headers": {
"X-API-Key": "YOUR_API_KEY"
}
}
}
}Get your API key at /activate. Tools that modify state (create contract, agent handshake) require a valid key. Read-only tools (list templates, verify hash) work without authentication.
Integrations
Supported Stack
Production integrations powering the Agent Mandate & Binding Registry.
x402 V2
LiveHTTP-native micropayments on Base L2. Multi-token: USDC, USDbC, DAI, ETH, WETH, cbETH, cbBTC.
A2A Agent Card
LiveJSON-RPC discovery at getamber.dev/.well-known/agent.json. Agents find and interact with Ambr automatically.
MCP Server
LiveModel Context Protocol integration. Add Ambr to Claude, Cursor, or any MCP-compatible agent.
Base L2
LivecNFT minting, SHA-256 hash storage, and counterparty-gated transfers on Base mainnet.
Stripe Payments
LiveCard payments via Stripe Checkout — Personal and Business plans plus one-time credit packs.
ZK Identity
LiveZero-knowledge identity verification via Groth16/BN128 zk-SNARKs powered by the Demos Network. Prove entity status without revealing identity.
Start Building
Free developer access — 25 mandates, no payment required. Full REST API, A2A discovery, and MCP integration.