Skip to content

§ 01 — Developers

Build on Ambr

REST API, A2A JSON-RPC, and MCP integration for deploying and verifying Agent Mandates.

Concepts & lifecycle → /docs

POST/v1/contracts

Create a new Ricardian Contract from a template. Returns a draft that both parties must sign.

POST /v1/contractstypescript
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'
POST/v1/contracts/:id/sign

Sign 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.

POST /v1/contracts/:id/signtypescript
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
GET/v1/contracts/:id

Query contract status, metadata, and amendment chain. Full text requires API key or share token.

GET /v1/contracts/:idtypescript
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 }]
GET/v1/contracts/:id/status

Public validity check — no auth required. Answers "is this authority in force right now?" from status, revocation, and expiry together.

GET /v1/contracts/:id/statustypescript
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
POST/v1/contracts/:id/revoke

Revoke 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.

POST /v1/contracts/:id/revoketypescript
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
POST/v1/contracts/:id/handshake

Submit a handshake response — accept, reject, or request changes to a draft contract. Includes visibility preference.

POST /v1/contracts/:id/handshaketypescript
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
POST/v1/contracts/:id/wallet-auth

Verify wallet ownership for contract access. Returns a signed challenge for authenticated reads.

POST /v1/contracts/:id/wallet-authtypescript
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
POST/v1/dashboard/wallet-auth

Dashboard login via wallet signature. Returns a session token for the Ambr dashboard.

POST /v1/dashboard/wallet-authtypescript
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
POST/v1/keys

Activate an API key. The developer tier is free (verified email, 25 mandates); credit-pack tiers verify a Base L2 USDC payment by transaction hash.

POST /v1/keystypescript
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/statusis_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

Claude Code MCP Configtypescript
{
  "mcpServers": {
    "ambr": {
      "type": "url",
      "url": "https://getamber.dev/api/mcp",
      "headers": {
        "X-API-Key": "YOUR_API_KEY"
      }
    }
  }
}

Cursor — .cursor/mcp.json

Cursor MCP Configtypescript
{
  "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

Live

HTTP-native micropayments on Base L2. Multi-token: USDC, USDbC, DAI, ETH, WETH, cbETH, cbBTC.

A2A Agent Card

Live

JSON-RPC discovery at getamber.dev/.well-known/agent.json. Agents find and interact with Ambr automatically.

MCP Server

Live

Model Context Protocol integration. Add Ambr to Claude, Cursor, or any MCP-compatible agent.

Base L2

Live

cNFT minting, SHA-256 hash storage, and counterparty-gated transfers on Base mainnet.

Stripe Payments

Live

Card payments via Stripe Checkout — Personal and Business plans plus one-time credit packs.

ZK Identity

Live

Zero-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.