Skip to content

Integration guide

Everything an agent does — identity, network, storage, payments — goes through the gateway, where policy is enforced and every decision is recorded. This page walks the full integration:

  1. Authenticate — send X-Imara-Token on every call. The token maps to a role, the role to capabilities. Admin routes take X-Admin-Token instead.
  2. Register an identityPOST /v1/agents mints a PENDING Agent DNA record with the agent’s Ed25519 public key, owner, zone, and capability grant.
  3. Activate — an operator approves with PUT /v1/agents/{id}/activate; approvedBy and approvalReason go straight into the audit trail.
  4. Apply policy — policy templates render org and zone ceiling documents. The effective chain for any agent is one GET away.
  5. Allowlist merchants — payees are registered PENDING and must be approved by a different identity (maker-checker) before any agent can pay them.
  6. Pay through the kernel — agents never hold keys. POST /v1/x402/fetch or /v1/proxy/fetch — the kernel enforces policy, pays, and records the receipt.

Before you start you need the kernel base URL, a session token mapped to a role, and — for lifecycle operations — the deployment admin token.

All routes below are served from your kernel gateway origin; the examples use $KERNEL as a stand-in. Two headers cover every surface:

  • X-Imara-Token carries a session or role token (a Bearer Authorization header works too). The kernel resolves the token to a role and the role to capabilities such as CAP_NETWORK, CAP_VFS_READ, and CAP_X402_OUT. The mapping lives in policy.toml ([tokens] and [roles.*] tables) and can be changed at runtime via POST /admin/policy/roles.
  • X-Admin-Token must equal the deployment’s auth_token and gates lifecycle mutations: agent activation, payee registration and approval, and runtime policy changes.
Terminal window
export KERNEL=https://kernel.example.com
export TOKEN=my-session-token # maps to a role in policy.toml
export ADMIN_TOKEN=my-admin-token # the deployment auth_token
# Sanity check: authenticated status call
curl -s "$KERNEL/status" -H "X-Imara-Token: $TOKEN"
# Define or update a role at runtime (admin)
curl -s -X POST "$KERNEL/admin/policy/roles" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "finance_agent",
"capabilities": ["CAP_VFS_READ", "CAP_NETWORK", "CAP_X402_OUT"],
"max_actions_per_minute": 60
}'

Every agent gets an Agent DNA record: a UUID identity (the dna_id) bound to an Ed25519 public key, an owner, a zone, a jurisdiction, and an explicit capability grant. The kernel also mints a human-readable SRN alias of the form srn:<partition>:<region>:<account>:agent/<zone>/<name>, usable anywhere the UUID is.

Generate an Ed25519 keypair client-side and submit the raw 32-byte public key as hex (64 characters). The private key never leaves your infrastructure — the kernel only ever verifies signatures against the registered public key. Registration creates the record in PENDING; a separate, admin-authenticated activation call moves it to ACTIVE. Pass parentDnaId when a supervising agent spawns this one, so revocation cascades down the hierarchy. idemKey makes retries safe.

Terminal window
# 1. Register (returns 201 with the new dna_id, status PENDING)
curl -s -X POST "$KERNEL/v1/agents" \
-H "Content-Type: application/json" \
-d '{
"agentName": "billing-bot",
"publicKeyHex": "<64-char hex of the raw Ed25519 public key>",
"ownerId": "9b2f6c1e-5f4a-4d2b-8c3e-7a1d9e0f2b45",
"ownerName": "Finance Ops",
"zone": "finance",
"jurisdiction": "ZA",
"capabilities": ["CAP_NETWORK", "CAP_VFS_READ", "CAP_X402_OUT"],
"parentDnaId": null,
"idemKey": "reg-billing-bot-001"
}'
# 2. Activate (admin) — PENDING -> ACTIVE, recorded in the audit trail
export DNA_ID=<dna_id from step 1>
curl -s -X PUT "$KERNEL/v1/agents/$DNA_ID/activate" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"idemKey": "act-billing-bot-001",
"approvedBy": "cto@example.com",
"approvalReason": "Reviewed capability grant for production"
}'
# 3. Verify authorisation (the hot-path check the kernel itself uses)
curl -s "$KERNEL/v1/agents/$DNA_ID/verify"

Governance: templates and the policy chain

Section titled “Governance: templates and the policy chain”

Policy is layered like IAM: an org ceiling caps everything in the organisation, a zone ceiling caps the department, and each agent carries a compiled leaf derived from its own grant. A request must clear all three. Starter templates render the two ceiling documents from a handful of parameters — what you own afterwards is ordinary, editable policy documents, content-addressed by hash.

