Reference

HTTP API

The machine-readable spec is /openapi.json (OpenAPI 3.1). Import it into Postman, Insomnia or a client generator. It is tested against the server's route table, so every route is in it.

Authentication

Scheme Header Who
admin Authorization: Bearer <AGENTGUARD_ADMIN_TOKEN> The owner / console
agent Authorization: Bearer ag_… An agent key from POST /v1/agents
cron Authorization: Bearer <CRON_SECRET> The scheduler (Supabase pg_cron or Vercel cron)
none — Public keys, anchors, enrollment and passkey ceremonies, health

Wrong or missing tokens get 401 {"error":"unauthorized"}.

Conventions

  • JSON in and out. Amounts are decimal strings ("10.80"), currencies ISO 4217.
  • Errors are {"error": "message"} with a 4xx/5xx status. 503 while the kill-switch is on.
  • POST /v1/agent/payments requires an idempotency_key. Replaying it with the same proposal returns the original result; with a different proposal it is rejected with 409.
  • Agents are rate limited (429), 30 requests per minute by default.

A payment, end to end

sh
curl -s -X POST $U/v1/agent/payments -H "authorization: Bearer $AGENT_KEY" -H 'content-type: application/json' -d '{
  "agent_id": "shopper", "mandate_id": "'$MID'", "idempotency_key": "order-1001",
  "paypal_order_id": "5O190127TN364715T", "agent_rationale": "API credits"}'
curl -s $U/v1/agent/payments/$PID -H "authorization: Bearer $AGENT_KEY"
curl -s $U/v1/agent/payments/$PID/receipt -H "authorization: Bearer $AGENT_KEY"

The response carries decision (approve, reject, needs_human), reasons and, once captured, the PayPal ids. See Payment flow.

Reference

Meta

GET /health

Health: ok, stuck payment count, last recovery run. Auth: None.

GET /healthz

Liveness probe (plain 200). Auth: None.

HEAD /healthz

Liveness probe, no body. Auth: None.

Jobs

GET /api/cron/anchor

Timestamp pending receipts and anchor the audit head (Bearer CRON_SECRET). Auth: CRON_SECRET.

POST /api/cron/anchor

Same as GET, for pg_net callers. Auth: CRON_SECRET.

GET /api/cron/maintenance

Expire recovery locks, retry notifications, converge stuck payments. Auth: CRON_SECRET.

POST /api/cron/maintenance

Same as GET, for pg_net callers. Auth: CRON_SECRET.

Keys

GET /.well-known/agentguard-key

Active issuer public key (legacy single-key form). Auth: None.

GET /.well-known/agentguard-keys

Issuer key set: every key with kid, status, created_at, retired_at, compromised_at. Pin this.. Auth: None.

POST /v1/keys/rotate

Retire the active issuer key and create a new one (audited). Auth: Admin token.

POST /v1/keys/{kid}/compromised

Mark a key compromised as of at (rotates first if active). Auth: Admin token.

Field Type Required Notes
at string ISO time; default now

Receipts

GET /v1/public/receipts/{id}

Public receipt lookup (only when AGENTGUARD_PUBLIC_RECEIPTS=1). Auth: None.

GET /v1/payments/{id}/receipt

Receipt for a payment. Auth: Admin token.

GET /v1/receipts

List receipts. Auth: Admin token.

Query: limit (Default 100).

GET /v1/receipts/{id}

Receipt by id. Auth: Admin token.

Setup

POST /v1/agents

Register an agent and return its API key (shown once). Auth: Admin token.

Field Type Required Notes
user_id string yes Owner user id
agent_id string yes Agent id

POST /v1/wallets/setup

Start PayPal wallet vaulting; returns the setup token with the buyer consent link. Auth: Admin token.

Field Type Required Notes
user_id string yes

GET /v1/wallets/return

PayPal redirect target after buyer consent; links the vaulted wallet. Auth: None.

Query: state (One-time state from /v1/wallets/setup), approval_token_id (PayPal setup token id).

POST /v1/wallets/link

Link an existing PayPal vault id to a user. Auth: Admin token.

Field Type Required Notes
user_id string yes
vault_id string yes
payer_email string

Mandates

POST /v1/mandates

Create a draft mandate from structured constraints. Auth: Admin token.

Field Type Required Notes
user_id string yes
agent_id string yes
purpose string yes
ttl_minutes integer
constraints MandateConstraints

POST /v1/mandates/draft-from-text

Draft a mandate from plain English (model proposes, deterministic parser bounds it). Auth: Admin token.

Field Type Required Notes
user_id string yes
agent_id string yes
text string yes

GET /v1/mandates

List mandates with spend state and summary. Auth: Admin token.

GET /v1/mandates/{id}

Mandate, status and spend. Auth: Admin token.

POST /v1/mandates/{id}/sign-request

Get WebAuthn options whose challenge is the mandate hash. Auth: Admin token.

POST /v1/mandates/{id}/confirm

Activate a mandate with the user's passkey assertion. Auth: Admin token.

Field Type Required Notes
mandate_hash string yes
assertion object yes WebAuthn assertion (navigator.credentials.get response, JSON-encoded) over the challenge returned by the matching *-request call

POST /v1/mandates/{id}/revoke-request

Get WebAuthn options for revocation. Auth: Admin token.

POST /v1/mandates/{id}/revoke

