{"openapi":"3.1.0","info":{"title":"AgentLedger API","version":"0.4.1"},"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/workspace/summary":{"get":{"summary":"Workspace Summary","description":"Every agent in the workspace with 30-day spend, budget status, the daily\nseries, and the alert feed. Workspace key only.","operationId":"workspace_summary_v1_workspace_summary_get","parameters":[{"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/workspace/export.csv":{"get":{"summary":"Workspace Export","operationId":"workspace_export_v1_workspace_export_csv_get","parameters":[{"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}/csv":{"get":{"summary":"Report Export","description":"Per-agent CSV. Two credentials can authorize it, exactly like the JSON\nreport: the agent's own secret, or the workspace key that owns it.","operationId":"report_export_v1_report__agent_id__csv_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"}}}}}}},"/dashboard":{"get":{"summary":"Dashboard","description":"The page itself is static: it holds no data and needs no auth. The key is\npasted in the browser and sent as a header, so there is nothing to leak in\nthe HTML, the URL, or a cache.","operationId":"dashboard_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/demo":{"get":{"summary":"Demo","description":"A populated dashboard with no signup and no credential.\n\nRendered from a fixture rather than a seeded production workspace: there is\nno workspace-deletion path in the API, so a 'demo workspace' would be\npermanent, and it would inflate the counters that /stats serves.","operationId":"demo_demo_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/demo/summary":{"get":{"summary":"Demo Summary","description":"The fixture the demo page renders. Public, synthetic, read-only — and it\nreturns the same shape as /v1/workspace/summary so the two cannot drift.","operationId":"demo_summary_v1_demo_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/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/mcp/server-card.json":{"get":{"summary":"Mcp Server Card","description":"Static MCP server card (SEP-1649 shape).\n\nGenerated from the live tools/list rather than hand-written, so directory\nscanners that cannot complete an automated scan (auth wall, WAF, bot rules)\nstill get accurate tools + schemas. Smithery and similar registries read\nthis path when scanning is blocked.","operationId":"mcp_server_card__well_known_mcp_server_card_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":{}}}}}}},"/.well-known/agent-card.json":{"get":{"summary":"Agent Card Json","description":"Alias of /.well-known/agent.json.\n\nAgents request this spelling 136 times in the live log (2026-09-14) and got\na 404 every time. `agent-card.json` is the name used by several crawler\nfamilies. Serving the same manifest under every spelling an agent actually\nasks for beats making them learn ours. Same content, no second source of\ntruth: this returns AGENT_JSON, it does not copy it.","operationId":"agent_card_json__well_known_agent_card_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/mcp.json":{"get":{"summary":"Mcp Wellknown Json","description":"MCP server descriptor at the spelling agents probe (23 live 404s).\n\nDistinct from /.well-known/mcp/server-card.json (which is the richer\nregistry card). This is the shorthand form crawlers look for.","operationId":"mcp_wellknown_json__well_known_mcp_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/x402":{"get":{"summary":"X402 Wellknown Json","description":"The till, published where agents actually look.\n\nAn agent with 1,343 successful MCP calls (`b628ad13e37e`) probed\n/.well-known/x402, /payments, /pricing, /payment and /monetization on\n2026-09-14 and every one returned 404. It was looking for how to pay us\nand we told it nothing. This is the signpost.\n\nValues are read from the same env the verifier uses, so this cannot drift\nfrom what the endpoint actually charges.","operationId":"x402_wellknown_json__well_known_x402_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/payments":{"get":{"summary":"Payments Wellknown Json","description":"Alias for the payment discovery document (agents probe both spellings).","operationId":"payments_wellknown_json__well_known_payments_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/agents.txt":{"get":{"summary":"Agents Txt","description":"Plain-text agent orientation: the .txt spelling crawlers request.\n\nContent is derived from AGENT_JSON and the live route set, so it cannot\nassert a capability the API does not have.","operationId":"agents_txt_agents_txt_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap Xml","description":"Sitemap for crawlers (69 live 404s on 2026-09-14).\n\nBuilt from the real route set, not a hand-written list, so it cannot\nadvertise a page that does not exist. Only public, human-readable pages are\nlisted; API and discovery paths are intentionally excluded because they are\nnot indexable content.\n\nServed with the XML media type: a sitemap declared text/plain is ignored by\nsome crawlers, which would make the whole route pointless.","operationId":"sitemap_xml_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Front Door","description":"The AI Agent City umbrella index (D-1239). Until D-1239 this path\nserved AgentLedger's own landing page directly — that content now lives\nat /agent-ledger, and this lists all products so the domain reads as the\numbrella it actually is.","operationId":"front_door__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/agent-ledger":{"get":{"summary":"Agent Ledger Page","description":"AgentLedger's own landing page — this is the pre-D-1239 root content,\nunchanged, moved here so the domain root can become the umbrella index.","operationId":"agent_ledger_page_agent_ledger_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/perimeter-watch":{"get":{"summary":"Perimeter Watch Page","operationId":"perimeter_watch_page_perimeter_watch_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/cited":{"get":{"summary":"Cited Page","operationId":"cited_page_cited_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/agent-watch":{"get":{"summary":"Agent Watch Page","operationId":"agent_watch_page_agent_watch_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/trust-scan":{"get":{"summary":"Trust Scan Page","operationId":"trust_scan_page_trust_scan_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"}}}}}}},"/img/dashboard.png":{"get":{"summary":"Dashboard Image","description":"The product screenshot on the landing page. Served from disk and cached\nhard: it is a static asset, not a per-request rendering.","operationId":"dashboard_image_img_dashboard_png_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/about":{"get":{"summary":"About Page","description":"D-1239: /about folded into the umbrella index at \"/\" — that IS the\nproduct list now, so this is a redirect rather than a second copy.","operationId":"about_page_about_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/security":{"get":{"summary":"Security Page","description":"Trust surface: exactly what is stored, what is not, and what a cap does\nand does not guarantee.","operationId":"security_page_security_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/quickstart":{"get":{"summary":"Quickstart Page","description":"Zero to a metered agent in five minutes. The install line is the git URL\nbecause nothing is published on PyPI yet, and the name agent-ledger there\nbelongs to a different author.","operationId":"quickstart_page_quickstart_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/compare":{"get":{"summary":"Compare Page","description":"Why this is not a trace viewer — the honest version, with dated prices.","operationId":"compare_page_compare_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"}}}}