Terminal window
# Browse the template catalog
curl -s "$KERNEL/v1/policy-templates" -H "X-Imara-Token: $TOKEN"
# Preview what a template would render (dry_run saves nothing)
curl -s -X POST "$KERNEL/v1/policy-templates/starter-retail/apply" \
-H "Content-Type: application/json" \
-d '{ "org_id": "<org uuid>", "dry_run": true }'
# Apply for real: renders, stores, and attaches org + zone ceilings
curl -s -X POST "$KERNEL/v1/policy-templates/starter-retail/apply" \
-H "Content-Type: application/json" \
-d '{ "org_id": "<org uuid>", "zone": "finance" }'
# Inspect the effective chain governing one agent
# (org_ceiling -> zone_ceiling -> leaf, with document hashes)
curl -s "$KERNEL/v1/agents/$DNA_ID/policy-chain"

Re-applying a template with identical parameters is harmless — the documents are content-addressed, so the same hashes are re-attached. The chain endpoint returns the same resolution the enforcement gate performs, so “denied by policy” is always inspectable by hash.

Agents can only pay merchants on the payee allowlist of their zone chain, and the registry is maker-checker enforced: the identity that registers a payee can never be the one that approves it. Registration creates the payee PENDING; approval by a different identity makes it APPROVED. Editing via /update resets the payee to PENDING with the editor as maker, so a second identity must re-approve before any agent can pay the edited merchant again.

Terminal window
# 1. Register a payee (admin) -> PENDING
curl -s -X POST "$KERNEL/v1/payees" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"zone": "finance",
"displayName": "Cloud Vendor Ltd",
"createdBy": "ops@example.com",
"aliases": [
{ "rail": "x402", "address": "0x1234abcd...ef56" }
]
}'
# 2. Approve with a DIFFERENT identity (admin) -> APPROVED
export PAYEE_ID=<payee_id from step 1>
curl -s -X POST "$KERNEL/v1/payees/$PAYEE_ID/approve" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "approvedBy": "cfo@example.com" }'
# Editing resets to PENDING and requires fresh approval
curl -s -X POST "$KERNEL/v1/payees/$PAYEE_ID/update" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"displayName": "Cloud Vendor (EU) Ltd",
"updatedBy": "ops@example.com",
"aliases": [{ "rail": "x402", "address": "0x1234abcd...ef56" }]
}'

Agents never touch payment keys. Two data-plane routes move money, both requiring CAP_X402_OUT on the caller’s role and both fail-closed:

  • POST /v1/x402/fetch fetches an HTTP-402-protected URL. The kernel meets the paywall itself: it enforces the recipient allowlist and spend ceiling, signs the payment authorization with the kernel-held wallet key, retries with the payment signature, and records the settlement receipt in the ledger.
  • POST /v1/proxy/fetch is the stricter, rail-neutral spend proxy. It requires a proven agent identity and a client-supplied idempotency_key — retries replay the recorded outcome instead of paying twice. The merchant must be an APPROVED payee. When a price crosses the escalation threshold the kernel answers 202 with an approval_id; a human approves it in the console, and the agent collects by retrying with the same idempotency key plus that id.
Terminal window
# Governed fetch of a 402-protected resource
curl -s -X POST "$KERNEL/v1/x402/fetch" \
-H "X-Imara-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "url": "https://api.example.com/paid/report" }'
# Spend proxy: idempotency_key is required
curl -s -X POST "$KERNEL/v1/proxy/fetch" \
-H "X-Imara-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/paid/report",
"method": "GET",
"idempotency_key": "order-2026-08-11-001"
}'
# If the kernel answered 202 { approval_id, ... }: after a human approves,
# retry with the SAME idempotency_key plus the approval_id
curl -s -X POST "$KERNEL/v1/proxy/fetch" \
-H "X-Imara-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/paid/report",
"method": "GET",
"idempotency_key": "order-2026-08-11-001",
"approval_id": "<approval_id from the 202>"
}'

Declines come back with stable reason codes you can branch on — MERCHANT_NOT_ALLOWLISTED, CEILING_EXCEEDED, POLICY_BLOCK, PRICE_CHANGED — while error strings stay free to change.

Every payment attempt — allowed or denied — is observable in three console surfaces: the Feed shows the live event stream, the Ledger holds the tamper-evident entries (written before any reservation is released), and every payment gets a Trace covering the attempt end to end. Each trace carries the id of its ledger entry, so you can jump from “how the decision was reached” to “proof that it was”. Traces are also queryable over the API:

Terminal window
# Recent traces, filterable by agent, zone, status, or ledger entry
curl -s "$KERNEL/v1/traces?dna=$DNA_ID&limit=20" -H "X-Imara-Token: $TOKEN"
# One trace, end to end
curl -s "$KERNEL/v1/traces/<trace_id>" -H "X-Imara-Token: $TOKEN"
# Ledger entries and chain verification
curl -s "$KERNEL/ledger" -H "X-Imara-Token: $TOKEN"
curl -s "$KERNEL/ledger/verify" -H "X-Imara-Token: $TOKEN"

See The audit ledger for how chain verification works and how to export the trail.