Revoke with the user's passkey (drafts can be discarded without one). Auth: Admin token.

Field Type Required Notes
assertion object WebAuthn assertion (navigator.credentials.get response, JSON-encoded) over the challenge returned by the matching *-request call
reason string

Passkeys

GET /v1/users/{id}/passkey

Whether the user has a passkey, and its credential id. Auth: Admin token.

POST /v1/users/{id}/enrollments

Issue a single-use enrollment link (15 min default). Auth: Admin token.

Field Type Required Notes
issued_by string
ttl_minutes integer

POST /v1/enroll/options

Redeem step 1: registration options for an enrollment token. Auth: None.

Field Type Required Notes
token string yes

POST /v1/enroll

Redeem step 2: register the passkey. Auth: None.

Field Type Required Notes
token string yes
response object yes navigator.credentials.create response

POST /v1/users/{id}/reenroll-request

Options for replacing a passkey with the current one. Auth: Admin token.

POST /v1/users/{id}/reenroll

Mint a re-enrollment token authorized by the current passkey. Auth: Admin token.

Field Type Required Notes
assertion object yes WebAuthn assertion (navigator.credentials.get response, JSON-encoded) over the challenge returned by the matching *-request call

GET /v1/users/{id}/recovery

Time-locked recovery state. Auth: Admin token.

POST /v1/users/{id}/recovery/cancel-request

Options to cancel a recovery with the current passkey. Auth: None.

POST /v1/users/{id}/recovery/cancel

Cancel a pending recovery. Auth: None.

Field Type Required Notes
assertion object yes WebAuthn assertion (navigator.credentials.get response, JSON-encoded) over the challenge returned by the matching *-request call

Approvals

GET /v1/approvals

Payments held for the owner. Auth: Admin token.

POST /v1/approvals/{id}/request

Approval statement and WebAuthn options (5-minute nonce). Auth: Admin token.

Field Type Required Notes
approve boolean yes

POST /v1/approvals/{id}

Approve or deny with the user's passkey. The admin token alone is refused.. Auth: Admin token.

Field Type Required Notes
approve boolean yes
assertion object yes WebAuthn assertion (navigator.credentials.get response, JSON-encoded) over the challenge returned by the matching *-request call
approver string

Agent

POST /v1/agent/payments

Propose a payment (30/min per agent; 429 above). Auth: Agent key.

Field Type Required Notes
mandate_id string yes
agent_id string yes
idempotency_key string yes
paypal_order_id string Merchant-created AUTHORIZE order (preferred). Do not combine with payee/currency/items.
payee object Vaulted path only (untrusted)
currency string Vaulted path only
items array Vaulted path only (untrusted)
context array Untrusted context the agent saw; scored for injection, advisory only
agent_rationale string

POST /v1/agent/products/search

Search the Channel3 product catalog within the mandate's currency and remaining budget (discovery only; never pays). Auth: Agent key.

Field Type Required Notes
mandate_id string yes
query string yes
limit integer 1-30, default 8
page_token string next_page_token from a previous search

GET /v1/agent/payments/{id}

Payment status (the calling agent's own only). Auth: Agent key.

GET /v1/agent/payments/{id}/receipt

Proof-of-Intent receipt for a captured payment. Auth: Agent key.

GET /v1/agent/mandates/{id}

The agent's own mandate, status and spend. Auth: Agent key.

Catalog

POST /v1/products/search

Owner view of the same Channel3 search, with an advisory mandate-fit check per product. Auth: Admin token.

Field Type Required Notes
mandate_id string yes
query string yes
limit integer
page_token string

GET /v1/products/status

Whether Channel3 product search is configured (CHANNEL3_API_KEY). Auth: Admin token.

Payments

GET /v1/payments

List payments. Auth: Admin token.

Query: limit (Default 200).

GET /v1/payments/stuck

Payments in reserved/authorized older than a threshold. Auth: Admin token.

Query: older_than_ms (Default 60000).

POST /v1/recover

Converge stuck payments with PayPal now. Auth: Admin token.

Field Type Required Notes
older_than_ms integer

Control

GET /v1/control/pause

Kill-switch state. Auth: Admin token.

POST /v1/control/pause

Block every new proposal and approval (503). Auth: Admin token.

Field Type Required Notes
reason string
by string

POST /v1/control/resume

Lift the kill-switch. Auth: Admin token.

Field Type Required Notes
by string

Audit

GET /v1/audit

Hash-chained audit log. Auth: Admin token.

Query: limit (Default 200).

GET /v1/audit/verify

Recompute and verify the whole chain. Auth: Admin token.

GET /v1/audit/attestation

Chain head signed with the active issuer key. Auth: Admin token.

POST /v1/audit/anchor

Anchor the chain head now (RFC 6962 Merkle root, RFC 3161 stamped). Auth: Admin token.

GET /v1/audit/anchors

Published anchors; keep copies to detect forks. Auth: None.

Query: limit (Default 100).

MandateConstraints

Field Type Required Notes
currency string yes ISO 4217
max_total string yes
max_per_transaction string
max_transactions integer yes
allowed_categories array of PHYSICAL_GOODS, DIGITAL_GOODS, DONATION, UNSPECIFIED
payee_allowlist array
payee_blocklist array
max_quantity_per_item integer
require_human_approval_over string
require_merchant_order boolean
sku_allowlist array

Edit this page on GitHub