# AgentLedger Per-agent spend management — the Datadog for agent spending. Track spend across x402/MPP/API-key rails, budget caps, anomaly alerts, audit trails. Payment acceptance (2026-09-17): POST /v1/billing/x402 accepts x402 payment (PAYMENT-SIGNATURE / X-PAYMENT header). MPP is NOT enabled on this deployment, so only x402 payment is accepted here. The custom `x402-base` method is not Stripe/Tempo — this service does not hold Stripe/Tempo settlement credentials. Machine-readable schema: GET /openapi.json (OpenAPI 3) · MCP manifest: GET /server.json Human/agent status page: GET /status (live health, version, uptime, counters) Buyer skill (how an agent buys this, as markdown): GET /skill.md Authentication (credential paths, as markdown): GET /auth.md Privacy: GET /privacy · Terms: GET /terms ## Products — the two questions (blueprint v2) TRUST — Before an agent acts — can it trust the counterparty? OUTCOME — After an agent acts — did it work? ### TRUST - trustscan — Scan before you trust slot=trust direction=outward connect.mcp: https://aiagentscity.com/mcp/trustscan connect.paid_call: POST https://aiagentscity.com/api/x402/trustscan/npm connect.human: https://aiagentscity.com/trust-scan free proof: POST /api/trustscan/npm returns registry facts + a typosquat screen for any package, unauthenticated — the same check is payable per call over x402 without the rate limit; remote deep scans are path-scoped to the host - agent-watch — Is this agent or MCP server real and still behaving? slot=trust direction=outward connect.mcp: https://aiagentscity.com/mcp/agent-watch connect.paid_call: POST https://aiagentscity.com/api/x402/agent-watch/probe connect.human: https://aiagentscity.com/agent-watch free proof: aw_health + aw_census MCP tools are open reads — live census data, no auth - perimeter-watch — Is your endpoint still what it claims — checked from the outside? slot=trust direction=outward connect.mcp: https://aiagentscity.com/mcp/perimeter-watch connect.human: https://aiagentscity.com/perimeter-watch free proof: free browser snapshot — https://entradox.github.io/perimeter-watch-site/ ### OUTCOME - benefits-city — Verified offer records — will this action actually pay? slot=outcome direction=outward connect.mcp: https://aiagentscity.com/benefits/mcp connect.human: https://aiagentscity.com/benefits free proof: search_bonuses / get_bonus MCP tools return hand-verified records with source URLs — no auth - cited — Prove the claim that AI recommends you — verbatim evidence slot=outcome direction=outward connect.mcp: https://aiagentscity.com/mcp/cited connect.human: https://aiagentscity.com/cited free proof: free browser scan — https://entradox.github.io/cited-site/ - agent-ledger — Metered, capped, receipted agent spend — proof of what every cent bought slot=outcome direction=spine connect.mcp: https://aiagentscity.com/mcp/ connect.rest: https://aiagentscity.com/v1/ connect.install: npx @aiagentscity/setup ledger connect.human: https://aiagentscity.com/agent-ledger free proof: POST /start mints a free workspace (3 agents, every rail) — no signup, no card ### INWARD (trust+outcome — answers both questions) - workbench-kit — Frame, gate, and close-receipt every agent task — proof of what your agents did slot=trust+outcome direction=inward connect.install: npx @aiagentscity/setup workbench connect.human: https://aiagentscity.com/workbench free proof: the whole kit is free and runs fully offline — `wb verify ` recomputes a close receipt against the local ledger Data handling: cost metadata only — agent id, rail, service label, amount, token counts, model, timestamp. There is no field for prompt or response content; none is collected and none is stored. Prompts and responses are never used to train models, and your data is never sold. ## Ownership (a workspace_key claims; an agent_secret writes) Claiming a NEW agent_id requires a workspace_key in the body of the first write (POST /v1/track or /v1/budget). Get one self-serve with no human at all by paying via POST /v1/billing/x402 (the paying wallet becomes the workspace identity — $0.10 via x402 buys a 24h Pro pass (unlimited agents) on the workspace your wallet resolves to — no card, no human, and renewable: every payment buys a fresh window (/v1/billing/x402 again, same call)), or by POST /start — no signup, no login, no card, but capped at 3 agents. (GET /start renders the form only; it does not mint.) settles on Base MAINNET (eip155:8453) in real USDC. The paying wallet pays real money; the workspace is live immediately. MPP is NOT enabled on this deployment, so only x402 payment is accepted here. Missing or invalid key on a new claim gets 401 workspace_key_required. That first write mints an `agent_secret` and returns it once, e.g. {"agent_secret": "...", "_note": "..."}. Save it — every later write to that same agent_id must include it in the body as "agent_secret" (the workspace_key is never needed again for that agent), or the request is rejected with 401. Reads /v1/report, /v1/tokens, and /v1/alerts all require an X-Agent-Secret or X-Workspace-Key header (either credential proving access to that agent_id) — missing/wrong gets 401. There is no unauthenticated read path, on REST or MCP. A free workspace is capped at 3 agents; a 4th new agent_id gets 402 with the paid path in the error body: pay via x402 yourself for a renewable Pro pass ($0.10 via x402 buys a 24h Pro pass (unlimited agents) on the workspace your wallet resolves to — no card, no human, and renewable: every payment buys a fresh window (/v1/billing/x402 again, same call)) — the only paid path on this domain. The cap is per workspace, not site-wide. Amounts per entry are capped at $100,000 and must be >= 0. Setting a budget makes it enforced going forward: a track() entry that would cross the monthly/daily cap is rejected with 402, not just logged. Dollar caps (monthly_cents/daily_cents) and token caps (monthly_tokens/ daily_tokens) are independent: dollar caps only cover non-"tokens" rails; token caps only cover rail="tokens" bookkeeping rows. A token-metered agent (flat-rate billing, no dollar amount per call) needs a token cap set — a dollar cap alone does not protect it. See ledger_api_docs("budget"). ## Validation rules (enforced on every write) - agent_id: 1-64 chars, letters/digits/./_/-, must start alphanumeric. Path traversal ("../", "/", leading dot) is rejected with 422. - rail: exactly one of "mpp", "x402", "api_key", "manual" (other values → 422). The special rail value "tokens" is reserved for internal token-burn bookkeeping rows: amount_cents must be 0. - amount_cents: integer 0-10000000 ($0-$100,000 per entry). - monthly_cents / daily_cents (budget): integers 0-10000000. ## Endpoints GET /health — liveness POST /v1/track — record a spend entry (mints/verifies agent_secret) body: {"agent_id": str, "rail": str, "amount_cents": int (0-10000000) — OPTIONAL: omit it and send tokens_in/tokens_out + model, and the amount is computed from the price table (GET /v1/pricing). An unpriced model is refused with 422 model_not_priced rather than recorded as zero. "service": str — OPTIONAL when auto-pricing; defaults to the model's provider "tokens_in": int (optional), "tokens_out": int (optional), "model": str (optional), "workspace_key": str (required to CLAIM a new agent_id), "agent_secret": str (required after the first call for this agent_id)} POST /v1/budget — set budget caps (mints/verifies agent_secret); once set, track() blocks entries that would cross the cap body: {"agent_id": str, "monthly_cents": int (0-10000000), "daily_cents": int (optional, 0-10000000), "monthly_tokens": int (optional, token-burn cap), "daily_tokens": int (optional, token-burn cap), "workspace_key": str (required to CLAIM a new agent_id), "agent_secret": str (required after the first call for this agent_id)} POST /v1/check : ask BEFORE spending: allowed true/false + reason code, estimate, the price used, and every budget window (cap/spent/remaining). Same decision the /proxy gate enforces. Read-only: records and reserves nothing. Requires X-Agent-Secret or X-Workspace-Key. body: {"agent_id": str, and ONE of: "amount_cents": int (any rail, e.g. an x402 purchase) | "model": str + "tokens_in"/"tokens_out": int | "model": str + "payload": the request body} reasons: within_budget | over_monthly_cap | over_daily_cap | over_monthly_token_cap | over_daily_token_cap | no_budget_set | unpriced_model | approved_permit POST /v1/approvals — request a one-shot spend permit for a spend the cap would refuse; pending for 1h, consumed once by the next track within its amount (workspace_key claims, agent_secret writes) body: {"agent_id": str, "amount_cents": int, "service": str, "reason": str (optional), "workspace_key": str (required to CLAIM), "agent_secret": str (after first write)} GET /v1/approvals — list permits: ?agent_id= + X-Agent-Secret or X-Workspace-Key, or X-Workspace-Key alone for every agent in the workspace (the supervisor view). Query: state=pending|approved|denied|consumed|expired POST /v1/approvals/{approval_id}/decide — approve or deny a pending permit. X-Workspace-Key only: an agent's own secret can request but can NEVER decide. body: {"agent_id": str, "decision": "approve"|"deny"} GET /v1/report/{agent_id} — spend report (query: days=30) — requires X-Agent-Secret or X-Workspace-Key POST /v1/report/{agent_id}/share — mint a read-only, EXPIRING link to the human report page (agent_secret OR workspace_key; default 7 days, max 90) POST /v1/report/{agent_id}/share/revoke — kill every outstanding share link for this agent GET /v1/report/{agent_id}/html?t= — the shared page itself. A BROWSER CANNOT SEND A HEADER, which is why this exists: open this URL directly, no credential, no curl. Bad/expired/revoked token returns a styled HTML error page, never raw JSON. The token reads that ONE agent_id only — it cannot write, rotate, or read others. GET /v1/tokens/{agent_id} — token burn report: in/out totals + by model (query: days=30) — requires X-Agent-Secret or X-Workspace-Key GET /v1/alerts/{agent_id} — alerts for agent — requires X-Agent-Secret or X-Workspace-Key POST /v1/agents/{agent_id}/rotate-secret — RECOVER a lost agent_secret: mints a new one, kills the old (workspace_key ONLY — never the agent_secret itself; 404 if unclaimed, 403 if the agent belongs to another workspace) POST /v1/agents/{agent_id}/revoke-secret — invalidate an agent's secret, keep its ledger (workspace_key ONLY; the agent_id stays claimed, so no other workspace can take it over; rotate back in when you want it writing) POST /v1/webhooks — register an alert destination for this workspace (X-Workspace-Key; http(s) URL only — no email rail) GET /v1/webhooks — list this workspace's destinations GET /v1/webhooks/deliveries — delivery receipts, successes AND failures DELETE /v1/webhooks/{id} — remove one destination Events: alert.raised, budget.warning (80%), budget.exceeded, anomaly.detected. Omit events to get all. Payloads carry cost metadata only — never a secret, prompt, or response. GET /v1/agents — owner-only: full cross-tenant listing (requires X-Al-Admin header) GET /stats — usage counters POST /v1/setup-event — connect-funnel sink for `npx @aiagentscity/setup`. Closed schema (extra fields → 422): product, outcome, harness, is_human_initiated, connect_attempt_id (uuid), failure_reason, cli_version, runtime_platform. Rate-limited; enumerated fields only are persisted. Headers are never stored — nothing credential-shaped belongs in a funnel log. Feeds setup_connect_funnel at /v1/metrics. POST /v1/billing/x402 — self-serve workspace minting for an agent with a wallet (X-PAYMENT header; the paying wallet IS the identity) settles on Base MAINNET (eip155:8453) in real USDC. The paying wallet pays real money; the workspace is live immediately. Use POST /start instead if you have no wallet (GET /start only renders the form). To upgrade an EXISTING workspace instead of minting a new one: add X-Workspace-Id + X-Workspace-Key headers to the paid request — the wallet binds to that workspace permanently (it becomes the workspace's payment identity and can reissue the workspace_key via /v1/billing/x402/recover) and renewals resolve it with no headers. On the x402 rail, refused binds return 4xx before settlement; a payment is never taken without granting. Payable resources (x402, per-call — no workspace needed, the payment IS the request; discovery metadata at /.well-known/x402): POST /api/x402/agent-watch/probe — {url} → MCP/agent endpoint probe: liveness, latency, tool count, auth metadata, price-challenge detection. Real MCP initialize handshake, not a ping. GET /api/x402/agent-watch/census — agent-economy census: how many known agent/ MCP endpoints are alive, dead or unknown. POST /api/x402/trustscan/npm — {package} → npm trust verdict: typosquat screen, deprecation, age, maintainers, weekly downloads, source link. GET /start — renders the start form (no signup, no login); does NOT mint. POST /start mints one and shows the workspace_key once. ## Getting started (the shortest path) pip install "aiagentscity-ledger[wrapper]" # or: uvx --from git+https://github.com/entradox/agent-ledger agent-ledger init agent-ledger init # mints a workspace, claims an agent, writes .env then, in your code: from openai import OpenAI import agentledger client = agentledger.wrap(OpenAI(api_key=OPENAI_KEY), agent_id="", agent_secret="") Every call now goes through the proxy: metered, and refused before the provider is contacted if it would cross a cap. Streaming, tool calls and retries are unchanged — wrap() only repoints the base URL and adds two headers; it does not patch or subclass the SDK. Anthropic's client works the same way. `agent-ledger share --agent-id --agent-secret ` prints a read-only link anyone can open in a browser. ## Proxy (enforcement — a cap that stops money, not just a record) POST /proxy/{provider}/{path} — provider = openai, anthropic, or ANY provider listed in providers.json (deepseek and moonshot ship as examples). Adding a vendor is a config edit, not a code change — spend must be meterable regardless of which vendor an agent calls. {path} is the provider's own path, e.g. /proxy/openai/v1/chat/completions headers: X-AL-Agent: (who gets billed) X-AL-Secret: (proves you may write for it) Authorization / x-api-key: YOUR provider credential — forwarded, NEVER stored flow: identify -> ESTIMATE the call's max cost -> if it would cross a cap, 402 to YOU and the provider is never contacted -> otherwise forward -> meter from the provider's real `usage` tokens PASS-THROUGH: AgentLedger holds no provider key. A caller can bypass the proxy entirely, and traffic that does is not enforced. Nothing here claims otherwise. An UNPRICED model is never blocked: the call passes through and an alert fires, because a silent zero would read as "this agent spends nothing". PRICING IS KEYED BY MODEL, not by provider — the same model costs the same through any carrier, so a caller cannot route around its own price by switching endpoints. Where a provider reports cached input (DeepSeek: prompt_cache_hit_tokens), the cache-hit rate is used; ignoring it would overstate a cache-heavy workload by roughly 50x. GET /v1/pricing — the price table in use + provenance (open read). Unverified entries are placeholders: check them against your provider. ## MCP Registry: io.github.entradox/agent-ledger Remote: https://aiagentscity.com/mcp/ Connect: npx @aiagentscity/setup ledger — one command; detects claude-code, codex, cursor, opencode, hermes and openclaw, registers the MCP the right way for each, and installs the vendored skill (manual: claude mcp add --transport http agent-ledger https://aiagentscity.com/mcp/) Workbench: npx @aiagentscity/setup workbench — local governance kit, not an MCP: ~/.workbench + `wb` CLI, frame-card pre-write gates wired into your harness, hashed close receipts. Runs fully offline. Tools exposed at POST /mcp/ (v0.4.7, 18): ledger_check_spend : ask before spending: allowed + reason + headroom (read-only; agent_secret or workspace_key) ledger_track — record a spend entry (workspace_key to claim, agent_secret after) ledger_set_budget — set a budget cap (workspace_key to claim, agent_secret after) ledger_report — get a spend report (agent_secret or workspace_key param) ledger_proxy_attach — point provider traffic at the budget-enforcing proxy so caps are checked BEFORE the provider is called (open read; returns base_url + headers, your provider credential stays with you) ledger_alerts — get alerts for an agent (agent_secret or workspace_key param) ledger_price — price a call from the same table the caps use, so a capped agent can report its cost honestly and plan before spending (open read) ledger_rotate_secret — recover a lost agent_secret (workspace_key param; old secret dies at once) ledger_revoke_secret — invalidate an agent's secret without deleting its history (workspace_key param) ledger_request_approval — ask the owner for a one-shot spend exception when the cap would refuse (agent_secret or workspace_key to claim) ledger_approvals — list permits: this agent's (agent_secret) or the whole workspace's (workspace_key — the supervisor view) ledger_approval_decide — approve/deny a pending permit (workspace_key only — an agent can never approve its own request) ledger_start — mint a workspace over MCP (no human) ledger_api_docs — self-serve docs by topic: quickstart|mcp|rest|budget|errors|idempotency|metering|all (open read) ledger_examples — runnable recipe by pattern: python_tracking|budget_enforcement|weekly_report|retry_safe_writes (open read) skills_list_tool — list this product's skills (open read) read_skill — read a product skill file by its skill:// URI (open read) ## Satellite products (MCP, proxied through this host) Every satellite exposes its own streamable-http MCP server at https://aiagentscity.com/mcp/ — the path resolves on this host and is proxied to the owning backend. POST JSON-RPC to the URL (Accept: application/json, text/event-stream; no auth needed to connect). Tool lists below are each server's live tools/list (2026-09-19). - agent-watch: /mcp/agent-watch (v0.1.0, 8 tools) aw_health, aw_check_endpoint, aw_census, aw_list_monitored, aw_alerts, aw_watch, skills_list_tool, read_skill STATUS live: tool calls execute via the city gateway, which translates them to the documented REST API. Re-probed 2026-10-07: the backend's native MCP dispatch completes initialize + tools/list + tools/call, so the gateway translation is a fast path rather than a workaround. - perimeter-watch: /mcp/perimeter-watch (v1.0, 6 tools) pw_health, pw_snapshot, pw_watch_status, pw_stats, skills_list_tool, read_skill STATUS live: same gateway translation as agent-watch, and native dispatch completes too (re-probed 2026-10-07). The free browser snapshot form works today: https://entradox.github.io/perimeter-watch-site/ - cited: /mcp/cited (v1.0, 9 tools) cited_health, cited_scan, cited_report, cited_watch_status, cited_stats, cited_api_docs, cited_examples, skills_list_tool, read_skill STATUS live: same gateway translation as agent-watch, and native dispatch completes too (re-probed 2026-10-07). The free browser scan form works today: https://entradox.github.io/cited-site/ - trustscan: /mcp/trustscan (v0.1.0, 4 tools, LIVE) trust_scan_server — security-scan an MCP server or skill package before trusting it (invisible-Unicode prompt injection, dangerous code patterns MCP001-MCP006, hardcoded secrets, typosquat names); returns a 0-100 score, letter grade, and findings. Read-only. NOTE: remote scans are path-scoped to the TrustScan host's own code — for YOUR package the reachable check is POST /api/trustscan/npm (free, registry facts + typosquat screen) or the same call payable per call over x402 without the rate limit. trust_scan_file — security-scan a single file on the host for invisible Unicode, dangerous patterns, and secrets. Read-only. skills_list_tool, read_skill (alias /mcp/trust-scan resolves to the same server) Re-probed 2026-10-07: native MCP dispatch completes initialize + tools/list + tools/call through the gateway. - benefits-city: /benefits/mcp (v0.3.0, 6 tools) search_bonuses, get_bonus, expiring_soon, compare_bonuses, benefits_api_docs, benefits_examples STATUS live: its own streamable-http server on this host (not a /mcp/ proxy). Probed 2026-10-13: initialize + tools/list + tools/call all complete; search_bonuses returns hand-verified records with source URLs — unauthenticated, read-only. Note: MCP and REST are credential-equivalent. ledger_report/ledger_alerts take agent_secret/workspace_key parameters and enforce the same access rule as GET /v1/report and GET /v1/alerts — there is no unauthenticated read path on either surface. Only the meta-doc tools (ledger_api_docs, ledger_examples) are open, and they expose no agent data. The agent-write routes POST /v1/track, POST /v1/budget, POST /v1/approvals and POST /v1/approvals/{id}/decide require AL-API-Version: 2026-09-01 — missing/invalid values are rejected with 400 version_header. Other /v1/* paths (/v1/check, /v1/webhooks, /v1/billing/*) are not version-gated. The /mcp/ endpoint itself does not require this header (MCP tool calls are not version-gated). POST /v1/track and POST /v1/budget accept an optional Idempotency-Key header (<=255 chars) for at-most-once retries. Pricing is as described above (Free = up to 3 agents per workspace, no expiry). The only paid path is the agent rail: an agent with a wallet buys a renewable 24-hour Pro pass over x402 — unlimited agents on the workspace its wallet resolves to, no signup or card. There is no human checkout and no monthly plan on this domain. Contact: entradox@icloud.com