{"openapi":"3.1.0","info":{"title":"AgentLedger API","version":"0.4.0"},"paths":{"/v1/track":{"post":{"summary":"Create Track","operationId":"create_track_v1_track_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/budget":{"post":{"summary":"Create Budget","operationId":"create_budget_v1_budget_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report/{agent_id}":{"get":{"summary":"Get Report","operationId":"get_report_v1_report__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report/{agent_id}/html":{"get":{"summary":"Get Report Html","description":"Human-readable version of /v1/report/{agent_id} — rendered instead of\nraw JSON. Two ways in:\n\n  * `X-Agent-Secret` / `X-Workspace-Key` header (the agent's own path), or\n  * `?t=<share token>` minted by POST /v1/report/{agent_id}/share.\n\nThe header path is for agents. The token path exists because a browser\ncannot send a header — so before D-1217 the \"shareable link\" these docs\nadvertised could not actually be opened by a human. A bad, expired or\nrevoked token gets a friendly HTML page, never raw JSON.","operationId":"get_report_html_v1_report__agent_id__html_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"t","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report/{agent_id}/share":{"post":{"summary":"Mint Share Link","description":"Mint a read-only, expiring link to this agent's report page.\n\nAuthorized by the agent's own secret OR its workspace_key: this is a READ\ngrant, so either credential that can already read the report may share it.\nThe token is scoped to THIS agent_id — it cannot write, cannot rotate, and\ncannot read another agent's report. TTL default 7 days, hard max 90.","operationId":"mint_share_link_v1_report__agent_id__share_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"ttl_days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Ttl Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/report/{agent_id}/share/revoke":{"post":{"summary":"Revoke Share Links","description":"Invalidate every outstanding share link for this agent_id.\n\nBumps the agent's share epoch, so tokens already handed out stop verifying.\nSame credentials as minting. Safe to call repeatedly.","operationId":"revoke_share_links_v1_report__agent_id__share_revoke_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alerts/{agent_id}":{"get":{"summary":"Get Alerts","operationId":"get_alerts_v1_alerts__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/tokens/{agent_id}":{"get":{"summary":"Token Report","description":"Token burn report: totals in/out, by model, per period. Separate from\ndollar spend — answers 'what is this agent burning on?'","operationId":"token_report_v1_tokens__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks":{"get":{"summary":"List Webhooks","description":"This workspace's registered alert destinations.","operationId":"list_webhooks_v1_webhooks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Create Webhook","description":"Register a destination for this workspace's alerts.\n\nWorkspace-scoped (X-Workspace-Key). `url` must be http(s) — point it at\nSlack, Discord, Zapier or your own endpoint. Email delivery is not offered\n(removed 2026-09-13): the product does not send mail to arbitrary addresses\non a user's behalf. Events: alert.raised, budget.warning (80%),\nbudget.exceeded, anomaly.detected — omit `events` to receive all of them.\n\nPayloads carry cost metadata only: event, agent_id, message, timestamp,\nreport URL. Never a secret, never a prompt, never a response.","operationId":"create_webhook_v1_webhooks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/deliveries":{"get":{"summary":"List Deliveries","description":"Delivery receipts, newest last. A failure here is visible on purpose —\na silently dropped alert is the exact thing this feature exists to stop.","operationId":"list_deliveries_v1_webhooks_deliveries_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}":{"delete":{"summary":"Delete Webhook","description":"Remove one registered destination.","operationId":"delete_webhook_v1_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agents/{agent_id}/rotate-secret":{"post":{"summary":"Rotate Secret","description":"Recover a lost agent_secret: mint a new one for an agent_id you own.\n\nAuthenticated by the WORKSPACE_KEY only — header X-Workspace-Key, or\nworkspace_key in the body. The previous secret stops working the moment\nthis returns; the new one is shown once, the same contract as a first\nclaim. The rotation is written to the agent's audit trail. An unclaimed\nagent_id is 404 (this is not a claim path), and an agent belonging to a\ndifferent workspace is ALSO 404 with the identical body — a different status\nwould confirm that the id exists somewhere else.","operationId":"rotate_secret_v1_agents__agent_id__rotate_secret_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RotateSecretRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agents/{agent_id}/revoke-secret":{"post":{"summary":"Revoke Secret","description":"Invalidate an agent_id's secret without deleting its ledger.\n\nSame workspace_key-only auth as rotate. Writes to the agent then fail with\n401 agent_secret_mismatch until the owner rotates a new secret in. The id\nstays CLAIMED, so no other workspace can claim it and inherit the spend\nhistory.","operationId":"revoke_secret_v1_agents__agent_id__revoke_secret_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RotateSecretRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agents/{agent_id}":{"delete":{"summary":"Delete Agent","description":"Remove an agent's ledger entirely. Owner-only (cron secret) — beta slots\nare per-product, so the operator can clear test/demo agents to free slots.","operationId":"delete_agent_v1_agents__agent_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/pricing":{"get":{"summary":"Pricing","description":"The price table in use, with provenance. Open read: it is public\ninformation and an agent deciding whether to route through the proxy needs\nit. Unverified entries are placeholders — see the _note.","operationId":"pricing_v1_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/proxy/{provider}/{path}":{"post":{"summary":"Proxy Call","description":"Forward one provider call, metering it and blocking it if the budget\nsays so. See the module docstring for the sequence.","operationId":"proxy_call_proxy__provider___path__post","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}},{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/agents":{"get":{"summary":"Get Agents","description":"Portfolio-wide listing across every agent_id ever claimed — owner-only.\n(Per-agent data at GET /v1/report/{agent_id}, /v1/tokens/{agent_id}, and\n/v1/alerts/{agent_id} requires X-Agent-Secret or X-Workspace-Key; this\nendpoint is the separate full cross-tenant dump.)","operationId":"get_agents_v1_agents_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/dashboard":{"get":{"summary":"Dashboard","description":"Owner-only dashboard: every claimed agent, its spend, and its budget\nstatus in one page. Same X-Al-Admin guard as /v1/agents and /v1/metrics.\nServer-rendered, no JS — reuses list_agents() + report(), both already\ncheap at current scale (a handful of claimed agents).","operationId":"dashboard_v1_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/metrics":{"get":{"summary":"Get Metrics","description":"Owner-only telemetry: funnel counters, revenue events, and reach —\nsame X-Al-Admin guard as /v1/agents.","operationId":"get_metrics_v1_metrics_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/stats":{"get":{"summary":"Stats","operationId":"stats_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/glama.json":{"get":{"summary":"Glama Claim","description":"Glama HTTP-challenge ownership verification file.","operationId":"glama_claim__well_known_glama_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/llms.txt":{"get":{"summary":"Llms Txt","operationId":"llms_txt_llms_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/robots.txt":{"get":{"summary":"Robots Txt","operationId":"robots_txt_robots_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/server.json":{"get":{"summary":"Server Json","description":"MCP server discovery manifest at the canonical root path.","operationId":"server_json_server_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/agent.json":{"get":{"summary":"Agent Json","description":"AEO capability manifest — agents discover what this product does,\npricing, auth, and how to call it (rules/agent-native-standard.md).","operationId":"agent_json__well_known_agent_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Front Door","description":"The front door. Until D-1162 this path 404'd — the only human page was\n/status, so a live product with a working payment link could not be\nreached from its own root.","operationId":"front_door__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/status":{"get":{"summary":"Status Page","description":"Live health. See _health_page().","operationId":"status_page_status_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/privacy":{"get":{"summary":"Privacy Page","operationId":"privacy_page_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/terms":{"get":{"summary":"Terms Page","operationId":"terms_page_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/start":{"get":{"summary":"Start Page","description":"Step one of the buy path. Deliberately does NOT mint: a mint on GET\nwould let any crawler, link-preview bot or accidental reload burn one of\nthe 50 launch-window workspaces and orphan a key nobody ever saw. The\nform POSTs to this same path, which does the minting.","operationId":"start_page_start_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"summary":"Start Mint","description":"Mint a workspace for a human with no signup, no login and no card, then\nshow its workspace_key exactly once — the same one-time reveal the\nretiring Google dashboard used. The payment link carries the workspace id\nas client_reference_id: that reference is what lets the Stripe webhook\nmark THIS workspace Pro. Without it a real payment would take the card\nand upgrade nothing (D-1162).\n\ngrant_scarcity=False: this is the free tier, not the launch grant. See\nworkspace_engine.create_workspace for why.","operationId":"start_mint_start_post","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/_beacon":{"get":{"summary":"Connect Beacon","description":"Fire-and-forget telemetry beacon for static-page interactions that have\nno natural server round-trip (launch-kit v0.3 item 4). The allowlist is\nclosed on purpose — an open `event` value would let a caller write\narbitrary metric kinds, and an open `ws` would let them invent workspaces\nin the onboarding stream. Both are validated here.","operationId":"connect_beacon_v1__beacon_get","parameters":[{"name":"event","in":"query","required":true,"schema":{"type":"string","title":"Event"}},{"name":"ws","in":"query","required":false,"schema":{"type":"string","default":"","title":"Ws"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stripe/webhook":{"post":{"summary":"Stripe Webhook","description":"Fulfillment: checkout.session.completed -> customers.jsonl (HMAC-verified).","operationId":"stripe_webhook_stripe_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/{email}":{"get":{"summary":"Billing Status","description":"Customer plan lookup — OWNER-ONLY (Opus audit round 3: was an open\nemail-enumeration oracle). Token = HMAC-SHA256(\"billing:<email>\",\nAL_ADMIN_SECRET), truncated to 32 hex chars; the operator computes it,\ncustomers never see billing state of other emails.","operationId":"billing_status_v1_billing__email__get","parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","title":"Email"}},{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/checkout":{"post":{"summary":"Create Checkout","description":"Checkout URL for an existing workspace, authenticated by its own\nworkspace_key.\n\nRe-keyed off the retired Google session (D-1162). The old version read an\n`al_session` cookie, which meant the product's only human purchase path\nrequired a login that the deployment could not serve — and it needed\nSTRIPE_API_KEY / AL_STRIPE_PRICE_ID, neither of which was set, so the\nroute could never have worked here.\n\nNo Stripe secret is needed: the URL is the existing live payment link\nwith client_reference_id appended. Stripe echoes that value back on\ncheckout.session.completed (docs.stripe.com/payment-links/url-parameters),\nwhich is exactly the field this module's webhook reads to call\nworkspace_engine.mark_pro(). No API key, no session, no Google.","operationId":"create_checkout_v1_billing_checkout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/billing/x402":{"post":{"summary":"X402 Billing","description":"Mint (or resolve) a workspace from a settled x402 payment.\n\nThe settlement transaction hash is the idempotency key, per spec 3b —\nimplemented on the SAME idempotency store /v1/track uses\n(idempotency_begin/store/release in ledger_engine), not a second\nbespoke mechanism. Consequences:\n\n- Replaying the SAME tx_hash returns the same workspace_id, but the\n  raw workspace_key is REDACTED from the cached replay. The key is\n  shown exactly once, in the original response; a replay must not\n  re-expose it to anyone who can name the tx_hash. Same precedent as\n  /v1/track and /v1/budget, which strip the minted agent_secret from\n  their cached payloads for the same reason.\n- A NEW tx_hash from an ALREADY-KNOWN wallet (a second real payment)\n  resolves to that wallet's existing workspace and returns\n  workspace_key: null — a key was already issued for this wallet and\n  only its hash is stored, so it cannot be re-shown. Nothing is\n  invalidated (the previous behavior silently reissued, breaking the\n  key the agent was already using).","operationId":"x402_billing_v1_billing_x402_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"BudgetRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"monthly_cents":{"type":"integer","minimum":0.0,"title":"Monthly Cents"},"daily_cents":{"type":"integer","minimum":0.0,"title":"Daily Cents","default":0},"monthly_tokens":{"type":"integer","minimum":0.0,"title":"Monthly Tokens","default":0},"daily_tokens":{"type":"integer","minimum":0.0,"title":"Daily Tokens","default":0},"agent_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Secret"},"workspace_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Key"}},"type":"object","required":["agent_id","monthly_cents"],"title":"BudgetRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"RotateSecretRequest":{"properties":{"workspace_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Key"}},"type":"object","title":"RotateSecretRequest"},"TrackRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"rail":{"type":"string","title":"Rail"},"amount_cents":{"anyOf":[{"type":"integer","maximum":10000000.0,"minimum":0.0},{"type":"null"}],"title":"Amount Cents"},"service":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service"},"tokens_in":{"type":"integer","minimum":0.0,"title":"Tokens In","default":0},"tokens_out":{"type":"integer","minimum":0.0,"title":"Tokens Out","default":0},"cache_hit_in":{"type":"integer","minimum":0.0,"title":"Cache Hit In","default":0},"cache_write_5m_in":{"type":"integer","minimum":0.0,"title":"Cache Write 5M In","default":0},"cache_write_1h_in":{"type":"integer","minimum":0.0,"title":"Cache Write 1H In","default":0},"model":{"type":"string","title":"Model","default":""},"agent_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Secret"},"workspace_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Key"}},"type":"object","required":["agent_id","rail"],"title":"TrackRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebhookRequest":{"properties":{"url":{"type":"string","title":"Url"},"events":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Events"},"label":{"type":"string","title":"Label","default":""}},"type":"object","required":["url"],"title":"WebhookRequest"}}}}