{"openapi":"3.1.0","info":{"title":"Hanzo Cloud API","description":"Composed from each subsystem's own projection of its router, in the fleet's mount order — every operation below is a route the subsystem that publishes it registered. Tagged by product: the first path segment after /v1/.","version":"v1"},"servers":[{"url":"https://api.hanzo.ai"}],"tags":[{"name":"admin","description":"Package admin is the operator's view of the fleet: orgs, users, roles, spend and system health."},{"name":"ads","description":"Package ads is your paid ad campaigns, launched and paused from one place."},{"name":"affiliates","description":"Package affiliates is a partner program that pays commission on what your referrals spend."},{"name":"agent","description":"Package agent is a conversation that uses your org's own tools to get an answer."},{"name":"agents","description":"Package agents is autonomous agents for your org: define them, run them, keep every run."},{"name":"ai","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"analytics","description":"Package analytics is product analytics: send an event, read back who did what."},{"name":"ask","description":"Package ask is a plain-language question about your business, answered with real numbers."},{"name":"audio","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"audit","description":"Package auditlog is your org's tamper-evident audit trail: every security-relevant event, hash-chained and readable."},{"name":"authors","description":"Package authors is a royalty for open-source work: your repo runs, you get paid."},{"name":"authz"},{"name":"auto","description":"Package auto is Hanzo Auto: build a flow from triggers and actions, publish it, and watch every run."},{"name":"automations","description":"Package automations is workflows that run themselves, on a schedule or a webhook."},{"name":"avatar","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"balancers","description":"Package do is the org-scoped private-network surface — /v1/vpcs and /v1/balancers — carved out of Hanzo's OWN house DigitalOcean account."},{"name":"base","description":"Package base is managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in."},{"name":"benchmark","description":"Package benchmark is one honest score for any model, on the tests everyone quotes."},{"name":"billing","description":"Package billing is your org's balance, what it has spent, and the cards it pays with."},{"name":"blueprint","description":"Package blueprint is what a template costs to run, worked out before you deploy."},{"name":"books","description":"Package books is double-entry accounting: chart of accounts, ledger, bank reconciliation, and the reports that prove the books balance."},{"name":"bot","description":"Package bot is your own machines, connected and ready to take a command."},{"name":"bots","description":"Package bots is a bot doing your work on a real desktop, live, while you watch."},{"name":"builds","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"campaign","description":"Package campaign is one go-to-market push across paid, organic and email at once."},{"name":"captable","description":"Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what."},{"name":"cart","description":"Package commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog."},{"name":"catalog","description":"Package catalog is one place to browse every project, app and site built here."},{"name":"channels","description":"Package channels is one inbox for the chat apps you connect — Discord, Slack, Teams, Telegram."},{"name":"chat","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"cloud","description":"Package venue is bring your own cloud: link a DigitalOcean, AWS or GCP account and its clusters show up ready to run work."},{"name":"cloudflare","description":"Package cloudflare is your Cloudflare account, managed from Hanzo: zones, Pages, Workers, Workers AI, R2, KV and D1."},{"name":"clusters","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"code","description":"Package code is search and symbols across your repos, for you and your agents."},{"name":"coding","description":"Package agents is autonomous agents for your org: define them, run them, keep every run."},{"name":"collections","description":"Package base is managed Hanzo Base: a hosted backend for your app — collections, records, access rules and sign-in."},{"name":"commands"},{"name":"commerce","description":"Package commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog."},{"name":"company","description":"Package company is incorporation end to end: pick a structure, add founders, pay, file, and e-sign."},{"name":"completions","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"compliance","description":"Package compliance is your KYC/KYB onboarding, accreditation records, and the evidence trail behind them."},{"name":"compute","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"connector","description":"Package integrations is how your org connects third-party accounts like Slack, and revokes them."},{"name":"connectors","description":"Package integrations is how your org connects third-party accounts like Slack, and revokes them."},{"name":"content","description":"Package content is marketing content from draft to published, on every channel."},{"name":"crawl","description":"Package crawl is any web page turned into clean markdown a model can read."},{"name":"crm","description":"Package crm is your sales pipeline: the companies, the people, the deals in play."},{"name":"csrf","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"dataroom","description":"Package dataroom is a secure document room you share by link and watch page by page."},{"name":"datastore","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"deploy","description":"Package deploy is Hanzo CD: see what each app is running, sync it, and roll back a bad release."},{"name":"destinations","description":"Package destinations is your events forwarded to the ad and analytics tools you use."},{"name":"dev-bridge","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"dns","description":"Package dns is your DNS records: the zones and records behind every name you point at Hanzo."},{"name":"docdb","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"docs","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"documents","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"domain","description":"Package domain is Hanzo Domains: search a name, see the price, buy it from your prepaid wallet."},{"name":"download","description":"Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS."},{"name":"embed","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"embeddings","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"enablement","description":"Package pricing is the price list: what every model, provider, GPU tier, tool and hosting plan costs."},{"name":"engine","description":"Package engine is Hanzo Engine: which models the serving runtime has loaded, and the GPUs under it."},{"name":"entitlements","description":"Package entitlements is what your org may run: what the plan grants, and which of those products are switched on."},{"name":"environments","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"errors","description":"Package analytics is product analytics: send an event, read back who did what."},{"name":"esign","description":"Package esign is a document out for signature, signed and filed with an audit trail."},{"name":"evals","description":"Package eval is scoring a model on your own data, with a judge you choose."},{"name":"event","description":"Package analytics is product analytics: send an event, read back who did what."},{"name":"exec","description":"Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS."},{"name":"experiments","description":"Package experiments is A/B testing anything: a flag, an ad, a subject line, a model."},{"name":"feedback","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"files","description":"Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS."},{"name":"finance"},{"name":"finetune","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"flags","description":"Package flags is feature flags: ship it dark, then turn it on for who you pick."},{"name":"fleet","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"flow","description":"Package flow is Hanzo Flow: build an agent workflow on a visual canvas, run it, and read every run."},{"name":"framework","description":"Package framework is document types you define: describe a record once, then create, list, submit and cancel documents against it."},{"name":"functions","description":"Package functions is your serverless code: publish it, call it over HTTP, watch every run and what it cost."},{"name":"gateway","description":"Package gateway is live control of the policy your API applies to every incoming request: CORS, rate limits, cache TTL and allowed methods, changed without a redeploy."},{"name":"generate-text-to-speech-audio","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"generate-text-to-speech-audio-stream","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"git","description":"Package git is Git hosting for your org: create repos, clone, push, and see what they cost."},{"name":"gpus","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"guide","description":"Package guide is a step-by-step checklist that gets your business running on AI."},{"name":"health","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"help","description":"Package help is a support desk: customers file tickets, your team answers them."},{"name":"iam","description":"Package iam is Hanzo's identity provider: users, organizations, applications, and the OIDC/OAuth2 endpoints every Hanzo service authenticates against."},{"name":"images","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"index","description":"Package index is fast full-text search over your own data, typos forgiven."},{"name":"indexers","description":"Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds."},{"name":"ingress","description":"Package ingress is your front door: automatic TLS certificates and hostname routing to any backend, changed live."},{"name":"insights","description":"Package analytics is product analytics: send an event, read back who did what."},{"name":"install-patch","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"integrations","description":"Package integrations is how your org connects third-party accounts like Slack, and revokes them."},{"name":"k8s","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"kb","description":"Package knowledge is your team's wiki and your agents' memory, searchable by meaning."},{"name":"keys","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"kms","description":"Package kms is secret custody: your org's secrets sealed at rest, plus threshold signing."},{"name":"kv","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"legal","description":"Package legal is the paperwork your company needs, drafted, signed and filed."},{"name":"licensing"},{"name":"links","description":"Package link is the unified AI login manager's registry: the org+user-scoped record of WHICH provider accounts (Claude Max, ChatGPT Plus, a Hanzo API key, a raw provider key) a developer has signed into, ON WHICH MACHINES, with each account's latest usage snapshot."},{"name":"logs"},{"name":"machines","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"marketing","description":"Package marketing is lifecycle email: drip sequences that reach the right people."},{"name":"marketplace","description":"Package marketplace is the shop for tools and agents: browse, install into your project, publish your own free or priced."},{"name":"mcp","description":"Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers."},{"name":"meet","description":"Package meet is the virtual office: it decides who may join a room and mints the short-lived token that lets them in."},{"name":"memory","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"mesh","description":"Package zt mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint — overlay networks, their routers and mesh services — served as clean, org-scoped REST off the unified cloud binary and fronting the Hanzo Zero Trust controller (hanzoai/zt, an OpenZiti-based fabric)."},{"name":"messages","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"metrics"},{"name":"ml","description":"Package ml is model serving: deploy a model behind an endpoint and call it."},{"name":"models","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"mq","description":"Package mq is queue and stream admin for your org: create them, watch them drain, ack what you pulled."},{"name":"networks","description":"Package zt mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint — overlay networks, their routers and mesh services — served as clean, org-scoped REST off the unified cloud binary and fronting the Hanzo Zero Trust controller (hanzoai/zt, an OpenZiti-based fabric)."},{"name":"notify","description":"Package notify is transactional email and SMS, sent through your org's own provider credential."},{"name":"o11y","description":"Package o11y is your logs, metrics and traces: ship them in, query them, chart them."},{"name":"oracles","description":"Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds."},{"name":"org","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"orgs","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"payments","description":"Package commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog."},{"name":"pipelines","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"plans","description":"Package plan is the plan catalog: every tier you can buy, what it costs, and what it grants."},{"name":"platform","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"plugins","description":"Package plugin is what each host is running, and how to change it: enable, disable, reload, or pin a service to a version."},{"name":"prefs","description":"Package prefs is your own settings — theme, density, pinned nav — following you across every Hanzo app."},{"name":"pricing","description":"Package pricing is the price list: what every model, provider, GPU tier, tool and hosting plan costs."},{"name":"process-speech-to-text","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"projects","description":"Package projects is where your sites live: create one, deploy a build, roll back to any release."},{"name":"prompts","description":"Package prompts is your prompt library, versioned, so nothing changes silently."},{"name":"pubsub","description":"Package pubsub is your message bus: publish, subscribe, and durable streams your apps read at their own pace."},{"name":"query","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"query_multiple","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"rag","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"referrals","description":"Package referrals is referral ATTRIBUTION: who referred whom, and whether that referee ever became a real customer."},{"name":"registry","description":"Package registry is your container and package registry: push images, pull them back, see what you store."},{"name":"releases","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"replay","description":"Package analytics is product analytics: send an event, read back who did what."},{"name":"rerank","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"research","description":"Package research is every experiment you have ever run, kept and comparable."},{"name":"responses","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"risk","description":"Package risk is HANZO RISK's model plane: the per-organisation feature surface and the per-organisation models trained on it."},{"name":"router","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"run","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"runner","description":"Package platform is Hanzo PaaS: deploy containers to your own tenant namespace — builds, releases, environments, logs, custom domains."},{"name":"s3","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"sandboxes","description":"Package sandbox is the ONE compute primitive: a sandbox is a gVisor pod that runs somebody else's code, and every lifetime is the same object."},{"name":"sbom","description":"Package sbom is what is inside a container image: every component, resolvable by digest or image ref."},{"name":"scrape","description":"Package websearch is a web search and a page fetch your agents can call."},{"name":"search","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"security","description":"Package security is secret scanning for your code: submit sources, get findings, masked never raw."},{"name":"sentry","description":"Package o11y is your logs, metrics and traces: ship them in, query them, chart them."},{"name":"settings","description":"Package settings is how an org configures each product it uses, secret fields included."},{"name":"share","description":"Package share is a public URL for a service on your own machine, and a list of what you have open."},{"name":"sites","description":"Package projects is where your sites live: create one, deploy a build, roll back to any release."},{"name":"skills","description":"Package skills is the skill catalogue an AI client reads to learn what it can do."},{"name":"social","description":"Package social is posting to every social account you own, now or on a schedule."},{"name":"sql","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"store","description":"Package commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog."},{"name":"summary","description":"Package o11y is your logs, metrics and traces: ship them in, query them, chart them."},{"name":"sync","description":"Package sync is data sync: link two endpoints and keep them in step, on a webhook, on a schedule, or on demand."},{"name":"tasks","description":"Package tasks is Hanzo Tasks: durable workflows that survive a crash, with every run visible and replayable."},{"name":"team","description":"Package team is your org's shared workspace: documents edited together, files, seats, and agents as teammates."},{"name":"templates","description":"Package templates is a gallery of starter kits you can deploy as they come."},{"name":"tools","description":"Package tools is everything your org can call, in one list: connector actions, functions, agents, skills and your own MCP servers."},{"name":"traces"},{"name":"tracker","description":"Package tracker is your org's issue tracker: projects, issues, and the filters to find them."},{"name":"traffic","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"translate","description":"Package translate is text in, the same text out in the language you asked for."},{"name":"upload","description":"Package exec is the code interpreter: run a snippet in a sandbox, and move files in and out of the session that sandbox IS."},{"name":"usage","description":"Package usage is what your org ran and what it cost, broken down per account."},{"name":"validators","description":"Package validators is one-click validator onboarding: prove your Genesis NFT, get a node provisioned, queue its registration."},{"name":"vector","description":"Package provisioning is one-click data add-ons: a SQL, key-value, document, vector, search or object store, wired straight into your app."},{"name":"videos","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"vpcs","description":"Package do is the org-scoped private-network surface — /v1/vpcs and /v1/balancers — carved out of Hanzo's OWN house DigitalOcean account."},{"name":"wallets","description":"Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them."},{"name":"webhooks","description":"Package webhooks is how your app hears about events: register an endpoint, pick the events, get each one delivered and signed."},{"name":"websearch","description":"Package websearch is a web search and a page fetch your agents can call."},{"name":"wecom-bot","description":"Package ai is Hanzo AI — the model API on /v1 (/v1/chat/completions, /v1/messages, /v1/models and the rest of hanzoai/ai's surface) — mounted into a cloud binary with the money, ingest and telemetry callbacks cloud BUILDS but cannot INSTALL."},{"name":"world","description":"Package world is a live news feed filtered to what your project cares about."},{"name":"x402","description":"Package x402 is pay-per-request over HTTP 402: quote a price, take the payment, serve the resource."}],"paths":{"/":{"get":{"operationId":"get","summary":"Browse your org's repositories","description":"The repository list for the signed-in caller's org — each repo with its description, default branch, size and last update. SIGNED OUT it renders the public explore page instead of refusing, because most Hanzo repos are open source and the open face is the default one; signed in, the caller's own org shows its private repositories alongside its public ones. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","x-app":"git"}},"/.well-known/agent-skills/index.json":{"get":{"operationId":"get_.well-known_agent-skills_index.json","summary":"The brand's master catalogue of agent skills","description":"The Agent Skills Discovery catalogue an AI client reads to learn what this deployment can do: every skill, with the sha256 of the SKILL.md that is actually served for it, so a client can verify the document it then fetches.\n\nThe catalogue is GENERATED from the per-service OpenAPI specs and embedded in the binary; this route serves those bytes verbatim and never re-derives them, which is what makes the digests hold. Which brand's catalogue you get is decided per request from the Host — api.hanzo.ai answers the Hanzo catalogue, api.lux.network the Lux one, api.zoo.ngo the Zoo one — never one brand's skills on another's surface; a Host whose brand has no embedded catalogue falls back to the deployment brand, then to hanzo.\n\nPublic by design: the discovery surface carries no secrets, so there is no bearer and no tenant scope. Answers `Cache-Control: public, max-age=300`, and a catalogue that is not embedded is `{\"error\":…}` at 404.","x-app":"skills"}},"/.well-known/agent-skills/{skill}/SKILL.md":{"get":{"operationId":"get_.well-known_agent-skills_by_skill_skill.md","summary":"One skill's document, as markdown","description":"Serves a single agent skill's SKILL.md as text/markdown — the instructions a client follows once index.json has told it the skill exists, and byte for byte the document that index.json's sha256 for that skill was computed over.\n\nThe skill segment is a flat, service-prefixed id (`ai_models`): one path segment with no separators, so a request can never address anything outside the embedded catalogue. An id of any other shape, or a skill the serving brand does not carry, is `{\"error\":…}` at 404 — the same answer, so a probe learns nothing about which is which.\n\nBrand resolution and caching are index.json's: the Host picks the catalogue, and the response is `Cache-Control: public, max-age=300`. Public — no bearer, no tenant scope.","parameters":[{"name":"skill","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"skills"}},"/.well-known/jwks":{"get":{"operationId":"get_.well-known_jwks","summary":"Publishes the public keys that verify the tokens issued here — the one URL you point a service at so it can check a token itself, offline, without calling back and without holding any secret of ours.","description":"Publishes the public keys that verify the tokens issued here — the\none URL you point a service at so it can check a token itself, offline, without\ncalling back and without holding any secret of ours.\n\nKeys appear here before they start signing and stay after they stop, so a\nrotation never leaves a live token unverifiable. Nothing private is ever\npublished.","x-app":"iam"}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"get_.well-known_oauth-authorization-server","summary":"Returns the OpenID Connect discovery document — the one URL you point a standards-compliant client at so it can find every other endpoint on its own, instead of you configuring them by hand.","description":"Returns the OpenID Connect discovery document — the one URL you\npoint a standards-compliant client at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins the issuer never sees it change.","x-app":"iam"}},"/.well-known/openid-configuration":{"get":{"operationId":"get_.well-known_openid-configuration","summary":"Returns the OpenID Connect discovery document — the one URL you point a standards-compliant client at so it can find every other endpoint on its own, instead of you configuring them by hand.","description":"Returns the OpenID Connect discovery document — the one URL you\npoint a standards-compliant client at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins the issuer never sees it change.","x-app":"iam"}},"/_/commerce/healthz":{"get":{"operationId":"get___commerce_healthz","summary":"Answers ok whenever the commerce subsystem is mounted.","description":"Answers ok whenever the commerce subsystem is mounted. It is registered\nbefore the module embed boots, so it keeps answering even when the embed\nfailed and every business route serves the fail-closed 503 — which is the\npoint: it reports that the process is reachable, never that the money plane\nis healthy. Unauthenticated, and under /_ so the ingress withholds it\npublicly.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/liveness"}}},"description":"ok"}},"x-app":"commerce"}},"/_/commerce/providers":{"get":{"operationId":"get___commerce_providers","summary":"List the payment providers configured for your own tenant","description":"Returns the caller's own tenant row projected to a public view with the KMS paths stripped, so a provider's name and enabled flag are visible and its credential location never is. The tenant is derived from the IAM owner claim and from nothing else — there is no tenant parameter to supply, so a cross-tenant read is not expressible. A tenant admin or a platform admin may call it; a plain authenticated user is refused 403 and an anonymous one 401. A caller whose owner claim has no tenant row gets a 404 byte-identical to the one a cross-tenant probe would get.","x-app":"commerce"}},"/_/commerce/providers/{name}":{"put":{"operationId":"put___commerce_providers_by_name","summary":"Turn one payment rail on or off for your own tenant","description":"Sets the enabled flag on ONE named rail — square, stripe, braintree, plaid, wire or crypto — for the tenant the caller's IAM owner claim resolves to, and answers the tenant name with the same name-and-enabled projection the provider list serves, so a console can render the result without a second read. One rail per call is a correctness requirement rather than a taste: a provider record also carries the KMS path naming where its credentials live, the list read deliberately strips that path, and a PUT that replaced the whole list from what a UI can see would write every rail back with an EMPTY path and silently disconnect each one from its credentials. Naming a single rail copies every other record forward byte for byte. The body must be an explicit enabled true or false — an absent field is 400, never a disable — and a name outside the known set is 400 that lists the names that mean something downstream, because a typo which reports success is worse than one that does not. A tenant admin or a platform admin may call it; anonymous is 401 and a signed-in non-admin 403. No tenant id is accepted from the client, so a cross-tenant write is not expressible, and a caller with no tenant row gets a 404 byte-identical to the one a probe for someone else's tenant would get. Setting a rail to the state it already holds succeeds and changes nothing; a rail the tenant has never carried is appended, which is how one is turned on for the first time — and it is appended with no credential path, so enabling a rail here does not by itself connect it to any credentials.","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/_/commerce/tenants":{"post":{"operationId":"post___commerce_tenants","summary":"Create a checkout tenant: hostnames, brand, IAM, IDV, providers and backend","description":"Registers a new hosted-checkout tenant so its hostnames resolve to their own branding, identity config, payment providers and broker backend. PLATFORM admin only — the reserved admin org's owner claim; an org owner with the org-level admin bit is refused 403 and an anonymous caller 401, so a tenant can never be minted from inside a tenant. A duplicate name is 409 and a malformed hostname 400. The response echoes only the identity and timestamps, never the provider records the caller just sent, and the mutation is audited by hash rather than by content so a credential that slips into the body is not replayable from the log.","x-app":"commerce"}},"/collaborator":{"get":{"operationId":"get_collaborator","summary":"Open the live collaborative-editing socket","description":"Upgrades to the hocuspocus WebSocket the Team editor syncs its Y.js documents over: binary frames of document name, message type and payload, with ONE socket multiplexing every document a tab has open. The server is a relay and an ordered update log, not a CRDT engine — it replays the log to each joining peer and broadcasts every update to the rest, which converges because Y.js updates are commutative and idempotent. There is no body; the response is a protocol upgrade.\n\nIT SITS OUTSIDE /v1 ON PURPOSE. The client derives both collaborator lanes from one configured URL — this socket at its root, the markup-snapshot RPC one segment in — so the path is fixed by the editor library's contract rather than chosen by this service.\n\nAUTH IS IN-BAND, PER DOCUMENT, NOT ON THE UPGRADE. The handshake gates only on browser Origin (403 outside the team surfaces; no Origin at all is admitted, which is what a non-browser sends), and then the first frame for a document must be an Auth message carrying the same session or workspace token every other team route verifies — a browser WebSocket cannot set an Authorization header, which is why the token rides inside the protocol. Anything else on an unauthenticated document is answered with one permission denial and nothing further.\n\nEvery document is authorized on its own: the document's workspace must be the token's workspace when the token pins one, and the caller must be a member of it. A mismatch, an unknown workspace and a non-member deny alike with \"document not found\". Rooms are keyed by org and workspace and the persisted log's key embeds both, so a foreign document id can neither join a room nor read a blob.\n\nThe server pings every twenty seconds and drops a socket silent for sixty, so a backgrounded tab — whose JS timers are throttled but whose network stack still auto-pongs — stays connected instead of dying into a reconnect loop.","x-app":"team"}},"/collaborator/rpc/{documentId}":{"post":{"operationId":"post_collaborator_rpc_by_documentid","summary":"CollabRPC is the collaborative-markup snapshot plane the Team front's editor speaks: createContent stores a document field's markup at a fresh, immutable blob ref and returns it, updateContent stores a new snapshot and answers nothing, and getContent reads back the exact snapshot a ref names.","description":"CollabRPC is the collaborative-markup snapshot plane the Team front's editor\nspeaks: createContent stores a document field's markup at a fresh, immutable\nblob ref and returns it, updateContent stores a new snapshot and answers\nnothing, and getContent reads back the exact snapshot a ref names.\n\ncreateContent ALSO seeds the live-editing update log from the front-supplied\nY.js update, so a dialog-authored description is visible in the collaborative\neditor — which replays that log — and not only in snapshot reads.\nupdateContent never touches that log: peers may be live-editing the document,\nand their edits are not this call's to overwrite.\n\nEvery call is scoped to the caller's VERIFIED session or workspace token: the\ndocumentId's workspace must be the token's workspace when the token names one,\nand the caller must be a member of it. An unknown workspace, another tenant's\nworkspace and a workspace the caller is not in all answer the same 404, so a\nprobe learns nothing about what exists.","parameters":[{"name":"documentId","in":"path","required":true,"description":"DocumentID addresses the document field, as\n\"\u003cworkspaceUuid\u003e|\u003cobjectClass\u003e|\u003cobjectId\u003e|\u003cobjectAttr\u003e\" — the\ncollaborator-client encodeDocumentId shape, from the path.","schema":{"type":"string"},"example":"6579…|tracker:class:Issue|issue-1|description"}],"requestBody":{"content":{"application/json":{"example":{"documentId":"6579…|tracker:class:Issue|issue-1|description","method":"getContent","payload":{"source":"issue-1-description-1730000000000"}},"schema":{"$ref":"#/components/schemas/collabRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/collabResult"}}},"description":"ok"}},"x-app":"team"}},"/explore":{"get":{"operationId":"get_explore","summary":"Discover public repositories across every org","description":"The open, unauthenticated face of the git host: every PUBLIC repository in the fleet, org-qualified, so a project can be found and cloned with no account at all — signing in is for private repos and for writes. Repositories live in per-org stores with no global index, so this unions each org's public rows and is bounded to a fixed number of stores per request, keeping discovery quick however many orgs exist. A fleet with no orgs yet is an empty page, not an error. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","x-app":"git"}},"/git":{"get":{"operationId":"get_git","summary":"Browse your org's repositories","description":"The repository list for the signed-in caller's org — each repo with its description, default branch, size and last update. SIGNED OUT it renders the public explore page instead of refusing, because most Hanzo repos are open source and the open face is the default one; signed in, the caller's own org shows its private repositories alongside its public ones. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","x-app":"git"}},"/git/explore":{"get":{"operationId":"get_git_explore","summary":"Discover public repositories across every org","description":"The open, unauthenticated face of the git host: every PUBLIC repository in the fleet, org-qualified, so a project can be found and cloned with no account at all — signing in is for private repos and for writes. Repositories live in per-org stores with no global index, so this unions each org's public rows and is bounded to a fixed number of stores per request, keeping discovery quick however many orgs exist. A fleet with no orgs yet is an empty page, not an error. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","x-app":"git"}},"/git/{org}/{repo}":{"get":{"operationId":"get_git_by_org_by_repo","summary":"Open a repository's home page","description":"A repository at a glance: its branches, the tree at the tip, its most recent commits, its README rendered, and the HTTPS and SSH clone URLs. `?ref=` selects a branch, tag or commit; the default branch is used when it is omitted. A repository with no commits yet renders its clone instructions rather than an error, which is what a caller who has just created one needs to see. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/git/{org}/{repo}/blob/{wildcard1}":{"get":{"operationId":"get_git_by_org_by_repo_blob_by_wildcard1","summary":"View a file in a repository","description":"One file's contents at one revision, with its size and line count. A BINARY file is reported as binary rather than dumped into the page. The path after /blob/ is the file and `?ref=` selects the branch, tag or commit. An unknown ref or a path that is not a file in it is 404. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/git/{org}/{repo}/commits":{"get":{"operationId":"get_git_by_org_by_repo_commits","summary":"Read a repository's commit log","description":"The hundred most recent commits on one ref, each with its author, message and date. `?ref=` selects the branch, tag or commit, defaulting to the repository's default branch; an unknown one is 404. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/git/{org}/{repo}/tree/{wildcard1}":{"get":{"operationId":"get_git_by_org_by_repo_tree_by_wildcard1","summary":"Browse a directory inside a repository","description":"The contents of one directory at one revision, with breadcrumbs back up and links onward into subdirectories and files. The path after /tree/ is the directory and `?ref=` selects the branch, tag or commit, defaulting to the repository's own default branch. An unknown ref is 404, as is a repository with no commits. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served on every host, which is how the console embeds the git browser under /git.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/meet":{"delete":{"operationId":"delete_meet","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"meet"},"get":{"operationId":"get_meet","summary":"The call client","description":"Serves the application shell on GET, which is the entry point a browser loads before it calls anything under /v1/meet/.\n\nThis is the call client itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","x-app":"meet"},"patch":{"operationId":"patch_meet","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"meet"},"post":{"operationId":"post_meet","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"meet"},"put":{"operationId":"put_meet","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"meet"}},"/meet/{wildcard1}":{"delete":{"operationId":"delete_meet_by_wildcard1","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"meet"},"get":{"operationId":"get_meet_by_wildcard1","summary":"The call client's assets and client-side routes","description":"Serves the static assets on GET, and returns the application shell for any path that is not a file — client-side routing means a deep link is a shell load, not a 404.\n\nThe one exception is /meet/assets/, which holds only content-addressed build output: a name that is not there is a purged chunk, never a route, and answers 404. Everywhere else a path that looks like a missing file answers 200 with the shell, so read the content type rather than the status when a resource seems to be missing.\n\nA bundle that was never built answers 503 under its own name on every path, which is a failed deploy rather than a missing page.\n\nThis is the call client itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"meet"},"patch":{"operationId":"patch_meet_by_wildcard1","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"meet"},"post":{"operationId":"post_meet_by_wildcard1","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"meet"},"put":{"operationId":"put_meet_by_wildcard1","summary":"Not served by the call client","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"meet"}},"/tasks":{"delete":{"operationId":"delete_tasks","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tasks"},"get":{"operationId":"get_tasks","summary":"The tasks console","description":"Serves the application shell on GET, which is the entry point a browser loads before it calls anything under /v1/tasks/.\n\nThis is the tasks console itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","x-app":"tasks"},"patch":{"operationId":"patch_tasks","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tasks"},"post":{"operationId":"post_tasks","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tasks"},"put":{"operationId":"put_tasks","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tasks"}},"/tasks/{wildcard1}":{"delete":{"operationId":"delete_tasks_by_wildcard1","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"get":{"operationId":"get_tasks_by_wildcard1","summary":"The tasks console's assets and client-side routes","description":"Serves the static assets on GET, and returns the application shell for any path that is not a file — client-side routing means a deep link is a shell load, not a 404.\n\nThe one exception is /tasks/assets/, which holds only content-addressed build output: a name that is not there is a purged chunk, never a route, and answers 404. Everywhere else a path that looks like a missing file answers 200 with the shell, so read the content type rather than the status when a resource seems to be missing.\n\nA bundle that was never built answers 503 under its own name on every path, which is a failed deploy rather than a missing page.\n\nThis is the tasks console itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"patch":{"operationId":"patch_tasks_by_wildcard1","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"post":{"operationId":"post_tasks_by_wildcard1","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"put":{"operationId":"put_tasks_by_wildcard1","summary":"Not served by the tasks console","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"}},"/tracker":{"delete":{"operationId":"delete_tracker","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tracker"},"get":{"operationId":"get_tracker","summary":"The tracker board","description":"Serves the application shell on GET, which is the entry point a browser loads before it calls anything under /v1/tracker/.\n\nThis is the tracker board itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","x-app":"tracker"},"patch":{"operationId":"patch_tracker","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tracker"},"post":{"operationId":"post_tracker","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tracker"},"put":{"operationId":"put_tracker","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","x-app":"tracker"}},"/tracker/{wildcard1}":{"delete":{"operationId":"delete_tracker_by_wildcard1","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"},"get":{"operationId":"get_tracker_by_wildcard1","summary":"The tracker board's assets and client-side routes","description":"Serves the static assets on GET, and returns the application shell for any path that is not a file — client-side routing means a deep link is a shell load, not a 404.\n\nThe one exception is /tracker/assets/, which holds only content-addressed build output: a name that is not there is a purged chunk, never a route, and answers 404. Everywhere else a path that looks like a missing file answers 200 with the shell, so read the content type rather than the status when a resource seems to be missing.\n\nA bundle that was never built answers 503 under its own name on every path, which is a failed deploy rather than a missing page.\n\nThis is the tracker board itself — HTML and hashed assets, not an API. Only GET and HEAD are served; every other method is refused 405. Hashed assets are returned immutable and cached for a year, while the shell is always revalidated, so a new deployment replaces a stale one on the next request.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"},"patch":{"operationId":"patch_tracker_by_wildcard1","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"},"post":{"operationId":"post_tracker_by_wildcard1","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"},"put":{"operationId":"put_tracker_by_wildcard1","summary":"Not served by the tracker board","description":"Published because this address accepts every method, but a static bundle has no writes: the request is refused 405 and nothing is read or changed.","parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"}},"/v1/admin/affiliates":{"get":{"operationId":"get_v1_admin_affiliates","summary":"Lists every affiliate across the fleet with its ORG exposed, plus a fleet summary of lifetime accrued, still-pending and paid commission in integer cents.","description":"Lists every affiliate across the fleet with its ORG exposed, plus a\nfleet summary of lifetime accrued, still-pending and paid commission in\ninteger cents.\n\nPLATFORM SUDO ONLY, and a non-admin is refused outright. This is the\ncross-tenant view and it names orgs — exactly what the partner-facing\nleaderboard refuses to do. There is deliberately no org-scoped variant of this\nread; a partner sees its own standing through its own dashboard. Bounded per\nrequest.","tags":["admin"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. Absent or non-positive means the default of\n500; anything above 1000 is clamped to 1000.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/directoryOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/affiliates/sweep":{"post":{"operationId":"post_v1_admin_affiliates_sweep","summary":"Runs the accrual: for each referred org it reads that org's metered spend for the current period and accrues commission to every affiliate up its referral chain, then answers how many sources were swept and how many NEW accruals landed.","description":"Runs the accrual: for each referred org it reads that org's metered\nspend for the current period and accrues commission to every affiliate up its\nreferral chain, then answers how many sources were swept and how many NEW\naccruals landed.\n\nThis is the cron path, and it is LATCHED at most once per affiliate, source\norg and period — so re-running it inside the same period accrues nothing\nfurther. Safe to retry, and safe to run by hand beside the schedule.\n\nCommission is a rate of Hanzo's MARGIN on that spend, never of the customer's\ngross bill, so every level's share summed over one source event stays within\nthe margin actually earned and the customer's charge is untouched. Nothing\naccrues past the third upline level, and only an APPROVED affiliate accrues at\nall.\n\nThe same spend read drives the OSS author royalty — one read, both programs —\nso the answer reports royalties accrued alongside. PLATFORM SUDO ONLY. Bounded\nper run; a source whose spend cannot be read is skipped and picked up next\ntime, never half-accrued.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accrualsOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/affiliates/{id}/approve":{"post":{"operationId":"post_v1_admin_affiliates_by_id_approve","summary":"Approves an affiliate and MINTS its referral code — the moment the partner has a working share link and starts accruing.","description":"Approves an affiliate and MINTS its referral code — the moment\nthe partner has a working share link and starts accruing.\n\nThe code is taken from the body if one is given, else the vanity code the\napplicant requested, else a slug derived for them. Codes are ONE global\nnamespace, so a taken code is a 409 and nothing is approved. The minted code\nis also mirrored as a link row so click tracking is uniform across every code\nthe affiliate holds; that mirror is best-effort and its failure never fails\nthe approval.\n\nApproval is what makes an affiliate eligible: before it, attribution against\nits code does not resolve and no sweep accrues to it. PLATFORM SUDO ONLY.\nAudited.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the affiliate to approve, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/approval"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/affiliates/{id}/payout":{"post":{"operationId":"post_v1_admin_affiliates_by_id_payout","summary":"Pays out accrued commission and answers the payout row with the affiliate's updated balances.","description":"Pays out accrued commission and answers the payout row with the\naffiliate's updated balances.\n\nThe amount is reserved atomically against the affiliate's PENDING commission —\naccrued minus paid — so a payout can never exceed what is owed. The METHOD\ndecides whether money actually moves: `credits` issues a commerce grant into\nthe affiliate ORG's own wallet, tagged so the ledger can tell an affiliate\npayout apart from an admin or referral grant; every other method — wire,\npaypal and the rest — is RECORD-ONLY: the payout row and the balances move,\nthe cash is disbursed out of band.\n\nThe amount is integer cents and must be positive. PLATFORM SUDO ONLY.\nAudited.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the affiliate to pay, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"amountCents":1200,"method":"credits","reference":"ledger-1"},"schema":{"$ref":"#/components/schemas/disbursal"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/payoutOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/affiliates/{id}/rate":{"post":{"operationId":"post_v1_admin_affiliates_by_id_rate","summary":"Sets one affiliate's DIRECT commission rate, in basis points of Hanzo's margin.","description":"Sets one affiliate's DIRECT commission rate, in basis points of\nHanzo's margin.\n\nThe rate is CAPPED so that the direct rate plus the platform-wide second- and\nthird-level rates can never exceed the whole margin — the structural guarantee\nthat everything paid on one source event stays inside the margin actually\nearned. The cap is resolved from the rates in force at the moment of the call\nand quoted in the refusal, because those switches move; a hardcoded bound\nwould start lying the moment somebody edits the schedule.\n\nOnly the direct level is per-affiliate. The second and third levels are\nplatform switches and are not settable here. The change applies to FUTURE\naccruals — commission already latched for a period is not recomputed. PLATFORM\nSUDO ONLY. Audited.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the affiliate whose direct rate moves, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"rateBps":2500},"schema":{"$ref":"#/components/schemas/rateSet"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/affiliates/{id}/suspend":{"post":{"operationId":"post_v1_admin_affiliates_by_id_suspend","summary":"Suspends an affiliate: it stops accruing on the next sweep, and its code stops resolving for new attributions.","description":"Suspends an affiliate: it stops accruing on the next sweep, and\nits code stops resolving for new attributions.\n\nIt CLAWS NOTHING BACK. Commission already accrued stays accrued and stays\npayable, and existing attribution edges are left standing — suspension ends\nearning, it does not unwind history. PLATFORM SUDO ONLY. Audited.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the affiliate's server-minted handle, \"aff_\"-prefixed.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/aimetrics":{"get":{"operationId":"adminAIMetrics","summary":"Is the fleet AI board: LLM generations over gen_ai spans (count, cost, avg/p95 latency, per-model), per-model usage from the live cloud_usage ledger, and the eval plane (traces, scores, score names, runs, and the average-score trend).","description":"Is the fleet AI board: LLM generations over gen_ai spans (count, cost,\navg/p95 latency, per-model), per-model usage from the live cloud_usage ledger, and\nthe eval plane (traces, scores, score names, runs, and the average-score trend).\n\nEvery signal degrades INDEPENDENTLY — a table that is absent or errors contributes its\nzero value and the read still succeeds. Generation latency is a SEPARATE query from\ngenerations and cost on purpose: a duration/attribute mismatch there must not zero\nthe two numbers that did read.","tags":["admin"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the lower time bound: 24h, 7d or 30d. Anything else reads as the\nboard's own default.","schema":{"type":"string"},"example":"7d"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"end":"2026-07-27T00:00:00Z","evalRuns":[],"o11yAiModels":[],"range":"7d","scoreNames":[],"scoreSeries":[],"start":"2026-07-20T00:00:00Z","topModels":[]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/aimetricsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/analytics":{"get":{"operationId":"adminAnalytics","summary":"Is the SaaS product-analytics board over the caller's tenant window: active customers, new and churned, retention, MRR, ARPU, the usage trend and the top customers by spend — every number folded from the commerce ledger, not sampled.","description":"Is the SaaS product-analytics board over the caller's tenant window: active\ncustomers, new and churned, retention, MRR, ARPU, the usage trend and the top\ncustomers by spend — every number folded from the commerce ledger, not sampled.\n\nThe window is the caller's, not the fleet's: a SuperAdmin gets every org, a\nwhite-label admin only their own subtree (core.ScopedOrgs, the one scope predicate).\n\nsources[] carries each upstream's freshness so a partial read is VISIBLE rather than\nsilently low: a ledger that answered for only some orgs marks commerce-ledger degraded\ninstead of publishing an undercount as healthy.","tags":["admin"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the lower time bound: 24h, 7d or 30d. Anything else reads as the\nboard's own default.","schema":{"type":"string"},"example":"30d"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"generatedAt":"2026-07-27T00:00:00Z","interval":"day","range":"30d","sources":[{"lastSync":"2026-07-27T00:00:00Z","name":"iam","ok":true,"rows":2}]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/analyticsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/applications":{"get":{"operationId":"adminApplications","summary":"Lists IAM applications for one owner org, forwarded VERBATIM from IAM's get-applications.","description":"Lists IAM applications for one owner org, forwarded VERBATIM from IAM's\nget-applications. These are the platform's OIDC clients — the console reads clientId\noff each row.","tags":["admin"],"parameters":[{"name":"owner","in":"query","required":false,"description":"Owner is the org whose rows to read. Defaults to the admin org, which owns the\nplatform's roles and applications.","schema":{"type":"string"},"example":"admin"},{"name":"p","in":"query","required":false,"description":"Page is the 1-based page number. Forwarded only when set — IAM applies its own\ndefault otherwise.","schema":{"type":"string"},"example":"1"},{"name":"pageSize","in":"query","required":false,"description":"PageSize is rows per page. Forwarded only when set.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"clientId":"cid","name":"hanzo-cloud","owner":"admin"}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/iamRowsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/audit":{"get":{"operationId":"adminAudit","summary":"Reads cloud's tamper-evident audit trail, newest first, with the chain's live integrity attached so a listing can be badged as verified.","description":"Reads cloud's tamper-evident audit trail, newest first, with the chain's live\nintegrity attached so a listing can be badged as verified.\n\nWhen cloud has no local store configured it falls back to forwarding IAM's own\nget-records trail verbatim — a DIFFERENT trail, federated so the endpoint never\nregresses to an empty list. Those rows carry no integrity of ours, so the field is\nnull there.","tags":["admin"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org restricts the trail to one tenant.","schema":{"type":"string"},"example":"acme"},{"name":"sub","in":"query","required":false,"description":"Sub restricts it to one actor (the validated subject that made the request).","schema":{"type":"string"}},{"name":"action","in":"query","required":false,"description":"Action restricts it to one action name, e.g. \"admin.waitlist.grant\".","schema":{"type":"string"},"example":"admin.waitlist.grant"},{"name":"resource","in":"query","required":false,"description":"Resource restricts it to one resource kind, e.g. \"credit-grant\".","schema":{"type":"string"}},{"name":"resourceId","in":"query","required":false,"description":"ResourceID restricts it to one resource instance.","schema":{"type":"string"}},{"name":"result","in":"query","required":false,"description":"Result restricts it to \"success\" or \"error\".","schema":{"type":"string"}},{"name":"since","in":"query","required":false,"description":"Since is the inclusive lower time bound, RFC3339. An unparseable value is\nignored rather than refused — one malformed filter must not hide the trail.","schema":{"type":"string"},"example":"2026-07-01T00:00:00Z"},{"name":"until","in":"query","required":false,"description":"Until is the upper time bound, RFC3339, with the same tolerance.","schema":{"type":"string"}},{"name":"pageSize","in":"query","required":false,"description":"PageSize is rows per page, default 100.","schema":{"type":"string"},"example":"50"},{"name":"p","in":"query","required":false,"description":"Page is the 1-based page number, driving the offset.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"action":"admin.waitlist.grant","org":"acme","resource":"waitlist","result":"success","seq":41,"sub":"z@hanzo.ai","ts":"2026-07-26T18:00:00Z"}],"integrity":{"brokenAt":-1,"count":42,"headHash":"9f2c","ok":true},"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/RecordsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/audit/verify":{"get":{"operationId":"adminAuditVerify","summary":"Walks the WHOLE hash chain and reports whether it is intact: how many records were checked, the head hash to pin externally against tail-truncation, and — when the chain is broken — the seq of the first bad record and why.","description":"Walks the WHOLE hash chain and reports whether it is intact: how many records\nwere checked, the head hash to pin externally against tail-truncation, and — when the\nchain is broken — the seq of the first bad record and why.\n\nbrokenAt is -1 exactly when ok is true. An unconfigured store is an honest failure\nhere rather than a fabricated pass.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"brokenAt":-1,"count":42,"headHash":"9f2c","ok":true},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/VerifyOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/authors":{"get":{"operationId":"get_v1_admin_authors","summary":"Returns the platform's whole author program — every org's author record, not the caller's — with each one's repository and deploy counts and a fleet roll-up of the money accrued, pending and paid.","description":"Returns the platform's whole author program — every org's author\nrecord, not the caller's — with each one's repository and deploy counts and a\nfleet roll-up of the money accrued, pending and paid.\n\nIt is a Hanzo platform operation: a caller who is not a SuperAdmin gets 403. It\nexposes the owning org of each author, which no tenant-facing read ever does.","tags":["admin"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit bounds the page. 0 or less means the default of 500; anything above\n1000 is clamped to 1000.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminBook"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/authors/sweep":{"post":{"operationId":"post_v1_admin_authors_sweep","summary":"Runs the accrual sweep across every approved author: for each of their deploying orgs it computes this period's royalty from that org's metered spend and latches it at most once per period.","description":"Runs the accrual sweep across every approved author: for each of\ntheir deploying orgs it computes this period's royalty from that org's metered\nspend and latches it at most once per period.\n\nIt is an OVERRIDE, not the mechanism: a background scheduler runs the same sweep on\nits own, and every author's dashboard read sweeps their own accruals lazily. This\nis the manual trigger for an operator who needs the numbers now. It is idempotent —\nthe per-period latch means running it twice accrues nothing the second time.\n\nA Hanzo platform operation: a caller who is not a SuperAdmin gets 403.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authorSweepResult"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/authors/{id}/approve":{"post":{"operationId":"post_v1_admin_authors_by_id_approve","summary":"Admits one author to EARNING, optionally on a negotiated royalty share.","description":"Admits one author to EARNING, optionally on a negotiated royalty\nshare. Until this runs, a connected author accrues nothing however many verified\nrepositories they have.\n\nA share override applies from here forward only — existing ledger rows keep the\nshare that was applied when they were written, because a rate change must never\nrewrite what was already owed.\n\nA Hanzo platform operation: a caller who is not a SuperAdmin gets 403.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the author to approve, from the path.","schema":{"type":"string"},"example":"aut_1f…"}],"requestBody":{"content":{"application/json":{"example":{"id":"aut_1f…","shareBps":2500},"schema":{"$ref":"#/components/schemas/approveRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authorResult"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/authors/{id}/basis":{"get":{"operationId":"get_v1_admin_authors_by_id_basis","summary":"Returns the audit trail behind ONE author's royalty — the same payload the author reads at /v1/authors/basis, from the same builder, so support sees exactly what the author sees rather than a parallel view free to drift.","description":"Returns the audit trail behind ONE author's royalty — the same\npayload the author reads at /v1/authors/basis, from the same builder, so support\nsees exactly what the author sees rather than a parallel view free to drift.\n\nThe data object carries: id, status, asOf, shareBps, platformShareBps,\ndefaultShareBps, shareSource, settlesTo, method (the formula, the rate card and the\nsizing), ledger (every row with its spend, the share applied then, the platform's\nmatching half, whether it satisfies the formula and the attribution edges that\nexplain it), reconciliation (does the ledger foot to the balance) and window (what\nslice was actually returned) — plus period when one was requested.\n\nA Hanzo platform operation: a caller who is not a SuperAdmin gets 403.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the author record's handle, from the path.","schema":{"type":"string"},"example":"aut_1f…"},{"name":"period","in":"query","required":false,"description":"Period is the UTC accrual month, YYYY-MM. Empty means every period; any other\nshape is refused with 400.","schema":{"type":"string"},"example":"2026-07"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/basisResult"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/authors/{id}/payout":{"post":{"operationId":"post_v1_admin_authors_by_id_payout","summary":"Records a payout of accrued royalty and settles it.","description":"Records a payout of accrued royalty and settles it.\n\nThe amount is RESERVED against the author's pending royalty atomically before\nanything is paid, so a payout can never exceed what is owed even under concurrent\ncalls. An external author's payout is then BACKED against the platform reserve\nfund — a second, independent guard — and refused with 402 if the reserve cannot\ncover it, with the reservation voided. A \"credits\" method issues the actual wallet\ngrant after both guards; a cash method is record-only. A first-party (treasury)\nauthor's royalty is realized into Hanzo's own reserve instead of an external\nwallet, and every payout row discloses which of the three it was.\n\nA Hanzo platform operation: a caller who is not a SuperAdmin gets 403.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the author to pay, from the path.","schema":{"type":"string"},"example":"aut_1f…"}],"requestBody":{"content":{"application/json":{"example":{"amountCents":25000,"id":"aut_1f…","method":"credits"},"schema":{"$ref":"#/components/schemas/payoutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/payoutResult"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/authors/{id}/suspend":{"post":{"operationId":"post_v1_admin_authors_by_id_suspend","summary":"Stops one author earning.","description":"Stops one author earning. Their record, verified claims and ledger\nare untouched — suspension halts future accrual, it does not erase what was already\nowed, and it does not delete the evidence behind it.\n\nA Hanzo platform operation: a caller who is not a SuperAdmin gets 403.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the author record's handle, \"aut_\"-prefixed.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authorResult"}}},"description":"ok"}},"x-app":"authors"}},"/v1/admin/bases":{"get":{"operationId":"adminBases","summary":"Lists the tenant Base instances in the caller's window — a SuperAdmin sees every tenant's, anyone else only their own subtree's.","description":"Lists the tenant Base instances in the caller's window — a SuperAdmin sees every\ntenant's, anyone else only their own subtree's.\n\nThe scope is enforced TWICE: the upstream is asked for the caller's org, AND every row\nit returns is re-checked against the resolved scope. An upstream that ignored the\nfilter therefore degrades to empty, never to a cross-tenant leak.\n\nThe Base engine is being embedded into cloud; until it lands this proxies\nBASE_ADMIN_URL and, when that is unset, answers 200 with an empty list and msg saying\nso — the honest not-yet state, never fabricated instances.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"created":"2026-03-01T00:00:00Z","name":"acme-base","org":"acme","plan":"pro","region":"nyc3","status":"running","url":"https://acme.base.hanzo.ai"}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/basesOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/caps":{"get":{"operationId":"adminCaps","summary":"Reads one org's usage caps: its spend alerts plus the derived period spend, over/warn state and reset time.","description":"Reads one org's usage caps: its spend alerts plus the derived period\nspend, over/warn state and reset time.\n\nThese are the SAME rows the customer edits in their own console — a platform override\nand a customer budget are one model, not two.","tags":["admin"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org is the tenant to act on. Required for a SuperAdmin — they must name their\ntarget; ignored for a white-label admin, who always acts on their own org.","schema":{"type":"string"},"example":"acme"},{"name":"id","in":"query","required":false,"description":"ID is the cap to edit or remove, from the path. Unused by the list and create ops.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"enforce":true,"id":"cap_1","limitCents":100000,"over":false,"periodSpendCents":42000,"resetsAt":"2026-08-01T00:00:00Z","warn":false}],"msg":"","status":"ok","total":0},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"},"post":{"operationId":"adminCreateCap","summary":"Sets a usage cap on one org — a platform override of a customer budget, written to the customer's own spend-alert rows.","description":"Sets a usage cap on one org — a platform override of a customer budget,\nwritten to the customer's own spend-alert rows. The body is commerce's spend-alert\ncontract, forwarded byte-for-byte.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"enforce":true,"limitCents":100000,"org":"acme"},"schema":{"$ref":"#/components/schemas/capIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"enforce":true,"id":"cap_1","limitCents":100000},"msg":"","status":"ok","total":0},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/caps/{id}":{"delete":{"operationId":"adminDeleteCap","summary":"Removes one cap by id, lifting the ceiling entirely.","description":"Removes one cap by id, lifting the ceiling entirely.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cap to edit or remove, from the path. Unused by the list and create ops.","schema":{"type":"string"},"example":"cap_1"},{"name":"org","in":"query","required":false,"description":"Org is the tenant to act on. Required for a SuperAdmin — they must name their\ntarget; ignored for a white-label admin, who always acts on their own org.","schema":{"type":"string"},"example":"acme"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"ok":true},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"},"patch":{"operationId":"adminUpdateCap","summary":"Edits one cap by id — raise or lower the ceiling, flip enforcement.","description":"Edits one cap by id — raise or lower the ceiling, flip enforcement. The\nbody is commerce's spend-alert patch contract, forwarded byte-for-byte.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cap to edit or remove, from the path. Unused by the list and create ops.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"enforce":false,"limitCents":250000,"org":"acme"},"schema":{"$ref":"#/components/schemas/capIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"enforce":false,"id":"cap_1","limitCents":250000},"msg":"","status":"ok","total":0},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/catalog":{"get":{"operationId":"get_v1_admin_catalog","summary":"Returns the full model and provider catalog annotated with each entry's enablement state, for the operator console.","description":"Returns the full model and provider catalog annotated with\neach entry's enablement state, for the operator console. Nothing is hidden:\nthis is the admin's view of what exists and what is currently off, in beta or\ngenerally available. SuperAdmin only; every other caller is refused.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminCatalogOut"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/admin/catalog/models/{wildcard1}":{"patch":{"operationId":"patch_v1_admin_catalog_models_by_wildcard1","summary":"Turn one model off, into beta for named orgs, or generally available","description":"Sets one model's availability overlay — and the price overrides applied on top of the catalog — then answers the new effective overlay, so a console needs no second read. The model id is the whole remaining path, so a slashed id like `acme/some-model-1` addresses intact.\n\nSuperAdmin only; every other caller is 403, decided before the body is read. The overlay is PLATFORM-WIDE — this is the catalog every org prices against, not a per-org setting — and `betaOrgs` is what narrows a beta to named orgs.\n\nOnly the fields the patch names change; an entry with no overlay yet starts from the catalog default, which is enabled. `state` is the coherent tri-state setter (`off`|`beta`|`ga`) and the low-level `enabled`/`beta` flags are applied AFTER it, so they win where both are sent; anything else in `state` is 400. A field sent as an explicit `null` arrives indistinguishable from an absent one, so null does not clear anything.\n\nThe rule worth reading twice: a disabled entry that still carries beta orgs IS a beta — `{\"enabled\":false,\"betaOrgs\":[\"acme\"]}` leaves acme seeing the model. Only an explicit `off` (or `beta:false`) with an empty list is the absolute kill switch that a user's own beta opt-in can never re-open.\n\n`overrides` is an RFC 7386 merge patch, stored and echoed back verbatim; it must be a JSON object or null — an array or a scalar is refused — and is bounded in size and nesting depth. An uninitialised overlay store answers 503.","tags":["admin"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"pricing"}},"/v1/admin/catalog/providers/{name}":{"patch":{"operationId":"patch_v1_admin_catalog_providers_by_name","summary":"Sets one provider's availability overlay.","description":"Sets one provider's availability overlay.\n\nThe overlay decides whether a provider is off, in beta for named orgs, or\ngenerally available, and carries the price overrides applied on top of the\ncatalog. Only the fields the patch names change; every other field keeps the\nvalue it had, and an absent overlay starts from the catalog default (enabled).\nAnswers the new effective overlay, so a console needs no second read.\n\nSuperAdmin only.","tags":["admin"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the provider the overlay belongs to, from the URL.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/providerPatchIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Overlay"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/admin/compute":{"get":{"operationId":"adminCompute","summary":"Rolls the fleet's compute usage up to one row per (org, app, project, kind): how many distinct machines ran in the window, how many are still active, what they billed, and when each group last emitted an event.","description":"Rolls the fleet's compute usage up to one row per (org, app, project, kind):\nhow many distinct machines ran in the window, how many are still active, what they\nbilled, and when each group last emitted an event. The console folds these into its\norg → app → project tree.\n\nA machine counts as ACTIVE when its LATEST lifecycle event is not a terminal one\n(stop/destroy/terminate/delete/off/shutdown/expire and their past tenses) — the same\nfold the console applies, done in the warehouse so the count is over every machine and\nnot just the page.\n\nHonest-empty when the warehouse is not connected or hanzo.compute_usage is not\nprovisioned yet: an empty list, never a fabricated fleet.","tags":["admin"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Kind narrows to one workload class (bot | machine | cluster | nodepool |\ncontainer | function | …). An OPEN spectrum matched as a plain string, lowercased\nto the warehouse's convention; empty means every kind.","schema":{"type":"string"},"example":"bot"},{"name":"org","in":"query","required":false,"description":"Org narrows to one tenant. Empty means every tenant — this board is\ncross-tenant by nature.","schema":{"type":"string"},"example":"acme"},{"name":"range","in":"query","required":false,"description":"Range is the lower time bound: 24h, 7d or 30d. Anything else reads as 30d.","schema":{"type":"string"},"example":"7d"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"active":2,"app":"support","kind":"bot","lastTs":"2026-07-26T18:00:00Z","machines":4,"org":"acme","project":"default","spendCents":900}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/computeOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/customers":{"get":{"operationId":"adminCustomers","summary":"Lists every customer org at a glance, sorted by slug: owner email, plan, suspend status, member count, balance, month-to-date spend and MRR.","description":"Lists every customer org at a glance, sorted by slug: owner email, plan,\nsuspend status, member count, balance, month-to-date spend and MRR.\n\nEach row costs one IAM read plus the org's money reads, fanned out under a fixed\nconcurrency ceiling so a large fleet cannot stampede the upstreams. Every read is\nbest-effort per row: an upstream miss degrades THAT field to its honest zero rather\nthan failing the fleet.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"balanceCents":5000,"created":"2026-01-04T00:00:00Z","display":"Acme","lastActive":"2026-07-26T18:00:00Z","mrrCents":9900,"org":"acme","ownerEmail":"ada@acme.com","plan":"pro","spendCents":12500,"status":"active","users":7}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/CustomersOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/customers/{org}":{"get":{"operationId":"adminCustomer","summary":"Answers GET /v1/admin/customers/:org.","description":"Answers GET /v1/admin/customers/:org.","tags":["admin"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the tenant slug from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerDetailOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/customers/{org}/credit":{"post":{"operationId":"adminGrantCredit","summary":"Issues a staff credit grant to the org named in the path — a comp, refund or promo — through the ONE credit-write path core.ApplyGrant, which validates the amount against the per-grant cap, checks the org exists, moves the money and records the tamper-evident audit row.","description":"Issues a staff credit grant to the org named in the path — a comp, refund\nor promo — through the ONE credit-write path core.ApplyGrant, which validates the\namount against the per-grant cap, checks the org exists, moves the money and records\nthe tamper-evident audit row.\n\nThe credit lands on the account account.Payer resolves, NOT necessarily the org: name\na member of a pooled org and the pool is credited. The receipt echoes the subject so\nthe caller can see which.","tags":["admin"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the tenant to credit. Required.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"amountCents":5000,"currency":"usd","reason":"launch comp","source":"trial"},"schema":{"$ref":"#/components/schemas/GrantIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"balanceCents":10000,"balanceExact":"100.000000000000000000","currency":"usd","grantedCents":5000,"org":"acme","source":"trial","subject":"acme","transactionId":"tx_01J"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/GrantOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/customers/{org}/reactivate":{"post":{"operationId":"adminReactivateCustomer","summary":"Restores access for every member of the org, undoing a suspend.","description":"Restores access for every member of the org, undoing a suspend. It\nreports the same per-user breakdown.","tags":["admin"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the tenant slug from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"affected":["ada","bob"],"failed":[],"org":"acme","suspended":false},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/AccessOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/customers/{org}/suspend":{"post":{"operationId":"adminSuspendCustomer","summary":"Cuts off every member of the org: IAM refuses a forbidden user at login AND at token issuance, so a suspended customer can neither sign in nor mint a fresh token.","description":"Cuts off every member of the org: IAM refuses a forbidden user at\nlogin AND at token issuance, so a suspended customer can neither sign in nor mint a\nfresh token. Fully reversible with ReactivateCustomer.\n\nThe result names every user updated and every user that was NOT — a partial failure\nleaves the org in a mixed state and says so instead of reporting a clean success.","tags":["admin"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the tenant slug from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"affected":["ada","bob"],"failed":[],"org":"acme","suspended":true},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/AccessOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/enablement":{"get":{"operationId":"get_v1_admin_enablement","summary":"Returns every item an operator has set an enablement state on — its global state (off, beta or ga) and the orgs granted its beta.","description":"Returns every item an operator has set an enablement state on —\nits global state (off, beta or ga) and the orgs granted its beta. An item\nnobody has touched is absent, because an untouched item is generally\navailable; the console composes the candidate list from the live catalog.\nSuperAdmin only; every other caller is refused.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminEnablementBoard"}}},"description":"ok"}},"x-app":"pricing"},"put":{"operationId":"put_v1_admin_enablement","summary":"Sets one item's global enablement state — off, beta or ga — and optionally replaces the list of orgs granted its beta.","description":"Sets one item's global enablement state — off, beta or ga — and\noptionally replaces the list of orgs granted its beta. It is generic over\nkind, so the same call manages models, providers and product features through\nthe one registry. `off` is an absolute kill switch: a self-service opt-in can\nnever re-open it. SuperAdmin only; every other caller is refused.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"betaOrgs":["acme"],"id":"labs","kind":"feature","state":"beta"},"schema":{"$ref":"#/components/schemas/setEnablementBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminEnablementItem"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/admin/finance":{"get":{"operationId":"adminFinance","summary":"Answers GET /v1/admin/finance.","description":"Answers GET /v1/admin/finance. It reads the multi-vendor COGS from commerce\n/v1/costs, the DO promo-credit/burn-down treasury view, and the fleet commerce revenue,\nthen hands them to ComputeFinance. SuperAdmin only.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/finance/backfill":{"post":{"operationId":"adminFinanceBackfill","summary":"Carries ONE org's current commerce prepaid balance into the native finance wallet — the one-time cutover between the two ledgers.","description":"Carries ONE org's current commerce prepaid balance into the native finance\nwallet — the one-time cutover between the two ledgers.\n\nIt is IDEMPOTENT: the deposit uses the fixed ref \"backfill:\u003corg\u003e\", so re-running it\ncredits the wallet at most once. Safe to retry.\n\nThe pre-migration balance is read from the CO-RESIDENT commerce ledger, not over HTTP:\nthe admin HTTP client dials an unroutable in-process address and would read $0, and a\nphantom zero would silently carry nothing while reporting success. When commerce is\nnot co-resident this fails rather than migrating nothing.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"org":"acme"},"schema":{"$ref":"#/components/schemas/BackfillIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"entryId":"fe_01J","migratedCents":50000,"org":"acme"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/BackfillOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/flags":{"get":{"operationId":"adminFlags","summary":"Reads the platform control-plane board: every runtime launch/release switch (waitlist, public signup, subsystem activation, gateway limits, network ids) with its LIVE value and where that value came from — a stored definition or the compiled-in default.","description":"Reads the platform control-plane board: every runtime launch/release\nswitch (waitlist, public signup, subsystem activation, gateway limits, network ids)\nwith its LIVE value and where that value came from — a stored definition or the\ncompiled-in default.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"switches":[{"category":"launch","description":"Gate chat behind the waitlist","key":"waitlist.chat","label":"Chat waitlist","source":"default","value":true}]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/flagsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/flags/{key}":{"put":{"operationId":"adminSetFlag","summary":"Stores or overwrites ONE platform switch's definition and answers with the whole board as it now stands.","description":"Stores or overwrites ONE platform switch's definition and answers with the\nwhole board as it now stands. The flip is hot: this pod applies it immediately and\npeers converge within one evaluation TTL (15s by default), with no redeploy.\n\nThe body reaches the flag engine BYTE-FOR-BYTE — it is the engine's definition\nformat, not this layer's, so a field the engine understands and admin does not must\nstill arrive intact. setFlagIn names the two fields that matter for documentation; it\nis not a filter.\n\nThe write is recorded in the store's activity log against the caller's email.","tags":["admin"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the switch to write, taken from the path (e.g. \"waitlist.chat\").","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"active":true,"filters":{"groups":[{"properties":[],"rollout_percentage":100}]}},"schema":{"$ref":"#/components/schemas/setFlagIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"switches":[{"category":"launch","description":"Gate chat behind the waitlist","key":"waitlist.chat","label":"Chat waitlist","source":"stored","value":true}]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/flagsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/grants":{"get":{"operationId":"adminGrants","summary":"Reads the credit-grant ledger across ALL orgs, newest first — who granted what to whom, when, and from which money bucket.","description":"Reads the credit-grant ledger across ALL orgs, newest first — who granted what\nto whom, when, and from which money bucket.\n\nIt is a PROJECTION of the tamper-evident audit trail, not a second store: every grant\nis written there as action \"admin.customer.credit\", so this view cannot drift from\nwhat actually happened, and FAILED grants appear too.\n\nA deployment with no local audit store has no history to project, and says so with an\nempty list and a msg rather than an error.","tags":["admin"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org filters by the ACTOR's org (the staff org that issued the grant), which is\nrarely what a reader wants — the target org is a row field, not a filter.","schema":{"type":"string"}},{"name":"result","in":"query","required":false,"description":"Result filters by outcome: \"success\" or \"error\". Empty returns both, which is\nthe point of this view — a refused grant is as interesting as a granted one.","schema":{"type":"string"},"example":"success"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. Default 200.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"actor":"z@hanzo.ai","amountCents":5000,"createdAt":"2026-07-26T18:00:00Z","currency":"usd","org":"acme","reason":"launch comp","result":"success","source":"trial","transactionId":"tx_01J"}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/GrantsOut"}}},"description":"ok"}},"x-app":"admin"},"post":{"operationId":"adminIssueGrant","summary":"Issues a credit grant to any org from the operator Grants view, with the target named in the body.","description":"Issues a credit grant to any org from the operator Grants view, with the\ntarget named in the body. It funnels through the SAME core.ApplyGrant that\nPOST /v1/admin/customers/:org/credit uses, so there is exactly ONE credit-write path\nand one audit trail behind both.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"amountCents":5000,"currency":"usd","org":"acme","reason":"launch comp","source":"trial"},"schema":{"$ref":"#/components/schemas/GrantIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"balanceCents":10000,"balanceExact":"100.000000000000000000","currency":"usd","grantedCents":5000,"org":"acme","source":"trial","subject":"acme","transactionId":"tx_01J"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/GrantOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra":{"get":{"operationId":"adminInfra","summary":"Serves the whole DigitalOcean infrastructure board: droplets, volumes, DOKS clusters and load balancers, each cross-referenced against every cluster's live Kubernetes state so the board can say what is safe to destroy and what is not.","description":"Serves the whole DigitalOcean infrastructure board: droplets, volumes, DOKS\nclusters and load balancers, each cross-referenced against every cluster's live\nKubernetes state so the board can say what is safe to destroy and what is not.\n\nIt is cached for up to a minute because one read is a fan-out over the DO API plus a\nfull pod/PV listing per cluster. Staleness is never load-bearing: every MUTATION\nre-scans from scratch and ignores this cache.\n\nOnly an unusable DO account is a hard failure. A partial read still produces a board,\nwith the failing source named in sources[] — except for clusters and volumes, which\nthe safety verdict depends on; without those the analysis degrades rather than\nclassifying anything it cannot prove.","tags":["admin"],"parameters":[{"name":"refresh","in":"query","required":false,"description":"Refresh, when present, forces a full re-scan instead of serving the cached\nsnapshot. Every MUTATION re-scans regardless — this is only for the reader.","schema":{"type":"string"},"example":"1"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"clusters":[],"loadBalancers":[],"nodes":[],"sources":[{"lastSync":"2026-07-27T00:00:00Z","name":"do.volumes","ok":true,"rows":2}],"volumes":[]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/ReadOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/clusters/{id}/nodepools/{pool}/scale":{"post":{"operationId":"adminScaleNodePool","summary":"Sets a node pool's node count — the ONE correct way to change how many nodes a DOKS cluster has.","description":"Sets a node pool's node count — the ONE correct way to change how many\nnodes a DOKS cluster has.\n\nThe response states what the board could NOT prove: DOKS picks which nodes a shrink\nremoves, so no particular pod is shown to survive one. See NodePool.ScaleTo.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DOKS cluster id, from the path.","schema":{"type":"string"}},{"name":"pool","in":"path","required":true,"description":"Pool is the node pool, from the path. Its DO id or its name — both are unique\nwithin a cluster, and an operator reads the name off the board.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"count":5},"schema":{"$ref":"#/components/schemas/ScaleIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"cluster":"hanzo-k8s","from":3,"pool":"workers","to":5},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/droplets/{id}":{"delete":{"operationId":"adminDeleteDroplet","summary":"Destroys a droplet the board has just proven is NOT a DOKS node.","description":"Destroys a droplet the board has just proven is NOT a DOKS node. There\nis no snapshot-first undo for a droplet the way there is for a volume: the local disk\ngoes with it.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO droplet id, from the path. Numeric.","schema":{"type":"string"}},{"name":"size","in":"query","required":false,"description":"Size is the target DigitalOcean size slug on resize, e.g. \"s-4vcpu-8gb\".","schema":{"type":"string"}},{"name":"disk","in":"query","required":false,"description":"Disk requests a PERMANENT resize that grows the disk. DO can never resize such a\ndroplet down again, so it defaults false — a CPU/RAM-only change, reversible.","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"deleted":true,"freedMonthlyCents":4800,"name":"worker-3"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/droplets/{id}/resize":{"post":{"operationId":"adminResizeDroplet","summary":"Changes a droplet's plan.","description":"Changes a droplet's plan. Same refusal as delete and for the same\nreason: a DOKS node's size is the node pool's to declare.\n\ndisk=true is a PERMANENT resize — the disk grows and DO can never resize the droplet\nDOWN again. disk=false (the default) changes CPU/RAM only and is reversible. DO\nrequires the droplet to be powered off and applies the change asynchronously, so the\nresponse carries the action to poll, not a completed change.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO droplet id, from the path. Numeric.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"disk":false,"size":"s-4vcpu-8gb"},"schema":{"$ref":"#/components/schemas/DropletIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"actionId":1234567,"actionStatus":"in-progress","from":"s-2vcpu-4gb","name":"worker-3","permanent":false,"to":"s-4vcpu-8gb"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/loadbalancers/{id}":{"delete":{"operationId":"adminDeleteLoadBalancer","summary":"Destroys a load balancer the board has just proven no live type=LoadBalancer Service in any cluster targets.","description":"Destroys a load balancer the board has just proven no live\ntype=LoadBalancer Service in any cluster targets.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO load balancer id, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"deleted":true,"freedMonthlyCents":1200,"ip":"1.2.3.4","name":"ingress-lb"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/nodes/{id}/cordon":{"post":{"operationId":"adminCordonNode","summary":"Marks one cluster node unschedulable — or schedulable again — and can drain the pods already on it.","description":"Marks one cluster node unschedulable — or schedulable again — and can drain\nthe pods already on it.\n\nIt is the ONE infra change that does not go through the run discipline, because there\nis no destructive verdict to check: cordoning is reversible and evicting respects the\ncluster's own PodDisruptionBudgets. It reads the cached board for the same reason.\nThe outcome is audited either way, and the result reports how many pods were evicted.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the node's droplet id, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"cordon":true,"drain":true},"schema":{"$ref":"#/components/schemas/CordonIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"evicted":7,"name":"worker-3","schedulable":false},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/volumes/{id}":{"delete":{"operationId":"adminDeleteVolume","summary":"Destroys a volume the board has just proven no PersistentVolume in any cluster references.","description":"Destroys a volume the board has just proven no PersistentVolume in any\ncluster references. Irreversible, so it snapshots first unless explicitly waived —\nthe snapshot IS the undo.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO volume id, from the path.","schema":{"type":"string"}},{"name":"snapshot","in":"query","required":false,"description":"Snapshot is the snapshot-first switch on DELETE. Anything other than the literal\n\"false\" snapshots before destroying — the snapshot IS the undo, so waiving it is\ndeliberate and explicit.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name is the snapshot name on the snapshot action. Blank gets a deterministic\n\"\u003cvolume\u003e-predelete-\u003cunix\u003e\" so the undo is findable in the DO console.","schema":{"type":"string"}},{"name":"sizeGiB","in":"query","required":false,"description":"SizeGiB is the target size on the resize action. A volume only ever grows —\nExpandTo is the verdict that refuses a shrink, so this is not validated here.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"deleted":true,"freedMonthlyCents":2000,"name":"acme-data","sizeGiB":200,"snapshotId":"snap-01J"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/volumes/{id}/resize":{"post":{"operationId":"adminResizeVolume","summary":"Grows a volume.","description":"Grows a volume. GROW ONLY — see Volume.ExpandTo for why the other\ndirection is a data migration this board deliberately refuses to run.\n\nThe MECHANISM follows the volume's owner, because there is exactly one way to grow each\nkind completely. A volume a PVC claims is grown by patching the claim: the CSI driver\nthen resizes the DigitalOcean device AND grows the filesystem on it, leaving claim, PV,\ndevice and filesystem all agreeing. Calling DigitalOcean directly for that volume would\ngrow the device while the PV kept declaring the old capacity and the filesystem never\ngrew at all. One operation, one correct mechanism per owner — not two ways to do it.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO volume id, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MutationOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/infra/volumes/{id}/snapshot":{"post":{"operationId":"adminSnapshotVolume","summary":"Takes a point-in-time snapshot of one volume — the undo a delete relies on, available on its own so an operator can take one before any risky change.","description":"Takes a point-in-time snapshot of one volume — the undo a delete relies\non, available on its own so an operator can take one before any risky change.\n\nIt re-scans the board first (never the cache) so the volume it snapshots is one that\nexists right now, and audits the outcome either way.","tags":["admin"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DO volume id, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"acme-data-before-migration"},"schema":{"$ref":"#/components/schemas/VolumeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"created":"2026-07-27T00:00:00Z","id":"snap-01J","name":"acme-data-before-migration","sizeGiB":200},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/VolumeSnapshotOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/invoices":{"get":{"operationId":"adminInvoices","summary":"Answers GET /v1/admin/invoices.","description":"Answers GET /v1/admin/invoices.\n\n\tGET /v1/admin/invoices?org=\u0026status=\u0026limit=","tags":["admin"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status filters on the invoice's LATEST lifecycle status (paid, open, void, …),\nmatched case-insensitively.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org filters to one tenant, matched exactly.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. total still reports the full match count.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicesOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/me":{"get":{"operationId":"adminMe","summary":"Answers with the validated operator identity — who the console is signed in as, which tier they are, and how wide their tenant window is.","description":"Answers with the validated operator identity — who the console is signed in as,\nwhich tier they are, and how wide their tenant window is. The fields come from the\nsanitized identity headers the gate just read, so they are authoritative and never\nclient-forgeable; nothing is looked up.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"displayName":"z","email":"z@hanzo.ai","isSuperAdmin":true,"isWhiteLabel":false,"name":"z","owner":"admin"},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/meOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/metrics":{"get":{"operationId":"adminMetrics","summary":"Answers GET /v1/admin/metrics by aggregating commerce.events directly (fleet-wide, no per-org fan-out).","description":"Answers GET /v1/admin/metrics by aggregating commerce.events directly\n(fleet-wide, no per-org fan-out). SuperAdmin only.\n\n\tGET /v1/admin/metrics?window=30d\u0026limit=20","tags":["admin"],"parameters":[{"name":"window","in":"query","required":false,"description":"Window is the movement window the new/churned MRR and the recent feed are\nmeasured over. Anything unrecognised falls back to the board default.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the top-customers table.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/money":{"get":{"operationId":"adminMoney","summary":"moneyBoardHandler answers GET /v1/admin/money.","description":"moneyBoardHandler answers GET /v1/admin/money.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoneyOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/o11y":{"get":{"operationId":"adminO11y","summary":"Is the fleet-wide observability board: LLM usage (requests, tokens, cost, errors, top orgs, top models), trace RED metrics (count, p50/p95/p99 latency in ms, error rate, top services), fleet log volume, and the O11yAI generation rollup — all aggregated across EVERY tenant, with no org filter applied.","description":"Is the fleet-wide observability board: LLM usage (requests, tokens, cost,\nerrors, top orgs, top models), trace RED metrics (count, p50/p95/p99 latency in ms,\nerror rate, top services), fleet log volume, and the O11yAI generation rollup — all\naggregated across EVERY tenant, with no org filter applied.\n\nEvery signal degrades INDEPENDENTLY. A table that is absent or errors contributes its\nzero value and the read still succeeds, so the board renders exactly what the\nwarehouse holds rather than failing whole because one of four sources is missing.\nSame when the warehouse is not connected at all: the zero board, never a fabricated\nfleet.","tags":["admin"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the lower time bound: 24h, 7d or 30d. Anything else reads as the\nboard's own default.","schema":{"type":"string"},"example":"7d"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"end":"2026-07-27T00:00:00Z","llm":{"costUsd":0,"generations":0},"logSeries":[],"range":"7d","series":[],"start":"2026-07-20T00:00:00Z","topModels":[],"topOrgs":[],"topServices":[],"totals":{"costCents":41200,"errors":37,"requests":10420,"tokens":8100000}},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/o11yOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/orgs":{"get":{"operationId":"adminOrgs","summary":"Lists the tenant directory one row per org, sorted by slug: member count and the org's month-to-date spend and credit balance, read live from IAM and commerce.","description":"Lists the tenant directory one row per org, sorted by slug: member count and the\norg's month-to-date spend and credit balance, read live from IAM and commerce.\n\nThe rows are the caller's tenant window, not the fleet: a SuperAdmin gets every org, a\nwhite-label admin only their own subtree. A per-org read that fails degrades THAT row\nto an honest zero — this panel carries no sources[] channel to report freshness on, so\nthe alternative would be a fleet total that silently reads healthy.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"created":"2026-01-04T00:00:00Z","creditsCents":5000,"display":"Acme","org":"acme","products":0,"spendCents":12500,"tokens":0,"users":7}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/orgsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/overview":{"get":{"operationId":"adminOverview","summary":"Is the Platform Overview tiles: how many orgs and users are in the caller's tenant window, the fleet workload counts, and month-to-date spend and credits.","description":"Is the Platform Overview tiles: how many orgs and users are in the caller's\ntenant window, the fleet workload counts, and month-to-date spend and credits.\n\nIt ALWAYS answers 200 — a tile board that fails as a whole because one upstream is\ndown is useless. Instead every upstream reports itself in sources[]: ok, degraded, or\nnot-configured. A commerce read that failed for ANY org marks that source degraded,\nbecause the spend/credits totals are then an undercount and must not read healthy.\n\ntokens30d is 0 for the same reason /usage has no series: there is no fleet token\ncounter to read yet.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"activeProducts":29,"creditsCents":10000,"drift":1,"lastSync":"2026-07-27T00:00:00Z","orgs":2,"products":31,"sources":[{"lastSync":"2026-07-27T00:00:00Z","name":"iam","ok":true,"rows":2}],"spendCents30d":250000,"tokens30d":0,"users":14},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/overviewOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/plugins":{"get":{"operationId":"adminPlugins","summary":"Reports what each host is actually running: every loaded plugin with its version, pid, uptime, reload and restart counts, and its measured CPU, RSS, thread and fd cost — read from the kernel, which is only answerable at all because a plugin is a process.","description":"Reports what each host is actually running: every loaded plugin with its\nversion, pid, uptime, reload and restart counts, and its measured CPU, RSS,\nthread and fd cost — read from the kernel, which is only answerable at all\nbecause a plugin is a process.\n\nReading this from deployment config would answer what was INTENDED. Only the\nprocess knows what is TRUE, and during a rolling upgrade the two disagree on\npurpose.","tags":["admin"],"parameters":[{"name":"scope","in":"query","required":false,"description":"Scope \"host\" answers for THIS host only. Default \"fleet\" fans out to every\nlive peer. A peer answers a host-scoped read, which is what stops the\nfan-out recursing.","schema":{"type":"string"},"example":"fleet"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"host":"cloud-0","plugins":[{"name":"billing","prefix":"/v1/billing","reloads":1,"restarts":0,"running":true,"source":"url","version":"9f2c…"}],"self":true}],"drift":[{"drifted":false,"name":"billing","running":1,"versions":["9f2c…"]}],"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/ListOut"}}},"description":"ok"}},"x-app":"plugins"}},"/v1/admin/plugins/{name}/disable":{"post":{"operationId":"adminDisablePlugin","summary":"Stops the plugin.","description":"Stops the plugin. Its routes STAY REGISTERED and answer 503 — not 404.\n\nThat is zip's choice and this keeps it. Removing the routes would mutate the\nroute table, and re-adding them on enable would grow it without bound across\nrepeated cycles, which is the invariant that makes reloads flat in memory. It\nis also the better answer: 404 says \"no such API\" and a client may cache it\nand stop retrying, while 503 says \"this API exists and is down right now\",\nwhich is true and retryable. Which of the two 503s this is — deliberate stop\nor crash — is what the status's disabled flag reports.","tags":["admin"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the app, from the path.","schema":{"type":"string"},"example":"billing"}],"requestBody":{"content":{"application/json":{"example":{"name":"billing"},"schema":{"$ref":"#/components/schemas/NameIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":[{"host":"cloud-0","ok":true}],"msg":"billing disabled","status":"ok"},"schema":{"$ref":"#/components/schemas/ActionOut"}}},"description":"ok"}},"x-app":"plugins"}},"/v1/admin/plugins/{name}/enable":{"post":{"operationId":"adminEnablePlugin","summary":"Brings a stopped or disabled plugin back on the artifact it already has: the zero Plugin names no new artifact, so Reload reuses the loaded spec and clears the disabled flag.","description":"Brings a stopped or disabled plugin back on the artifact it already\nhas: the zero Plugin names no new artifact, so Reload reuses the loaded spec\nand clears the disabled flag. Named for what an operator means by it.","tags":["admin"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the app, from the path.","schema":{"type":"string"},"example":"billing"}],"requestBody":{"content":{"application/json":{"example":{"name":"billing"},"schema":{"$ref":"#/components/schemas/NameIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":[{"host":"cloud-0","ok":true}],"msg":"billing enabled","status":"ok"},"schema":{"$ref":"#/components/schemas/ActionOut"}}},"description":"ok"}},"x-app":"plugins"}},"/v1/admin/plugins/{name}/reload":{"post":{"operationId":"adminReloadPlugin","summary":"Swaps a plugin for another build without dropping a request.","description":"Swaps a plugin for another build without dropping a request. The\nreplacement is started and proven to be LISTENING before any traffic moves to\nit, so a bad build leaves the old one serving and returns an error rather\nthan a hole; the old process then drains before it is killed.\n\nWith a version or url+sum it pins; naming a digest this host has run before is\nthe rollback, and costs no network because the digest IS the cache key. With\nneither it restarts what is already loaded.\n\nFleet scope applies it to one host at a time and STOPS at the first failure,\nso a build that cannot come up reaches exactly one host.","tags":["admin"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the app, from the path. It must be one the manifest declares.","schema":{"type":"string"},"example":"billing"}],"requestBody":{"content":{"application/json":{"example":{"name":"billing","version":"v1.2.3"},"schema":{"$ref":"#/components/schemas/ReloadIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":[{"host":"cloud-0","ok":true,"version":"9f2c…"}],"msg":"billing -\u003e 9f2c…","status":"ok"},"schema":{"$ref":"#/components/schemas/ActionOut"}}},"description":"ok"}},"x-app":"plugins"}},"/v1/admin/products":{"get":{"operationId":"adminProducts","summary":"Lists the fleet workload registry: every operator App CR across the platform namespaces with its declared vs running image tag, reconciled health/phase and drift verdict.","description":"Lists the fleet workload registry: every operator App CR across the platform\nnamespaces with its declared vs running image tag, reconciled health/phase and drift\nverdict. Optionally narrowed by kind, tier or env, each an exact match.\n\nThe rows are the SAME observation /v1/platform/fleet renders — read through the in-process\nplatform seam, not a second k8s client — so the two boards can never disagree about what\nthe fleet is. A PaaS plane that is not co-resident yields an honestly empty registry,\nnever a fabricated row.","tags":["admin"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Kind matches the operator App CR's declared spec.role (sql|kv|generic|ingress).","schema":{"type":"string"}},{"name":"tier","in":"query","required":false,"description":"Tier matches the derived infra grouping (cloud|data|edge|daemon|paas|app).","schema":{"type":"string"},"example":"data"},{"name":"env","in":"query","required":false,"description":"Env matches the lifecycle namespace (main|test|dev).","schema":{"type":"string"},"example":"main"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"cluster":"hanzo-k8s","declaredTag":"v1.4.2","drift":false,"driftSeverity":"ok","env":"main","health":"green","kind":"sql","latestTag":"","name":"sql","namespace":"hanzo","org":"hanzoai","phase":"Running","repo":"hanzoai/sql","runningTag":"v1.4.2","tier":"data","updated":""}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/productsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/promos":{"get":{"operationId":"adminPromo","summary":"Reads the current platform plan promo — the singleton discount offer, e.g.","description":"Reads the current platform plan promo — the singleton discount offer, e.g.\nthe 50%-off launch promo. Commerce stores it in the reserved platform namespace, so\nthe org sent with the read is the admin org and the service token is what passes\ncommerce's own platform-admin gate.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"active":true,"end":"2026-09-01T00:00:00Z","percentOff":50,"plans":["pro"],"start":"2026-07-01T00:00:00Z"},"msg":"","status":"ok","total":0},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"},"put":{"operationId":"adminSetPromo","summary":"Upserts the platform plan promo — the ONE place the offer is configured.","description":"Upserts the platform plan promo — the ONE place the offer is configured.\n\nThe body is commerce's own promo contract and is forwarded BYTE-FOR-BYTE, so no field\ncommerce accepts is dropped in transit. promoIn names its documented fields.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"active":true,"end":"2026-09-01T00:00:00Z","percentOff":50,"plans":["pro"],"start":"2026-07-01T00:00:00Z"},"schema":{"$ref":"#/components/schemas/promoIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"active":true,"end":"2026-09-01T00:00:00Z","percentOff":50,"plans":["pro"],"start":"2026-07-01T00:00:00Z"},"msg":"","status":"ok","total":0},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/providers/credit":{"get":{"operationId":"adminProvidersCredit","summary":"Serves GET /v1/admin/providers/credit — the per-provider upstream credit ledger.","description":"Serves GET /v1/admin/providers/credit — the per-provider upstream\ncredit ledger. SuperAdmin-guarded (see Routes).","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvidersCreditOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/referrals":{"get":{"operationId":"get_v1_admin_referrals","summary":"Answers the referral board: the top referrers by lifetime commission, the funnel conversion rate (referred orgs that have actually produced commission, over all referred orgs), and the accrual LIABILITY the platform owes, broken out by upline level.","description":"Answers the referral board: the top referrers by lifetime\ncommission, the funnel conversion rate (referred orgs that have actually\nproduced commission, over all referred orgs), and the accrual LIABILITY the\nplatform owes, broken out by upline level.\n\nRead the liability figure carefully — it is commission accrued and NOT yet\npaid, so it is money owed, not money spent, and the per-level split says how\nmuch of it comes from direct referrals versus the second and third levels.\n\nPLATFORM SUDO ONLY, cross-tenant, and it names orgs. It reads the SAME single\nattribution spine the accrual itself walks, so the board and the ledger cannot\ndisagree. Amounts are integer cents.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/referralsOut"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/admin/referrals/bonuses":{"get":{"operationId":"get_v1_admin_referrals_bonuses","summary":"Returns every referral edge in the directory with a fleet summary.","description":"Returns every referral edge in the directory with a fleet summary.\n\nSuperAdmin only, fail-closed. This is the ATTRIBUTION directory — who referred\nwhom and whether that referee became a customer. It carries no amounts because\nthis package issues none. The cross-tenant referral ANALYTICS board (top\nreferrers, conversion) is a different surface, GET /v1/admin/referrals, owned by\nthe affiliates subsystem over the shared attribution spine.","tags":["admin"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit is how many referrals to return, as a decimal string in the `?limit=`\nquery. Absent, unparseable or non-positive means 500; over 1000 is clamped to\n1000. It is a string rather than a number because the parse that has always\nserved this route trims surrounding whitespace, and one parse rule is better\nthan two.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminBonusesEnvelope"}}},"description":"ok"}},"x-app":"referrals"}},"/v1/admin/referrals/sweep":{"post":{"operationId":"post_v1_admin_referrals_sweep","summary":"Qualify-checks every pending referral and advances the ones that now qualify.","description":"Qualify-checks every pending referral and advances the ones that now qualify.\n\nSuperAdmin only, fail-closed. This is the cron path, and the ONLY path that\nadvances a referral: a referee QUALIFIES once they have made metered spend — the\nhonest signal that they actually used the product rather than merely signing up.\n\nQualifying moves NO money. It records that an attribution became a real customer;\nwhat is owed for that is an affiliate payable in commerce, settled by wire or to a\nconnected wallet. One pass is bounded, so a large backlog drains over several runs\ninstead of wedging one request, and the latch makes the transition at-most-once\nunder a concurrent sweep.\n\nIt reads nothing from the caller — the counters it returns are the whole result.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sweepEnvelope"}}},"description":"ok"}},"x-app":"referrals"}},"/v1/admin/revenue":{"get":{"operationId":"adminRevenue","summary":"Is the fleet money board: total prepaid balances held, total realized spend, MRR, ARPU, a per-customer table sorted highest-revenue first, and a real 30-day spend trend from the usage ledger.","description":"Is the fleet money board: total prepaid balances held, total realized spend,\nMRR, ARPU, a per-customer table sorted highest-revenue first, and a real 30-day spend\ntrend from the usage ledger.\n\nORTHOGONAL to /v1/admin/finance, which is the COGS/margin view of what WE pay vendors.\nThis is the customer side: what each customer holds, spends and subscribes to.\n\narpu divides realized spend by PAYING customers, not by all of them — a fleet of free\nsignups must not deflate the number. A customer counts as paying when it has spend or\nMRR.\n\nAn org whose money did not read degrades to honest zeros and marks the commerce source\ndegraded in sources[], so a partial fleet read is visible instead of quietly low.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"arpuCents":16363,"customers":42,"generatedAt":"2026-07-27T00:00:00Z","mrrCents":99000,"payingCustomers":11,"perCustomer":[],"sources":[{"lastSync":"2026-07-27T00:00:00Z","name":"iam","ok":true,"rows":42}],"spendTrend":[],"totalBalancesCents":250000,"totalSpendCents":180000},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/RevenueOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/roles":{"get":{"operationId":"adminRoles","summary":"Lists IAM roles for one owner org, forwarded VERBATIM from IAM's get-roles.","description":"Lists IAM roles for one owner org, forwarded VERBATIM from IAM's get-roles.","tags":["admin"],"parameters":[{"name":"owner","in":"query","required":false,"description":"Owner is the org whose rows to read. Defaults to the admin org, which owns the\nplatform's roles and applications.","schema":{"type":"string"},"example":"admin"},{"name":"p","in":"query","required":false,"description":"Page is the 1-based page number. Forwarded only when set — IAM applies its own\ndefault otherwise.","schema":{"type":"string"},"example":"1"},{"name":"pageSize","in":"query","required":false,"description":"PageSize is rows per page. Forwarded only when set.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"displayName":"Ops","name":"ops","owner":"admin"}],"msg":"","status":"ok","total":1},"schema":{"$ref":"#/components/schemas/iamRowsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/services":{"get":{"operationId":"adminServices","summary":"Reads the launch board: every hosted service in the registry with its LIVE waitlist mode, evaluated through the flag engine.","description":"Reads the launch board: every hosted service in the registry with its LIVE\nwaitlist mode, evaluated through the flag engine. This is the \"remove the waitlist one\nservice at a time\" view.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"services":[{"description":"","displayName":"Chat","hosts":["chat.hanzo.ai"],"service":"chat","waitlistMode":true}]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/servicesOut"}}},"description":"ok"}},"x-app":"admin"},"post":{"operationId":"adminUpsertService","summary":"Onboards a hosted service, or edits one, so a new host comes under the launch gate WITHOUT a redeploy.","description":"Onboards a hosted service, or edits one, so a new host comes under the\nlaunch gate WITHOUT a redeploy. Re-registering an existing service PRESERVES its live\nswitch — editing the hosts of a service that is already open must not silently close\nit again.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"description":"Hanzo Chat","displayName":"Chat","hosts":["chat.hanzo.ai"],"service":"chat","waitlistMode":true},"schema":{"$ref":"#/components/schemas/ServiceInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"service":{"description":"Hanzo Chat","displayName":"Chat","hosts":["chat.hanzo.ai"],"service":"chat","waitlistMode":true}},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/serviceOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/services/{service}/mode":{"post":{"operationId":"adminSetServiceMode","summary":"Flips ONE service's waitlist switch — the launch lever.","description":"Flips ONE service's waitlist switch — the launch lever. Hot: it takes\neffect on this pod immediately and on peers within one evaluation TTL, with no\nredeploy. An unknown service is a 404, not a silent create; onboarding goes through\nupsertService.","tags":["admin"],"parameters":[{"name":"service","in":"path","required":true,"description":"Service is the slug to flip, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"waitlistMode":false},"schema":{"$ref":"#/components/schemas/serviceModeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"service":{"description":"Hanzo Chat","displayName":"Chat","hosts":["chat.hanzo.ai"],"service":"chat","waitlistMode":false}},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/serviceOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/subscriptions":{"get":{"operationId":"adminSubscriptions","summary":"Answers GET /v1/admin/subscriptions.","description":"Answers GET /v1/admin/subscriptions.\n\n\tGET /v1/admin/subscriptions?org=\u0026status=\u0026limit=","tags":["admin"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status filters on the subscription's LATEST lifecycle status (active, trialing,\ncanceled, …), matched case-insensitively.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org filters to one tenant, matched exactly.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. total still reports the full match count.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/subsystems":{"get":{"operationId":"adminSubsystems","summary":"subsystems answers GET /v1/admin/subsystems.","description":"subsystems answers GET /v1/admin/subsystems. ?range=24h|7d|30d bounds the telemetry\nwindow (default 30d) — the same enum, and the same helpers, as the o11y board.","tags":["admin"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range bounds the telemetry window: 24h, 7d or 30d. Anything else, including\nempty, resolves to the default through the same o11yRange the o11y board uses.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubsystemsOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/sync":{"post":{"operationId":"adminSync","summary":"Answers the operator's \"Sync now\" button.","description":"Answers the operator's \"Sync now\" button. There is nothing to kick: admin\naggregates LIVE on every read, so the button is just a re-read. It acknowledges\nhonestly with started:true rather than pretending a batch job was queued.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"started":true},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/syncOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/treasury":{"get":{"operationId":"get_v1_admin_treasury","summary":"Returns the whole treasury board for a SuperAdmin: the reserve fund report, the recent double-entry journal, and the Hanzo L1 anchor status of the ledger root.","description":"Returns the whole treasury board for a SuperAdmin: the reserve\nfund report, the recent double-entry journal, and the Hanzo L1 anchor status of\nthe ledger root. ?limit= bounds the journal page.","tags":["admin"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the journal entries returned. Out of range or unparseable takes the default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adminReportOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/treasury/anchor":{"post":{"operationId":"post_v1_admin_treasury_anchor","summary":"Commits the current ledger root to Hanzo L1, making the books tamper-evident on chain, and returns the anchoring status.","description":"Commits the current ledger root to Hanzo L1, making the books\ntamper-evident on chain, and returns the anchoring status. When the chain path\nis wired it signs and submits the anchor transaction and records it; when it is\nnot, it returns the root that WOULD be committed plus the exact remaining\nwiring step and records nothing false. A submit that fails still answers 200\nwith the anchor's own status set to \"error\" — the attempt is the product.\nSuperAdmin only.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/anchorOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/treasury/anchor/signer":{"put":{"operationId":"put_v1_admin_treasury_anchor_signer","summary":"Installs the reserve's threshold MPC wallet as the signer for on-chain anchors, and returns its EVM address so an operator can fund it for gas.","description":"Installs the reserve's threshold MPC wallet as the signer\nfor on-chain anchors, and returns its EVM address so an operator can fund it\nfor gas. It provisions-or-resolves the caller org's treasury wallet on the\ndeployed MPC ring and installs it, so every later anchor commits the ledger\nroot SIGNED BY THE QUORUM WALLET instead of a lone KMS key. Idempotent — a\nrepeat resolves the same wallet, which is why the address is a PUT. SuperAdmin\nonly.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/signerOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/treasury/policy":{"post":{"operationId":"post_v1_admin_treasury_policy","summary":"Sets the revenue-share basis points a sweep accrues into the reserve fund and returns the stored policy.","description":"Sets the revenue-share basis points a sweep accrues into the\nreserve fund and returns the stored policy. 0–10000; the change is audited.\nSuperAdmin only.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"revenueShareBps":2000},"schema":{"$ref":"#/components/schemas/policyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/policyOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/treasury/seed":{"post":{"operationId":"post_v1_admin_treasury_seed","summary":"Injects bootstrap capital into the reserve fund so backed payouts can begin before the first revenue-share sweep, and returns the journal entry it wrote.","description":"Injects bootstrap capital into the reserve fund so backed payouts\ncan begin before the first revenue-share sweep, and returns the journal entry\nit wrote. A repeat of the same ref is at-most-once and reports created=false.\nSuperAdmin only.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"amountCents":500000,"memo":"founding capital","ref":"seed:2026-q3"},"schema":{"$ref":"#/components/schemas/seedRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seedOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/treasury/sweep":{"post":{"operationId":"post_v1_admin_treasury_sweep","summary":"Posts the revenue-share accrual for one period — revenue into the reserve fund, at the current policy's basis points — and returns what it moved.","description":"Posts the revenue-share accrual for one period — revenue into the\nreserve fund, at the current policy's basis points — and returns what it moved.\nIt is idempotent per period: a re-run of a period already swept accrues nothing\nand reports created=false. SuperAdmin only.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"period":"2026-07","revenueCents":100000},"schema":{"$ref":"#/components/schemas/sweepRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sweepOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/admin/usage":{"get":{"operationId":"adminUsage","summary":"Returns the month-to-date money totals: one org's when org names one, else the fleet sum across every org a SuperAdmin can see.","description":"Returns the month-to-date money totals: one org's when org names one, else the\nfleet sum across every org a SuperAdmin can see.\n\nseries and byProduct are ALWAYS empty. A daily trend and a per-product split are not\nderivable from the commerce billing API — they live in insights/datastore — so this\nanswers with the honest empty arrays rather than fabricating a shape the console would\nthen chart. Same reason tokens and requests are 0: there is no fleet counter to read.","tags":["admin"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org reads ONE tenant's month-to-date total instead of the fleet sum. Honoured\nfor a SuperAdmin only — a white-label admin always reads their own org.","schema":{"type":"string"},"example":"acme"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"byProduct":[],"series":[],"totals":{"requests":0,"spendCents":12500,"tokens":0}},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/usageOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/usage/funding":{"get":{"operationId":"adminUsageFunding","summary":"Splits our upstream AI usage by how it was FUNDED: one row per (provider, model) over the window, tagged credit (provider grant still remaining), paid (grant exhausted) or paid_only (no grant at all).","description":"Splits our upstream AI usage by how it was FUNDED: one row per (provider,\nmodel) over the window, tagged credit (provider grant still remaining), paid (grant\nexhausted) or paid_only (no grant at all).\n\nThe class is resolved at the PROVIDER level from the credit ledger, not per call — the\nper-call split, and the `byo` class, arrive when the metering write stamps a funding\ncolumn on cloud_usage and this can GROUP BY it directly. Until then a provider with\nremaining grant reports all of its usage as credit, which is right in aggregate and\napproximate at the boundary where a grant runs out mid-window.\n\nAn unparseable window falls back to the last 30 days rather than refusing: this is a\ndashboard read, and a typo in a date must not blank the board.","tags":["admin"],"parameters":[{"name":"from","in":"query","required":false,"description":"From is the inclusive start of the window. Unparseable or absent, together with\nTo, falls back to the last 30 days.","schema":{"type":"string"},"example":"2026-07-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"To is the exclusive end of the window.","schema":{"type":"string"},"example":"2026-07-27T00:00:00Z"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"cost_cents":420,"funding":"credit","model":"llama-3.3-70b","provider":"digitalocean","requests":310,"tokens":1200000}],"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/UsageFundingOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/users":{"get":{"operationId":"adminUsers","summary":"Lists the user directory across the caller's tenant window, one page at a time.","description":"Lists the user directory across the caller's tenant window, one page at a time.\ntotal is IAM's REAL total, so the console can page through it.\n\nA SuperAdmin may aim the read at one tenant with org; a white-label admin cannot — for\nthem the owner is hard-pinned to their own org and org is ignored, which is what keeps\nthe directory from becoming a cross-tenant read.","tags":["admin"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org narrows the directory to ONE tenant. Honoured for a SuperAdmin only — a\nwhite-label admin is pinned to their own org and this is ignored.","schema":{"type":"string"},"example":"acme"},{"name":"q","in":"query","required":false,"description":"Query is a free-text filter, matched by IAM as a \"contains\" over the user name.","schema":{"type":"string"},"example":"ada"},{"name":"p","in":"query","required":false,"description":"Page is the 1-based page number. Defaults to \"1\"; IAM returns zero rows AND a\nzero total when it is unset, so this layer never leaves it empty.","schema":{"type":"string"},"example":"1"},{"name":"pageSize","in":"query","required":false,"description":"PageSize is rows per page. Defaults to \"200\", the shared admin page size.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"created":"2026-01-04T00:00:00Z","displayName":"Ada","email":"ada@acme.com","forbidden":false,"isAdmin":true,"isSuperAdmin":false,"lastSignin":"2026-07-01T09:12:00Z","name":"ada","owner":"acme","tag":""}],"msg":"","status":"ok","total":222},"schema":{"$ref":"#/components/schemas/usersOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/volumes":{"get":{"operationId":"adminVolumes","summary":"Returns the realtime block-storage board: the DigitalOcean volume fleet (count, capacity, monthly list cost, per-volume region and attachment) plus the analytics datastore's OWN fill, read from its system.disks.","description":"Returns the realtime block-storage board: the DigitalOcean volume fleet\n(count, capacity, monthly list cost, per-volume region and attachment) plus the\nanalytics datastore's OWN fill, read from its system.disks.\n\nA volume's usedGiB and pct are null, always: DO exposes capacity and attachment but no\nfill, so the console renders \"—\" rather than a number nobody measured. The datastore\ncard is the one real fill here, and it is the number to scale on.\n\nThe two sources degrade independently — a DO outage still returns the datastore fill,\nand a disconnected datastore still returns the DO fleet.","tags":["admin"],"responses":{"200":{"content":{"application/json":{"example":{"data":{"alerts":[],"datastore":{"mount":"/var/lib/datastore","name":"default","pct":40.7,"sizeGiB":200,"usedGiB":81.4},"fleet":{"count":2,"monthlyUsd":30,"pct":null,"totalGiB":300,"usedGiB":null},"volumes":[{"attached":true,"id":"v1","name":"datastore-data","pct":null,"region":"nyc3","service":"","sizeGiB":200,"usedGiB":null}]},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/volumesOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/waitlist":{"get":{"operationId":"adminWaitlist","summary":"Reads one waitlist's leaderboard from the Hanzo waitlist engine — position, points and referral standing per entry — proxied server-authed with the engine secret, never a client credential.","description":"Reads one waitlist's leaderboard from the Hanzo waitlist engine — position,\npoints and referral standing per entry — proxied server-authed with the engine secret,\nnever a client credential.\n\nThe engine's payload is forwarded VERBATIM as data; the console normalizes it. When\nthe engine is not configured on this deployment the read still succeeds, with an empty\nobject and a msg saying so, so the panel shows an honest not-wired state instead of an\nerror the operator would chase.","tags":["admin"],"parameters":[{"name":"waitlist","in":"query","required":false,"description":"Waitlist is the waitlist slug to read (e.g. \"chat\"). The engine decides what an\nempty slug means.","schema":{"type":"string"},"example":"chat"},{"name":"page","in":"query","required":false,"description":"Page is the 1-based page number.","schema":{"type":"string"},"example":"1"},{"name":"pageSize","in":"query","required":false,"description":"PageSize is entries per page.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"example":{"data":{"entries":[{"email":"ada@acme.com","points":120,"position":7}],"total":842},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/admin/waitlist/boost":{"post":{"operationId":"adminWaitlistBoost","summary":"Grants a user waitlist points, moving them up toward the access cutoff.","description":"Grants a user waitlist points, moving them up toward the access cutoff.\nThis is the access lever: the cutoff itself does not move, the person does.\n\nIt funnels through the engine's verified grant seam (POST /v1/waitlist/award with\nsource=\"grant\" — the ONE path that honours an explicit points amount) and writes a\ntamper-evident audit row either way, so a FAILED grant is recorded too. The reason\nfield goes only to that row.","tags":["admin"],"requestBody":{"content":{"application/json":{"example":{"email":"ada@acme.com","points":50,"reason":"design partner","waitlist":"chat"},"schema":{"$ref":"#/components/schemas/waitlistBoostRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"data":{"email":"ada@acme.com","points":170,"position":3},"msg":"","status":"ok"},"schema":{"$ref":"#/components/schemas/rawOut"}}},"description":"ok"}},"x-app":"admin"}},"/v1/ads/campaigns":{"get":{"operationId":"get_v1_ads_campaigns","summary":"Returns the caller org's ad campaigns, most recently updated first, optionally narrowed to one lifecycle status.","description":"Returns the caller org's ad campaigns, most recently updated\nfirst, optionally narrowed to one lifecycle status. The listing is bounded by\nthe org: another tenant's campaigns are not reachable from here at all.","tags":["ads"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status filters to one lifecycle state (draft, active, paused, completed).\nEmpty returns every campaign the org has.","schema":{"type":"string"},"example":"active"},{"name":"limit","in":"query","required":false,"description":"Limit caps how many campaigns come back: default 200, maximum 1000. A\nvalue that is not a positive integer reads as the default.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignList"}}},"description":"ok"}},"x-app":"ads"},"post":{"operationId":"post_v1_ads_campaigns","summary":"Registers a new ad campaign for the caller's org and answers 201 with the stored row.","description":"Registers a new ad campaign for the caller's org and answers\n201 with the stored row. It only records the campaign — nothing is sent to the\nad network until POST /v1/ads/campaigns/{id}/launch runs it. The org is\nstamped by the server from the validated principal, so a body can never place\na campaign in another tenant.","tags":["ads"],"requestBody":{"content":{"application/json":{"example":{"budget":50000,"name":"Spring Launch","objective":"conversions","platform":"meta"},"schema":{"$ref":"#/components/schemas/campaignInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdCampaign"}}},"description":"created"}},"x-app":"ads"}},"/v1/ads/campaigns/{id}":{"delete":{"operationId":"delete_v1_ads_campaigns_by_id","summary":"Removes one of the caller org's campaigns and answers 204 with no body.","description":"Removes one of the caller org's campaigns and answers 204 with\nno body. It deletes the stored record only: a campaign already launched keeps\nrunning on the ad network, which must be stopped there. An id another org owns\nreads as not found.","tags":["ads"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"camp_2f9c1d"}],"responses":{"204":{"description":"no content"}},"x-app":"ads"},"get":{"operationId":"get_v1_ads_campaigns_by_id","summary":"Returns one of the caller org's campaigns.","description":"Returns one of the caller org's campaigns. An id another org owns\nreads as not found, so the response cannot confirm that it exists.","tags":["ads"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"camp_2f9c1d"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdCampaign"}}},"description":"ok"}},"x-app":"ads"},"put":{"operationId":"put_v1_ads_campaigns_by_id","summary":"Replaces the user-owned fields of one of the caller org's campaigns and answers the stored row.","description":"Replaces the user-owned fields of one of the caller org's\ncampaigns and answers the stored row. It is a full replace, not a patch: every\nfield is written from the request, so an omitted one is cleared. externalId is\nlaunch-owned and is never touched here, so editing a campaign cannot break its\nlink to a live provider execution.","tags":["ads"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"camp_2f9c1d"}],"requestBody":{"content":{"application/json":{"example":{"budget":75000,"id":"camp_2f9c1d","name":"Spring Launch","platform":"meta","status":"paused"},"schema":{"$ref":"#/components/schemas/updateCampaignIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdCampaign"}}},"description":"ok"}},"x-app":"ads"}},"/v1/ads/campaigns/{id}/launch":{"post":{"operationId":"post_v1_ads_campaigns_by_id_launch","summary":"Run one of your stored campaigns on its ad network","description":"Creates the campaign on its platform under the CALLER ORG'S own connected ad account, records the provider campaign id, flips the stored campaign to active and answers the updated record. No ad-network token is held here: it is resolved from KMS through the org's connector at launch time, BEFORE any provider call, so an org that has not connected that platform gets 424 and no spend can ever start on a connection the org did not make. Meta is executed for real; a campaign on a platform whose provider is not wired yet answers 501 even when the connector is connected, and an edge failure at the platform is 502. The optional {account} body overrides the target ad account for this launch and is TOLERANT — a malformed or non-JSON body is ignored and the campaign launches on its stored account rather than being refused. A campaign id another org owns reads as not found.","tags":["ads"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ads"}},"/v1/ads/summary":{"get":{"operationId":"get_v1_ads_summary","summary":"Rolls the caller org's ad campaigns up into four numbers: how many campaigns exist, how many are active, and the summed budget and spend across all of them.","description":"Rolls the caller org's ad campaigns up into four numbers: how many\ncampaigns exist, how many are active, and the summed budget and spend across\nall of them. Budget and spend are MINOR units (cents), the same units the\ncampaign rows carry. It counts only this org's campaigns.","tags":["ads"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adSummary"}}},"description":"ok"}},"x-app":"ads"}},"/v1/affiliates":{"get":{"operationId":"get_v1_affiliates","summary":"Answers the caller org's OWN affiliate standing: status, referral code and share link, commission rate, how many orgs it has referred, and its lifetime accrued, still-pending and already-paid commission in integer cents, with its payout history.","description":"Answers the caller org's OWN affiliate standing: status, referral\ncode and share link, commission rate, how many orgs it has referred, and its\nlifetime accrued, still-pending and already-paid commission in integer cents,\nwith its payout history.\n\nAn org that never applied gets an honest `isAffiliate:false` and the default\nrate rather than a 404 — the console renders the apply form off that answer.\n\nThe affiliate is resolved from the VALIDATED org, never from a field, so this\ncan only ever read the caller's own row; without a principal it is refused. It\nis a PURE READ: nothing accrues until the sweep runs. Commission is earned on\nHanzo's MARGIN, never on the referred customer's bill, so nothing here changes\nwhat that customer pays.","tags":["affiliates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateStanding"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/apply":{"post":{"operationId":"post_v1_affiliates_apply","summary":"Enrolls the caller's OWN org as an affiliate at status `applied`, optionally requesting a vanity code, and answers the record — 201 on the first apply, 200 with `created:false` afterwards.","description":"Enrolls the caller's OWN org as an affiliate at status `applied`,\noptionally requesting a vanity code, and answers the record — 201 on the first\napply, 200 with `created:false` afterwards.\n\nIDEMPOTENT, first apply wins: one affiliate per org, so re-applying never\ncreates a second row and never resets an existing approval. Applying is not\njoining — no code is minted and nothing accrues until staff approve, which is\nwhere both the code and the commission rate come from.\n\nThe org is the validated caller's, never a field. A malformed vanity code is\nrefused up front; the code is only REQUESTED here, and approval may mint a\ndifferent one if the requested code is taken.","tags":["affiliates"],"requestBody":{"content":{"application/json":{"example":{"requestedCode":"acme"},"schema":{"$ref":"#/components/schemas/applyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/application"}}},"description":"ok"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/application"}}},"description":"created"}},"x-app":"affiliates"}},"/v1/affiliates/attribute":{"post":{"operationId":"post_v1_affiliates_attribute","summary":"Records the first-touch edge every later commission is computed from: the caller's org was referred by the affiliate that owns this code.","description":"Records the first-touch edge every later commission is computed\nfrom: the caller's org was referred by the affiliate that owns this code.\n\nThe REFERRED org is the validated caller, never a field. A caller that could\nname the referred org could attach itself to somebody else's revenue. The\naffiliate is resolved from the code, and only an APPROVED affiliate's code\nresolves.\n\nFIRST TOUCH WINS, set once: one affiliate per referred org, so a re-post\nanswers the existing edge with `created:false` rather than moving the\nattribution. Self-attribution is refused, and so is a code that would make a\ncycle in the upline chain. An unknown code is a 404, deliberately: an\naffiliate code IS a public shareable link, so whether one is real is public by\ndesign, and the caller legitimately needs to know its link resolved.\n\nA user-level mirror of the edge is written best-effort; a conflict there never\nfails the org attribution, which is the money-bearing one.","tags":["affiliates"],"requestBody":{"content":{"application/json":{"example":{"code":"acme"},"schema":{"$ref":"#/components/schemas/attributeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/attribution"}}},"description":"ok"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/attribution"}}},"description":"created"}},"x-app":"affiliates"}},"/v1/affiliates/click":{"post":{"operationId":"post_v1_affiliates_click","summary":"Counts a click on a share link.","description":"Counts a click on a share link. PUBLIC — it takes no principal, because\na visitor clicking a shareable link has no session yet.\n\nThe ping folds into an in-memory buffer and NEVER writes the money database\nsynchronously, so a click flood cannot contend with the accrual and payout\nwrite path; tallies are flushed in one batch on the next authenticated links\nread and at shutdown. Clicks are a vanity metric: no accrual and no payout\never reads them — those key on real metered spend — so click inflation cannot\nmove money.\n\nAny well-formed code is accepted WITHOUT checking that it exists,\ndeliberately: this is not a code-existence oracle. `counted` reports that the\nbuffer took the ping, not that the code is real; an unknown code simply no-ops\nat flush time.","tags":["affiliates"],"requestBody":{"content":{"application/json":{"example":{"code":"acme"},"schema":{"$ref":"#/components/schemas/clickRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/clickCount"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/leaderboard":{"get":{"operationId":"get_v1_affiliates_leaderboard","summary":"Answers the top affiliates by lifetime accrued commission, shown by OPT-IN HANDLE with aggregate figures only, plus the caller's own exact rank.","description":"Answers the top affiliates by lifetime accrued commission, shown by\nOPT-IN HANDLE with aggregate figures only, plus the caller's own exact rank.\n\nIt never discloses an org identity and never a referred org's usage. An\naffiliate that has set no handle still OCCUPIES its rank but is not listed —\nso opting out hides the name, not the position, and the visible board must not\nbe read as a complete roster.\n\nThe caller's own row carries its exact GLOBAL rank, computed over the whole\napproved set rather than over the page, so it is right well outside the top of\nthe board. Only an approved affiliate has a rank. Requires a validated\nprincipal; a signed-in non-affiliate may read the board but gets no personal\nrow.","tags":["affiliates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateBoard"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/me":{"get":{"operationId":"get_v1_affiliates_me","summary":"Answers the richer self-view: the same lifetime accrued, pending and paid commission and payout history, plus the caller's downline broken out by upline LEVEL — direct, second, third — each with the rate paid at that level and how many orgs sit there.","description":"Answers the richer self-view: the same lifetime accrued, pending and paid\ncommission and payout history, plus the caller's downline broken out by upline\nLEVEL — direct, second, third — each with the rate paid at that level and how\nmany orgs sit there.\n\nCommission is MULTI-LEVEL: a referred org's spend pays up its referral chain,\nthree levels deep and no further. The direct level is the affiliate's own\nnegotiated rate; the second and third are platform-wide switches, read live,\nso the schedule shown is the one actually in force rather than one compiled\nin. A caller that has not applied still gets that schedule alongside\n`isAffiliate:false`, so the console can show what it would earn.\n\nScoped to the validated org and nothing else, and refused without a\nprincipal. A PURE READ — it reports the downline but accrues nothing.","tags":["affiliates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateSelf"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/me/earnings":{"get":{"operationId":"get_v1_affiliates_me_earnings","summary":"Answers the caller's own commission ledger: per period, the margin it earned against and the commission taken from that margin; and per referred org, that referral's aggregate contribution.","description":"Answers the caller's own commission ledger: per period, the margin it\nearned against and the commission taken from that margin; and per referred\norg, that referral's aggregate contribution. Integer cents throughout.\n\nThe per-org view deliberately carries the affiliate's OWN earned share and NOT\nthe referred org's spend or margin. An affiliate is entitled to what it\nearned, not to a restatement of its customer's usage — the period view is\nwhere the margin base appears, aggregated across every referral.\n\nScoped server-side to the validated caller's affiliate; a caller that is not\none gets `isAffiliate:false`.","tags":["affiliates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateEarnings"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/me/handle":{"post":{"operationId":"post_v1_affiliates_me_handle","summary":"Sets the caller's public leaderboard display name, or clears it.","description":"Sets the caller's public leaderboard display name, or clears it.\n\nThe handle IS the opt-in. An empty handle opts out: the affiliate keeps its\nrank and can still see its own row, it simply stops being listed to anyone\nelse. That is the whole privacy control — there is no separate visibility\nflag, and no way to be listed without choosing a name.\n\nRequires a validated principal and an existing affiliate record; apply first.\nThe handle is bounded and restricted to letters, digits, space, hyphen,\nunderscore and dot.","tags":["affiliates"],"requestBody":{"content":{"application/json":{"example":{"handle":"acme partners"},"schema":{"$ref":"#/components/schemas/handleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handleSet"}}},"description":"ok"}},"x-app":"affiliates"}},"/v1/affiliates/me/links":{"get":{"operationId":"get_v1_affiliates_me_links","summary":"Answers the caller's share links, each with its URL and its funnel: clicks tracked, signups — orgs attributed with that code — and conversions, meaning how many of those signups have actually produced commission.","description":"Answers the caller's share links, each with its URL and its funnel:\nclicks tracked, signups — orgs attributed with that code — and conversions,\nmeaning how many of those signups have actually produced commission.\n\nSignups and conversions are DERIVED from the commission ledger and never\nstored, so they cannot drift from the money. Clicks are the one stored counter\nand the one that is pure vanity.\n\nAny pending public click pings are folded into the store before the read, in\none batch — which is how the counters stay current without a database write\nper click. Scoped to the validated caller's own affiliate; a non-affiliate\ngets `isAffiliate:false` and the link cap.","tags":["affiliates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateLinks"}}},"description":"ok"}},"x-app":"affiliates"},"post":{"operationId":"post_v1_affiliates_me_links","summary":"Mints a new share link for the caller's own affiliate and answers it with its full URL, 201.","description":"Mints a new share link for the caller's own affiliate and answers it\nwith its full URL, 201.\n\nAPPROVAL IS REQUIRED: an org that has applied but is not approved is refused,\nbecause a link that cannot accrue is a link that quietly loses the referral. A\nrequested vanity code must be valid and free across the WHOLE directory —\ncodes are one global namespace, so a taken code is a 409 rather than a silent\nalias. Omit the code and a random one is minted.\n\nBounded per affiliate. The label is cosmetic: it is trimmed, stripped of\ncontrol characters and capped, and it is never part of a code.","tags":["affiliates"],"requestBody":{"content":{"application/json":{"example":{"label":"twitter"},"schema":{"$ref":"#/components/schemas/createLinkRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/linkMint"}}},"description":"created"}},"x-app":"affiliates"}},"/v1/agent":{"post":{"operationId":"post_v1_agent","summary":"Run one tool-calling round against your org's own tools","description":"Answers one turn of a conversation with four things: the model's `reply`, the `actions` the server executed on the caller's behalf, the `ops` the client must apply itself, and the `conversationId` the turn was recorded under.\n\nThe split between actions and ops is the rule most easily got wrong. A tool call is executed HERE only when the chosen preset is server-executing AND the tool resolves in the caller's own scope; every other call is handed back as an op for the client to apply to its own graph or UI. A tool that fails still comes back as an action, carrying its error rather than failing the round.\n\n`preset` selects the system prompt and the tool set (`capability` is a legacy alias for it); an unknown one is refused. `conversationId` continues an existing thread, and its absence starts one. A validated principal with a non-empty org is required — the org is the sole authority for both persistence and tool scope, and is NEVER read from the body.\n\nA completion refused for the caller's own reason — 402 insufficient balance, 429, 403 — is relayed with its own status and body verbatim, so the real billing message reaches the client instead of an opaque gateway error. Only a genuine upstream fault becomes a 502.","tags":["agent"],"x-app":"agent"}},"/v1/agent/conversations":{"get":{"operationId":"get_v1_agent_conversations","summary":"List the agent threads in your org","description":"Returns a summary of every agent conversation in the caller's org — id, derived title, and when it was last appended to — for populating a thread list.\n\nScoped to the caller's org and nothing else, and that isolation is structural rather than a filter: conversations are persisted in a store opened PER ORG, so there is no query in which another tenant's threads could appear. A validated principal with a non-empty org is required; 403 without one.","tags":["agent"],"x-app":"agent"}},"/v1/agent/conversations/{id}":{"get":{"operationId":"get_v1_agent_conversations_by_id","summary":"Read one agent thread in full","description":"Returns every message of one conversation in order — role, content, the assistant's tool calls where it made any, and each message's creation time — which is the transcript a client replays to resume a thread.\n\nThe lookup happens inside the caller's OWN per-org store, so an id belonging to another tenant is not refused, it is simply absent: the answer is 200 with an empty message list. Read it as \"no such conversation for you\" rather than as an empty thread. A validated principal with a non-empty org is required; 403 without one.","tags":["agent"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agent"}},"/v1/agent/presets":{"get":{"operationId":"get_v1_agent_presets","summary":"List the agent presets available to a caller","description":"Returns the preset catalog: each entry's id, its description and whether it is server-executing — the flag that decides if a preset's tool calls run here or come back for the client to apply. The ids are what POST /v1/agent accepts in `preset`.\n\nThe catalog is compiled into the build, identical for every caller, and this is the one read in the group that needs no principal.","tags":["agent"],"x-app":"agent"}},"/v1/agents":{"get":{"operationId":"get_v1_agents","summary":"Returns every agent defined in the caller's org, each with the number of runs recorded against it.","description":"Returns every agent defined in the caller's org, each with the\nnumber of runs recorded against it.","tags":["agents"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agentList"}}},"description":"ok"}},"x-app":"agents"},"post":{"operationId":"post_v1_agents","summary":"Defines an agent in the caller's org: a model, a system prompt (instructions) and a set of tool names.","description":"Defines an agent in the caller's org: a model, a system prompt\n(instructions) and a set of tool names. The name must be unique in the org and\nmatch ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. An omitted model takes the\ndeployment's configured default; a named one is checked against the gateway's\nserved catalog, so a model this deployment never serves is refused here rather\nthan failing at run time. A long-running agent must carry a 5-field cron\nschedule (the scheduler would otherwise never fire it) and counts against a\nper-org cap on scheduled agents.","tags":["agents"],"requestBody":{"content":{"application/json":{"example":{"instructions":"be terse","model":"enso-flash","name":"helper"},"schema":{"$ref":"#/components/schemas/createAgentIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agentView"}}},"description":"created"}},"x-app":"agents"}},"/v1/agents/activity":{"get":{"operationId":"get_v1_agents_activity","summary":"Serves the org-wide recent-activity feed.","description":"Serves the org-wide recent-activity feed. Events are REAL: each\nrecorded run is an invoked (ok) or failed (error) event; each agent's own\ncreate/update timestamps are created/updated events. Merged, newest first,\ncapped. Nothing is invented — an org with no agents and no runs gets [].","tags":["agents"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/activityFeed"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/builds":{"get":{"operationId":"get_v1_agents_builds","summary":"Returns the public index of every published build, most recently updated first, so a gallery can link straight to the story behind each product.","description":"Returns the public index of every published build, most recently\nupdated first, so a gallery can link straight to the story behind each product.\nPUBLIC, no tenancy: publishing is the author's act, and only published root\nsessions appear here.","tags":["agents"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the page. Absent, zero or over 500 reads as 100.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildList"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/builds/{org}/{project}":{"get":{"operationId":"get_v1_agents_builds_by_org_by_project","summary":"Returns the readable build of one product: the agent session that produced it, turn by turn — the prompts, the reasoning, the commits each turn produced — plus the exact `git log` that re-derives every commit binding from git itself, so nothing here has to be taken on trust.","description":"Returns the readable build of one product: the agent session that\nproduced it, turn by turn — the prompts, the reasoning, the commits each turn\nproduced — plus the exact `git log` that re-derives every commit binding from\ngit itself, so nothing here has to be taken on trust.\n\nPUBLIC, no tenancy: it answers only for a session its author explicitly\npublished, which is what makes it safe to be anonymous. An unpublished session\nis invisible here no matter who asks; its owner reads it through the org-scoped\n/v1/agents/sessions routes, which need a validated principal.","tags":["agents"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the org that published the build, from the path.","schema":{"type":"string"},"example":"hanzo"},{"name":"project","in":"path","required":true,"description":"Project is the product's slug, from the path.","schema":{"type":"string"},"example":"landing"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/metrics":{"get":{"operationId":"get_v1_agents_metrics","summary":"Serves the invocations-over-time histogram for the org's Agents dashboard.","description":"Serves the invocations-over-time histogram for the org's Agents\ndashboard. Every point is a REAL count of recorded runs in that time bucket —\none series line per agent that ran in the window. The Resource Usage rollup is\nall-null because this store meters no CPU/memory/storage/cost; the console\nrenders those as \"—\" rather than a fabricated figure. No runs =\u003e empty series\n(an honest \"not connected / no activity yet\"), never a synthesized trend.","tags":["agents"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the window to bucket: 24H, 7D or 30D. Anything else reads as 30D.","schema":{"type":"string"},"example":"7D"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/metricsView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/runs":{"get":{"operationId":"get_v1_agents_runs","summary":"Returns the org's agent runs across EVERY agent, newest first — what ran here, for whom, on which model, how long it took, and why it failed.","description":"Returns the org's agent runs across EVERY agent, newest first —\nwhat ran here, for whom, on which model, how long it took, and why it failed.\n\nIt is the feed the per-agent history could not be: an operator asking \"what is\nthis tenant's agent plane doing\" does not start out knowing an agent ref, and\nanswering by listing the agents and then paging each one's history is N+1 round\ntrips to reconstruct one ordering the database already has (RunsSince, ordered\nby created_at over the org index).\n\nThe org is the CALLER's, resolved from identity by tenantStore — never a\nparameter. There is deliberately no org field on orgRunsQuery to forge: run\nhistory is the tenant's own record, and the only tenant this can answer for is\nthe one asking.","tags":["agents"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps how many runs come back, newest first. Absent, zero or out of\nrange (1..200) reads as 50.","schema":{"type":"integer"},"example":20},{"name":"status","in":"query","required":false,"description":"Status keeps only runs with this outcome (\"ok\" or \"error\"). Empty keeps\nboth. It is the filter an operator reaches for first — \"show me what broke\"\n— and answering it here rather than by paging the whole history client-side\nis the difference between a usable feed and a download.","schema":{"type":"string"},"example":"error"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runList"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/sessions":{"get":{"operationId":"get_v1_agents_sessions","summary":"Returns the caller org's live sessions, newest first — each with its event count, its direct-child count and a one-line preview of its latest event.","description":"Returns the caller org's live sessions, newest first — each with\nits event count, its direct-child count and a one-line preview of its latest\nevent. With no filter it returns ROOT sessions only, so a dashboard shows one\nrow per flow rather than one per subagent; ?root= or ?parent= descends.","tags":["agents"],"parameters":[{"name":"root","in":"query","required":false,"description":"Root scopes the page to one subagent tree (its root session id).","schema":{"type":"string"}},{"name":"parent","in":"query","required":false,"description":"Parent scopes the page to the direct children of one session. Ignored when\nroot is set; with neither, only ROOT sessions come back.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Status filters to running, paused, done or error.","schema":{"type":"string"},"example":"running"},{"name":"project","in":"query","required":false,"description":"Project filters to the sessions tagged with one product slug.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the page. Absent, zero or over 500 reads as 100.","schema":{"type":"integer"},"example":20}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sessionList"}}},"description":"ok"}},"x-app":"agents"},"post":{"operationId":"post_v1_agents_sessions","summary":"Opens a live agent session in the caller's org — the row every surface (the CLI's outer agent, hanzo.bot, the console, chat) hangs its activity off.","description":"Opens a live agent session in the caller's org — the row every\nsurface (the CLI's outer agent, hanzo.bot, the console, chat) hangs its\nactivity off. A session with a parentSessionId becomes a subagent of that\nsession and inherits its root, so one flow is one tree; without one it is\nitself a root. Registering with a terminal status records a session that has\nalready finished.","tags":["agents"],"requestBody":{"content":{"application/json":{"example":{"agent":"hanzo-dev","host":"gpu-01","title":"ship the landing page"},"schema":{"$ref":"#/components/schemas/registerReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sessionView"}}},"description":"created"}},"x-app":"agents"}},"/v1/agents/sessions/stream":{"get":{"operationId":"get_v1_agents_sessions_stream","summary":"Live session and event updates for the caller's org, as Server-Sent Events.","description":"Holds the connection open as text/event-stream and pushes a frame each time the org's registry moves: an `event: session` frame carrying the same session shape the list and detail reads answer with (a registration, an update, or a login-manager revoke tearing a session down), and an `event: event` frame carrying one appended turn. Optional ?root=\u003csession id\u003e narrows the feed to a single subagent tree.\n\nRequires a validated principal carrying an org; 403 without one. Org-scoped fail-closed: the bus filters on tenant before it fans out, so a subscriber only ever receives its own org's updates, and ?root= narrows that further but can never widen it.\n\nDelivery is best-effort and the GET reads remain the source of truth. A subscriber that falls more than 256 frames behind is DROPPED — its channel is closed and the stream ends — so one stuck dashboard can never back-pressure a session write; the client reconnects and re-reads the session endpoints to resynchronise. A `: ping` comment every 25 seconds holds the connection open through proxies and is how a departed client is noticed.","tags":["agents"],"x-app":"agents"}},"/v1/agents/sessions/{id}":{"get":{"operationId":"get_v1_agents_sessions_by_id","summary":"Returns one session with its direct child sessions and its 50 most recent events, oldest of those first.","description":"Returns one session with its direct child sessions and its 50 most\nrecent events, oldest of those first.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to act on, from the path.","schema":{"type":"string"},"example":"sess_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sessionDetail"}}},"description":"ok"}},"x-app":"agents"},"patch":{"operationId":"patch_v1_agents_sessions_by_id","summary":"Updates a session's surface-owned truth: its status, its title, the run-target it is dispatched to, and the product it built plus whether that build's story is public.","description":"Updates a session's surface-owned truth: its status, its title,\nthe run-target it is dispatched to, and the product it built plus whether that\nbuild's story is public. A FINISHED session stays finished — reopening a\ndone/error run would fabricate liveness — and publishing is refused unless the\nsession names the project it built, because the public build route is keyed on\n(org, project).","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to update, from the path.","schema":{"type":"string"},"example":"sess_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"sess_1","status":"done"},"schema":{"$ref":"#/components/schemas/patchSessionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sessionView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/sessions/{id}/control":{"get":{"operationId":"get_v1_agents_sessions_by_id_control","summary":"Returns the steering commands (pause/resume/stop/message) recorded against the caller's own session that are newer than the cursor, oldest first, with the cursor to poll from next.","description":"Returns the steering commands (pause/resume/stop/message)\nrecorded against the caller's own session that are newer than the cursor,\noldest first, with the cursor to poll from next. It is how a locally started\n`hanzo code` session — which is not task-backed, so nothing forwards its\ncommands to an execution engine — consumes what the dashboard posted. Read-only\nand bounded at 200 per poll, so a steady poll is cheap and an applied command is\nnever redelivered.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session whose commands are being drained, from the path.","schema":{"type":"string"},"example":"sess_1"},{"name":"after","in":"query","required":false,"description":"After is the last seq this poller applied; only commands newer than it come\nback. Absent or negative reads as 0, which drains from the beginning.","schema":{"type":"integer"},"example":12}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlDrain"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/sessions/{id}/events":{"post":{"operationId":"post_v1_agents_sessions_by_id_events","summary":"Append one turn to a session's ordered log.","description":"Records a message, tool-call, spawn, log, status or control turn against the session and answers 201 with the stored event, including the monotonic `seq` the store assigned — the cursor every reader pages from. The same turn is fanned out live to every stream subscriber watching that session's tree.\n\nRequires a validated principal carrying an org, and the session must already exist IN THAT ORG: an id belonging to another tenant is a 404 exactly like one that does not exist, so the log can never be written across a tenant boundary. `actor` defaults to the calling principal when the body names none. `kind` must be one of the six above, and `payload` must be valid JSON of at most 64 KiB.\n\nThe payload is scanned for credentials BEFORE it is stored, and a hit REFUSES the write with 422 rather than redacting it: {status, code: \"secret_in_transcript\", error, findings:[…]}, each finding naming the rule, severity, line, a masked preview and a SHA-256 fingerprint the author can match against the value they rotate. The detected value itself appears nowhere in that body, because it was never stored. That in-band findings array is the reason this operation cannot be typed.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/sessions/{id}/message":{"post":{"operationId":"post_v1_agents_sessions_by_id_message","summary":"Send text into a running session.","description":"Records `message` as a durable control event carrying the caller's text and answers 200 with {command, event, forwarded} — this is how a dashboard steers an agent mid-run. It is the one command with a required body: a `message` (up to 16 KiB) or a `payload`, and 400 with neither. The credential scan that guards an appended turn covers `payload` here; `message` is bounded but not scanned. \n\nRequires a validated principal carrying an org, and the session must exist IN THAT ORG — a foreign id is a 404, so no tenant can steer another's agents. A FINISHED session (done or error) refuses every command with 409: a run that has ended cannot be steered.\n\nTHE COMMAND IS AN INTENT, NOT A STATE CHANGE. Nothing here writes the session's status. A 200 means the command was durably recorded and delivered, never that the agent has actually paused, resumed or stopped; the status becomes paused, done or error only when the surface running the agent reports it back through a session update. That surface learns of the command in one of two ways: a task-backed session (one carrying a workflow id, with a tasks backend wired) has it forwarded to the durable-execution engine, and `forwarded` says so; everything else is record-only, and the running surface — a locally started `hanzo code` session, for one — drains it by polling the session's control endpoint. Today that is every session: the only controller wired forwards nothing, so `forwarded` is false and polling is how a command arrives. If a forward is attempted and fails, the answer is 502 stating that the command was recorded but not forwarded: the intent is never lost.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/sessions/{id}/pause":{"post":{"operationId":"post_v1_agents_sessions_by_id_pause","summary":"Ask a running session to pause.","description":"Records `pause` as a durable control event on the session and answers 200 with {command, event, forwarded} — the stored event carries the `seq` that orders it against every other turn. \n\nRequires a validated principal carrying an org, and the session must exist IN THAT ORG — a foreign id is a 404, so no tenant can steer another's agents. A FINISHED session (done or error) refuses every command with 409: a run that has ended cannot be steered.\n\nTHE COMMAND IS AN INTENT, NOT A STATE CHANGE. Nothing here writes the session's status. A 200 means the command was durably recorded and delivered, never that the agent has actually paused, resumed or stopped; the status becomes paused, done or error only when the surface running the agent reports it back through a session update. That surface learns of the command in one of two ways: a task-backed session (one carrying a workflow id, with a tasks backend wired) has it forwarded to the durable-execution engine, and `forwarded` says so; everything else is record-only, and the running surface — a locally started `hanzo code` session, for one — drains it by polling the session's control endpoint. Today that is every session: the only controller wired forwards nothing, so `forwarded` is false and polling is how a command arrives. If a forward is attempted and fails, the answer is 502 stating that the command was recorded but not forwarded: the intent is never lost.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/sessions/{id}/resume":{"post":{"operationId":"post_v1_agents_sessions_by_id_resume","summary":"Ask a paused session to carry on.","description":"Records `resume` as a durable control event on the session and answers 200 with {command, event, forwarded}. The session is NOT required to be paused first: the only status this refuses is a finished one, because the live status is the running surface's to report rather than this endpoint's to enforce. \n\nRequires a validated principal carrying an org, and the session must exist IN THAT ORG — a foreign id is a 404, so no tenant can steer another's agents. A FINISHED session (done or error) refuses every command with 409: a run that has ended cannot be steered.\n\nTHE COMMAND IS AN INTENT, NOT A STATE CHANGE. Nothing here writes the session's status. A 200 means the command was durably recorded and delivered, never that the agent has actually paused, resumed or stopped; the status becomes paused, done or error only when the surface running the agent reports it back through a session update. That surface learns of the command in one of two ways: a task-backed session (one carrying a workflow id, with a tasks backend wired) has it forwarded to the durable-execution engine, and `forwarded` says so; everything else is record-only, and the running surface — a locally started `hanzo code` session, for one — drains it by polling the session's control endpoint. Today that is every session: the only controller wired forwards nothing, so `forwarded` is false and polling is how a command arrives. If a forward is attempted and fails, the answer is 502 stating that the command was recorded but not forwarded: the intent is never lost.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/sessions/{id}/stop":{"post":{"operationId":"post_v1_agents_sessions_by_id_stop","summary":"Ask a session to stop for good.","description":"Records `stop` as a durable control event on the session and answers 200 with {command, event, forwarded}. Stop is the one command that CANCELS a task-backed session's durable workflow instead of signalling it — pause, resume and message are cooperative signals the workflow decides how to act on, while this tears it down, with the request's `message` recorded as the cancellation reason (a default stands in when none is given). \n\nRequires a validated principal carrying an org, and the session must exist IN THAT ORG — a foreign id is a 404, so no tenant can steer another's agents. A FINISHED session (done or error) refuses every command with 409: a run that has ended cannot be steered.\n\nTHE COMMAND IS AN INTENT, NOT A STATE CHANGE. Nothing here writes the session's status. A 200 means the command was durably recorded and delivered, never that the agent has actually paused, resumed or stopped; the status becomes paused, done or error only when the surface running the agent reports it back through a session update. That surface learns of the command in one of two ways: a task-backed session (one carrying a workflow id, with a tasks backend wired) has it forwarded to the durable-execution engine, and `forwarded` says so; everything else is record-only, and the running surface — a locally started `hanzo code` session, for one — drains it by polling the session's control endpoint. Today that is every session: the only controller wired forwards nothing, so `forwarded` is false and polling is how a command arrives. If a forward is attempted and fails, the answer is 502 stating that the command was recorded but not forwarded: the intent is never lost.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/sessions/{id}/tree":{"get":{"operationId":"get_v1_agents_sessions_by_id_tree","summary":"Returns the subagent-flow graph rooted at this session: the session, its children, their children, each node carrying its own event count.","description":"Returns the subagent-flow graph rooted at this session: the session,\nits children, their children, each node carrying its own event count. One\nindexed read pulls the whole flow (every node of a flow shares a root id), so\nthe shape is assembled in memory rather than by walking the store per node.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to act on, from the path.","schema":{"type":"string"},"example":"sess_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/treeNode"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/targets":{"get":{"operationId":"get_v1_agents_targets","summary":"Returns every machine registered to the caller's org, newest first, each with its live session load.","description":"Returns every machine registered to the caller's org, newest\nfirst, each with its live session load.","tags":["agents"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/targetList"}}},"description":"ok"}},"x-app":"agents"},"post":{"operationId":"post_v1_agents_targets","summary":"Registers a machine as an agent target, or re-links one that is already registered.","description":"Registers a machine as an agent target, or re-links one that is\nalready registered. Re-linking is idempotent and keyed on org+host+owner, so a\nmachine that reconnects refreshes its own row rather than piling up duplicates;\nit answers 200, while a first registration answers 201.","tags":["agents"],"requestBody":{"content":{"application/json":{"example":{"host":"gpu-01","kind":"gpu","label":"workshop"},"schema":{"$ref":"#/components/schemas/targetReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/targetView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/targets/{id}":{"delete":{"operationId":"delete_v1_agents_targets_by_id","summary":"Deregisters one machine.","description":"Deregisters one machine. Only its owner, or an org admin, may\nremove it; an unknown id, a cross-org id and a machine owned by someone else\nall answer the same not-found, so a probe learns nothing about what exists.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the target to act on, from the path.","schema":{"type":"string"},"example":"tgt_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/targetDeleted"}}},"description":"ok"}},"x-app":"agents"},"get":{"operationId":"get_v1_agents_targets_by_id","summary":"Returns one registered machine, with its live session load.","description":"Returns one registered machine, with its live session load.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the target to act on, from the path.","schema":{"type":"string"},"example":"tgt_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/targetView"}}},"description":"ok"}},"x-app":"agents"},"patch":{"operationId":"patch_v1_agents_targets_by_id","summary":"Updates one machine in place.","description":"Updates one machine in place. Every field is optional; a field the\nrequest omits is left alone. A metrics patch IS a heartbeat — the server stamps\nits own clock, so a client can neither forge nor backdate staleness.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the target to update, from the path.","schema":{"type":"string"},"example":"tgt_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"tgt_1","status":"draining"},"schema":{"$ref":"#/components/schemas/patchTargetIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/targetView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/targets/{id}/claim":{"post":{"operationId":"post_v1_agents_targets_by_id_claim","summary":"ClaimRoutedRun is the machine's long poll for work: it authenticates the daemon, stamps the liveness the dispatch gate reads (the poll IS the proof a runner is listening), and waits up to 25 seconds for the next run addressed to THIS machine.","description":"ClaimRoutedRun is the machine's long poll for work: it authenticates the\ndaemon, stamps the liveness the dispatch gate reads (the poll IS the proof a\nrunner is listening), and waits up to 25 seconds for the next run addressed to\nTHIS machine. It answers the run when one arrives and 204 with no body when the\nwindow elapses, on which the daemon re-polls immediately.\n\nTWO independent proofs are required and both fail closed to the same 403: the\ncaller must own this machine (or be an org admin) AND present its claim key in\nX-Target-Key. A run offered to one machine is unreachable from another's claim.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the target to act on, from the path.","schema":{"type":"string"},"example":"tgt_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/routedRunOut"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/targets/{id}/key":{"post":{"operationId":"post_v1_agents_targets_by_id_key","summary":"Mints (or rotates) the claim key a `hanzo code --serve` daemon presents to claim work for this machine, and returns it ONCE: only its SHA-256 hash is stored.","description":"Mints (or rotates) the claim key a `hanzo code --serve`\ndaemon presents to claim work for this machine, and returns it ONCE: only its\nSHA-256 hash is stored. Rotating supersedes any prior daemon, so only the\nmachine's owner — or an org admin — may call it; every other caller gets the\nsame not-found an unknown id gets, and learns nothing about what exists.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the target to act on, from the path.","schema":{"type":"string"},"example":"tgt_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/claimKeyOut"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/targets/{id}/runs/{runId}/report":{"post":{"operationId":"post_v1_agents_targets_by_id_runs_by_runid_report","summary":"Completes a claimed run: it delivers the terminal result to the run's durable owner, which is what lets that workflow finish.","description":"Completes a claimed run: it delivers the terminal result to the\nrun's durable owner, which is what lets that workflow finish. Scoped to (org,\ntarget, run) and claim-key authenticated, so a machine can only ever report a\nrun it legitimately holds. Idempotent — a report for an unknown or\nalready-finished run answers delivered:false rather than failing, because the\nsession's terminal state was already set by the machine's own stream.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine reporting, from the path.","schema":{"type":"string"},"example":"tgt_1"},{"name":"runId","in":"path","required":true,"description":"RunID is the routed run being completed, from the path.","schema":{"type":"string"},"example":"run_1"}],"requestBody":{"content":{"application/json":{"example":{"branch":"hanzo/fix","changed":true,"id":"tgt_1","ok":true,"runId":"run_1"},"schema":{"$ref":"#/components/schemas/reportRunIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reportOut"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/{ref}":{"delete":{"operationId":"delete_v1_agents_by_ref","summary":"Removes an agent and every run recorded against it.","description":"Removes an agent and every run recorded against it. Answers 204.","tags":["agents"],"parameters":[{"name":"ref","in":"path","required":true,"description":"Ref is the agent's public id (the agent_… handle create and list return) or\nits org-unique name, from the path. Either resolves the same agent.","schema":{"type":"string"},"example":"helper"}],"responses":{"204":{"description":"no content"}},"x-app":"agents"},"get":{"operationId":"get_v1_agents_by_ref","summary":"Returns one agent with its system prompt and its 20 most recent runs.","description":"Returns one agent with its system prompt and its 20 most recent runs.\nThe ref is the agent's public id or its org-unique name — a created agent is\nimmediately gettable by whatever create handed back.","tags":["agents"],"parameters":[{"name":"ref","in":"path","required":true,"description":"Ref is the agent's public id (the agent_… handle create and list return) or\nits org-unique name, from the path. Either resolves the same agent.","schema":{"type":"string"},"example":"helper"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agentDetail"}}},"description":"ok"}},"x-app":"agents"},"patch":{"operationId":"patch_v1_agents_by_ref","summary":"Changes an agent in place.","description":"Changes an agent in place. Every field is optional; a field the\nrequest omits keeps its stored value. The resulting mode+schedule are\nre-validated together, so a partial update can never leave a long-running\nagent without the cron the scheduler needs to fire it, and a transition INTO\nlong-running counts against the per-org cap on scheduled agents.","tags":["agents"],"parameters":[{"name":"ref","in":"path","required":true,"description":"Ref is the agent to update — its public id or org-unique name, from the path.","schema":{"type":"string"},"example":"helper"}],"requestBody":{"content":{"application/json":{"example":{"instructions":"be terse and cite sources","ref":"helper"},"schema":{"$ref":"#/components/schemas/updateAgentIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/agentView"}}},"description":"ok"}},"x-app":"agents"}},"/v1/agents/{ref}/run":{"post":{"operationId":"post_v1_agents_by_ref_run","summary":"Run one of your org's agents and get the recorded run back.","description":"Composes the agent's stored instructions with the caller's `input`, executes one real chat completion through the same in-process AI client the rest of the console uses, and answers with the run that was recorded: its id, status, model, output, duration and error. Every run this returns reflects an execution that actually happened — a model failure is recorded and reported, never hidden and never fabricated. A transient upstream failure (429, 5xx, empty choices) is retried up to three times with jittered backoff, and a configured failover model is tried before the run is called an error.\n\n`ref` is the agent's public `agent_…` id or its org-unique name; either resolves the same agent, and it must belong to the caller's org, so an agent in another tenant is a 404 exactly like one that does not exist. A validated principal is required and the check is made twice on purpose: this route MOVES MONEY, so the debit's principal requirement is asserted where the money moves rather than inherited from the tenant lookup.\n\nThe org's balance is authorized BEFORE any inference, so an unfunded tenant gets 402 and no free compute, and a billing plane that cannot answer gets 503 rather than a free run. The flat per-run fee is an operator knob; setting it to zero makes runs free and removes the balance gate with them. Only a SUCCESSFUL run is billed, attributed to the model actually used — a failover run bills the model it fell over to, not the one it started on. A deployment with no inference wired answers 503 before any of this.\n\nTHE RULE A READER GETS WRONG: a failed run is a 502 whose body is the RUN, not an error envelope. The execution happened, the run was persisted to this agent's history, and its `error` field is the product — so a client that treats every non-2xx as an opaque failure throws away the only account of what went wrong. Each run also opens a root session in the live session registry, best-effort: a bookkeeping failure there never fails the run, because the run and its billing already happened.","tags":["agents"],"parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents"}},"/v1/agents/{ref}/runs":{"get":{"operationId":"get_v1_agents_by_ref_runs","summary":"Returns one agent's execution history, newest first — each run's input, its output or its error, and how long it took.","description":"Returns one agent's execution history, newest first — each run's\ninput, its output or its error, and how long it took. Every row is a run that\nactually happened.","tags":["agents"],"parameters":[{"name":"ref","in":"path","required":true,"description":"Ref is the agent's public id or its org-unique name, from the path.","schema":{"type":"string"},"example":"helper"},{"name":"limit","in":"query","required":false,"description":"Limit caps how many runs come back, newest first. Absent, zero or out of\nrange (1..200) reads as 50.","schema":{"type":"integer"},"example":20}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runList"}}},"description":"ok"}},"x-app":"agents"}},"/v1/ai/account":{"get":{"operationId":"get_v1_ai_account","summary":"Account","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/activities":{"get":{"operationId":"get_v1_ai_activities","summary":"List activities","description":"List the caller's activities.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/answer":{"get":{"operationId":"get_v1_ai_answer","summary":"Answer","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/articles":{"get":{"operationId":"get_v1_ai_articles","summary":"List articles","description":"List the caller's articles.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_articles","summary":"Create a article","description":"Create one article.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/articles/global":{"get":{"operationId":"get_v1_ai_articles_global","summary":"List articles across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/articles/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_articles_by_owner_by_name","summary":"Delete a article","description":"Delete one article.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_articles_by_owner_by_name","summary":"Retrieve a article","description":"Read one article by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_articles_by_owner_by_name","summary":"Update a article","description":"Update one article. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_articles_by_owner_by_name","summary":"Replace a article","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/assets":{"get":{"operationId":"get_v1_ai_assets","summary":"List assets","description":"List the caller's assets.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_assets","summary":"Create a asset","description":"Create one asset.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/assets/scan":{"post":{"operationId":"post_v1_ai_assets_scan","summary":"Scan (asset)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/assets/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_assets_by_owner_by_name","summary":"Delete a asset","description":"Delete one asset.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_assets_by_owner_by_name","summary":"Retrieve a asset","description":"Read one asset by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_assets_by_owner_by_name","summary":"Update a asset","description":"Update one asset. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_assets_by_owner_by_name","summary":"Replace a asset","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/assets/{owner}/{name}/scan":{"post":{"operationId":"post_v1_ai_assets_by_owner_by_name_scan","summary":"Scan (asset)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/chats":{"get":{"operationId":"get_v1_ai_chats","summary":"List chats","description":"List the caller's chats.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_chats","summary":"Create a chat","description":"Create one chat.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/chats/global":{"get":{"operationId":"get_v1_ai_chats_global","summary":"List chats across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/chats/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_chats_by_owner_by_name","summary":"Delete a chat","description":"Delete one chat.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_chats_by_owner_by_name","summary":"Retrieve a chat","description":"Read one chat by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_chats_by_owner_by_name","summary":"Update a chat","description":"Update one chat. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_chats_by_owner_by_name","summary":"Replace a chat","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/connections":{"get":{"operationId":"get_v1_ai_connections","summary":"Lists the org's connectable AI accounts and whether each is currently connected.","description":"Lists the org's connectable AI accounts and whether each is\ncurrently connected. Never returns a key or a kms:// reference.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_connections","summary":"Connects (or reconnects) a third-party AI account for the org by sealing the supplied key into KMS and upserting the org's provider row.","description":"Connects (or reconnects) a third-party AI account for the org by\nsealing the supplied key into KMS and upserting the org's provider row. The raw\nkey is sealed BEFORE the row is built and is never persisted or echoed.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/connections/{provider}":{"delete":{"operationId":"delete_v1_ai_connections_by_provider","summary":"Disconnects a third-party AI account: it deactivates the org's row so completion resolution falls back to the global Hanzo account (no BYO), and best-effort tombstones the sealed secret.","description":"Disconnects a third-party AI account: it deactivates the org's\nrow so completion resolution falls back to the global Hanzo account (no BYO), and\nbest-effort tombstones the sealed secret. Idempotent.","tags":["ai"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_connections_by_provider","summary":"Disconnects a third-party AI account: it deactivates the org's row so completion resolution falls back to the global Hanzo account (no BYO), and best-effort tombstones the sealed secret.","description":"Disconnects a third-party AI account: it deactivates the org's\nrow so completion resolution falls back to the global Hanzo account (no BYO), and\nbest-effort tombstones the sealed secret. Idempotent.","tags":["ai"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/connections/{provider}/authorize":{"get":{"operationId":"get_v1_ai_connections_by_provider_authorize","summary":"Begins an OAuth connection for the caller's org: it binds the org into a signed state and sends the caller to the provider's authorize URL.","description":"Begins an OAuth connection for the caller's org: it binds the\norg into a signed state and sends the caller to the provider's authorize URL. By\ndefault it 302-redirects (a top-level browser \"connect your login\" click); a\nSPA/BFF that needs to drive the redirect itself passes ?format=json and gets\n{authorizeUrl} in the standard envelope. The org is the VERIFIED principal, so\nonly the caller's own connection can result.","tags":["ai"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/connections/{provider}/callback":{"get":{"operationId":"get_v1_ai_connections_by_provider_callback","summary":"Completes OAuth: the org is recovered from the SIGNED state (not a header), the code is exchanged for a token, the token is SEALED into KMS (never the row/logs) through the same path as a BYOK key, and the org's provider row is upserted to \"connected\".","description":"Completes OAuth: the org is recovered from the SIGNED state\n(not a header), the code is exchanged for a token, the token is SEALED into KMS\n(never the row/logs) through the same path as a BYOK key, and the org's provider\nrow is upserted to \"connected\". The browser is then redirected back to the console\nwith ?ai_connected=\u003cprovider\u003e (or ?ai_connect_error=\u003cprovider\u003e on failure). Because\nthe org comes from the state THIS server signed, an attacker cannot land their\ntoken in a victim org — which is why this endpoint is state-authenticated rather\nthan credential-gated.","tags":["ai"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/connections/{provider}/usage":{"get":{"operationId":"get_v1_ai_connections_by_provider_usage","summary":"Imports the caller org's usage for a connected third-party account.","description":"Imports the caller org's usage for a connected third-party\naccount. The org is resolved from the VERIFIED principal (requireConnectionOrg), so a\ntenant reads only its own connection. The key is unsealed SERVER-SIDE and never\nreturned. An unconnected account, a missing importer, or a scope-denied provider all\nreturn a 200 ProviderUsage with connected/available flags + a human note — the UI's\nhonest-empty states — never a fabricated figure.","tags":["ai"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/dashboards/agents":{"get":{"operationId":"get_v1_ai_dashboards_agents","summary":"Dashboards Agents","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/dashboards/vm":{"get":{"operationId":"get_v1_ai_dashboards_vm","summary":"Dashboards Vm","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/deployments":{"get":{"operationId":"get_v1_ai_deployments","summary":"List deployments","description":"List the caller's deployments.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_deployments","summary":"Create a application","description":"Create one application.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/deployments/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_deployments_by_owner_by_name","summary":"Delete a application","description":"Delete one application.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_deployments_by_owner_by_name","summary":"Retrieve a application","description":"Read one application by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_deployments_by_owner_by_name","summary":"Update a application","description":"Update one application. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_deployments_by_owner_by_name","summary":"Replace a application","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/deployments/{owner}/{name}/deploy":{"post":{"operationId":"post_v1_ai_deployments_by_owner_by_name_deploy","summary":"Deploy (application)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/deployments/{owner}/{name}/undeploy":{"post":{"operationId":"post_v1_ai_deployments_by_owner_by_name_undeploy","summary":"Undeploy (application)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files":{"get":{"operationId":"get_v1_ai_files","summary":"List files","description":"List the caller's files.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_files","summary":"Create a file","description":"Create one file.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/activate":{"post":{"operationId":"post_v1_ai_files_activate","summary":"Activate (file)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/active":{"get":{"operationId":"get_v1_ai_files_active","summary":"Active (file)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/global":{"get":{"operationId":"get_v1_ai_files_global","summary":"List files across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/upload":{"post":{"operationId":"post_v1_ai_files_upload","summary":"Upload (file)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_files_by_owner_by_name","summary":"Delete a file","description":"Delete one file.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_files_by_owner_by_name","summary":"Retrieve a file","description":"Read one file by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_files_by_owner_by_name","summary":"Update a file","description":"Update one file. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_files_by_owner_by_name","summary":"Replace a file","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/files/{owner}/{name}/vectors":{"post":{"operationId":"post_v1_ai_files_by_owner_by_name_vectors","summary":"Vectors (file)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/forms":{"get":{"operationId":"get_v1_ai_forms","summary":"List forms","description":"List the caller's forms.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_forms","summary":"Create a form","description":"Create one form.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/forms/data":{"get":{"operationId":"get_v1_ai_forms_data","summary":"Data (form)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/forms/global":{"get":{"operationId":"get_v1_ai_forms_global","summary":"List forms across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/forms/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_forms_by_owner_by_name","summary":"Delete a form","description":"Delete one form.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_forms_by_owner_by_name","summary":"Retrieve a form","description":"Read one form by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_forms_by_owner_by_name","summary":"Update a form","description":"Update one form. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_forms_by_owner_by_name","summary":"Replace a form","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/graphs":{"get":{"operationId":"get_v1_ai_graphs","summary":"List graphs","description":"List the caller's graphs.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_graphs","summary":"Create a graph","description":"Create one graph.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/graphs/global":{"get":{"operationId":"get_v1_ai_graphs_global","summary":"List graphs across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/graphs/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_graphs_by_owner_by_name","summary":"Delete a graph","description":"Delete one graph.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_graphs_by_owner_by_name","summary":"Retrieve a graph","description":"Read one graph by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_graphs_by_owner_by_name","summary":"Update a graph","description":"Update one graph. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_graphs_by_owner_by_name","summary":"Replace a graph","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/k8s-status":{"get":{"operationId":"get_v1_ai_k8s-status","summary":"K8s Status","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/mcp/tools":{"get":{"operationId":"aiMCPTools","summary":"Tools reports what THIS PROCESS's MCP door carries: how many tools its own registry projects, optionally their names, and which subsystems this process composed.","description":"Tools reports what THIS PROCESS's MCP door carries: how many tools its own\nregistry projects, optionally their names, and which subsystems this process\ncomposed. It is the answer to \"is this door up and does it have anything behind\nit\" — a question a status code cannot answer, since an empty door and a full\none are both 200. What the FLEET's door carries is the fleet door's own answer:\nPOST /v1/mcp, tools/list, which asks every subsystem and names the ones that\ndid not reply.","tags":["ai"],"parameters":[{"name":"names","in":"query","required":false,"description":"Names asks for this process's tool NAMES and not only how many there are.\nOff by default: a list of names is a page, and the question this op exists\nto answer (\"is the door up and does it have anything behind it\") is answered\nby the count.","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/aiMCPSurface"}}},"description":"ok"}},"x-app":"ai"}},"/v1/ai/messages":{"get":{"operationId":"get_v1_ai_messages","summary":"List messages","description":"List the caller's messages.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_messages","summary":"Create a message","description":"Create one message.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/messages/global":{"get":{"operationId":"get_v1_ai_messages_global","summary":"List messages across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/messages/welcome":{"delete":{"operationId":"delete_v1_ai_messages_welcome","summary":"Welcome (message)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/messages/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_messages_by_owner_by_name","summary":"Delete a message","description":"Delete one message.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_messages_by_owner_by_name","summary":"Retrieve a message","description":"Read one message by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_messages_by_owner_by_name","summary":"Update a message","description":"Update one message. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_messages_by_owner_by_name","summary":"Replace a message","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/messages/{owner}/{name}/answer":{"get":{"operationId":"get_v1_ai_messages_by_owner_by_name_answer","summary":"Answer (message)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/nodes":{"get":{"operationId":"get_v1_ai_nodes","summary":"List nodes","description":"List the caller's nodes.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_nodes","summary":"Create a node","description":"Create one node.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/nodes/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_nodes_by_owner_by_name","summary":"Delete a node","description":"Delete one node.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_nodes_by_owner_by_name","summary":"Retrieve a node","description":"Read one node by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_nodes_by_owner_by_name","summary":"Update a node","description":"Update one node. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_nodes_by_owner_by_name","summary":"Replace a node","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/nodes/{owner}/{name}/tunnel":{"get":{"operationId":"get_v1_ai_nodes_by_owner_by_name_tunnel","summary":"Tunnel (node)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_nodes_by_owner_by_name_tunnel","summary":"Tunnel (node)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/preferences":{"patch":{"operationId":"patch_v1_ai_preferences","summary":"Preferences","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_preferences","summary":"Preferences","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/prometheus":{"get":{"operationId":"get_v1_ai_prometheus","summary":"Prometheus","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/providers":{"get":{"operationId":"get_v1_ai_providers","summary":"List providers","description":"List the caller's providers.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_providers","summary":"Create a provider","description":"Create one provider.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/providers/global":{"get":{"operationId":"get_v1_ai_providers_global","summary":"List providers across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/providers/mcp-tools":{"post":{"operationId":"post_v1_ai_providers_mcp-tools","summary":"Mcp Tools (provider)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/providers/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_providers_by_owner_by_name","summary":"Delete a provider","description":"Delete one provider.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_providers_by_owner_by_name","summary":"Retrieve a provider","description":"Read one provider by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_providers_by_owner_by_name","summary":"Update a provider","description":"Update one provider. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_providers_by_owner_by_name","summary":"Replace a provider","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records":{"get":{"operationId":"get_v1_ai_records","summary":"List records","description":"List the caller's records.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_records","summary":"Create a record","description":"Create one record.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/batch":{"post":{"operationId":"post_v1_ai_records_batch","summary":"Batch (record)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/commit":{"post":{"operationId":"post_v1_ai_records_commit","summary":"Commit (record)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/commit-second":{"post":{"operationId":"post_v1_ai_records_commit-second","summary":"Commit Second (record)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/query":{"get":{"operationId":"get_v1_ai_records_query","summary":"Query (record)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/query-second":{"get":{"operationId":"get_v1_ai_records_query-second","summary":"Query Second (record)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/records/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_records_by_owner_by_name","summary":"Delete a record","description":"Delete one record.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_records_by_owner_by_name","summary":"Retrieve a record","description":"Read one record by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_records_by_owner_by_name","summary":"Update a record","description":"Update one record. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_records_by_owner_by_name","summary":"Replace a record","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/remote-connections":{"get":{"operationId":"get_v1_ai_remote-connections","summary":"List remote-connections","description":"List the caller's remote-connections.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_remote-connections","summary":"Create a connection","description":"Create one connection.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/remote-connections/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_remote-connections_by_owner_by_name","summary":"Delete a connection","description":"Delete one connection.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_remote-connections_by_owner_by_name","summary":"Retrieve a connection","description":"Read one connection by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_remote-connections_by_owner_by_name","summary":"Update a connection","description":"Update one connection. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_remote-connections_by_owner_by_name","summary":"Replace a connection","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/remote-connections/{owner}/{name}/start":{"post":{"operationId":"post_v1_ai_remote-connections_by_owner_by_name_start","summary":"Start (connection)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/remote-connections/{owner}/{name}/stop":{"post":{"operationId":"post_v1_ai_remote-connections_by_owner_by_name_stop","summary":"Stop (connection)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/routes":{"get":{"operationId":"get_v1_ai_routes","summary":"List routes","description":"List the caller's routes.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_routes","summary":"Create a model-route","description":"Create one model-route.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/routes/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_routes_by_owner_by_name","summary":"Delete a model-route","description":"Delete one model-route.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_routes_by_owner_by_name","summary":"Retrieve a model-route","description":"Read one model-route by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_routes_by_owner_by_name","summary":"Update a model-route","description":"Update one model-route. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_routes_by_owner_by_name","summary":"Replace a model-route","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scales":{"get":{"operationId":"get_v1_ai_scales","summary":"List scales","description":"List the caller's scales.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_scales","summary":"Create a scale","description":"Create one scale.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scales/global":{"get":{"operationId":"get_v1_ai_scales_global","summary":"List scales across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scales/public":{"get":{"operationId":"get_v1_ai_scales_public","summary":"Public (scale)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scales/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_scales_by_owner_by_name","summary":"Delete a scale","description":"Delete one scale.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_scales_by_owner_by_name","summary":"Retrieve a scale","description":"Read one scale by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_scales_by_owner_by_name","summary":"Update a scale","description":"Update one scale. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_scales_by_owner_by_name","summary":"Replace a scale","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scans":{"get":{"operationId":"get_v1_ai_scans","summary":"List scans","description":"List the caller's scans.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_scans","summary":"Create a scan","description":"Create one scan.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/scans/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_scans_by_owner_by_name","summary":"Delete a scan","description":"Delete one scan.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_scans_by_owner_by_name","summary":"Retrieve a scan","description":"Read one scan by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_scans_by_owner_by_name","summary":"Update a scan","description":"Update one scan. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_scans_by_owner_by_name","summary":"Replace a scan","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/signin":{"post":{"operationId":"post_v1_ai_signin","summary":"Signin","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/signin-sessions":{"get":{"operationId":"get_v1_ai_signin-sessions","summary":"List signin-sessions","description":"List the caller's signin-sessions.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_signin-sessions","summary":"Create a session","description":"Create one session.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/signin-sessions/duplicated":{"get":{"operationId":"get_v1_ai_signin-sessions_duplicated","summary":"Duplicated (session)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/signin-sessions/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_signin-sessions_by_owner_by_name","summary":"Delete a session","description":"Delete one session.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_signin-sessions_by_owner_by_name","summary":"Retrieve a session","description":"Read one session by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_signin-sessions_by_owner_by_name","summary":"Update a session","description":"Update one session. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_signin-sessions_by_owner_by_name","summary":"Replace a session","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/signout":{"post":{"operationId":"post_v1_ai_signout","summary":"Signout","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores":{"get":{"operationId":"get_v1_ai_stores","summary":"List stores","description":"List the caller's stores.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_stores","summary":"Create a store","description":"Create one store.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores/global":{"get":{"operationId":"get_v1_ai_stores_global","summary":"List stores across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores/names":{"get":{"operationId":"get_v1_ai_stores_names","summary":"Names (store)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores/providers":{"get":{"operationId":"get_v1_ai_stores_providers","summary":"Providers (store)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_stores_by_owner_by_name","summary":"Delete a store","description":"Delete one store.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_stores_by_owner_by_name","summary":"Retrieve a store","description":"Read one store by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_stores_by_owner_by_name","summary":"Update a store","description":"Update one store. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_stores_by_owner_by_name","summary":"Replace a store","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/stores/{owner}/{name}/vectors":{"post":{"operationId":"post_v1_ai_stores_by_owner_by_name_vectors","summary":"Vectors (store)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/system":{"get":{"operationId":"get_v1_ai_system","summary":"System","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tasks":{"get":{"operationId":"get_v1_ai_tasks","summary":"List tasks","description":"List the caller's tasks.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_tasks","summary":"Create a task","description":"Create one task.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tasks/global":{"get":{"operationId":"get_v1_ai_tasks_global","summary":"List tasks across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tasks/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_tasks_by_owner_by_name","summary":"Delete a task","description":"Delete one task.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_tasks_by_owner_by_name","summary":"Retrieve a task","description":"Read one task by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_tasks_by_owner_by_name","summary":"Update a task","description":"Update one task. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_tasks_by_owner_by_name","summary":"Replace a task","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tasks/{owner}/{name}/analyze":{"post":{"operationId":"post_v1_ai_tasks_by_owner_by_name_analyze","summary":"Analyze (task)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tasks/{owner}/{name}/document":{"post":{"operationId":"post_v1_ai_tasks_by_owner_by_name_document","summary":"Document (task)","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/templates":{"get":{"operationId":"get_v1_ai_templates","summary":"List templates","description":"List the caller's templates.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_templates","summary":"Create a template","description":"Create one template.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/templates/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_templates_by_owner_by_name","summary":"Delete a template","description":"Delete one template.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_templates_by_owner_by_name","summary":"Retrieve a template","description":"Read one template by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_templates_by_owner_by_name","summary":"Update a template","description":"Update one template. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_templates_by_owner_by_name","summary":"Replace a template","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/training-contribution":{"get":{"operationId":"get_v1_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tree-files":{"post":{"operationId":"post_v1_ai_tree-files","summary":"Create a tree-file","description":"Create one tree-file.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/tree-files/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_tree-files_by_owner_by_name","summary":"Delete a tree-file","description":"Delete one tree-file.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_tree-files_by_owner_by_name","summary":"Update a tree-file","description":"Update one tree-file. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_tree-files_by_owner_by_name","summary":"Replace a tree-file","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/usages":{"get":{"operationId":"get_v1_ai_usages","summary":"List usages","description":"List the caller's usages.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/usages/by-user":{"get":{"operationId":"get_v1_ai_usages_by-user","summary":"By User (usage)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/usages/cloud":{"get":{"operationId":"get_v1_ai_usages_cloud","summary":"Cloud (usage)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/usages/range":{"get":{"operationId":"get_v1_ai_usages_range","summary":"Range (usage)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/usages/user-names":{"get":{"operationId":"get_v1_ai_usages_user-names","summary":"User Names (usage)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/vectors":{"get":{"operationId":"get_v1_ai_vectors","summary":"List vectors","description":"List the caller's vectors.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_vectors","summary":"Create a vector","description":"Create one vector.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/vectors/all":{"delete":{"operationId":"delete_v1_ai_vectors_all","summary":"All (vector)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/vectors/global":{"get":{"operationId":"get_v1_ai_vectors_global","summary":"List vectors across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/vectors/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_vectors_by_owner_by_name","summary":"Delete a vector","description":"Delete one vector.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_vectors_by_owner_by_name","summary":"Retrieve a vector","description":"Read one vector by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_vectors_by_owner_by_name","summary":"Update a vector","description":"Update one vector. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_vectors_by_owner_by_name","summary":"Replace a vector","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/version":{"get":{"operationId":"get_v1_ai_version","summary":"Version","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/videos":{"get":{"operationId":"get_v1_ai_videos","summary":"List videos","description":"List the caller's videos.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_videos","summary":"Create a video","description":"Create one video.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/videos/global":{"get":{"operationId":"get_v1_ai_videos_global","summary":"List videos across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/videos/upload":{"post":{"operationId":"post_v1_ai_videos_upload","summary":"Upload (video)","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/videos/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_videos_by_owner_by_name","summary":"Delete a video","description":"Delete one video.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_videos_by_owner_by_name","summary":"Retrieve a video","description":"Read one video by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_videos_by_owner_by_name","summary":"Update a video","description":"Update one video. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_videos_by_owner_by_name","summary":"Replace a video","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/workflows":{"get":{"operationId":"get_v1_ai_workflows","summary":"List workflows","description":"List the caller's workflows.","tags":["ai"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_ai_workflows","summary":"Create a workflow","description":"Create one workflow.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/workflows/global":{"get":{"operationId":"get_v1_ai_workflows_global","summary":"List workflows across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"github.com/hanzoai/ai"}},"/v1/ai/workflows/{owner}/{name}":{"delete":{"operationId":"delete_v1_ai_workflows_by_owner_by_name","summary":"Delete a workflow","description":"Delete one workflow.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_ai_workflows_by_owner_by_name","summary":"Retrieve a workflow","description":"Read one workflow by its (owner, name) key.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_ai_workflows_by_owner_by_name","summary":"Update a workflow","description":"Update one workflow. PATCH and PUT reach the same handler, which has always taken a whole object.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_ai_workflows_by_owner_by_name","summary":"Replace a workflow","description":"Identical to PATCH — the handler takes a whole object either way.","tags":["ai"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/analytics/health":{"get":{"operationId":"get_v1_analytics_health","summary":"Health reports whether the event plane can take a write and the warehouse can answer a read.","description":"Health reports whether the event plane can take a write and the warehouse can\nanswer a read.\n\nIt reports the analytics subsystem's own liveness in BOTH directions: plane is\nthe event plane it WRITES (the bus and the JetStream stream every accepted\nevent is published to, both named in the report), and datastore is the\nwarehouse it READS, with each read lens's table reported as it is provisioned\n(the LLM usage ledger and the product-event table).\n\nEITHER ONE DOWN IS A 503, and the report says WHICH — they are probed\nindependently and never collapse into a single bit. This endpoint used to\nreport the read half only, and answered 200/ok while every POST /v1/event\nfailed on a stream that could not bind: a total ingest outage behind a green\nprobe. A readiness gate here now gates on the write path too.\n\nplane.ready IS A REAL PROBE and walks the ingest path itself — the same\nconnection and the same stream a publish uses — so it cannot answer ready while\na publish would 503. plane.reason carries the plane's own error text when it is\nfalse.\n\ndatastore IS NOT PROBED WITH A QUERY. It is the state of the process's own\nshared client — established, and not since closed — so a warehouse accepting\nconnections and failing reads still reports true. Degraded CARRIES the report\n(status, the failing half, reason) as its body rather than an error envelope,\nso a gate reads the cause off the same object it got at 200.\n\nA MISSING LENS TABLE IS NOT A FAILURE and never moves the status: a lens\nreported available:false answers honest-empty rather than erroring, so a fresh\ndeployment whose collector has not emitted yet is legitimately 200 with the\nproduct-event lens unavailable. The lens block is reported whenever the\nwarehouse is REACHABLE — including on a report degraded by the plane, where the\ntables genuinely were probed — and is absent only when the warehouse is not,\nhaving nothing to say about tables it could not reach.\n\nUnauthenticated on purpose — liveness has to be probe-able — and it reads NO\ntenant data: table existence and stream presence only, never a row and never an\nevent.","tags":["analytics"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthReport"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthReport"}}},"description":"service unavailable"}},"x-app":"analytics"}},"/v1/analytics/overview":{"get":{"operationId":"get_v1_analytics_overview","summary":"Overview returns the caller org's analytics KPIs for one time window.","description":"Overview returns the caller org's analytics KPIs for one time window. Three lenses\nover one warehouse: llm is the live per-org LLM usage ledger (requests, tokens,\nspend, models, providers, errors) and is always real; web (pageviews, visitors,\nsessions) and commerce (orders, revenue, AOV) read the product-event table and\nreport available=false rather than fabricating zeros when it holds nothing yet.\n\nThe org is the validated principal's — never a parameter — so a caller can only\never read its own tenant. 403 without a validated bearer, 400 on an unknown range,\n503 when the warehouse is unreachable.","tags":["analytics"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: 24h, 7d or 30d. Default 24h. Ignored when both\nstart and end are given. An unknown value is a 400.","schema":{"type":"string"},"example":"7d"},{"name":"start","in":"query","required":false,"description":"Start is the inclusive lower bound of a custom window, RFC3339. Requires end.","schema":{"type":"string"}},{"name":"end","in":"query","required":false,"description":"End is the exclusive upper bound of a custom window, RFC3339. Requires start.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Overview"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/analytics/timeseries":{"get":{"operationId":"get_v1_analytics_timeseries","summary":"Timeseries returns the caller org's LLM usage over time as an evenly-spaced series.","description":"Timeseries returns the caller org's LLM usage over time as an evenly-spaced series.\nOne point per hour or per day — the bucket the window implies, 24h giving hours and\n7d/30d giving days — carrying requests, total tokens and spend in cents. Empty\nbuckets are filled with zeros so a client charts a continuous line.\n\nThe org is the validated principal's — never a parameter. 403 without a validated\nbearer, 400 on an unknown range, 503 when the warehouse is unreachable.","tags":["analytics"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: 24h, 7d or 30d. Default 24h. Ignored when both\nstart and end are given. An unknown value is a 400.","schema":{"type":"string"},"example":"30d"},{"name":"start","in":"query","required":false,"description":"Start is the inclusive lower bound of a custom window, RFC3339. Requires end.","schema":{"type":"string"}},{"name":"end","in":"query","required":false,"description":"End is the exclusive upper bound of a custom window, RFC3339. Requires start.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Timeseries"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/analytics/top":{"get":{"operationId":"get_v1_analytics_top","summary":"Top returns the caller org's ranked lenses for one window, five of them at once.","description":"Top returns the caller org's ranked lenses for one window, five of them at once.\nmodels ranks LLM models by spend and is always real; products ranks commerce orders\nby revenue; topPages ranks requested paths, topReferrers the external referrer\ndomains (\"(direct)\" for a missing or same-origin one) and topSources the utm_source\ncampaigns (\"(none)\" when absent), each by pageviews. Every lens carries each row's\nshare of the in-window total, so a top-N honestly shows the long tail.\n\nThe four event lenses report available=false rather than fabricating zeros when the\nproduct-event table holds nothing yet. The org is the validated principal's — never\na parameter. 403 without a validated bearer, 400 on an unknown range, 503 when the\nwarehouse is unreachable.","tags":["analytics"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: 24h, 7d or 30d. Default 24h. Ignored when both\nstart and end are given. An unknown value is a 400.","schema":{"type":"string"},"example":"7d"},{"name":"start","in":"query","required":false,"description":"Start is the inclusive lower bound of a custom window, RFC3339. Requires end.","schema":{"type":"string"}},{"name":"end","in":"query","required":false,"description":"End is the exclusive upper bound of a custom window, RFC3339. Requires start.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds every ranked lens in the response. Default 10, maximum 100; a\nvalue at or below zero, or one that is not a number, takes the default.","schema":{"type":"integer"},"example":25}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Top"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/ask":{"post":{"operationId":"post_v1_ask","summary":"Ask a grounded question about your own org","description":"Answers a natural-language question about the CALLER'S OWN org, from real figures rather than from the model's memory.\n\nThe question is classified to a grounded domain, that domain's read runs IN-PROCESS under the caller's own credentials, and only then is the result narrated. So the figures and their sources are the domain's, resolved before any model call and never altered by one — a wrong answer is a wrong query, never an invention.\n\nDomains: books (the org's ledger) and web (search, news, research, deep). A validated principal is required; the answer is scoped to that principal's org and nothing else.","tags":["ask"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/askRequest"}}}},"x-app":"ask"}},"/v1/audio/foley":{"post":{"operationId":"post_v1_audio_foley","summary":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music, /foley — that the Zen family serves natively.","description":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music,\n/foley — that the Zen family serves natively. It resolves the SKU and, for a Zen\nmodel, forwards to zen's matching verb billed per call at the discovered price.\nThese verbs are Zen-native; a non-Zen model is rejected.","tags":["audio"],"x-app":"github.com/hanzoai/ai"}},"/v1/audio/music":{"post":{"operationId":"post_v1_audio_music","summary":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music, /foley — that the Zen family serves natively.","description":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music,\n/foley — that the Zen family serves natively. It resolves the SKU and, for a Zen\nmodel, forwards to zen's matching verb billed per call at the discovered price.\nThese verbs are Zen-native; a non-Zen model is rejected.","tags":["audio"],"x-app":"github.com/hanzoai/ai"}},"/v1/audio/speech":{"post":{"operationId":"post_v1_audio_speech","summary":"The OpenAI-compatible TTS endpoint (POST /v1/audio/speech).","description":"The OpenAI-compatible TTS endpoint (POST /v1/audio/speech). It\nauthenticates the caller, resolves `model` to its TTS provider (the SAME model-route\nresolution the chat/images/video endpoints use — so a BYO node registered as a TTS\nprovider works transparently), synthesizes the audio, and streams the bytes back.\nOne code path, OpenAI-shaped, no store/message coupling (unlike the legacy\n/v1/generate-text-to-speech-audio which is bound to a chat store).","tags":["audio"],"x-app":"github.com/hanzoai/ai"}},"/v1/audio/voice":{"post":{"operationId":"post_v1_audio_voice","summary":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music, /foley — that the Zen family serves natively.","description":"Serves the generative audio verbs — /v1/audio/voice (TTS), /music,\n/foley — that the Zen family serves natively. It resolves the SKU and, for a Zen\nmodel, forwards to zen's matching verb billed per call at the discovered price.\nThese verbs are Zen-native; a non-Zen model is rejected.","tags":["audio"],"x-app":"github.com/hanzoai/ai"}},"/v1/audit":{"get":{"operationId":"get_v1_audit","summary":"List reads the caller's OWN org audit trail, newest first, with the total the filter matched so a console can page it.","description":"List reads the caller's OWN org audit trail, newest first, with the total the\nfilter matched so a console can page it.\n\nEvery filter is optional and applies WITHIN the caller's org — the org itself is\nthe validated principal's and can never be widened by a request. Fails closed:\nan absent principal is a true \"not signed in\" (401), and a deployment with no\nlocal tamper-evident store answers an honest 501 rather than silently serving\nsomebody else's trail.","tags":["audit"],"parameters":[{"name":"sub","in":"query","required":false,"description":"Sub narrows the trail to one actor — the validated subject that made the\nrequest. Blank means every actor in the org.","schema":{"type":"string"}},{"name":"action","in":"query","required":false,"description":"Action narrows it to one action name, e.g. \"machine.create\".","schema":{"type":"string"},"example":"machine.create"},{"name":"resource","in":"query","required":false,"description":"Resource narrows it to one resource TYPE, e.g. \"apikey\".","schema":{"type":"string"}},{"name":"resourceId","in":"query","required":false,"description":"ResourceID narrows it to one resource instance.","schema":{"type":"string"}},{"name":"result","in":"query","required":false,"description":"Result narrows it to one outcome: \"success\", \"deny\" or \"error\".","schema":{"type":"string"},"example":"success"},{"name":"since","in":"query","required":false,"description":"Since is the inclusive lower time bound, RFC3339. An unparseable value is\nignored rather than refused — one malformed filter must not hide the trail.","schema":{"type":"string"},"example":"2026-07-01T00:00:00Z"},{"name":"until","in":"query","required":false,"description":"Until is the upper time bound, RFC3339, with the same tolerance.","schema":{"type":"string"}},{"name":"pageSize","in":"query","required":false,"description":"PageSize is rows per page, default 100. A value that is not a positive\ninteger falls back to the default.","schema":{"type":"string"},"example":"50"},{"name":"p","in":"query","required":false,"description":"Page is the 1-based page number, driving the offset. Anything below 2 reads\nthe first page.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trailPage"}}},"description":"ok"}},"x-app":"audit"}},"/v1/authors":{"get":{"operationId":"get_v1_authors","summary":"Returns the caller's author-program dashboard: enrolment status, linked forge login, verified repositories and owner-wide claims, recorded deploys, accrued / pending / paid royalty, and the payout history.","description":"Returns the caller's author-program dashboard: enrolment status,\nlinked forge login, verified repositories and owner-wide claims, recorded deploys,\naccrued / pending / paid royalty, and the payout history.\n\nIt answers ONE OF TWO SHAPES from this address. An org that has never connected\ngets {\"isAuthor\": false, \"defaultShareBps\", \"badgeBase\"} — an honest \"not enrolled\"\nrather than a 404, so the console can render the connect form. An enrolled org gets\nthe dashboard: isAuthor, id, status, githubLogin, verified, verifyCode, verifyFile,\nverifySnippet, shareBps, badgeBase, repos, orgs, deploys, accruedCents,\npendingCents, paidCents, payouts and ledger.\n\nFor an APPROVED author this read ALSO runs the accrual sweep opportunistically, so\nthe dashboard is self-updating. That is why the royalty AUDIT lives at its own\naddress: an audit must not move the money it is auditing.","tags":["authors"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"authors"}},"/v1/authors/basis":{"get":{"operationId":"get_v1_authors_basis","summary":"Returns the AUDIT TRAIL behind the caller's own royalty: every ledger row with the spend it was computed from, the share applied at the time, the platform's matching half, whether each row satisfies the formula, and the attribution edges that already existed when the row was written.","description":"Returns the AUDIT TRAIL behind the caller's own royalty: every\nledger row with the spend it was computed from, the share applied at the time, the\nplatform's matching half, whether each row satisfies the formula, and the\nattribution edges that already existed when the row was written.\n\nIt answers ONE OF TWO SHAPES. An org that has never connected gets\n{\"isAuthor\": false, \"defaultShareBps\"} — never a 404, which would answer \"is this\norg an author?\" for anyone who asked. An enrolled org gets the basis: isAuthor, id,\nstatus, asOf, shareBps, platformShareBps, defaultShareBps, shareSource, settlesTo,\nmethod (the formula, the rate card and the sizing), ledger, reconciliation, window,\nand period when one was requested.\n\nThis read NEVER sweeps, and that is the point of it being a separate address from\nthe dashboard: an audit must not move the money it is auditing, so calling it N\ntimes leaves the balances and the ledger byte-identical.","tags":["authors"],"parameters":[{"name":"period","in":"query","required":false,"description":"Period is the UTC accrual month, YYYY-MM. Empty means every period; any other\nshape is refused with 400, because the period is echoed back and used as a SQL\nfilter and is only ever accepted in the one form the accrual latch mints.","schema":{"type":"string"},"example":"2026-07"}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"authors"}},"/v1/authors/connect":{"post":{"operationId":"post_v1_authors_connect","summary":"Enrols the caller's org in the author program at status \"connected\" and returns its enrolment, including the verify code the file method needs.","description":"Enrols the caller's org in the author program at status \"connected\"\nand returns its enrolment, including the verify code the file method needs. It is\nIDEMPOTENT: a second call returns the same enrolment rather than a conflict.\n\nThe forge login is taken from IAM's LINKED account for the provider when there is\none — that is identity proof, not a claim — and only otherwise from the login in\nthe body, which then has to be proven per repository. Connecting does not admit an\norg to earning: a platform reviewer approves that separately.\n\nAnswers 201 when it enrolled the org and 200 when it found an existing enrolment.","tags":["authors"],"requestBody":{"content":{"application/json":{"example":{"login":"octocat","provider":"github"},"schema":{"$ref":"#/components/schemas/connectRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/enrolment"}}},"description":"ok"}},"x-app":"authors"}},"/v1/authors/deploys/record":{"post":{"operationId":"post_v1_authors_deploys_record","summary":"Records that the caller's org deployed a project built from a source repository, which is the edge that makes an author's work earn royalty.","description":"Records that the caller's org deployed a project built from a\nsource repository, which is the edge that makes an author's work earn royalty.\n\nIt is deliberately NOT an error for a deploy to attribute to nobody: a project\nbuilt from no repository, or from one no author has verified, answers\n{\"recorded\": false, \"reason\"} so a deploy pipeline can fire this on every deploy\nwithout branching. Attribution resolves per-repository first, then owner-wide, so a\nrepository with its own claim always earns for its own author.\n\nA deploy of a Hanzo-maintained template attributes to the platform treasury, and a\nself-deploy (the author's own org deploying its own repository) is recorded for\nprovenance but excluded from accrual. The edge is idempotent per\nrepository+project+org.\n\nAnswers 201 when it recorded a new edge and 200 otherwise.","tags":["authors"],"requestBody":{"content":{"application/json":{"example":{"project":"prj_1f…","repoUrl":"github.com/octocat/hello-world"},"schema":{"$ref":"#/components/schemas/deployRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deployRecord"}}},"description":"ok"}},"x-app":"authors"}},"/v1/authors/repos/verify":{"post":{"operationId":"post_v1_authors_repos_verify","summary":"Proves that the caller owns a repository — or a whole OWNER — and records the claim, which is what makes deploys of that code earn royalty.","description":"Proves that the caller owns a repository — or a whole OWNER — and\nrecords the claim, which is what makes deploys of that code earn royalty.\n\nOwnership is proven the SAME two ways in both cases, tried in order: an IAM-linked\nforge token with admin or push permission, or a hanzo.json on the default branch\ncarrying the author's verify code. Claiming an OWNER proves it against that\nowner's \".github\" control repository, and is exactly as strong as a per-repository\nclaim — an owner the caller cannot prove is refused with 422, never assumed.\n\nA per-repository claim wins over an owner-wide one, so a specifically-claimed\nrepository always earns for its own author. A repository another author has\nalready verified is a 409. The org must have connected first.\n\nAnswers 201 when it recorded a new claim and 200 when the claim already existed.","tags":["authors"],"requestBody":{"content":{"application/json":{"example":{"repoUrl":"github.com/octocat/hello-world"},"schema":{"$ref":"#/components/schemas/verifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/claim"}}},"description":"ok"}},"x-app":"authors"}},"/v1/authz/check":{"post":{"operationId":"post_v1_authz_check","summary":"Ask whether a subject may act on an object","description":"Answers one policy question — may this subject take this action on this object — against the CALLER'S OWN org policy set, and answers it with a bare allow/deny.\n\nThe org comes from the gateway-minted X-Org-Id and picks the per-org enforcer, so a decision is always rendered by that tenant's policies and never by another's. A request carrying no org is refused rather than answered from a shared or default set: collapsing tenants together is the one failure a policy engine must not have.\n\nBody: {sub, obj, act}, all three required. The reply echoes them beside `allow` so a cached or logged decision carries the question it answered.","tags":["authz"],"x-app":"authz"}},"/v1/authz/health":{"get":{"operationId":"get_v1_authz_health","summary":"Liveness of the policy engine","description":"Reports that the authz process is up. Unauthenticated by design and never org-scoped: it answers while every tenant's enforcer is still cold, because a probe that needed a tenant would fail for reasons that have nothing to do with the process being alive.","tags":["authz"],"x-app":"authz"}},"/v1/authz/readyz":{"get":{"operationId":"get_v1_authz_readyz","summary":"Readiness of the policy engine","description":"Reports that the authz process is ready to serve decisions. Unauthenticated and not org-scoped, for the same reason health is: readiness is a property of this process, not of any one tenant's policy set.","tags":["authz"],"x-app":"authz"}},"/v1/auto/flows":{"get":{"operationId":"get_v1_auto_flows","summary":"Flows lists the caller's flows, newest first.","description":"Flows lists the caller's flows, newest first. The list is scoped by the\nproduct to the caller's org — it can only ever hold the caller's own flows.","tags":["auto"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"},"post":{"operationId":"post_v1_auto_flows","summary":"Creates a flow in the caller's org.","description":"Creates a flow in the caller's org. The org is stamped\nserver-side from the validated principal — there is no field by which a\ncaller could place a flow in another org.","tags":["auto"],"requestBody":{"content":{"application/json":{"example":{"data":{"edges":[],"nodes":[{"id":"t","type":"webhook"}]},"name":"notify-on-signup"},"schema":{"$ref":"#/components/schemas/autoCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/flows/{flow}":{"delete":{"operationId":"delete_v1_auto_flows_by_flow","summary":"Deletes one of the caller's flows.","description":"Deletes one of the caller's flows. A foreign id answers 404 and\ndeletes nothing.","tags":["auto"],"parameters":[{"name":"flow","in":"path","required":true,"description":"Flow is the flow's id, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"},"get":{"operationId":"get_v1_auto_flows_by_flow","summary":"Flow reads one of the caller's flows — the full record, graph included.","description":"Flow reads one of the caller's flows — the full record, graph included. A\nflow outside the caller's org answers 404, indistinguishable from one that\ndoes not exist.","tags":["auto"],"parameters":[{"name":"flow","in":"path","required":true,"description":"Flow is the flow's id, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"},"patch":{"operationId":"patch_v1_auto_flows_by_flow","summary":"Patches one of the caller's flows: the name, the graph, or both — only the stated fields move.","description":"Patches one of the caller's flows: the name, the graph, or both\n— only the stated fields move.","tags":["auto"],"parameters":[{"name":"flow","in":"path","required":true,"description":"Flow is the flow's id, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"notify-on-signup-v2"},"schema":{"$ref":"#/components/schemas/autoUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/flows/{flow}/publish":{"post":{"operationId":"post_v1_auto_flows_by_flow_publish","summary":"Publish snapshots the flow's current graph as its next immutable version and arms the flow's triggers.","description":"Publish snapshots the flow's current graph as its next immutable version\nand arms the flow's triggers. Past versions stay addressable in the product\nfor rollback; runs always execute the graph as it was dispatched.","tags":["auto"],"parameters":[{"name":"flow","in":"path","required":true,"description":"Flow is the flow's id, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/pieces":{"get":{"operationId":"get_v1_auto_pieces","summary":"Pieces lists the product's built-in piece catalog: the trigger and action types a flow's nodes can use (webhook, schedule, http, set, branch), each with its input descriptors.","description":"Pieces lists the product's built-in piece catalog: the trigger and action\ntypes a flow's nodes can use (webhook, schedule, http, set, branch), each\nwith its input descriptors. The catalog is compiled into the product —\nadding a piece is a product release, not a platform call.","tags":["auto"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/runs":{"get":{"operationId":"get_v1_auto_runs","summary":"Runs lists the caller's run records, newest first — optionally one flow's.","description":"Runs lists the caller's run records, newest first — optionally one flow's.\nEach record carries the run's status (queued, running, completed, failed),\nits input, and its output once the run finished.","tags":["auto"],"parameters":[{"name":"flow","in":"query","required":false,"description":"Flow narrows the list to one flow's runs when present.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"},"post":{"operationId":"post_v1_auto_runs","summary":"Start begins one asynchronous run of a flow: the product dispatches the graph to its durable execution engine (the hanzo tasks plane) and answers immediately with the run record in status running.","description":"Start begins one asynchronous run of a flow: the product dispatches the\ngraph to its durable execution engine (the hanzo tasks plane) and answers\nimmediately with the run record in status running. Poll the run until it\nreaches completed — its output then holds each node's result keyed by node\nid — or failed, with the error. A flow whose engine is unreachable answers\nthe product's 503: dispatch is real or it is refused, never queued into the\nvoid.","tags":["auto"],"requestBody":{"content":{"application/json":{"example":{"flow":"k2fj93m1x8qplzv","input":{"who":"world"}},"schema":{"$ref":"#/components/schemas/autoStart"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/runs/{run}":{"get":{"operationId":"get_v1_auto_runs_by_run","summary":"Run reads one run record: status, input, output (each executed node's result keyed by node id once completed), error detail if it failed, and timestamps.","description":"Run reads one run record: status, input, output (each executed node's\nresult keyed by node id once completed), error detail if it failed, and\ntimestamps. A run outside the caller's org answers 404.","tags":["auto"],"parameters":[{"name":"run","in":"path","required":true,"description":"Run is the run's id, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"auto"}},"/v1/auto/status":{"get":{"operationId":"get_v1_auto_status","summary":"Status reports whether the auto service is reachable — its own health endpoint as an honest lens for \"is the automation plane up\".","description":"Status reports whether the auto service is reachable — its own health\nendpoint as an honest lens for \"is the automation plane up\".","tags":["auto"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/autoStatus"}}},"description":"ok"}},"x-app":"auto"}},"/v1/automations/connectors":{"get":{"operationId":"get_v1_automations_connectors","summary":"Connectors returns the connector catalogue.","description":"Connectors returns the connector catalogue. Each entry is an external service a\nflow step can invoke, carrying its auth descriptor and the input properties of its\nactions and triggers. The catalogue is the same for every tenant, so the gate is a\nvalidated principal rather than a per-org view.","tags":["automations"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/connectors/{id}/run":{"post":{"operationId":"post_v1_automations_connectors_by_id_run","summary":"Run executes one connector action in-process and answers the outcome.","description":"Run executes one connector action in-process and answers the outcome. The\ncaller's resolved credential travels in `auth`, delivered to the action\nverbatim — the runtime resolves no credential itself. An action that ran and\nfailed (or an action name the connector does not have) answers ok:false with\nthe failure message, not an HTTP error; an unknown connector is 404 and a\nmissing action 422.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the connector to run, from the path.","schema":{"type":"string"},"example":"notion"}],"requestBody":{"content":{"application/json":{"example":{"action":"create_page","auth":"secret-token","id":"notion","props":{"title":"Hello"}},"schema":{"$ref":"#/components/schemas/runIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runResp"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/flows":{"get":{"operationId":"get_v1_automations_flows","summary":"Returns the caller org's automations, most-recently-updated first.","description":"Returns the caller org's automations, most-recently-updated first. The\noptional `limit` query bounds the page.","tags":["automations"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit bounds the page (default 200, maximum 1000).","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/flowPage"}}},"description":"ok"}},"x-app":"automations"},"post":{"operationId":"post_v1_automations_flows","summary":"Creates an automation and its initial DRAFT version in one call.","description":"Creates an automation and its initial DRAFT version in one call. The\nnew flow is DISABLED — creating it does not arm its trigger; POST\n/v1/automations/flows/{id}/enable does that.","tags":["automations"],"requestBody":{"content":{"application/json":{"example":{"displayName":"Nightly Sync","trigger":{"displayName":"Start","name":"trigger","settings":{"pieceName":"core","triggerName":"manual"},"strategy":"MANUAL","type":"PIECE_TRIGGER"}},"schema":{"$ref":"#/components/schemas/createFlowReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/populatedFlow"}}},"description":"created"}},"x-app":"automations"}},"/v1/automations/flows/{id}":{"delete":{"operationId":"delete_v1_automations_flows_by_id","summary":"Deletes one automation, its versions and its run history.","description":"Deletes one automation, its versions and its run history. It answers\nno content, and a flow of another org answers not-found.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to act on, from the path.","schema":{"type":"string"},"example":"flow_1"}],"responses":{"204":{"description":"no content"}},"x-app":"automations"},"get":{"operationId":"get_v1_automations_flows_by_id","summary":"Returns one automation and its latest version.","description":"Returns one automation and its latest version. That is the flow record\nplus the step tree the builder edits; a flow of another org answers not-found.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to act on, from the path.","schema":{"type":"string"},"example":"flow_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/populatedFlow"}}},"description":"ok"}},"x-app":"automations"},"patch":{"operationId":"patch_v1_automations_flows_by_id","summary":"Updates one automation's metadata in place.","description":"Updates one automation's metadata in place. Every field is optional; a\nfield the request omits is left alone. Publishing a version pins which one runs,\nand is refused unless that version belongs to this flow.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to update, from the path.","schema":{"type":"string"},"example":"flow_1"}],"requestBody":{"content":{"application/json":{"example":{"folderId":"ops","id":"flow_1"},"schema":{"$ref":"#/components/schemas/patchFlowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/flows/{id}/disable":{"post":{"operationId":"post_v1_automations_flows_by_id_disable","summary":"Disarms a flow's trigger and marks it DISABLED.","description":"Disarms a flow's trigger and marks it DISABLED. Its schedule and its\nevent subscriptions are dropped, so a disabled flow is never a live target; runs\nalready in flight are unaffected, and it can still be started on demand.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to act on, from the path.","schema":{"type":"string"},"example":"flow_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/flows/{id}/enable":{"post":{"operationId":"post_v1_automations_flows_by_id_enable","summary":"Arms a flow's trigger and marks it ENABLED.","description":"Arms a flow's trigger and marks it ENABLED. A POLLING trigger gets a\ncron schedule on the durable engine; a WEBHOOK trigger gets a subscription in the\nrouting index, so an inbound event starts it; a MANUAL trigger arms nothing and\nstill runs on demand.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to act on, from the path.","schema":{"type":"string"},"example":"flow_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Flow"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/flows/{id}/operations":{"post":{"operationId":"post_v1_automations_flows_by_id_operations","summary":"Edit a flow — rename it, retarget its trigger, or add, move and delete steps","description":"Applies ONE flow operation and answers the thing it changed. The operation is named by `type`, with its arguments under `request`: `CHANGE_NAME`, `UPDATE_TRIGGER`, `ADD_ACTION`, `UPDATE_ACTION`, `MOVE_ACTION`, `DELETE_ACTION` edit the flow's LATEST version and answer with that version, and `CHANGE_STATUS` instead enables or disables the flow and answers with the FLOW. Two response shapes on one address is the rule a reader would otherwise get wrong, and it is why this route is not a typed op.\n\nEdits land on the latest version only — the published version a run executes is untouched until it is republished — and the whole resulting step tree is re-validated against the step-count and size caps after every operation, so a long sequence of `ADD_ACTION` calls cannot grow a flow past a bound one step at a time (422 when it would). Org-scoped and fails closed: a validated principal is required (403 without one), the flow and its version are read under the caller's OWN org so another tenant's id is a 404, and an operation whose `request` does not decode is a 400.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"automations"}},"/v1/automations/flows/{id}/run":{"post":{"operationId":"post_v1_automations_flows_by_id_run","summary":"Starts one durable run of a flow now.","description":"Starts one durable run of a flow now. It runs the flow's published\nversion if one is pinned, else its latest, and answers the run record it created.\nThe run is bounded by the org's per-minute run-start budget and its in-flight\nconcurrency ceiling; over either, or with the engine not ready, no run is started\nand no run id is burned.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to act on, from the path.","schema":{"type":"string"},"example":"flow_1"}],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRun"}}},"description":"created"}},"x-app":"automations"}},"/v1/automations/flows/{id}/versions":{"get":{"operationId":"get_v1_automations_flows_by_id_versions","summary":"Returns one flow's versions, newest first.","description":"Returns one flow's versions, newest first. The optional `limit`\nquery bounds the page.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow whose versions to list, from the path.","schema":{"type":"string"},"example":"flow_1"},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page (default 200, maximum 1000).","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/versionPage"}}},"description":"ok"}},"x-app":"automations"},"post":{"operationId":"post_v1_automations_flows_by_id_versions","summary":"Adds a new DRAFT version to a flow.","description":"Adds a new DRAFT version to a flow. The version is created invalid\nunless it carries a trigger, and it does not become the running version until it\nis published (PATCH the flow's publishedVersionId) or becomes the latest.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the flow to add a version to, from the path.","schema":{"type":"string"},"example":"flow_1"}],"requestBody":{"content":{"application/json":{"example":{"displayName":"v2","id":"flow_1"},"schema":{"$ref":"#/components/schemas/createVersionIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowVersion"}}},"description":"created"}},"x-app":"automations"}},"/v1/automations/hooks/{source}/{event}":{"post":{"operationId":"post_v1_automations_hooks_by_source_by_event","summary":"Fire an event that starts every enabled flow subscribed to it","description":"Delivers one event to the org's automation triggers and answers `{matched:n}` — how many enabled flows had a webhook trigger on this `(source, event)` key and were started by it. A zero match is a success, not an error: nothing was subscribed.\n\nThe path is the trigger key and the JSON object body is the event payload, threaded into each started run as `{{trigger.*}}` with all of its keys intact — which is why this is not a typed op, since a declared input struct would silently DISCARD every payload key it had no field for. Re-delivery is a no-op: an `X-Idempotency-Key` header dedupes, and with none the body is content-hashed instead, so a hammer of identical posts collapses to ONE run rather than minting a fresh one per post. An in-platform producer may propagate `X-Causation-Depth` so a firing that a flow caused is bounded against a loop; an absent or invalid header reads as depth 0, an external origin.\n\nAuthenticated and org-scoped, unlike a provider's public webhook URL: a validated principal is required (403 without one) and the org is that principal's, never the body's, so a producer can only fire into its own tenant's flows. Both path segments are required (400) and a payload over the size limit is a 413.","tags":["automations"],"parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"}},{"name":"event","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"automations"}},"/v1/automations/pieces":{"get":{"operationId":"get_v1_automations_pieces","summary":"Pieces is the retired-name alias of the connector catalogue.","description":"Pieces is the retired-name alias of the connector catalogue. It serves exactly\nwhat GET /v1/automations/connectors serves, under the name this surface used\nbefore \"piece\" (the ActivePieces term) became \"connector\", and stays valid for\nclients pinned to the old path. Prefer /connectors.","tags":["automations"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/runs":{"get":{"operationId":"get_v1_automations_runs","summary":"Returns the caller org's run history, newest first.","description":"Returns the caller org's run history, newest first. The optional\n`flowId` query narrows it to one flow and `limit` bounds the page.","tags":["automations"],"parameters":[{"name":"flowId","in":"query","required":false,"description":"FlowID narrows the history to one flow. Omit it for the whole org's runs.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page (default 200, maximum 1000).","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runPage"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/runs/{id}":{"get":{"operationId":"get_v1_automations_runs_by_id","summary":"Returns one run.","description":"Returns one run. A run that has not reached a terminal status is refreshed\nfrom the durable engine first — scoped to the org's own namespace — so the caller\nsees live progress rather than the last status that happened to be persisted.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the run to read, from the path.","schema":{"type":"string"},"example":"run_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowRun"}}},"description":"ok"}},"x-app":"automations"}},"/v1/automations/runs/{id}/resume":{"post":{"operationId":"post_v1_automations_runs_by_id_resume","summary":"Release a run waiting at an approval step, with the approval payload","description":"Delivers the durable `resume` signal to a run parked on a `wait_for_approval` waitpoint and answers `{resumed:true}` once the engine has taken it.\n\nThe body is an ARBITRARY JSON value — object, array, string, number — delivered VERBATIM into the workflow as that waitpoint's output, so it is what the steps after the approval read as their input. An empty body resumes with no payload. That open shape is why this route is not a typed op: an operation's input can carry the payload or the run address, never both.\n\nOrg-scoped and fails closed: a validated principal is required (403 without one), the run is read under the caller's OWN org so another tenant's run id is a 404, a body that is not JSON is a 400, and a payload over the size limit is a 413 — it becomes durable engine state, so it is bounded here rather than after it lands. The resume is audited as `automations.run.resume`.","tags":["automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"automations"}},"/v1/avatar":{"post":{"operationId":"post_v1_avatar","summary":"Set your profile photo","description":"Stores one image as the signed-in user's profile photo and answers the URL it is served from, which is also written to the user's IAM record — so every surface that already renders `avatar` picks it up with no further call.\n\nThe body is a multipart form with a `file` part. The format is decided by the BYTES, never the filename or the part's Content-Type: png, jpeg, gif and webp are accepted and everything else is refused with 415, so an SVG cannot be stored as a picture and later served as a program. Over 8 MiB is 413; empty is 400.\n\nThe photo is addressed by the sha256 of its bytes, so setting a new one yields a new URL rather than a stale cache of the old face. The caller is taken from the validated identity ONLY — there is no way to name a different subject — so this always sets your own photo, and a caller with no organization yet is refused.","tags":["avatar"],"x-app":"account"}},"/v1/avatar/{org}/{user}/{digest}":{"get":{"operationId":"get_v1_avatar_by_org_by_user_by_digest","summary":"Fetch a profile photo","description":"Streams a profile photo's raw BYTES. This is the address stored on the user's IAM record and rendered directly by an `\u003cimg\u003e`, so it takes no credentials — the 64-hex content digest in the path is the capability, and it can only be produced by someone who already has the image.\n\nThe Content-Type is derived from the stored bytes and the response carries nosniff, so only a real raster image is ever served and only under its true type. Anything else — a miss, a malformed path, an object that is not an image — is one 404, and a hit caches for a year because the address is the content.","tags":["avatar"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"user","in":"path","required":true,"schema":{"type":"string"}},{"name":"digest","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"account"}},"/v1/balancers":{"get":{"operationId":"get_v1_balancers","summary":"Returns every load balancer the caller's org owns, under the friendly names the org created them with.","description":"Returns every load balancer the caller's org owns, under the\nfriendly names the org created them with. Same account-wide filter as the VPC\nlisting: a load balancer outside the caller's \"o\"\u003corgHash\u003e- namespace is never\nin the answer.","tags":["balancers"],"responses":{"200":{"content":{"application/json":{"example":{"loadBalancers":[{"id":"lb-1","ip":"10.0.0.1","name":"edge","status":"active","targets":3,"type":"REGIONAL"}]},"schema":{"$ref":"#/components/schemas/lbList"}}},"description":"ok"}},"x-app":"do"},"post":{"operationId":"post_v1_balancers","summary":"Creates a load balancer in the caller's org namespace and answers 201 with it.","description":"Creates a load balancer in the caller's org namespace and\nanswers 201 with it. The physical DigitalOcean name is derived server-side from\nthe validated org; a name that already exists there is a 409. Omitting\nforwarding rules yields a usable HTTP 80→80 load balancer rather than a 422.","tags":["balancers"],"requestBody":{"content":{"application/json":{"example":{"forwarding_rules":[{"entry_port":443,"entry_protocol":"https","target_port":8080,"target_protocol":"http"}],"name":"edge","region":"nyc3"},"schema":{"$ref":"#/components/schemas/createLBReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/lbView"}}},"description":"created"}},"x-app":"do"}},"/v1/balancers/{id}":{"delete":{"operationId":"delete_v1_balancers_by_id","summary":"Removes one of the caller org's load balancers and answers 204.","description":"Removes one of the caller org's load balancers and answers\n204. Ownership is confirmed by re-fetching the resource before anything is\ndeleted, so a cross-tenant id is a 404 rather than a delete of another org's\nload balancer.","tags":["balancers"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DigitalOcean resource id (a UUID), from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"do"},"get":{"operationId":"get_v1_balancers_by_id","summary":"Returns one of the caller org's load balancers by id.","description":"Returns one of the caller org's load balancers by id. One that\nexists in another org's namespace is reported 404, never 403 — the same\nexistence-oracle guard the VPC read applies.","tags":["balancers"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DigitalOcean resource id (a UUID), from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/lbView"}}},"description":"ok"}},"x-app":"do"}},"/v1/base/health":{"get":{"operationId":"get_v1_base_health","summary":"Reports that the base subsystem is serving.","description":"Reports that the base subsystem is serving.\n\nIt is deliberately INDEPENDENT of whether this deployment actually embeds the\nBase engine: the route answers before the CLOUD_BASE_EMBED gate and before the\n/v1/base/* wildcard, so a liveness probe measures the process rather than an\noptional feature, and the wildcard can never shadow it. It reads no tenant, so a\nprober that sends no principal is answered rather than refused.","tags":["base"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/baseHealth"}}},"description":"ok"}},"x-app":"base"}},"/v1/benchmark/catalog":{"get":{"operationId":"get_v1_benchmark_catalog","summary":"The canonical public benchmarks this arena runs","description":"Lists the top-14 set every major provider reports — the id, title, axis, item count and upstream source of each — with `native` marking the ones the standardized harness runs today; the rest are registered and adapter-pending. These ids are the vocabulary the rest of the surface takes: a run names them, and the leaderboard and compare read them from `?benchmark=`. The catalog is deployment-wide and identical for every caller — there is no tenant in it.","tags":["benchmark"],"x-app":"benchmark"}},"/v1/benchmark/compare":{"get":{"operationId":"get_v1_benchmark_compare","summary":"The only sound head-to-head: two models on the items they BOTH answered","description":"Scores model `?a=` against model `?b=` on one benchmark, paired over the items both arms actually completed. It answers the common-item count, each arm's correct count, the rescues each way (items one got right and the other did not), the net, and a two-sided exact McNemar p over the discordant pairs.\n\nPairing is what makes it valid. Reading two leaderboard rows against each other compares one model's coverage with another's, so an arm that only ran the easy subset looks better than it is; this endpoint refuses that by construction — items only one arm attempted are dropped before anything is counted. A p of 1 with zero discordant pairs means the arms never disagreed, not that they are identical. Both `a` and `b` are required (400 without them); the benchmark defaults to GPQA-Diamond.","tags":["benchmark"],"x-app":"benchmark"}},"/v1/benchmark/leaderboard":{"get":{"operationId":"get_v1_benchmark_leaderboard","summary":"Per-model scores for one benchmark: what we measured beside what the vendor claims","description":"Answers one row per model for the benchmark named by `?benchmark=` (GPQA-Diamond when omitted), carrying `measured` — the accuracy our own harness got — beside `published`, the provider's own claim, and `gap`, the claim minus the measurement. The gap is the point of the arena; provider-reported claims have run materially hot against one standardized harness.\n\nThe two planes are NEVER blended, and that is the rule to read the rows by: a model we have measured but no vendor has claimed for shows `published` null, a model with only a claim shows `measured` null, and `gap` exists only where both do. Each row also carries `n`, the number of items actually attempted — coverage differs between models, so two `measured` values at different `n` are not comparable and the compare endpoint is what settles that properly. Rows are ordered by measured accuracy, unmeasured last. Scores are deployment-wide evidence, not per-tenant.","tags":["benchmark"],"x-app":"benchmark"}},"/v1/benchmark/presets":{"get":{"operationId":"get_v1_benchmark_presets","summary":"The router blends available to compose from","description":"Lists preset router blends — a named set of model `arms`, the `rank` they escalate through and the `panel` width that bounds fan-out — each served by the model layer as `enso-\u003cname\u003e`. Today it answers exactly one row, the reference blend: a worked example written in models we name, published as an example of the FORM. It is deliberately not the composition of a Hanzo-served tier — the tier name exists to abstract that — so fork it and swap arms by what the leaderboard measures on your own tasks rather than reading it as a disclosure.","tags":["benchmark"],"x-app":"benchmark"},"post":{"operationId":"post_v1_benchmark_presets","summary":"Compose a router blend from the arms that win your tasks","description":"Validates a blend — `name`, its `arms`, the `rank` they escalate through and the `panel` fan-out width — and answers 202 with the preset and the `enso-\u003cname\u003e` it would be served as. It VALIDATES AND ECHOES: the definition is not persisted yet, so a preset accepted here is not one the model layer will resolve. Treat the response as a check on the blend, not a promise to serve it.\n\nDefaults fill the shape rather than refusing it: an omitted `rank` becomes the arms in declared order and a `panel` below 1 becomes 1. The one real invariant is that rank may only name arms the blend declares — the same rule the model catalog enforces — and a rank naming anything else is a 422 listing exactly which entries were undeclared. A blend with no name or no arms is a 400.","tags":["benchmark"],"x-app":"benchmark"}},"/v1/benchmark/runs":{"post":{"operationId":"post_v1_benchmark_runs","summary":"Queue a benchmark run against a catalog model or your own endpoint","description":"Admits a request to run one or more catalog benchmarks against `model` — a catalog model id — or against `endpoint`, an endpoint of your own on the chat-completions wire, and answers 202 with what was queued. It ADMITS AND QUEUES ONLY: nothing is executed on this call and no scores come back with it. Results land in the leaderboard as the worker completes them.\n\nCost is bounded by the store rather than by a quota: attempts are append-only and keyed by (benchmark, item, model), so an (item, model) pair already attempted is skipped instead of re-spent, and re-queuing the same run is close to free. Validation is up front and total — a request with neither `model` nor `endpoint` is a 400, one with no benchmarks is a 400, and any benchmark id outside the catalog is a 422 naming exactly which ids were unknown, so a typo never silently queues a partial run.","tags":["benchmark"],"x-app":"benchmark"}},"/v1/billing/accounts":{"get":{"operationId":"get_v1_billing_accounts","summary":"The billing account you are signed in to","description":"Returns the billing accounts visible to the caller. One organisation is exactly one billing account here, so an authenticated caller sees precisely one: their own. The list shape is the honest one — it is what a caller with access to several would receive — rather than a promise that more will ever appear for a token scoped to a single org.\n\nThe account is derived from the validated org claim and from nothing the caller sends, so there is no account parameter and a cross-tenant read is not expressible. An unauthenticated call is 401.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/accounts/{id}/members":{"get":{"operationId":"get_v1_billing_accounts_by_id_members","summary":"Who is on a billing account","description":"Returns the members of one billing account. The id must be the caller's OWN account — the handler compares it against the org resolved from the token and answers 403 when they differ, which is what guards this route: unlike its siblings it carries no subject key for the pin to overwrite, so it checks the path segment itself.\n\nThe roster it can answer is currently the requesting user alone. Membership lives in IAM, not in the ledger, and this operation reports what commerce actually holds rather than inventing a roster from a source it does not read. An unauthenticated call is 401.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/alerts":{"get":{"operationId":"get_v1_billing_alerts","summary":"List your org's spend caps and rate limits","description":"Returns the caps and alerts keyed to the caller's own billing subject, each with its threshold, enforcement flag, soft-warning percentage and current period spend. Any authenticated member of the org may read them — only the writes require an admin. The rows are keyed on the org subject the enforcement gate itself reads, which is why a cap created here is the one that actually binds. A caller with no resolvable org or subject gets an empty list, never another tenant's caps.","tags":["billing"],"x-app":"commerce"},"post":{"operationId":"post_v1_billing_alerts","summary":"Set a spend cap or rate limit on your org","description":"Creates a cap for the caller's own org and answers the stored row with its current period spend. A spend cap is a FINANCIAL SAFETY control, so writing one requires an ORG ADMIN, a platform admin, or the internal service token — a plain authenticated member is refused 403, because a member who could delete the cap could uncap the org's spend and a member who could set a one-cent enforcing cap could deny the whole org. The cap is always keyed to the caller's own billing subject: a userId in the body is overwritten, never honored, so a cap cannot be planted on another subject. At least one of a positive threshold or a positive rateLimitRpm is required, softPct must be within 0 to 100, and an org that has reached its row limit is refused 400.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/alerts/authorize":{"get":{"operationId":"get_v1_billing_alerts_authorize","summary":"The per-request spend-cap verdict the metering gate consumes","description":"Answers allow, reason, capCents, spentCents and warnPct for a proposed amount against a (project, service) scope — the verdict the request-edge metering gate reads before admitting a call. It evaluates EVERY covering cap and the most restrictive enforcing one wins; soft caps and an enforcing project cap whose project axis is not validated never block, they only raise the warning utilization. It is a service-to-service read authenticated by the internal service token with the org pinned by the gateway, not a browser call. Two rules matter: the spend it scores comes from the finance ledger's current-month total, and it FAILS OPEN on unknown spend — a transient read failure allows rather than denies, so a backend blip never bills-blocks an under-cap customer, while a known overage still denies.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/alerts/{id}":{"delete":{"operationId":"delete_v1_billing_alerts_by_id","summary":"Remove one of your org's spend caps","description":"Deletes the addressed cap and answers 204. Requires an ORG ADMIN, a platform admin, or the internal service token — deleting a cap uncaps the org's spend, so a plain member is refused 403. Ownership is checked per row and a cap the caller does not own is refused as 404 rather than 403, so the response cannot confirm that another org's id exists.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_billing_alerts_by_id","summary":"Change one of your org's spend caps","description":"Applies only the fields the body actually carries — title, threshold, project, service, enforce, softPct, rateLimitRpm — and leaves the rest as stored, answering the merged row with its current period spend. Requires an ORG ADMIN, a platform admin, or the internal service token, for the same reason creation does: a member who could edit the cap could raise it to nothing or drop it to a punitive floor. Ownership is checked per row and a cap the caller does not own is refused as 404, never 403, so the id space cannot be probed.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/balance":{"get":{"operationId":"get_v1_billing_balance","summary":"Prepaid credit the caller's org can still spend","description":"Answers the spendable prepaid balance of the wallet this caller bills from — the same wallet the AI prepaid gate reads before admitting a paid request, the edge meter debits, and a top-up credits.\n\nThe wallet is an ADDRESS, not an org: `account` echoes the key resolved within the ledger — the org's shared pool for a tenant org, a personal account for a member of the shared signup org. The echo is the point. A browser could only GUESS its own payer by decoding its own token, and a guess that disagrees with the server is how money lands in an account the gate never reads.\n\n`balance`, `holds` and `available` are whole USD cents, ROUNDED from the ledger's exact 18-decimal value. On the co-resident ledger `holds` is 0 and `available` equals `balance`: the gate's reservations live in its own pod and are never posted, so the settled balance IS the spendable one.\n\nThe ledger is the caller's own org, taken from the VALIDATED IAM owner claim and never from a client header. No validated principal is 401 — with one exception, the trusted in-process service token the AI gate itself presents, which reads the gateway-pinned org and nothing it could name. A balance that cannot be READ is 502, never 0: unknown is not broke.","tags":["billing"],"x-app":"billing"}},"/v1/billing/credit-balance":{"get":{"operationId":"get_v1_billing_credit-balance","summary":"What is left of your credit, as one number","description":"Returns the total credit still available to the caller's own subject — the sum of what the grants have left, which is the figure the console shows above the usage meter. It is the balance a metered act draws down, so it answers the one question a customer asks before spending: how much is there.\n\nLike every read in this family the subject is pinned to the caller before the handler runs, so the userId parameter the handler reads can never name another tenant. For the grants BEHIND this number — each with its original amount and its expiry — read /v1/billing/credits. A subject with no credit is zero, which is an answer and not an error.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/credits":{"get":{"operationId":"get_v1_billing_credits","summary":"List the credit grants on your org's balance","description":"Returns the caller org's credit grants — each with its original amount, what remains and when it expires — so a customer can see what was given and what is left before metered spend draws it down. It is a READ of the caller's own subject, pinned before the handler runs, so a grant belonging to another tenant is simply absent. Granting credit is not this route and never has been: minting lands on the mint-gated POST /v1/billing/credit, which no browser can reach. Reading an empty balance is an empty array, not an error.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/crypto/deposit":{"post":{"operationId":"post_v1_billing_crypto_deposit","summary":"Get a deposit address for a crypto top-up","description":"Mints a deposit address held by the MPC signer fleet — no single party holds the key — on the chain and token you name, and returns it with the intent that tracks it.\n\nThe account credited is the PINNED caller's, never a value in the body, so a deposit cannot be aimed at someone else's balance. A caller who already has an open intent gets that same address back rather than a new one, so reloading the page cannot spray keygens across the signer fleet.\n\nNO BALANCE MOVES HERE. This hands out an address; the chain watcher credits the account when a real transfer confirms, which is also why an address handed out and never funded costs nothing and expires nothing.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/crypto/deposit/{id}":{"get":{"operationId":"get_v1_billing_crypto_deposit_by_id","summary":"Follow one crypto deposit to settlement","description":"Answers the addressed deposit intent's current state — pending until a transfer is seen, confirming while the chain buries it, succeeded once it is credited — so a payment page can poll one deposit rather than the whole balance.\n\nScoped to the caller: an intent belonging to another payer is not found and answers 404, never another account's state. The credit itself is the chain watcher's to make; this read reports it and never performs it.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/crypto/options":{"get":{"operationId":"get_v1_billing_crypto_options","summary":"Which chains and tokens a crypto top-up can use","description":"Answers the custody processor's LIVE capability list — the chains and the tokens on each that this deployment can actually take a deposit on. A payment page renders its asset picker straight from it rather than from a list of its own, so a chain the processor stops supporting disappears from the picker instead of minting an address nothing watches.\n\nIt is a capability read, not an account read: it says what may be paid with, never anything about this caller's balance or deposits.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/invoices":{"get":{"operationId":"get_v1_billing_invoices","summary":"List your org's billing invoices","description":"Returns the caller org's invoices with a count, read from that org's own namespaced store, narrowable by userId, status or subscriptionId. The org is the one the gateway validated and the caller's billing subject is pinned into the query before the handler runs, so a read can never widen past the caller. A request that carries no resolvable org gets an honest empty list rather than an error or another tenant's rows.","tags":["billing"],"x-app":"commerce"},"post":{"operationId":"raiseInvoice","summary":"Raise a draft invoice against a customer","description":"Raises a DRAFT invoice against a customer in the caller's own org.\n\nThe invoice is not collectible yet: a draft exists so it can be read and\ncorrected, and issueInvoice is the separate act that turns it into a demand for\npayment. The subtotal and amount due are computed from the lines, so there is\nno total to send and none to get wrong.\n\nThe billing org is the caller's, taken from the validated principal, so an\ninvoice can only ever be raised on the caller's own books.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RaiseInvoiceIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}},"description":"created"}},"x-app":"commerce"}},"/v1/billing/invoices/{id}":{"get":{"operationId":"getInvoice","summary":"Read one invoice","description":"Reads one invoice out of the caller's org.\n\nThe org scopes the read by construction — the store is namespaced to it — so an\nid belonging to another tenant is not found rather than found and then filtered.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the invoice id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/billing/invoices/{id}/collect":{"post":{"operationId":"collectInvoice","summary":"Collect an issued invoice from credits, balance, then card","description":"Collects an issued invoice: credit grants first, then prepaid balance, then the\ncard on file — the same waterfall the dunning workflow runs.\n\nA DECLINE IS NOT AN ERROR. It answers with paid=false, a reason, and the\ninvoice still open, because a declined collection is a normal business outcome\nthat must remain retryable — and because sealing it as a failure would wedge\ndunning behind a replayed decline. Only a successful collection is sealed, so a\nretry of a paid invoice replays the receipt instead of charging again.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the invoice id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectOut"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/billing/invoices/{id}/issue":{"post":{"operationId":"issueInvoice","summary":"Issue a draft invoice, making it collectible","description":"Issues a draft invoice: moves it to OPEN, assigns its number, and makes it\ncollectible.\n\nOnly a draft can be issued. An invoice already open, paid or void is refused\nwith the state machine's own reason rather than being silently re-issued, which\nwould mint a second number for one debt.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the invoice id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/billing/invoices/{id}/pdf":{"get":{"operationId":"get_v1_billing_invoices_by_id_pdf","summary":"Download one invoice as a PDF attachment","description":"Renders the addressed invoice as a single-page PDF and answers it as an attachment named after the invoice number. The render is a pure function of the invoice — no timestamps, no random ids — so the same invoice always produces identical bytes and a re-download is stable. The invoice is resolved inside the caller org's own namespace, so an id belonging to another tenant is simply absent and reads as 404; a caller with no validated org gets 401 rather than a document.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/invoices/{id}/void":{"post":{"operationId":"voidInvoice","summary":"Void a draft or issued invoice","description":"Voids a draft or issued invoice — the cancel.\n\nA paid invoice cannot be voided: money has moved, and the correction for that\nis a refund, not an erasure. The state machine refuses it and that refusal is\nthe answer.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the invoice id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceOut"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/billing/methods":{"get":{"operationId":"get_v1_billing_methods","summary":"Your saved cards, masked — the customer read","description":"Answers the cards saved against your own account as masked descriptors: brand, last four, expiry and the processor's reusable reference. No card number and no security code exist here to return; both live at the processor and never enter this system. It is what a checkout prefills its payment step from.\n\nThe customer face of the list a service token reads at /v1/billing/portal/methods — same rows, different principal, no hop between them.\n\nThe subject filter is pinned to the VALIDATED caller before the handler runs, so the answer is your own account's cards whatever customerId the request carries, and another org's rows are outside the namespace entirely. A caller who is not signed in is refused before the read.","tags":["billing"],"x-app":"commerce"},"post":{"operationId":"post_v1_billing_methods","summary":"Save a card for later charges","description":"Vaults the card the processor already holds — you send its one-time reference, never a card number — as a reusable card on file, and stores the billing address with it. That vaulted card is what a subscription renewal or an auto-recharge charges later, which is why saving one is the step that makes a monthly plan billable at all.\n\nIt charges nothing. Saving a card moves no money; the first charge is whatever arrangement you then attach it to.\n\nThe subject is pinned from the validated caller and OVERWRITES the customerId in the body while leaving the card fields untouched, so a card can only ever be attached to the caller's OWN account whatever the body claims. That pin is the whole control on this write, not decoration: this is the one handler in the family that reads its subject from the body.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/methods/{id}":{"delete":{"operationId":"delete_v1_billing_methods_by_id","summary":"Remove one of your saved cards","description":"Detaches the addressed card: the stored reference is removed here AND withdrawn from the processor's vault, so nothing is left that a later charge could bill.\n\nThe customer twin of DELETE /v1/billing/portal/methods/{id}. The id is resolved INSIDE your own org namespace, so a card that is not yours is simply not found there and answers 404 — never 403, which would confirm the id exists.\n\nRemoving the card an auto-recharge or a running lease bills leaves that arrangement with nothing to charge; that is yours to decide.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/mode":{"post":{"operationId":"post_v1_billing_mode","summary":"Move an org between sandbox and live billing","description":"Flips the org's live flag, which is the single authority for both the payment environment and the ledger bucket its transactions land in. This is a money-MINT control, not a customer action: it is gated on the internal service token AND platform scope, so an ORG ADMIN CANNOT move their own org — otherwise a tenant could drop itself into sandbox and stop paying. The rule most callers get wrong is the default: an org that has never been flipped transacts in SANDBOX, which is why a production-credentialled deployment can still hand a buyer a sandbox card form. When the deployment pins the payment environment explicitly, that pin governs and this flag only marks the transactions.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/payouts":{"get":{"operationId":"get_v1_billing_payouts","summary":"List your org's payouts, newest first","description":"Returns the caller org's payout records ordered by creation time descending, read from that org's own namespaced store. The org is the gateway-validated one and the caller's billing subject is pinned before the handler runs, so the list is the caller's own and cannot be widened. A request with no resolvable org gets an empty array rather than an error.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/plans":{"get":{"operationId":"get_v1_billing_plans","summary":"The public plan catalog, annotated with the active platform promotion","description":"Returns every subscription tier a buyer can choose, each carrying the platform promo currently in effect, optionally narrowed with the category query. Prices come from the admin-editable plan authority in the database; the embedded catalog is only a loud-failing fallback, so a failed seed or a query error serves the known plans rather than a silently blank list. It is a catalog read, not an entitlement read — it says what may be bought, never what this caller has.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/portal/methods":{"get":{"operationId":"get_v1_billing_portal_methods","summary":"Cards saved against the caller's org, masked — the portal read","description":"Answers the org's saved payment methods as masked descriptors: brand, last four, expiry and the processor's reusable reference. No card number and no security code exist here to return; both live at the processor and never enter this system.\n\nThis is the SERVICE-TOKEN face of the same list a customer reads at /v1/billing/methods. Both are served here, in this process, and answer the same rows; they are two addresses because they admit two different principals, not because either forwards to the other.\n\nThe customer filter is pinned to the VALIDATED caller before the handler runs, so a browser sees only its own subject's cards whatever customerId it sends; only a caller holding the internal service token may name the subject, and the org it may name it within is fixed by the gateway. Cross-tenant is closed by the org namespace for both, so an id or a subject from another org resolves to nothing. A caller who is neither is refused before the read.","tags":["billing"],"x-app":"commerce"},"post":{"operationId":"post_v1_billing_portal_methods","summary":"Save a card on a subject's behalf — the portal attach","description":"The service-token twin of POST /v1/billing/methods: it vaults the processor's one-time reference as a reusable card on file for the named subject, with its billing address, and moves no money doing it.\n\nIt exists so an internal caller can complete the family it can already read and detach. The subject it may name is pinned to the org the gateway fixed, so the service token acts WITHIN one tenant and never across tenants; a caller holding no service token is refused before the write.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/portal/methods/{id}":{"delete":{"operationId":"delete_v1_billing_portal_methods_by_id","summary":"Remove a saved card — the portal detach","description":"Detaches the addressed card: the stored reference is removed here AND withdrawn from the processor's vault, so nothing is left that a later charge could bill.\n\nThe service-token twin of the customer's DELETE /v1/billing/methods/{id}, at its own address for the same reason the portal list is — a different principal, on the same rows, in this same process.\n\nThe id is resolved INSIDE the caller's org namespace, so another tenant's card is not found there and answers 404 — never 403, which would confirm the id exists. That bound holds for the service token too: it may act for any subject within the org the gateway pinned, and for no subject outside it.\n\nRemoving the card an auto-recharge or a running lease bills leaves that arrangement with nothing to charge; that is the customer's call to make.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/recharge/run-all":{"post":{"operationId":"post_v1_billing_recharge_run-all","summary":"Platform sweep: top up every org whose balance has fallen below its own threshold","description":"Walks every organization and, for those that enabled auto-recharge and whose available balance (balance minus holds) has fallen under their configured threshold, charges their default payment method off-session and credits the balance, answering a per-org result row for each one it touched. This is the platform cron's door, not a customer's: it is gated on the internal service token AND platform scope, so an org admin cannot run the fleet-wide sweep. An org above its threshold is skipped silently; an org with no default payment method is reported as an uncharged row with the reason rather than failing the whole run.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/settings":{"get":{"operationId":"get_v1_billing_settings","summary":"The public payment-provider config your card form needs to initialize","description":"Answers the Square application id, location id, environment and live flag the browser's card iframe boots against — public values only, never a secret. Resolution lives in one place shared with the public tenant projection, so the card form can never initialize against a different Square application than the one commerce will actually charge. It deliberately does NOT hydrate credentials from KMS: the dialog blocks on this call, so it answers from the org and the deployment environment without a round trip, and an org with no per-org credentials gets the deployment's own public app id.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/subscribe/card":{"post":{"operationId":"post_v1_billing_subscribe_card","summary":"Subscribe to a paid plan with a card, charged for the first period immediately","description":"Vaults the tokenized card as a reusable card-on-file, charges the first period, and creates the subscription — answering the subscription and invoice ids with the amount charged. The price is SERVER-AUTHORITATIVE: it is the plan's catalog price times billable seats and a client-supplied amount is never consulted, so a scripted request cannot underpay; a per-seat plan below its minimum seats is refused, and a free plan is refused outright because this address is the paid path. The card PAN never reaches this service — the browser tokenizes it and only the single-use nonce arrives here. The subject is the caller's own org, with an in-org user honored only inside that bound, and an idempotency key (or, absent one, the nonce itself) makes a retry replay the first result instead of charging twice.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/subscriptions":{"get":{"operationId":"get_v1_billing_subscriptions","summary":"List your org's subscriptions","description":"Returns the caller org's subscriptions with a count, narrowable by userId or status, read from that org's own namespaced store. The org is the gateway-validated one and the caller's billing subject is pinned before the handler runs. A request with no resolvable org gets an empty list and a zero count rather than an error.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/subscriptions/{id}/cancel":{"post":{"operationId":"post_v1_billing_subscriptions_by_id_cancel","summary":"Cancel a subscription, at period end by default","description":"Cancels the addressed subscription and answers its updated state, emitting the cancellation event the rest of the platform keys on. The default is to cancel AT PERIOD END — a body that fails to parse falls back to it — so the customer keeps what they paid for unless atPeriodEnd is explicitly false. The subscription is resolved inside the caller's own org namespace, so another tenant's id is a 404, and the write carries the browser anti-CSRF gate because it is reachable with an ambient cookie.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/subscriptions/{id}/reactivate":{"post":{"operationId":"post_v1_billing_subscriptions_by_id_reactivate","summary":"Undo a pending cancellation and keep the subscription running","description":"Clears the scheduled cancellation on the addressed subscription and answers its updated state. It is the inverse of cancel and applies to a subscription that is still within its period; one the engine will not reactivate is refused 400 with the reason. The subscription is resolved inside the caller's own org namespace, so another tenant's id reads as 404, and the write carries the browser anti-CSRF gate.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/tier":{"get":{"operationId":"get_v1_billing_tier","summary":"The subject's plan tier and the balance a metered call is admitted on","description":"Answers one subject's resolved tier — name, display name, agent ceiling and allowed models — with the balance that admits their next metered call: prepaidAvailable, creditsRemaining, dailyRemaining and the effectiveAvailable those fold into. The ai router reads it per request to pick that caller's rate-limit tier. It sits on the org-resolving chain because a tier is org state, and the subject keys are pinned to the validated caller before the handler runs, so a browser read is always the caller's own; user is required, which only a service-to-service caller can omit and be refused 400 for. The tier is an upstream tier claim, or an explicit tier override, when either is present — that is the service-to-service contract — and is otherwise DERIVED from the org's active and trialing subscriptions, the highest one winning, its paid-ness read from the plan catalog by slug rather than from the subscription's own stored copy. The rule to get right is effectiveAvailable and not prepaidAvailable: granted credits spend too, credits first, so an account funded only by a grant reads zero prepaid while holding real spendable credit — and with the daily term zero on every tier there is no free allowance behind it, so a zero-balance account is gated. A subscription-store error answers 500 rather than downgrading to free, so a transient failure never reports a paid subscriber as unsubscribed.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/topup":{"post":{"operationId":"post_v1_billing_topup","summary":"Add credit to your balance by charging one of your saved cards","description":"Charges a card the caller already has on file, named by paymentMethodId, and credits the caller's own balance — the SAVED-card twin of topup/token, sharing the one charge-and-credit core the auto-recharge cron runs on. The credit lands on the caller's OWN billing subject: the request body's subject field is pinned to the caller before the handler sees it, so a top-up can never be redirected to another subject or outside the caller's org. It is screened for risk before any money moves, exactly as the token path is, because both credit the SPENDABLE wallet. The rule most callers get wrong is that paymentMethodId is NOT covered by that subject pin — it is a card id, not a subject key — so it is checked separately, and a card belonging to any other subject answers 404 rather than 403: a permission error would confirm the id exists, which is an ownership oracle over other people's cards.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/topup/token":{"post":{"operationId":"post_v1_billing_topup_token","summary":"Add credit to your balance by charging a tokenized card once","description":"Charges the single-use card token for the given amount and credits the caller's own balance, answering the transaction id and the new balance — the one-time top-up path, with no payment method saved. The amount is bounded SERVER-SIDE (roughly a one dollar floor and a five thousand dollar ceiling by deployment policy) and the check runs before any money moves, because the browser cap is not a control against a scripted request. The credit lands on the caller's OWN billing subject — the same key the usage gate debits — and can never be redirected outside the caller's org. Retries are safe: an idempotency key, or absent one the amount within a short window, replays the first result, and if that guard store is unreachable the call is refused with 503 rather than risking a second real charge.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/transactions":{"get":{"operationId":"get_v1_billing_transactions","summary":"List the movements on your own balance, newest first","description":"Returns the caller's own ledger movements — every credit and debit against the subject the usage gate charges — newest first, with a count and the subject they belong to, so a customer can reconcile a bill against the acts that produced it. Paging is limit and offset, and the currency can be narrowed.\n\nThe subject is NOT the caller's to choose. The handler filters on a user parameter, and that parameter is overwritten with the caller's own billing subject before the handler runs — so naming another subject returns your own rows rather than theirs, and the read can never disagree with the wallet it describes. An unauthenticated call is 401 rather than 403, because a browser re-authenticates on the first and only reports the second. No movements is an empty list, not an error.","tags":["billing"],"x-app":"commerce"}},"/v1/billing/usage":{"get":{"operationId":"get_v1_billing_usage","summary":"Every billed call the caller's org made, attributed to a product","description":"Answers one row per BILLED call against the caller's org — transaction id, amount, timestamp and the metered unit. This is the raw charged ledger, not a rollup.\n\nEach row is stamped with a canonical `metadata.product` derived from what the meter persisted: `agent` becomes agents, `provisioning` becomes the provisioned kind, a token-metered row becomes inference, anything else keeps its metering surface. The ledger has no product field of its own, so this read is where that dimension is made real — from the SAME charged rows, never a second meter. A row that already carries its own product WINS, so the derivation stops the day the meter records one.\n\n`product=\u003cid\u003e` filters to one product server-side. `groupBy=product` reduces to `{product,requests,amountCents}` rollups instead of rows.\n\n`amount` is whole USD cents, ROUNDED; `decimal` beside it is the SAME debit exact, as an 18-decimal USD string. Sum `decimal`. A page of sub-cent token calls totals correctly there and totals ZERO in `amount` — that difference is real money.\n\nScoped to the caller's own org's books, where the org's ledger file IS the tenant boundary; no client-supplied subject is ever forwarded. 401 without a validated principal. The co-resident read returns the 2000 most recent debits, newest first; `start` and `end` narrow the window only on the split-deploy upstream.","tags":["billing"],"x-app":"billing"}},"/v1/billing/usage/accounts":{"get":{"operationId":"get_v1_billing_usage_accounts","summary":"Answers per-account totals for the linked provider accounts the gateway ROUTED this caller's traffic through — requests, prompt and completion tokens, recorded cost — plus their honest sum.","description":"Answers per-account totals for the linked provider accounts the\ngateway ROUTED this caller's traffic through — requests, prompt and completion\ntokens, recorded cost — plus their honest sum.\n\nThis is the one read in the billing namespace scoped to the PERSON, not the\norg. Rows are keyed on (validated org, validated user), so a caller sees the\naccounts THEY linked and never a colleague's, even inside one org — everything\nelse under /v1/billing is org-wide. Neither key is ever read from the request\nbody or the query.\n\nIt is a ROUTING counter, not the money ledger. `costCents` is 0 for an account\nbilled by its own subscription, where the plan pays the provider directly, so\nthese totals do not reconcile against what the org was charged.\n/v1/billing/usage is the charged ledger.\n\n401 without a validated principal. Where the linked-account plane is not\nresident the answer is an honest 501 — never an empty breakdown, which would\nread as no usage.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accounts"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/billing/usage/accounts.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/billing/webhooks/{provider}":{"post":{"operationId":"post_v1_billing_webhooks_by_provider","summary":"Payment-provider webhook intake for settlement and subscription lifecycle events","description":"Accepts a payment provider's event, verifies it, records it for audit, and applies subscription lifecycle changes to the matching local row. There is no bearer here and there cannot be: the provider's SIGNATURE over the body IS the authentication, so a request with no recognized signature header is 400 and one whose signature does not verify is 401. The provider path segment is only a hint for dashboard configuration — verification picks the processor regardless of what the URL says. Redelivery is safe: an event id already recorded is acknowledged as a duplicate without re-applying any side effect, which matters because providers retry for days until they see a 2xx.","tags":["billing"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/billing/wire":{"get":{"operationId":"get_v1_billing_wire","summary":"Where to wire funds, and the reference that credits them to you","description":"Answers the receiving bank details for the brand this deployment serves — the account the funds actually land in, hydrated per brand rather than hard-coded — together with the payment reference to put on the transfer.\n\nTHE REFERENCE IS THE POINT. It carries your own billing key, and it is how an arriving wire is attributed to your account; a transfer sent without it arrives as an unidentified receipt. That is why this read is gated at all: an unpinned caller would be handed an unattributable reference.\n\nReading it credits nothing and reserves nothing. A wire is settled by an operator when the bank shows the funds, so the balance moves on receipt, not on this call.","tags":["billing"],"x-app":"commerce"}},"/v1/blueprint":{"get":{"operationId":"get_v1_blueprint","summary":"Returns every deployable blueprint with its service count and estimated monthly compute cost.","description":"Returns every deployable blueprint with its service count and estimated\nmonthly compute cost.\n\nIt is the lightweight index the console renders as a template gallery before\ndrilling into one stack's bill of images — GET /v1/blueprint/sbom?template=\u003cid\u003e\nis the detail view. The cost is the same figure the deploy path meters the\ndeploying org on and the 20% author royalty is taken from, priced from the\nactive rate card (GET /v1/blueprint/health echoes that card).","tags":["blueprint"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/blueprintIndex"}}},"description":"ok"}},"x-app":"blueprint"}},"/v1/blueprint/health":{"get":{"operationId":"get_v1_blueprint_health","summary":"Reports blueprint liveness and echoes the compute rate card in force.","description":"Reports blueprint liveness and echoes the compute rate card in force.\n\nThe rate card is the one the estimator actually applies after the operator env\noverlay, so an operator can confirm a tuned knob took effect rather than\ninferring it from a price. Not JWT-gated — a liveness probe must be reachable —\nand it always answers 200 while the subsystem is mounted.","tags":["blueprint"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/blueprintHealth"}}},"description":"ok"}},"x-app":"blueprint"}},"/v1/blueprint/sbom":{"get":{"operationId":"get_v1_blueprint_sbom","summary":"A blueprint's bill of images and what running it costs","description":"Answers a blueprint's SBOM — the container images its compose stack runs, each with the CPU/memory footprint that was applied to it — together with the compute cost that footprint prices out to on the active rate card.\n\nONE address, TWO shapes at 200: `?template=\u003cid\u003e` returns that blueprint's Estimate alone (404 on an id no embedded blueprint carries), and no `template` returns `{data:[Estimate]}` for every blueprint — the batch the console's template gallery reads in one round-trip.\n\nThe blueprints are reference content embedded in the binary and validated at mount, so this read is the same for every caller and is scoped to no tenant. The per-hour figure it returns is the one the deploy path meters the deploying org on and the 20% author royalty is taken from; GET /v1/blueprint/health echoes the rate card it was priced from.","tags":["blueprint"],"x-app":"blueprint"}},"/v1/books/accounts":{"get":{"operationId":"get_v1_books_accounts","summary":"Returns the org's chart of accounts — the seeded fixed chart every posting key in the ledger refers to.","description":"Returns the org's chart of accounts — the seeded fixed chart every\nposting key in the ledger refers to.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Account"},"type":"array"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/ask":{"post":{"operationId":"post_v1_books_ask","summary":"Answers a plain-language question about the caller's own books — \"what is my MRR?\", \"how long is my runway?\" — with figures taken from their ledger, never a guessed number.","description":"Answers a plain-language question about the caller's own books — \"what is my\nMRR?\", \"how long is my runway?\" — with figures taken from their ledger, never a guessed\nnumber. A deterministic keyword router picks the intent and reads the real metrics, and\nthose figures, followups and report sources are computed BEFORE any model call and are\nnever altered by one: the optional narration seam only rephrases the sentence, and it\ndegrades silently to the templated answer when no AI plane is wired. It is strictly\nread-only — it restates the books, it never posts to them.","tags":["books"],"requestBody":{"content":{"application/json":{"example":{"question":"how long is my runway?"},"schema":{"$ref":"#/components/schemas/AskRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskResponse"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/bank/exchange":{"post":{"operationId":"post_v1_books_bank_exchange","summary":"Finish connecting a bank account (not yet available)","description":"ANSWERS 501 UNCONDITIONALLY. It is the intended second hop of the bank-linking handshake — trade the provider's short-lived public token for the durable access credential and seal that credential into KMS — and nothing on the HTTP path reaches an implementation today.\n\nThe durable bank credential is the reason this hop exists: it is meant to be sealed server-side and never handed back to the caller. Since the route never succeeds, no credential is stored by it and no bank is connected through it.\n\nDocumented as refusing rather than declared with a success body, for the same reason as the first hop: it has never sent one, and stating a shape it has never produced would put a return type in every SDK for a call that always fails. A caller with no principal gets 401 before the 501.","tags":["books"],"x-app":"books"}},"/v1/books/bank/import":{"post":{"operationId":"post_v1_books_bank_import","summary":"Import a bank statement file into your books","description":"Takes a bank statement as RAW BYTES — the file exactly as downloaded, OFX, QFX or CSV, not wrapped in JSON — parses every row, books it against the caller org's own ledger, and answers the tally: how many rows were seen, how many vouchers posted, how many inflows reconciled, how many raised a question, how many were own-account transfers, and how many were skipped.\n\nRE-IMPORTING THE SAME STATEMENT DOES NOT DOUBLE-BOOK. Every row goes through the same posting choke point every other source uses, keyed idempotently, so an overlapping statement — the usual case, since exports overlap at the month boundary — lands its new rows and counts the rest as skipped. Skipped is the number to read on a second import.\n\nIt is READ-ONLY against the bank: this ingests, it never sends money. Scoped to the caller's own org from the validated principal, and refused without one; `sandbox=true` writes the org's sandbox ledger instead of its real books. An empty body is a 400, and a file the parser cannot read is a 400 carrying the parser's reason rather than a partial import. On a deployment whose import parser is not built, this answers 501 rather than mishandling the file.","tags":["books"],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankTally"}}},"description":"Success"}},"x-app":"books"}},"/v1/books/bank/sync":{"post":{"operationId":"post_v1_books_bank_sync","summary":"Pulls every connected bank (Plaid/Teller) for the caller's org, maps each fetched transaction to a posting and books it idempotently, then advances that connector's cursor so the next sync resumes where this one stopped.","description":"Pulls every connected bank (Plaid/Teller) for the caller's org, maps each\nfetched transaction to a posting and books it idempotently, then advances that\nconnector's cursor so the next sync resumes where this one stopped. One connector's\noutage is skipped rather than failing the whole sync. It reports the batch: how many\ntransactions were seen, how many vouchers posted, how many inflows reconciled against\nthe processor clearing account, how many raised a question, how many were own-account\ntransfers, and how many were already-processed no-ops. It is READ-ONLY against the\nbank — it ingests, it never sends money.","tags":["books"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BankTally"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/bank/token":{"post":{"operationId":"post_v1_books_bank_token","summary":"Begin connecting a bank account (not yet available)","description":"ANSWERS 501 UNCONDITIONALLY. It is the intended first hop of the bank-linking handshake — mint the short-lived session token a browser hands to the provider's link widget — and nothing on the HTTP path reaches an implementation today.\n\nThe connectors behind it are written and tested; only the wiring is missing, so an org cannot connect a bank through the API at all. Until that lands, bank data reaches the books by statement import.\n\nIt is documented as refusing rather than declared with a success body precisely because it has never sent one. A response schema here would be invention: every generated SDK would carry a return type for a call that has only ever failed. A caller with no principal gets 401 before the 501.","tags":["books"],"x-app":"books"}},"/v1/books/bank/transactions":{"get":{"operationId":"get_v1_books_bank_transactions","summary":"Returns the org's normalized bank transactions, newest first — every row the import and connector paths have ingested, with its amount in exact cents, its direction, and whether it has been matched to a voucher yet.","description":"Returns the org's normalized bank transactions, newest first —\nevery row the import and connector paths have ingested, with its amount in exact cents,\nits direction, and whether it has been matched to a voucher yet.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back; 500 when absent or not positive.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BankTxnRow"},"type":"array"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/bank/unreconciled":{"get":{"operationId":"get_v1_books_bank_unreconciled","summary":"Returns the org's unmatched bank inflows and their open clarifying questions — the queue a human answers so an unexplained deposit is never guessed into revenue.","description":"Returns the org's unmatched bank inflows and their open clarifying\nquestions — the queue a human answers so an unexplained deposit is never guessed into\nrevenue.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unreconciledOut"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/export":{"get":{"operationId":"get_v1_books_export","summary":"Returns the complete financial package for the caller's org over (from, to]: the trial balance, the P\u0026L, the balance sheet, and the GL detail behind them — the four statements a tax preparer or an investor asks for, assembled from the one ledger in a single read so they cannot disagree with each other.","description":"Returns the complete financial package for the caller's org over\n(from, to]: the trial balance, the P\u0026L, the balance sheet, and the GL detail behind\nthem — the four statements a tax preparer or an investor asks for, assembled from the\none ledger in a single read so they cannot disagree with each other.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the RFC3339 start of the window, exclusive. Empty means all time.","schema":{"type":"string"},"example":"2026-01-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 end of the window, inclusive. Empty means up to now.","schema":{"type":"string"},"example":"2026-12-31T23:59:59Z"},{"name":"format","in":"query","required":false,"description":"Format is the export encoding. Only \"json\" is supported; empty means json.","schema":{"type":"string"},"example":"json"},{"name":"limit","in":"query","required":false,"description":"Limit caps the GL detail rows included as the audit trail; 5000 when absent\nor not positive.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinancialPackage"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/gl":{"get":{"operationId":"get_v1_books_gl","summary":"ListGL returns the org's most recent GL Entry rows, newest first.","description":"ListGL returns the org's most recent GL Entry rows, newest first. This is the raw\ndouble-entry detail behind every statement: one row per leg, with its debit, credit,\nposting time and the source that booked it.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back; 500 when absent or not positive.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/GLRow"},"type":"array"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/inbox":{"get":{"operationId":"get_v1_books_inbox","summary":"Returns the org's open document queue — everything uploaded but not yet booked, newest first, each with its extracted summary and the confidence the scanner resolved its category at.","description":"Returns the org's open document queue — everything uploaded but not yet\nbooked, newest first, each with its extracted summary and the confidence the scanner\nresolved its category at. A booked document drops out of the queue.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxOut"}}},"description":"ok"}},"x-app":"books"},"post":{"operationId":"post_v1_books_inbox","summary":"Queue a document for later scanning","description":"Takes a document as RAW BYTES and queues it in the caller org's inbox as `unsorted`, answering the queued item. It is the drop box: get the paperwork in now, read it later.\n\nIt EXTRACTS NOTHING and calls no model — that is what separates it from the scan. Nothing is proposed and nothing is posted; the item simply waits to be scanned, and a booked document leaves the queue.\n\nIDEMPOTENT BY CONTENT: the item's id is the file hash, so re-uploading the same bytes answers the existing item rather than adding a duplicate row — and it is the same id a scan of those bytes uses, which is how the two routes address one document. Scoped to the caller's own org from the validated principal and refused without one; `sandbox=true` targets the sandbox ledger, and `filename` is recorded for display. An empty or oversized upload is a 400.","tags":["books"],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InboxItem"}}},"description":"Success"}},"x-app":"books"}},"/v1/books/metrics":{"get":{"operationId":"get_v1_books_metrics","summary":"Metrics returns the org's deterministic SaaS-metrics snapshot over an optional (from, to] window — MRR, ARR, revenue, COGS, burn, gross margin, net income, cash, deferred revenue, monthly burn and runway — as raw int64-cent figures AND the same figures already formatted.","description":"Metrics returns the org's deterministic SaaS-metrics snapshot over an optional\n(from, to] window — MRR, ARR, revenue, COGS, burn, gross margin, net income, cash,\ndeferred revenue, monthly burn and runway — as raw int64-cent figures AND the same\nfigures already formatted. Every number is the ledger, aggregated the one way the books\ndefine it, never a guess; it is the grounded read the unified /v1/ask advisor replays.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the RFC3339 start of the window, exclusive. Empty means all time.","schema":{"type":"string"},"example":"2026-01-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 end of the window, inclusive. Empty means up to now.","schema":{"type":"string"},"example":"2026-06-30T23:59:59Z"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricsResponse"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/pnl":{"get":{"operationId":"get_v1_books_pnl","summary":"Returns the org's accrual-basis Profit \u0026 Loss over an optional (from, to] window of RFC3339 posting times: recognized revenue, matched cost, and the net.","description":"Returns the org's accrual-basis Profit \u0026 Loss over an optional (from, to]\nwindow of RFC3339 posting times: recognized revenue, matched cost, and the net.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the RFC3339 start of the window, exclusive. Empty means all time.","schema":{"type":"string"},"example":"2026-01-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 end of the window, inclusive. Empty means up to now.","schema":{"type":"string"},"example":"2026-03-31T23:59:59Z"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PnL"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/position":{"get":{"operationId":"get_v1_books_position","summary":"Returns the org's Balance Sheet as of `to` (empty = all time), with the Assets == Liabilities + Equity equation proof.","description":"Returns the org's Balance Sheet as of `to` (empty = all time), with the\nAssets == Liabilities + Equity equation proof.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 instant the statement is struck as of. Empty means all time.","schema":{"type":"string"},"example":"2026-03-31T23:59:59Z"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceSheet"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/questions":{"get":{"operationId":"get_v1_books_questions","summary":"Returns the clarifying questions the caller's own recent GL raises — the unusual postings a founder should look at (outliers, reversals, round-offs, uncosted revenue, an overdrawn wallet), sharpest first.","description":"Returns the clarifying questions the caller's own recent GL raises — the\nunusual postings a founder should look at (outliers, reversals, round-offs, uncosted\nrevenue, an overdrawn wallet), sharpest first. An empty list means the books look clean;\nthe detector is deterministic over the ledger and invents nothing.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuestionsResponse"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/rules":{"get":{"operationId":"get_v1_books_rules","summary":"Returns the org's auto-categorization rules, highest priority first.","description":"Returns the org's auto-categorization rules, highest priority first. A rule\nis a standing instruction — \"anything whose merchant contains X books to category Y\" —\nand it overrides a vendor's default category, so this is the list that decides how a\nfuture bill classifies itself.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/rulesOut"}}},"description":"ok"}},"x-app":"books"},"post":{"operationId":"post_v1_books_rules","summary":"Creates or updates one auto-categorization rule, keyed by its pattern — writing a pattern that already exists REPLACES that row's category and priority.","description":"Creates or updates one auto-categorization rule, keyed by its pattern —\nwriting a pattern that already exists REPLACES that row's category and priority. The\ncategory is normalized to a real COA expense account, and anything unrecognized becomes\n5900 Uncategorized rather than a guessed real account. It answers the row exactly as\nstored, so the caller sees the normalization. A rule overrides a vendor's default\ncategory, so this is the standing instruction that decides how a future bill classifies.","tags":["books"],"requestBody":{"content":{"application/json":{"example":{"category":"cloud","pattern":"aws","priority":5},"schema":{"$ref":"#/components/schemas/Rule"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rule"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/scan":{"post":{"operationId":"post_v1_books_scan","summary":"Scan a receipt or invoice into a proposed voucher","description":"Takes a receipt or invoice as RAW BYTES — a PDF, an image or plain text, uploaded under its own content type, not wrapped in JSON — extracts what the document says, resolves the vendor's expense category, and answers a DRAFT carrying a balanced voucher proposed for it.\n\nNOTHING IS POSTED. That split is the whole design: the model only ever produces a structured reading of the document, the voucher is assembled deterministically in Go from that reading, and the ledger is written only by the separate book call a human confirms. So a misread scan can propose a wrong draft; it cannot move money. Amounts are exact integer cents end to end — the extraction returns cents, never a decimal — so no rounding enters the ledger.\n\nThe draft's id is the FILE HASH, and that is what makes booking idempotent: re-scanning the same bytes addresses the same draft rather than queuing a second one. A row is written to the org's document inbox as a side effect, moving it from unsorted to draft. Scoped to the caller's own org from the validated principal and refused without one; `sandbox=true` targets the sandbox ledger, and `filename` is recorded for the inbox. An empty or oversized upload is a 400, and a deployment with no scanner model answers 501.","tags":["books"],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanDraft"}}},"description":"Success"}},"x-app":"books"}},"/v1/books/scan/book":{"post":{"operationId":"post_v1_books_scan_book","summary":"Posts a reviewed scanned bill to the ledger.","description":"Posts a reviewed scanned bill to the ledger. It is the scanner's ONLY write:\nthe voucher goes through the same post() choke point every other source uses, so it is\nchecked to balance (Σdebit == Σcredit) and is idempotent by (scan, scanId) — re-booking\nthe same scan answers posted=false and writes nothing. A bill whose economic identity\n(vendor, total, issue date) already posted under a DIFFERENT scan is refused 409 unless\noverride is set, which is what stops the same receipt re-scanned into a new file hash\nfrom double-booking. An unbalanced voucher is refused 400.","tags":["books"],"requestBody":{"content":{"application/json":{"example":{"scanId":"a5f3c1","voucher":{"description":"GitHub","legs":[{"account":"5300","debit":1234},{"account":"2001","credit":1234}]}},"schema":{"$ref":"#/components/schemas/BookRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookResponse"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/sync":{"post":{"operationId":"post_v1_books_sync","summary":"Sync ingests the caller's OWN org from commerce into BOTH ledgers (live and sandbox) and reports how many new vouchers posted to each.","description":"Sync ingests the caller's OWN org from commerce into BOTH ledgers (live and sandbox)\nand reports how many new vouchers posted to each. It is idempotent — money that has\nalready been booked posts nothing on a repeat — and it is read-only against commerce:\nit never mints a deposit, a credit or a payout, only the accounting twin of money that\nalready moved.","tags":["books"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncTally"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/transactions":{"get":{"operationId":"get_v1_books_transactions","summary":"Returns the org's booked ledger as a single-line register, newest first: one row per voucher, with its date, description, vendor, category, source and amount in exact cents.","description":"Returns the org's booked ledger as a single-line register, newest\nfirst: one row per voucher, with its date, description, vendor, category, source and\namount in exact cents. It is the double-entry ledger projected to the register a human\nreads, filterable by posting-time window, category and vendor. Strictly read-only — it\nrestates the books, it never moves them.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the RFC3339 start of the posting-time window, inclusive.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 end of the posting-time window, inclusive.","schema":{"type":"string"}},{"name":"category","in":"query","required":false,"description":"Category filters to one COA account, named by number (\"5300\") or by category\nslug (\"software\").","schema":{"type":"string"},"example":"software"},{"name":"vendor","in":"query","required":false,"description":"Vendor filters to rows whose vendor or description contains this text,\ncase-insensitively.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back; 200 when absent or not positive.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/transactionsOut"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/trial":{"get":{"operationId":"get_v1_books_trial","summary":"Returns the org's trial balance over an optional [from, to] window of RFC3339 posting times, including the opening/closing columns and the TotalDebit == TotalCredit proof that the books balance.","description":"Returns the org's trial balance over an optional [from, to] window of\nRFC3339 posting times, including the opening/closing columns and the\nTotalDebit == TotalCredit proof that the books balance.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the RFC3339 start of the window, exclusive. Empty means all time.","schema":{"type":"string"},"example":"2026-01-01T00:00:00Z"},{"name":"to","in":"query","required":false,"description":"To is the RFC3339 end of the window, inclusive. Empty means up to now.","schema":{"type":"string"},"example":"2026-03-31T23:59:59Z"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrialBalance"}}},"description":"ok"}},"x-app":"books"}},"/v1/books/vendors":{"get":{"operationId":"get_v1_books_vendors","summary":"Returns the org's vendor book: each canonical vendor, the alias spellings a receipt may print it under, and the expense account new bills from it default to.","description":"Returns the org's vendor book: each canonical vendor, the alias spellings a\nreceipt may print it under, and the expense account new bills from it default to. A\nvendor here is what makes a scanned bill self-classify instead of asking again.","tags":["books"],"parameters":[{"name":"sandbox","in":"query","required":false,"description":"Sandbox reads the org's SANDBOX ledger when it is exactly \"true\"; anything else\nreads the live one.","schema":{"type":"string"},"example":"false"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/vendorsOut"}}},"description":"ok"}},"x-app":"books"},"post":{"operationId":"post_v1_books_vendors","summary":"Creates or updates one vendor in the org's vendor book, keyed by its canonical name — writing a canonical name that already exists REPLACES that row's aliases and default category.","description":"Creates or updates one vendor in the org's vendor book, keyed by its\ncanonical name — writing a canonical name that already exists REPLACES that row's\naliases and default category. A category given as a slug (\"software\") is normalized to\nits real COA expense account, and anything unrecognized becomes 5900 Uncategorized\nrather than a guessed real account. It answers the row exactly as stored, so the caller\nsees the normalization. Recording a vendor is what makes future bills from it\nself-classify instead of asking again.","tags":["books"],"requestBody":{"content":{"application/json":{"example":{"aliases":["github.com"],"canonical":"GitHub","defaultCategory":"software"},"schema":{"$ref":"#/components/schemas/VendorRow"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VendorRow"}}},"description":"ok"}},"x-app":"books"}},"/v1/bot/connect":{"get":{"operationId":"get_v1_bot_connect","summary":"The socket a bot node dials and holds open to become invokable.","description":"Upgrades to a WebSocket and keeps it for the life of the node. cloud writes a challenge frame immediately; the node answers with a connect frame naming the protocol range it speaks, the role `node`, its own node id, and the display name, platform, agent version, capabilities and commands it reports for itself. On acceptance the session is registered, the node appears in this org's node list, and invocations begin arriving as frames on the same connection.\n\nThe upgrade needs a validated principal and answers 403 without one. The org is the gateway's verdict — injected after IAM validation and after any client copy is stripped — and is never read from the request itself, because a caller that could name an org could attach a machine into someone else's tenant.\n\nA request carrying an Origin header is refused outright. A node is a daemon and a browser has no business here; since no same-origin policy applies to WebSockets, a page could otherwise ride a signed-in viewer's session into registering a node. Removing the whole category is the gate, not an allowlist of brand domains. The handshake deadline is one fixed instant rather than a per-read timer, so a peer cannot hold a pre-handshake socket open indefinitely by sending frames this endpoint ignores.\n\nTwo things to get right. Everything the node declares about itself — capabilities, commands, platform — is a SELF-REPORT: it is useful to show and never load-bearing, because what the node may actually be asked to run is decided at this socket against the deployment's allowlist. And a node can only ever answer calls placed on its own connection: correlation ids are minted under the connection id and checked against it, so naming another node's in-flight call resolves nothing.","tags":["bot"],"x-app":"bot"}},"/v1/bot/nodes":{"get":{"operationId":"get_v1_bot_nodes","summary":"Returns the caller org's currently connected bot nodes: what each one calls itself, the platform it runs on, its agent version, when its socket was established, and the capabilities and commands it reported.","description":"Returns the caller org's currently connected bot nodes: what each one\ncalls itself, the platform it runs on, its agent version, when its socket was\nestablished, and the capabilities and commands it reported.\n\nOnly this org's nodes are listed — the org is half of every key in the table it\nreads — and only nodes attached to THIS replica, because the list is of live\nsockets rather than of registrations. The capability and command lists are the\nnode's own self-report: useful to show, never load-bearing, because what a node\nmay actually be asked to do is decided at the socket against the deployment's\nallowlist.","tags":["bot"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/nodesView"}}},"description":"ok"}},"x-app":"bot"}},"/v1/bot/nodes/{id}/invoke":{"post":{"operationId":"post_v1_bot_nodes_by_id_invoke","summary":"Ask one of your connected machines to run a command, and get its answer back.","description":"Sends {command, params, timeoutMs, idempotencyKey} to the named node and answers with what the node returned: {ok, payload, code, message}, where payload is the node's own JSON passed through — cloud routes the call, it does not interpret the result. A reply that is not valid JSON becomes an empty payload rather than corrupting the response, which ok and code already qualify.\n\nNeither the node nor the org is a body field: the node is the path and the org is the caller's validated identity, and a field for either would be a field somebody could set to a stranger's. A validated principal is required (403 without one), and a node id that belongs to another org answers exactly like one that does not exist — not found — so this cannot be used to probe another tenant's fleet.\n\nAuthorization happened ONCE, at the socket, on the replica holding that node — the only place that knows what the node declared it can do. A node attached to a different replica is reached through the peer forward and is authorized by the same code with the same session in hand, so a local node and a forwarded one cannot get different answers. The timeout defaults to 30s and is clamped to 5 minutes, so one request can never pin a node's socket open indefinitely.\n\nsystem.run is rewritten before dispatch: its approval control fields are re-derived from the approval record and whatever the caller claimed is discarded, because a caller that could pre-approve itself is the whole thing approvals exist to prevent. No approval registry is wired today, so an invocation CLAIMING an approval is refused while an ordinary one is unaffected.\n\nThe one thing to get right: a refusal is a 403 carrying a DOMAIN body — {error, code, reason} — not the flat error envelope the rest of cloud returns, and the same body comes back whether the pre-flight sanitize refused it or the node's own gate did. Switch on `code`. The remaining failures are ordinary statuses: the node not answering in time is 504, and a node that disconnected or could not be reached is 502.","tags":["bot"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bot"}},"/v1/bot/peer/invoke":{"post":{"operationId":"post_v1_bot_peer_invoke","summary":"Replica-to-replica forward of one invocation to the pod holding the node's socket.","description":"A machine hop, not a caller-facing route. A node's socket lands on one replica while invocations land on any, so the replica that took the request forwards it here to the one that actually holds the node, and returns that answer as its own.\n\nIt authenticates with the shared peer token, compared in constant time, and carries no user identity at all. That is why the org arrives IN THE BODY here: the forwarding replica already derived it from a gateway-validated header, so the value is a fact being relayed rather than a claim being made. On any caller-facing route the same field would be a cross-tenant invoke primitive.\n\nIt fails closed on its own configuration: with no peer token set, or a half-wired cluster that has presence but no way to forward, it serves 503 and forwards nothing — an unauthenticated endpoint that takes an org from a body is precisely the hole. A missing or wrong token is 403, and the forwarded body is bounded on read.\n\nTwo things to get right. Its refusals are text/plain rather than the JSON every zip error uses, so a client decoding them as JSON will fail on the error path only. And an invocation that RAN but was denied still answers 200 here, carrying a stable error token in the JSON body — no such node, timeout, node gone, denied, failed — which the calling replica maps back onto the status codes a caller sees. Authorization already ran on this replica at the socket and is deliberately not repeated.","tags":["bot"],"x-app":"bot"}},"/v1/bot/{wildcard1}":{"delete":{"operationId":"delete_v1_bot_by_wildcard1","summary":"Relay one of the bot runtime's own operational paths","description":"Forwards a request to the bot runtime — the service that executes channels and skills — and hands back its answer unchanged. `/v1/bot` is stripped before forwarding, because the runtime serves bare paths: /v1/bot/health reaches it as /health.\n\nThis is the runtime's OPS face, not a control plane. A liveness probe is not a tenant-scoped resource, so it stays a relay rather than being reimplemented in Go; everything a tenant can ACT on is native and typed at /v1/bots.\n\nA validated principal is required and the request is refused with 403 before anything is forwarded — the runtime trusts the identity headers it receives as gateway-minted, so an unauthenticated call must never be allowed to hand it a victim tenant. The caller's Authorization, org, user, email, project and environment headers ride along; nothing is minted here. The runtime's own status code and Content-Type come back verbatim (frequently not JSON), the body is bounded at 16 MiB, and a runtime that cannot be reached is 502.\n\nOne registration owns this address for every method, so which methods actually answer is the runtime's decision, not this edge's.","tags":["bot"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bots"},"get":{"operationId":"get_v1_bot_by_wildcard1","summary":"Relay one of the bot runtime's own operational paths","description":"Forwards a request to the bot runtime — the service that executes channels and skills — and hands back its answer unchanged. `/v1/bot` is stripped before forwarding, because the runtime serves bare paths: /v1/bot/health reaches it as /health.\n\nThis is the runtime's OPS face, not a control plane. A liveness probe is not a tenant-scoped resource, so it stays a relay rather than being reimplemented in Go; everything a tenant can ACT on is native and typed at /v1/bots.\n\nA validated principal is required and the request is refused with 403 before anything is forwarded — the runtime trusts the identity headers it receives as gateway-minted, so an unauthenticated call must never be allowed to hand it a victim tenant. The caller's Authorization, org, user, email, project and environment headers ride along; nothing is minted here. The runtime's own status code and Content-Type come back verbatim (frequently not JSON), the body is bounded at 16 MiB, and a runtime that cannot be reached is 502.\n\nOne registration owns this address for every method, so which methods actually answer is the runtime's decision, not this edge's.","tags":["bot"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bots"},"patch":{"operationId":"patch_v1_bot_by_wildcard1","summary":"Relay one of the bot runtime's own operational paths","description":"Forwards a request to the bot runtime — the service that executes channels and skills — and hands back its answer unchanged. `/v1/bot` is stripped before forwarding, because the runtime serves bare paths: /v1/bot/health reaches it as /health.\n\nThis is the runtime's OPS face, not a control plane. A liveness probe is not a tenant-scoped resource, so it stays a relay rather than being reimplemented in Go; everything a tenant can ACT on is native and typed at /v1/bots.\n\nA validated principal is required and the request is refused with 403 before anything is forwarded — the runtime trusts the identity headers it receives as gateway-minted, so an unauthenticated call must never be allowed to hand it a victim tenant. The caller's Authorization, org, user, email, project and environment headers ride along; nothing is minted here. The runtime's own status code and Content-Type come back verbatim (frequently not JSON), the body is bounded at 16 MiB, and a runtime that cannot be reached is 502.\n\nOne registration owns this address for every method, so which methods actually answer is the runtime's decision, not this edge's.","tags":["bot"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bots"},"post":{"operationId":"post_v1_bot_by_wildcard1","summary":"Relay one of the bot runtime's own operational paths","description":"Forwards a request to the bot runtime — the service that executes channels and skills — and hands back its answer unchanged. `/v1/bot` is stripped before forwarding, because the runtime serves bare paths: /v1/bot/health reaches it as /health.\n\nThis is the runtime's OPS face, not a control plane. A liveness probe is not a tenant-scoped resource, so it stays a relay rather than being reimplemented in Go; everything a tenant can ACT on is native and typed at /v1/bots.\n\nA validated principal is required and the request is refused with 403 before anything is forwarded — the runtime trusts the identity headers it receives as gateway-minted, so an unauthenticated call must never be allowed to hand it a victim tenant. The caller's Authorization, org, user, email, project and environment headers ride along; nothing is minted here. The runtime's own status code and Content-Type come back verbatim (frequently not JSON), the body is bounded at 16 MiB, and a runtime that cannot be reached is 502.\n\nOne registration owns this address for every method, so which methods actually answer is the runtime's decision, not this edge's.","tags":["bot"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bots"},"put":{"operationId":"put_v1_bot_by_wildcard1","summary":"Relay one of the bot runtime's own operational paths","description":"Forwards a request to the bot runtime — the service that executes channels and skills — and hands back its answer unchanged. `/v1/bot` is stripped before forwarding, because the runtime serves bare paths: /v1/bot/health reaches it as /health.\n\nThis is the runtime's OPS face, not a control plane. A liveness probe is not a tenant-scoped resource, so it stays a relay rather than being reimplemented in Go; everything a tenant can ACT on is native and typed at /v1/bots.\n\nA validated principal is required and the request is refused with 403 before anything is forwarded — the runtime trusts the identity headers it receives as gateway-minted, so an unauthenticated call must never be allowed to hand it a victim tenant. The caller's Authorization, org, user, email, project and environment headers ride along; nothing is minted here. The runtime's own status code and Content-Type come back verbatim (frequently not JSON), the body is bounded at 16 MiB, and a runtime that cannot be reached is 502.\n\nOne registration owns this address for every method, so which methods actually answer is the runtime's decision, not this edge's.","tags":["bot"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"bots"}},"/v1/bots":{"get":{"operationId":"get_v1_bots","summary":"List returns the caller org's live bot runs, read from the bot runtime and projected into the console contract with each run's live session URL derived here.","description":"List returns the caller org's live bot runs, read from the bot runtime and projected\ninto the console contract with each run's live session URL derived here.\n\nThe org is ALWAYS the validated principal's org, NEVER a request field, and it is\nwhat scopes the runtime's answer — so one tenant can never enumerate another's\nruns. A runtime that cannot answer is an error, not an empty list: [] would tell\nthe caller \"your org has no runs\", which is a different claim from \"we could not\nask\", and the difference is the whole reason this endpoint exists.","tags":["bots"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotRuns"}}},"description":"ok"}},"x-app":"bots"}},"/v1/bots/run":{"post":{"operationId":"post_v1_bots_run","summary":"Reserved address for launching a bot run — not implemented, always 501","description":"Answers 501 to every call. The bot runtime exposes no launch operation, so nothing here can start a sandbox, and this address is published rather than dropped because it is reserved: routes resolve by specificity, so the `run` literal can never bind as a run id against its neighbour `/v1/bots/:runId/stop`.\n\nThe refusal is total and takes no input. The handler never reads the body, so any bytes at all — malformed JSON included — get the same 501; no run id is minted, no session URL is handed back, and no per-run fee is charged. That is the point: the earlier version minted an id the runtime had never heard of, pointed it at a VNC node that did not exist, and took real money for it.\n\nListing and stopping runs are live and org-scoped. Only the launch is missing, and it returns in the same change that can prove a bot boots.","tags":["bots"],"x-app":"bots"}},"/v1/bots/{runId}/stop":{"post":{"operationId":"post_v1_bots_by_runid_stop","summary":"Stop terminates one of the caller org's own bot runs and reports its terminal state.","description":"Stop terminates one of the caller org's own bot runs and reports its terminal state.\n\nThe own-key guard is the org: it is the caller's validated org, never theirs to\nchoose, and the runtime resolves the run id UNDER it. A run belonging to another\ntenant is not among this org's runs, so it answers absent — the same 404 a\nnonexistent id gets, which is what keeps this from being an oracle.\n\nAbsence is honoured ONLY when the runtime answers it. A runtime that does not\nserve stop reports nothing about the run, and reporting \"stopped\" on that basis\nwould be a stop that cannot fail — so it is a 502.","tags":["bots"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotStopped"}}},"description":"ok"}},"x-app":"bots"}},"/v1/builds":{"get":{"operationId":"get_v1_builds","summary":"Returns real build records for your org.","description":"Returns real build records for your org.\n\nIt lists the org's BuildKit build records — the git build step behind a deploy —\neach with the repo it built, the short commit, its status, when it started and\nhow long it took. These are real records or an honest empty list; a build appears\nhere because one ran, never because a page needed a row. Builds are created only\nby /deploy and the push-to-deploy hook. Requires a validated principal; 403\nwithout one.","tags":["builds"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildBoard"}}},"description":"ok"}},"x-app":"platform"}},"/v1/campaign":{"get":{"operationId":"get_v1_campaign","summary":"Returns the org's campaigns, newest first, optionally narrowed to one status.","description":"Returns the org's campaigns, newest first, optionally narrowed to\none status.\n\nA campaign is the top-level go-to-market object: a value that SPANS channels\n(paid, organic, email) and fans out to the executor for each. The listing is\norg-scoped server-side, so one org can never see another's campaigns.","tags":["campaign"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status keeps only campaigns in that state: draft, live, paused or failed.\nEmpty means any.","schema":{"type":"string"},"example":"live"},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page. 0 or less means the default of 200; anything above\n1000 is clamped to 1000.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignPage"}}},"description":"ok"}},"x-app":"campaign"},"post":{"operationId":"post_v1_campaign","summary":"Creates a campaign as a DRAFT and returns it.","description":"Creates a campaign as a DRAFT and returns it.\n\nA draft is inert: nothing is sent, no connector is touched and no budget is\ncommitted until the campaign is launched. The channels named here are validated\nand de-duplicated by kind (one executor per kind), and every channel starts\n\"pending\" whatever the caller claims — a client can never assert a launched\nstate.","tags":["campaign"],"requestBody":{"content":{"application/json":{"example":{"budget":250000,"channels":[{"kind":"paid","platform":"meta"}],"content":["Ship faster"],"name":"Spring launch"},"schema":{"$ref":"#/components/schemas/campaignWrite"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignRecord"}}},"description":"created"}},"x-app":"campaign"}},"/v1/campaign/summary":{"get":{"operationId":"get_v1_campaign_summary","summary":"Returns the org's go-to-market roll-up: how many campaigns exist, how many are live, their total budget in cents, and which channel executors this deployment can actually reach.","description":"Returns the org's go-to-market roll-up: how many campaigns\nexist, how many are live, their total budget in cents, and which channel\nexecutors this deployment can actually reach.\n\nThe channel list is the deployment's honest capability, not a wish: a kind\nmissing from it is one a launch will record as \"unavailable\" rather than fail\non.","tags":["campaign"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignSummary"}}},"description":"ok"}},"x-app":"campaign"}},"/v1/campaign/{id}":{"delete":{"operationId":"delete_v1_campaign_by_id","summary":"Removes one campaign of the caller's org and answers 204 with no body.","description":"Removes one campaign of the caller's org and answers 204 with no\nbody. 404 when the org has no campaign with that id.\n\nIt deletes the RECORD, not the executions: a campaign whose channels are live\non a provider should be paused first, or those executions keep running with\nnothing here to report them.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign's server-minted handle, \"cmp_\"-prefixed.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"campaign"},"get":{"operationId":"get_v1_campaign_by_id","summary":"Returns one campaign of the caller's org — its name, audience, creatives, channels with their per-channel launch state, schedule, budget and status.","description":"Returns one campaign of the caller's org — its name, audience,\ncreatives, channels with their per-channel launch state, schedule, budget and\nstatus. 404 when the org has no campaign with that id.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign's server-minted handle, \"cmp_\"-prefixed.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignRecord"}}},"description":"ok"}},"x-app":"campaign"},"put":{"operationId":"put_v1_campaign_by_id","summary":"Rewrites a campaign's core fields — name, audience, creatives, schedule and budget — and returns the updated campaign.","description":"Rewrites a campaign's core fields — name, audience, creatives,\nschedule and budget — and returns the updated campaign.\n\nChannels are replaced ONLY while the campaign is still a draft. Once it is\nlaunched its channels carry provider state (an external id, a live status), so\nthey are added and removed explicitly through the channels sub-resource\ninstead; a whole-object write would silently orphan a running execution.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign to update, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"budget":500000,"name":"Spring launch"},"schema":{"$ref":"#/components/schemas/campaignUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignRecord"}}},"description":"ok"}},"x-app":"campaign"}},"/v1/campaign/{id}/channels":{"post":{"operationId":"post_v1_campaign_by_id_channels","summary":"Adds a channel to a campaign, or REPLACES the one it already has of that kind, and returns the updated campaign.","description":"Adds a channel to a campaign, or REPLACES the one it already\nhas of that kind, and returns the updated campaign.\n\nA campaign carries at most one channel per kind, because the kind IS the\nexecutor: adding a second \"paid\" channel would mean two ad accounts running one\ncampaign with no way to tell their results apart. The new channel starts\n\"pending\" — adding it does not launch it.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign to add the channel to, from the path.","schema":{"type":"string"},"example":"cmp_1f…"}],"requestBody":{"content":{"application/json":{"example":{"account":"list_42","id":"cmp_1f…","kind":"email","platform":"sendgrid"},"schema":{"$ref":"#/components/schemas/channelAdd"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignRecord"}}},"description":"ok"}},"x-app":"campaign"}},"/v1/campaign/{id}/channels/{kind}":{"delete":{"operationId":"delete_v1_campaign_by_id_channels_by_kind","summary":"Drops one channel from a campaign and returns the updated campaign.","description":"Drops one channel from a campaign and returns the updated\ncampaign. 404 when the campaign carries no channel of that kind.\n\nIt removes the channel from the PLAN. A channel that is live at its provider\nshould be paused first — dropping the row here leaves nothing to pause it with\nafterwards.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign, from the path.","schema":{"type":"string"}},{"name":"kind","in":"path","required":true,"description":"Kind is the channel to remove: paid, organic or email.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignRecord"}}},"description":"ok"}},"x-app":"campaign"}},"/v1/campaign/{id}/launch":{"post":{"operationId":"post_v1_campaign_by_id_launch","summary":"Launch a campaign across every channel it declares","description":"Pushes the campaign live on each of its channels through that channel's executor and answers the whole campaign with the per-channel outcome written back onto it.\n\nThe fan-out is BEST-EFFORT PER CHANNEL, and the honest reading of the result is the rule most callers get wrong: one channel failing never aborts the others, so each channel row carries its own `live`, `failed` or `unavailable` status and detail, and a paid launch can be live while an email launch failed. The campaign itself is `live` when AT LEAST ONE channel launched and `failed` only when none did — `live` is not a claim that every channel launched. Repeating the call is safe: a channel already live is skipped, never re-launched. A campaign carrying more than one creative has its variant assigned here by the experiment seam and tagged as `utm_content`.\n\nOrg-scoped and fails closed: a valid bearer is required (403 without one), the campaign is read under the caller's OWN org so another tenant's id is a 404, and a campaign with no channels is a 400 — there is nothing to launch. Each executor resolves its own org's connector token from the org passed to it, so a launch can never spend through another tenant's connector.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"campaign"}},"/v1/campaign/{id}/metrics":{"get":{"operationId":"get_v1_campaign_by_id_metrics","summary":"Returns a campaign's results over a window: the analytics funnel (impressions, clicks, conversions, revenue, visitors), the spend each channel's connector reports, and the derived growth KPIs — CTR, CVR, CAC and ROAS.","description":"Returns a campaign's results over a window: the analytics\nfunnel (impressions, clicks, conversions, revenue, visitors), the spend each\nchannel's connector reports, and the derived growth KPIs — CTR, CVR, CAC and\nROAS.\n\nThere is exactly ONE metrics plane and nothing is stored here: the funnel is an\nanalytics query over the campaign's utm_campaign-tagged events, and the spend is\neach provider's own number read through the org's connector. A warehouse that is\nnot emitting yet degrades to available:false with zeroes — honest-empty, never a\n500 and never a fabricated number. When the campaign runs more than one creative\nand an experiment is wired, abTest carries the A/B analysis.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign to report on, from the path.","schema":{"type":"string"},"example":"cmp_1f…"},{"name":"range","in":"query","required":false,"description":"Range is the lookback window: 24h, 7d, 30d or 90d. Anything else, including\nempty, reads as 30d.","schema":{"type":"string"},"example":"7d"},{"name":"start","in":"query","required":false,"description":"Start is an explicit RFC3339 window start. Honored only together with End,\nand only when End is after it.","schema":{"type":"string"}},{"name":"end","in":"query","required":false,"description":"End is an explicit RFC3339 window end.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/campaignResults"}}},"description":"ok"}},"x-app":"campaign"}},"/v1/campaign/{id}/pause":{"post":{"operationId":"post_v1_campaign_by_id_pause","summary":"Pause every live channel on a campaign at its provider","description":"Pauses each live channel on its provider and answers the whole campaign, moved to `paused`, with the per-channel outcome written back onto it.\n\nOnly channels that are live and carry a provider reference are touched; a channel whose executor is no longer wired is marked `unavailable` and one whose pause errored is marked `failed`, with the reason on the row. The campaign still reports `paused` in both cases, and that is deliberate rather than sloppy: no live channel remains that this process will meter, and the rows say exactly which provider was not reached so it can be settled by hand.\n\nOrg-scoped and fails closed: a valid bearer is required (403 without one) and the campaign is read under the caller's OWN org, so another tenant's id is a 404.","tags":["campaign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"campaign"}},"/v1/captable/classes":{"get":{"operationId":"get_v1_captable_classes","summary":"Returns the caller org's share classes, in creation order.","description":"Returns the caller org's share classes, in creation order. A\nshare class is what a certificate is issued in, and every class the company\nhas authorized appears. The response is a bare JSON array, not an envelope.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/captableShareClass"},"type":"array"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_classes","summary":"Define a share class","description":"Creates a class of stock — its authorized share count, votes per share, par and issue price, seniority, conversion rights and liquidation/participation multiples — which is what shares, priced rounds and equity plans are then issued against.\n\nTwo fields are the company's to assign, not the caller's: the class index auto-increments per company, and the certificate prefix is DERIVED from the class type (CS for COMMON, PS for anything else), so a prefix in the body is ignored.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/classes/{id}":{"patch":{"operationId":"patch_v1_captable_classes_by_id","summary":"Amend a share class","description":"Rewrites one share class — the amendment path for a class whose authorized count, price, seniority or preference terms have changed.\n\nIt REPLACES the class rather than merging into it: every field is taken from this body, so an omitted field resets to the create-time default instead of keeping its current value. Send the full class. The index and the derived prefix are unchanged by an amendment. An id that is not this company's is not found.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"captable"}},"/v1/captable/company":{"get":{"operationId":"get_v1_captable_company","summary":"Returns the caller org's cap-table company record.","description":"Returns the caller org's cap-table company record. The row is\nseeded when the tenant's store first opens, so it always exists; its name and\nincorporation details are set with PUT /v1/captable/company.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCompany"}}},"description":"ok"}},"x-app":"captable"},"put":{"operationId":"put_v1_captable_company","summary":"Sets the caller org's company name and incorporation details.","description":"Sets the caller org's company name and incorporation details.\nThe name is required; the three incorporation fields are optional and each is\nstored as empty when omitted, so a call that sends only a name CLEARS them.\nThe company row itself is seeded when the tenant's store first opens, so this\nnever creates one.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCompanyUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableUpdated"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/convertibles":{"get":{"operationId":"get_v1_captable_convertibles","summary":"Returns the caller org's convertible notes, newest first.","description":"Returns the caller org's convertible notes, newest first. A\nnote's principal sits OUTSIDE issued equity until it converts, so it is not\npart of the share counts.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableNotes"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_convertibles","summary":"Record a convertible note","description":"Records a convertible note held by a stakeholder: the principal, the conversion cap, discount and interest rate, MFN, and the issue and board-approval dates.\n\nThe stakeholder must already exist in this company, and the note's public id must be unused there — a reused id is a conflict rather than an overwrite. Like a SAFE, this records the instrument only; conversion is not performed here.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/convertibles/{id}":{"delete":{"operationId":"delete_v1_captable_convertibles_by_id","summary":"Removes one of the caller org's convertible notes, taking its principal out of the cap table's unconverted-instrument totals.","description":"Removes one of the caller org's convertible notes, taking its\nprincipal out of the cap table's unconverted-instrument totals. An id this org\ndoes not hold is not found.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the convertible note to delete.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableDeleted"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/investments":{"get":{"operationId":"get_v1_captable_investments","summary":"Returns the caller org's investments, newest first.","description":"Returns the caller org's investments, newest first. It spans\nevery round, so it is the flat ledger of cheques written into the company,\neach naming its investor and the round it went into.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableInvestments"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/options":{"get":{"operationId":"get_v1_captable_options","summary":"Returns the caller org's option grants, newest first.","description":"Returns the caller org's option grants, newest first. Each row is\njoined to its grantee and its equity plan. Grants that are EXERCISED, EXPIRED\nor CANCELLED are listed here but do not dilute the cap table.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableOptions"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_options","summary":"Grant options from an equity plan","description":"Records an option grant to a stakeholder under an equity plan — quantity, exercise price, ISO/NSO type, cliff and vesting years, and the issue, expiration, vesting-start, board-approval and Rule 144 dates.\n\nThe stakeholder and the equity plan must both already exist in this company, and the grant id must be unused there — a reused grant id is a conflict, so a grant can never be overwritten by a later one carrying the same number.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/options/{id}":{"delete":{"operationId":"delete_v1_captable_options_by_id","summary":"Removes one of the caller org's option grants, taking its shares out of the cap table's granted-options and fully-diluted counts.","description":"Removes one of the caller org's option grants, taking its shares\nout of the cap table's granted-options and fully-diluted counts. An id this org\ndoes not hold is not found.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the option grant to delete.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableDeleted"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/plans":{"get":{"operationId":"get_v1_captable_plans","summary":"Returns the caller org's equity plans, newest first.","description":"Returns the caller org's equity plans, newest first. An equity\nplan is an option pool: a reserve of shares, drawn from one share class, that\noption grants are written against.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableEquityPlans"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_plans","summary":"Open an equity incentive plan","description":"Reserves a pool of shares out of a share class for option grants, with the board approval and effective dates and what happens to cancelled options.\n\nThe share class must already exist in this company — a plan cannot reserve out of nothing. Note the field name the bundle reads for the cancellation behaviour is `defaultCancellatonBehavior`; that spelling is the wire, and a correctly spelled key is simply not seen.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/rounds":{"get":{"operationId":"get_v1_captable_rounds","summary":"Returns the caller org's fundraising rounds, newest first.","description":"Returns the caller org's fundraising rounds, newest first. A round\ngroups a fundraising event; a PRICED round also carries the share class and\nprice per share it issues at.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableRounds"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_rounds","summary":"Open a funding round","description":"Opens a round with its name, type and target amount. It starts OPEN with nothing raised; investments are then added to it, and closing it is its own call.\n\nA PRICED round is the constrained case: it requires a share class that exists in this company and a price per share above zero, because that price is what converts each investment into issued shares. Its pre-money valuation is optional. A non-priced round carries none of the three.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/rounds/{id}":{"get":{"operationId":"get_v1_captable_rounds_by_id","summary":"Returns one of the caller org's fundraising rounds together with every investment written into it, oldest first.","description":"Returns one of the caller org's fundraising rounds together with every\ninvestment written into it, oldest first. A round id that does not exist in the\ncaller's org is not found — including one that exists in another tenant, since\nthe org comes from the caller's principal and is part of the lookup.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the round to read. It is the path segment: the URL is the addressing\nauthority, and the org it is resolved in comes from the caller's principal,\nso an id from another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableRoundDetail"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/rounds/{id}/close":{"post":{"operationId":"post_v1_captable_rounds_by_id_close","summary":"Closes one of the caller org's fundraising rounds, recording the close date and moving its status to CLOSED.","description":"Closes one of the caller org's fundraising rounds, recording the\nclose date and moving its status to CLOSED. Only an OPEN round can be closed:\na round that is already closed — like an id this org does not hold — is not\nfound. Closing a round does not change what was invested in it.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the round to close. It is the path segment: the URL is the\naddressing authority, and the org it is resolved in comes from the\ncaller's principal, so an id from another tenant is simply not found.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableRoundCloseRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableUpdated"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/rounds/{id}/investments":{"post":{"operationId":"post_v1_captable_rounds_by_id_investments","summary":"Record an investment into a round","description":"Records what a stakeholder put into a round and adds it to the round's raised total.\n\nOn a PRICED round this ISSUES SHARES as well as recording the money: the amount is divided by the round's price per share, rounded DOWN to whole shares, and a new certificate for them is issued to the investor in the round's share class — so an amount too small to buy one whole share is refused rather than recorded as a zero-share investment. On a non-priced round the money is recorded and no shares are issued.\n\nThe round must exist in this company and still be OPEN — a closed round refuses further investment — and the investor must already be a stakeholder here. The date defaults to today when omitted.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"captable"}},"/v1/captable/safes":{"get":{"operationId":"get_v1_captable_safes","summary":"Returns the caller org's SAFEs, newest first.","description":"Returns the caller org's SAFEs, newest first. A SAFE is a simple\nagreement for future equity: its capital sits OUTSIDE issued equity until it\nconverts, so it is not part of the share counts.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableSafes"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_safes","summary":"Record a SAFE","description":"Records a Simple Agreement for Future Equity held by a stakeholder: the capital in, the valuation cap and discount, MFN and pro-rata rights, pre- or post-money type, and the issue and board-approval dates.\n\nThe stakeholder must already exist in this company, and the SAFE's public id must be unused there — a reused id is a conflict rather than an overwrite. This records the instrument; it does not convert it, so nothing is issued against a share class until a round does that.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/safes/{id}":{"delete":{"operationId":"delete_v1_captable_safes_by_id","summary":"Removes one of the caller org's SAFEs, taking its capital out of the cap table's unconverted-instrument totals.","description":"Removes one of the caller org's SAFEs, taking its capital out of the\ncap table's unconverted-instrument totals. An id this org does not hold is not\nfound.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the SAFE to delete.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableDeleted"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/shares":{"get":{"operationId":"get_v1_captable_shares","summary":"Returns the caller org's share certificates, newest first.","description":"Returns the caller org's share certificates, newest first. Each row\nis joined to its holder and its share class, so a certificate names who holds\nit and what class it is in without a second call.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableShares"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_shares","summary":"Issue a share certificate","description":"Issues shares of a class to a stakeholder as a certificate: quantity, price and capital contributed, the vesting cliff and term, the legends on the certificate, and the issue, Rule 144, vesting-start and board-approval dates.\n\nBoth the stakeholder and the share class must already exist in this company, and the certificate id must be unused there — a reused id is a conflict, never a silent overwrite of an existing certificate.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/shares/transfer":{"post":{"operationId":"post_v1_captable_shares_transfer","summary":"Transfer shares to another stakeholder","description":"Moves shares from one certificate to another stakeholder, in one atomic step.\n\nOMITTING `quantity` transfers the WHOLE certificate, which simply reassigns it and answers newShareId null — that is the difference between a full and a partial transfer, and it is why quantity is absent rather than zero. A partial transfer shrinks the source certificate and issues a NEW one to the recipient, so it requires a `certificateId` for that new certificate and refuses a reused one. The quantity must be between 1 and what the source certificate actually holds; the recipient must be a stakeholder of this same company.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/shares/{id}":{"delete":{"operationId":"delete_v1_captable_shares_by_id","summary":"Removes one of the caller org's share certificates, taking its shares out of the cap table's outstanding and fully-diluted counts.","description":"Removes one of the caller org's share certificates, taking its\nshares out of the cap table's outstanding and fully-diluted counts. An id this\norg does not hold is not found.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the share certificate to delete.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableDeleted"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/stakeholders":{"get":{"operationId":"get_v1_captable_stakeholders","summary":"Returns the caller org's stakeholders, newest first.","description":"Returns the caller org's stakeholders, newest first. The\nresponse is a bare JSON array, not an envelope. Each row carries the holder's\ncontact and address fields alongside the company's name.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/captableStakeholder"},"type":"array"}}},"description":"ok"}},"x-app":"captable"},"post":{"operationId":"post_v1_captable_stakeholders","summary":"Add stakeholders to the cap table","description":"Records the people and institutions that can hold equity — the rows every share, option, SAFE, note and investment is issued to.\n\nThe body is ONE stakeholder object or an ARRAY of them, and the array is the point: a whole roster loads in a single call. Email is the identity within the company, so a stakeholder whose email is already on the table is SKIPPED rather than duplicated or rejected — the 201 reports how many rows were actually inserted, which is what makes re-running an import safe. Validation is all-or-nothing across the batch: one bad entry refuses the whole array.\n\nWrites the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.","tags":["captable"],"x-app":"captable"}},"/v1/captable/stakeholders/{id}":{"delete":{"operationId":"delete_v1_captable_stakeholders_by_id","summary":"Removes one of the caller org's stakeholders.","description":"Removes one of the caller org's stakeholders. It REFUSES to\norphan issued equity: a holder that still holds share certificates or option\ngrants cannot be deleted, and answers 400 saying so — release or transfer the\nholdings first. An id this org does not hold is not found.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the stakeholder to delete.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableDeleted"}}},"description":"ok"}},"x-app":"captable"},"patch":{"operationId":"patch_v1_captable_stakeholders_by_id","summary":"Changes one of the caller org's stakeholders.","description":"Changes one of the caller org's stakeholders. It is a\nPARTIAL update: only the fields the request names are written, and a field\nsent as null clears that column. A request that names no updatable field is\nrefused, and an id this org does not hold is not found.\n\nThe values are stored as sent. Unlike adding a stakeholder, this route does\nnot check the email's shape or the type and relationship vocabularies, so it\ncan record a value that adding one would have rejected.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the stakeholder to update. It is the path segment: the URL is the\naddressing authority, and the org it is resolved in comes from the\ncaller's principal, so an id from another tenant is simply not found.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableStakeholderPatch"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableUpdated"}}},"description":"ok"}},"x-app":"captable"}},"/v1/captable/summary":{"get":{"operationId":"get_v1_captable_summary","summary":"Computes the caller org's cap table.","description":"Computes the caller org's cap table. It answers who owns what on a\nfully-diluted basis: outstanding shares, granted options, per-stakeholder\nownership percentages, each share class's authorized versus issued position,\nand the capital sitting on SAFEs and convertible notes that have not yet\nconverted. Only non-terminal option grants dilute — EXERCISED, EXPIRED and\nCANCELLED grants are excluded, so equity issued through an exercised option is\nnever counted twice.","tags":["captable"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableSummary"}}},"description":"ok"}},"x-app":"captable"}},"/v1/cart":{"post":{"operationId":"openCart","summary":"Open a cart for a shopper to fill","description":"Opens an empty cart for a shopper to fill, and answers it with its new id.\n\nThis is the first step of a sale: hold the id, add items to it with\nsetCartItem, then hand it to checkout. Every field of the request is optional —\nan empty body opens a perfectly good anonymous cart — and the fields exist only\nto pre-fill what is already known about the shopper.\n\nThe STORE defaults to the org's own default storefront, so a merchant selling\nthrough one storefront never has to name it. The CURRENCY defaults to usd; note\nthat checkout overrides it with the store's own currency when the sale is\nauthorized, so a currency set here is a hint rather than a commitment.\n\nThe cart is created in the CALLER'S OWN org namespace, taken from the validated\nprincipal and never from the body, so a cart can never be opened on another\ntenant's books.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["cart"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartOpen"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cart"}}},"description":"created"}},"x-app":"commerce"}},"/v1/cart/{id}":{"get":{"operationId":"getCart","summary":"Read one cart with its lines and totals","description":"Reads one cart: its lines, its status and what it comes to.\n\nThis is what a storefront calls to render the basket, and what a support agent\ncalls to see what a shopper is looking at. The totals are the cart's STORED\ntally — shipping and tax stay zero until checkout resolves a shipping option\nand a tax region, so a cart total before checkout is the merchandise total and\nis meant to be.\n\nThe org scopes the read by construction: the store is namespaced to it, so a\ncart id belonging to another tenant is simply not found rather than found and\nthen filtered, and answers 404 rather than 403 so the id space cannot be probed.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["cart"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cart's id, as the open call answered it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cart"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/cart/{id}/discard":{"post":{"operationId":"discardCart","summary":"Discard a cart the shopper abandoned","description":"Discards a cart the shopper abandoned, and answers it in its final state.\n\nA discarded cart is CLOSED, not deleted: the row stays, so abandoned-basket\nreporting and any follow-up that keys on it still have something to read. It\nstops being a cart anything will check out, which is the point — it is how a\nstorefront says \"this basket is over\" without destroying the evidence that it\nexisted.\n\nDiscarding is idempotent: a cart already discarded answers its stored state\nrather than failing, so a retry is safe.\n\nThe cart is resolved inside the caller's own org namespace, so another tenant's\nid answers 404.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["cart"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cart's id, as the open call answered it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cart"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/cart/{id}/item":{"post":{"operationId":"setCartItem","summary":"Set one item's quantity in a cart; zero removes it","description":"Sets how many of one item a cart holds, and answers the whole updated cart.\n\nThis is the ONE way a cart's contents change. The quantity is the RESULT, not a\ndelta: sending 3 leaves 3 however many were there before, so a retry is safe and\na double-submit cannot double an order. ZERO REMOVES the line — there is\ndeliberately no separate delete, because removal is the same act at the boundary\nvalue and a second spelling would be a second set of edge cases.\n\nName the item with EITHER product OR variant, never both. Prefer variant for\nanything sold in sizes, colours or tiers: the price and the stock belong to the\nvariant, so a product-level line on a varianted product prices the wrong thing.\nEither may be given as an id or as the human key — a product's URL slug, a\nvariant's SKU — which is what lets a storefront add to cart straight from a\nproduct page URL without a lookup first.\n\nThe item's price and name are CACHED onto the line as it is added, so the cart\nkeeps the price the shopper was shown even if the catalog moves underneath it.\n\nAn item that resolves to nothing in the catalog is refused 400 and the cart is\nleft exactly as it was; nothing is partially applied.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["cart"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cart to amend, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartItemSet"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cart"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/catalog":{"get":{"operationId":"get_v1_catalog","summary":"Browse searches AND browses the cross-org catalog: every project, app and site the fleet has built, whichever org built it.","description":"Browse searches AND browses the cross-org catalog: every project, app and site\nthe fleet has built, whichever org built it.\n\nIt reads TWO corpora and returns them as one page — the published,\nworld-readable catalog that every caller sees, plus the caller's OWN org's\nprivate entries when the request carries a validated principal. Each row says\nwhich it came from in `scope`, so a client can warn before sharing a link. An\nanonymous caller simply gets the published one; no filter can ever widen a\ncaller into another tenant's corpus, because the query that would return it is\nnever run for them.\n\nA request with no q is a browse rather than a search, and both answer the same\nshape: the page, the total before paging, and the facet counts over the whole\nmatching set.","tags":["catalog"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q is the free-text query the lexical index scores relevance on. Empty is a\nbrowse rather than a search — the same request either way.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org narrows to one builder org: hanzo | lux | zoo. Case-insensitive.","schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"description":"Kind narrows to repo | site. Case-insensitive.","schema":{"type":"string"}},{"name":"origin","in":"query","required":false,"description":"Origin narrows to what a row IS to you: template | community | third-party |\nproduct. This is the axis the two hanzo.app lanes are cut on.","schema":{"type":"string"},"example":"template"},{"name":"archetype","in":"query","required":false,"description":"Archetype narrows to one project archetype. Case-insensitive.","schema":{"type":"string"}},{"name":"language","in":"query","required":false,"description":"Language narrows to one implementation language. Case-insensitive.","schema":{"type":"string"},"example":"typescript"},{"name":"template","in":"query","required":false,"description":"Template narrows a lane to ONE lineage: the id of the parent everything\nreturned was forked from.","schema":{"type":"string"}},{"name":"forkable","in":"query","required":false,"description":"Forkable is tri-state: \"true\" selects the forkable rows, \"false\" selects the\nrest, and anything else — including absent — applies no filter at all.","schema":{"type":"string"},"example":"true"},{"name":"limit","in":"query","required":false,"description":"Limit caps the page at 200, default 50. A value that is not a non-negative\ninteger falls back to the default.","schema":{"type":"string"},"example":"20"},{"name":"offset","in":"query","required":false,"description":"Offset is where the page starts, default 0, with the same tolerance.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/catalogPage"}}},"description":"ok"}},"x-app":"catalog"}},"/v1/catalog/entries":{"get":{"operationId":"get_v1_catalog_entries","summary":"The raw catalog entries, including the unpublished ones","description":"Returns every catalog row as stored — the admin view, which unlike the public projection includes entries that are not published. It is cross-tenant platform data, so the gate is a PLATFORM admin: an org-level admin is refused 403 no matter how privileged they are inside their own org, enforced by the handler itself and not only by the route's token middleware.","tags":["catalog"],"x-app":"commerce"},"post":{"operationId":"post_v1_catalog_entries","summary":"Add a catalog entry","description":"Creates a catalog row from the body and answers it at 201. The slug is required and is the globally-unique catalog key, so a second entry claiming a slug already in use is refused 409 rather than shadowing the first. PLATFORM admin only — this is cross-tenant pricing and packaging data, and an org-level admin is refused 403.","tags":["catalog"],"x-app":"commerce"}},"/v1/catalog/entries/{wildcard1}":{"delete":{"operationId":"delete_v1_catalog_entries_by_wildcard1","summary":"Remove a catalog entry","description":"Deletes the entry with the addressed slug and answers 204. The slug is matched as a trailing wildcard, not a single segment, because a model slug contains a slash. PLATFORM admin only — an org-level admin is refused 403 — and an unknown slug is 404, so the call is safe to repeat but not silently idempotent.","tags":["catalog"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_catalog_entries_by_wildcard1","summary":"Replace a catalog entry, keeping its slug","description":"Loads the addressed entry, applies the body over it and answers the stored result. The slug is the entry's IDENTITY and is re-stamped from the path after decoding, so a slug in the body is ignored and a rename is impossible through this address. The slug is matched as a trailing wildcard rather than one path segment because a model's slug IS its callable id and those contain a slash — a segment parameter would stop at it and leave most catalog rows unaddressable. PLATFORM admin only; an unknown slug is 404.","tags":["catalog"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/catalog/models":{"post":{"operationId":"post_v1_catalog_models","summary":"Land a syncer's view of the model catalog: upstream costs and machine facts","description":"Takes a batch of model rows and upserts each one's upstream COST and machine-observable facts, answering what was created and changed. It deliberately touches nothing a human owns — not the retail price, not the markup, not the entitlement tier — so a sync can never overwrite an administrator's pricing decision. The gate is a PLATFORM principal rather than a platform ADMIN, because the caller is normally a scheduled job holding the internal service token, which carries platform scope but no admin claim.","tags":["catalog"],"x-app":"commerce"}},"/v1/catalog/models/refresh":{"post":{"operationId":"post_v1_catalog_models_refresh","summary":"Refresh the model catalog by reading the upstream provider","description":"Pulls the upstream model list and lands it through the same upsert the push door uses, so the rule that a sync owns cost and an administrator owns price holds no matter which door a row came through. It takes no body — the upstream is READ rather than told. If that upstream cannot be read the call answers 502 and writes NOTHING: a sync that cannot see its source must never conclude the source is empty, because that conclusion would withdraw every model on sale. The gate is a PLATFORM principal so the scheduled job's service token qualifies.","tags":["catalog"],"x-app":"commerce"}},"/v1/catalog/seed":{"post":{"operationId":"post_v1_catalog_seed","summary":"Seed the embedded catalog, without disturbing edits already made","description":"Upserts the shipped catalog seed and answers how many entries it created. It is idempotent and non-destructive — an entry an administrator has since edited is left alone — so it is safe to run against a live catalog to fill in what is missing. PLATFORM admin only; an org-level admin is refused 403.","tags":["catalog"],"x-app":"commerce"}},"/v1/channels":{"get":{"operationId":"get_v1_channels","summary":"Returns every chat transport channels can talk to — Discord, Slack, Teams and Telegram — with the caller org's own facts on each: whether it is connected and to which account, what the transport supports, the org's DM and group access policies, and how many pairing requests are pending approval.","description":"Returns every chat transport channels can talk to — Discord, Slack, Teams\nand Telegram — with the caller org's own facts on each: whether it is\nconnected and to which account, what the transport supports, the org's DM and\ngroup access policies, and how many pairing requests are pending approval. The\norder is fixed, so a console can render the same rows every time. A policy that\ncannot be read leaves that channel's policy fields empty rather than failing\nthe whole listing.","tags":["channels"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/chatChannels"}}},"description":"ok"}},"x-app":"channels"}},"/v1/channels/allowlist":{"get":{"operationId":"get_v1_channels_allowlist","summary":"Returns the caller org's access policy for one channel: whether DMs are pairing-gated, allowlisted or open, whether group rooms are open, allowlisted or disabled, the config-managed DM and group allow entries, the senders approved through PAIRING (read-only here), and the org's named access groups.","description":"Returns the caller org's access policy for one channel: whether\nDMs are pairing-gated, allowlisted or open, whether group rooms are open,\nallowlisted or disabled, the config-managed DM and group allow entries, the\nsenders approved through PAIRING (read-only here), and the org's named access\ngroups. An unknown channel is a 404.","tags":["channels"],"parameters":[{"name":"channel","in":"query","required":false,"description":"Channel is the transport to read: discord, slack, teams or telegram.\nRequired; an unknown value is a 404.","schema":{"type":"string"},"example":"slack"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/allowlistView"}}},"description":"ok"}},"x-app":"channels"},"put":{"operationId":"put_v1_channels_allowlist","summary":"Edits the caller org's access policy for one channel and answers the policy as GET would, so both verbs return ONE shape.","description":"Edits the caller org's access policy for one channel and answers\nthe policy as GET would, so both verbs return ONE shape. It requires ORG ADMIN.\nEvery field but `channel` is optional and applied only when provided: an empty\npolicy string leaves that policy alone, an absent or null list leaves that list\nalone, and an EMPTY list clears it. It writes only CONFIG-sourced allow entries\n— senders approved through pairing belong to the approval flow, so a policy\nedit can never revoke one. An unknown channel is a 404.","tags":["channels"],"requestBody":{"content":{"application/json":{"example":{"channel":"slack","dm":["U024BE7LH"],"dmPolicy":"allowlist"},"schema":{"$ref":"#/components/schemas/allowlistPutIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/allowlistView"}}},"description":"ok"}},"x-app":"channels"}},"/v1/channels/inbox":{"get":{"operationId":"get_v1_channels_inbox","summary":"Returns the messages people have sent to the caller org's connected chat bots, oldest first, in the portable envelope shape every transport normalises into.","description":"Returns the messages people have sent to the caller org's connected chat\nbots, oldest first, in the portable envelope shape every transport normalises\ninto. It is a CURSOR feed, not a search: pass the returned cursor back as\n`since` to get only what has arrived since. Only this org's messages are\nstored under this org, so the feed can never carry another tenant's chat.","tags":["channels"],"parameters":[{"name":"since","in":"query","required":false,"description":"Since is the exclusive cursor: only messages with a higher row id come\nback. Empty starts at the beginning. Must parse as an integer.","schema":{"type":"string"},"example":"1042"},{"name":"limit","in":"query","required":false,"description":"Limit caps how many messages come back. Empty or 0 uses the store's\ndefault page size. Must parse as an integer.","schema":{"type":"string"},"example":"100"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/inboxPage"}}},"description":"ok"}},"x-app":"channels"}},"/v1/channels/pairing":{"get":{"operationId":"get_v1_channels_pairing","summary":"Returns the pairing requests waiting for the caller org to approve — one per person who messaged a connected bot on a channel whose DM policy is \"pairing\" and who is not allowed yet.","description":"Returns the pairing requests waiting for the caller org to approve\n— one per person who messaged a connected bot on a channel whose DM policy is\n\"pairing\" and who is not allowed yet. Each row carries the CODE an org admin\npasses to POST /v1/channels/pairing/approve. Expired requests are not\nreturned. Codes are capability strings: they are shown here, and never logged.","tags":["channels"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pairingQueue"}}},"description":"ok"}},"x-app":"channels"}},"/v1/channels/pairing/approve":{"post":{"operationId":"post_v1_channels_pairing_approve","summary":"Turns one pending pairing code into a standing allow entry, so that person can DM the org's bot on that channel from now on.","description":"Turns one pending pairing code into a standing allow entry, so\nthat person can DM the org's bot on that channel from now on. It requires ORG\nADMIN, not merely membership. The first approval an org makes on a channel also\nbootstraps that sender as the channel's owner, which the answer reports. An\nunknown or expired code is a 404, and a code always belongs to exactly one\norg, so it can never approve someone into another tenant.","tags":["channels"],"requestBody":{"content":{"application/json":{"example":{"channel":"telegram","code":"PAIR-7Q2M"},"schema":{"$ref":"#/components/schemas/approvePairingIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pairingApproved"}}},"description":"ok"}},"x-app":"channels"}},"/v1/channels/{channel}/send":{"post":{"operationId":"post_v1_channels_by_channel_send","summary":"Send a message from your org's bot to one chat room","description":"Delivers text, attachments and actions to one room on a connected chat transport — discord, slack, teams or telegram — and answers that transport's own receipt, the `messageId` it assigned and the Unix second it landed. An unknown channel is a 404.\n\nThe body is the envelope's NARROW outbound projection: `room`, `text`, `attachments`, `actions`, `replyTo` and `idempotency`, and nothing else. Identity is not a field — the channel is the path segment and the sender is the caller's validated org — so a body carrying `sender`, `account` or `channel` is refused with 400 rather than having it silently dropped. `room.id` is required, and so is something to say: text, or at least one attachment.\n\nRequires a validated principal; 403 without one. The room must already belong to the caller's org — each transport verifies the binding itself, so a room this org has not bound is 403 and a room whose route the bot has never learned is 409, meaning someone has to message the bot there first. A transport that fails answers 502 carrying status and shape only, never a token.\n\nSending is at-most-once only if you ask for it: pass an `idempotency` string and a replay answers 200 with the PRIOR receipt instead of sending twice, while a send that fails releases the key so the caller can re-attempt. Bodies over 1 MiB are refused. All four transports currently render text only, so attachments and actions are flattened deterministically to one line each after the text rather than dropped.","tags":["channels"],"parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"channels"}},"/v1/chat":{"post":{"operationId":"post_v1_chat","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["chat"],"x-app":"github.com/hanzoai/ai"}},"/v1/chat/completions":{"post":{"operationId":"post_v1_chat_completions","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["chat"],"x-app":"github.com/hanzoai/ai"}},"/v1/cloud":{"get":{"operationId":"get_v1_cloud","summary":"Returns the clouds this deployment can link and what linking each one needs — the DigitalOcean token, the AWS role and external id, the GCP credential JSON, the Azure app — plus whether the provider can be linked without storing any long-lived secret.","description":"Returns the clouds this deployment can link and what linking each\none needs — the DigitalOcean token, the AWS role and external id, the GCP\ncredential JSON, the Azure app — plus whether the provider can be linked without\nstoring any long-lived secret. It is the catalog a \"connect a cloud\" screen\nrenders; it reports no account and no credential.","tags":["cloud"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/providersView"}}},"description":"ok"}},"x-app":"venue"}},"/v1/cloud/accounts":{"get":{"operationId":"get_v1_cloud_accounts","summary":"Lists the caller org's linked cloud accounts across every provider: which account each one is at the provider, which fleet clusters it folded, and when it was last discovered.","description":"Lists the caller org's linked cloud accounts across every provider:\nwhich account each one is at the provider, which fleet clusters it folded, and\nwhen it was last discovered. Metadata only — a sealed credential never appears in\na response. Another org's accounts are not visible and not countable.","tags":["cloud"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/cloudAccountsView"}}},"description":"ok"}},"x-app":"venue"}},"/v1/cloud/{provider}/accounts":{"post":{"operationId":"post_v1_cloud_by_provider_accounts","summary":"Links one of the caller org's cloud accounts and folds the Kubernetes clusters it finds there into the ONE Hanzo fleet, so they appear at /v1/clusters and can run work like any managed or bring-your-own cluster.","description":"Links one of the caller org's cloud accounts and folds the Kubernetes\nclusters it finds there into the ONE Hanzo fleet, so they appear at /v1/clusters\nand can run work like any managed or bring-your-own cluster. Answers 201.\n\nThe credential is verified LIVE against the provider BEFORE anything is stored,\nso a bad one is refused and nothing is written; it is then sealed in the org's own\nKMS namespace and never appears in a response, the account index, or a log line.\nDiscovery follows, and a cluster that fails to fold is reported as DATA in the\nclusters list rather than failing the link.\n\nRe-linking a label that already exists re-seals its credential and re-folds it, so\nthis is how a rotated token is replaced. Requires org admin.","tags":["cloud"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the cloud being linked, from the path: digitalocean, aws, gcp\nor azure.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/venueLinkRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accountFoldView"}}},"description":"created"}},"x-app":"venue"}},"/v1/cloud/{provider}/accounts/{label}":{"delete":{"operationId":"delete_v1_cloud_by_provider_accounts_by_label","summary":"Forgets one linked cloud account: it detaches every fleet cluster THIS account folded (its own names, in its own shard — a neighbour's cluster of the same name is untouched), deletes the sealed credential, and drops the index row.","description":"Forgets one linked cloud account: it detaches every fleet cluster\nTHIS account folded (its own names, in its own shard — a neighbour's cluster of\nthe same name is untouched), deletes the sealed credential, and drops the index\nrow.\n\nIt is idempotent and deliberately not an existence oracle: an account this org\ndoes not hold answers exactly the same as one it just removed. A cluster that\nfails to detach is logged and the unlink continues, so a dead provider cannot\nstrand a credential. Requires org admin.","tags":["cloud"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the cloud the account belongs to: digitalocean, aws, gcp or\nazure. An unknown provider is not found.","schema":{"type":"string"}},{"name":"label","in":"path","required":true,"description":"Label is the org-chosen name of the account within that provider. Empty\nmeans \"default\"; anything outside 1–64 of [A-Za-z0-9._-] is refused.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/unlinkedView"}}},"description":"ok"}},"x-app":"venue"}},"/v1/cloud/{provider}/accounts/{label}/sync":{"post":{"operationId":"post_v1_cloud_by_provider_accounts_by_label_sync","summary":"Re-discovers one already-linked cloud account and reconciles what it folded: kubeconfigs are refreshed, clusters that appeared since the last sync are folded, and clusters this account folded that the provider no longer returns are detached — only this account's own, in the fleet shard it was linked into.","description":"Re-discovers one already-linked cloud account and reconciles what it\nfolded: kubeconfigs are refreshed, clusters that appeared since the last sync are\nfolded, and clusters this account folded that the provider no longer returns are\ndetached — only this account's own, in the fleet shard it was linked into.\n\nIt is idempotent, it reads the credential already sealed at link time, and a\ndiscovery failure leaves the existing fold set alone rather than mass-detaching\nit. An account this org has not linked is not found. Requires org admin.","tags":["cloud"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the cloud the account belongs to: digitalocean, aws, gcp or\nazure. An unknown provider is not found.","schema":{"type":"string"}},{"name":"label","in":"path","required":true,"description":"Label is the org-chosen name of the account within that provider. Empty\nmeans \"default\"; anything outside 1–64 of [A-Za-z0-9._-] is refused.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accountFoldView"}}},"description":"ok"}},"x-app":"venue"}},"/v1/cloudflare/ai/run/{wildcard1}":{"post":{"operationId":"post_v1_cloudflare_ai_run_by_wildcard1","summary":"Run a Cloudflare Workers AI model and get its output back","description":"Runs a Workers AI model — the model id is the rest of the path, e.g. `@cf/meta/llama-3.1-8b-instruct` — on the org's OWN Cloudflare account and relays the model's output. The request body is whatever the chosen model takes (a prompt, chat messages, a base64 audio clip) and is forwarded unchanged; the response is the model's own, which for an image or audio model is BYTES under Cloudflare's content type rather than JSON. Both halves are why this is not a typed op.\n\nIt is the ONE PRICED route on this plane, because a run is inference rather than passthrough. The org's own token already paid Cloudflare for the compute, so Hanzo debits only the thin BYO routing fee — never the full inference cost — and meters it on the SAME `ai` product axis and per-project caps as every other model call, so Workers AI spend sums with LLM spend. The fee has a floor, so every run leaves a usage row even for a modality that reports no tokens, and it emits one gen_ai span with `gen_ai.system = cloudflare`.\n\nGated by BALANCE, not by the admin bit that guards the destructive verbs here: a validated org is enough, and a frozen, broke or over-cap org is refused with the fleet-wide 402/503 billing contract BEFORE any byte reaches Cloudflare — no run, and no account discovery either. An empty or oversized body is 400, as is a model id that is not a plain Cloudflare model path; 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"cloudflare"}},"/v1/cloudflare/d1/databases":{"get":{"operationId":"get_v1_cloudflare_d1_databases","summary":"Lists the D1 databases on the org's Cloudflare account.","description":"Lists the D1 databases on the org's Cloudflare account. Any org\nmember may read.","tags":["cloudflare"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page of databases to return.","schema":{"type":"string"}},{"name":"per_page","in":"query","required":false,"description":"PerPage is how many databases one page holds.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name filters to the database with this name.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"post":{"operationId":"post_v1_cloudflare_d1_databases","summary":"Creates a D1 database on the org's Cloudflare account.","description":"Creates a D1 database on the org's Cloudflare account.\nRequires org admin.","tags":["cloudflare"],"requestBody":{"content":{"application/json":{"example":{"name":"orders"},"schema":{"$ref":"#/components/schemas/databaseCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/d1/databases/{database}":{"delete":{"operationId":"delete_v1_cloudflare_d1_databases_by_database","summary":"Deletes a D1 database and everything stored in it.","description":"Deletes a D1 database and everything stored in it. Requires\norg admin.","tags":["cloudflare"],"parameters":[{"name":"database","in":"path","required":true,"description":"Database is the Cloudflare D1 database id or name.","schema":{"type":"string"},"example":"orders"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/d1/databases/{database}/query":{"post":{"operationId":"post_v1_cloudflare_d1_databases_by_database_query","summary":"Run a SQL statement against a D1 database","description":"Executes a statement on one D1 database on the org's OWN Cloudflare account and relays D1's result set. `sql` is required and `params` carries the bound values in placeholder order — use them rather than interpolating values into the statement.\n\nThe body is checked for a non-empty `sql` and then forwarded VERBATIM, so every field D1 accepts reaches D1 even though only two are named here; the declared schema is open for that reason. That verbatim forward is why this is not a typed op — decoding and re-encoding the body would drop `params`, where the query's bound values live. Requires ORG ADMIN (403 otherwise); a malformed body or missing `sql` is 400; 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"database","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/D1Query"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{}}},"description":"Success"}},"x-app":"cloudflare"}},"/v1/cloudflare/kv/namespaces":{"get":{"operationId":"get_v1_cloudflare_kv_namespaces","summary":"KVNamespaceList lists the Workers KV namespaces on the org's Cloudflare account.","description":"KVNamespaceList lists the Workers KV namespaces on the org's Cloudflare\naccount. Any org member may read.","tags":["cloudflare"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page of namespaces to return.","schema":{"type":"string"}},{"name":"per_page","in":"query","required":false,"description":"PerPage is how many namespaces one page holds.","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order names the field to sort by, and Direction sorts asc or desc.","schema":{"type":"string"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"post":{"operationId":"post_v1_cloudflare_kv_namespaces","summary":"KVNamespaceCreate creates a Workers KV namespace on the org's Cloudflare account.","description":"KVNamespaceCreate creates a Workers KV namespace on the org's Cloudflare\naccount. Requires org admin. Cloudflare mints the namespace id the value routes\naddress.","tags":["cloudflare"],"requestBody":{"content":{"application/json":{"example":{"title":"sessions"},"schema":{"$ref":"#/components/schemas/namespaceCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/kv/namespaces/{namespace}":{"delete":{"operationId":"delete_v1_cloudflare_kv_namespaces_by_namespace","summary":"KVNamespaceDelete deletes a Workers KV namespace and every key in it.","description":"KVNamespaceDelete deletes a Workers KV namespace and every key in it. Requires\norg admin.","tags":["cloudflare"],"parameters":[{"name":"namespace","in":"path","required":true,"description":"Namespace is the Cloudflare KV namespace id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/kv/namespaces/{namespace}/values/{key}":{"delete":{"operationId":"delete_v1_cloudflare_kv_namespaces_by_namespace_values_by_key","summary":"KVValueDelete removes one key from a Workers KV namespace.","description":"KVValueDelete removes one key from a Workers KV namespace. Requires org admin.","tags":["cloudflare"],"parameters":[{"name":"namespace","in":"path","required":true,"description":"Namespace is the Cloudflare KV namespace id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"},{"name":"key","in":"path","required":true,"description":"Key is the key within that namespace. KV keys are broad (up to 512 bytes),\nso this one is escaped rather than charset-restricted.","schema":{"type":"string"},"example":"session/abc"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"get":{"operationId":"get_v1_cloudflare_kv_namespaces_by_namespace_values_by_key","summary":"Read a Workers KV value as its stored bytes","description":"Answers one KV key's value from the org's OWN Cloudflare account as RAW BYTES under the content type it was written with — not wrapped in a JSON envelope, which is why this is not a typed op. Any org member may read. A key that does not exist is Cloudflare's own 404; an invalid namespace, or a key that is empty, over 512 bytes, not valid UTF-8, or carries a control character, is 400; 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"namespace","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"cloudflare"},"put":{"operationId":"put_v1_cloudflare_kv_namespaces_by_namespace_values_by_key","summary":"Write a Workers KV value from the request body","description":"Stores one KV key on the org's OWN Cloudflare account. The REQUEST BODY IS THE VALUE, forwarded verbatim under the caller's own Content-Type (`text/plain` when none is sent), so a value is never re-encoded on the way in — which is why this is not a typed op. `expiration` and `expiration_ttl` may ride the query string and are passed through to Cloudflare. Requires ORG ADMIN (403 otherwise); the same namespace and key validation as the read answers 400; 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"namespace","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"cloudflare"}},"/v1/cloudflare/pages/projects":{"get":{"operationId":"get_v1_cloudflare_pages_projects","summary":"Lists the org's Cloudflare Pages projects.","description":"Lists the org's Cloudflare Pages projects. Any org member may read.","tags":["cloudflare"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"post":{"operationId":"post_v1_cloudflare_pages_projects","summary":"Creates a Cloudflare Pages project on the org's account.","description":"Creates a Cloudflare Pages project on the org's account. Requires\norg admin. Only the modeled fields reach Cloudflare, so an unmodeled key in the\nrequest is dropped rather than forwarded.","tags":["cloudflare"],"requestBody":{"content":{"application/json":{"example":{"name":"marketing-site","production_branch":"main"},"schema":{"$ref":"#/components/schemas/PagesProjectCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/pages/projects/{project}":{"delete":{"operationId":"delete_v1_cloudflare_pages_projects_by_project","summary":"Deletes a Cloudflare Pages project, and with it every deployment it has ever made.","description":"Deletes a Cloudflare Pages project, and with it every deployment it\nhas ever made. Requires org admin.","tags":["cloudflare"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the Pages project name.","schema":{"type":"string"},"example":"marketing-site"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"get":{"operationId":"get_v1_cloudflare_pages_projects_by_project","summary":"Reads one Cloudflare Pages project — its build config, deployment configs and latest deployment.","description":"Reads one Cloudflare Pages project — its build config, deployment\nconfigs and latest deployment. Any org member may read.","tags":["cloudflare"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the Pages project name.","schema":{"type":"string"},"example":"marketing-site"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/pages/projects/{project}/deployments":{"post":{"operationId":"post_v1_cloudflare_pages_projects_by_project_deployments","summary":"Trigger a new Pages deployment for a project","description":"Starts a build and deployment of one Cloudflare Pages project on the org's OWN Cloudflare account, and relays Cloudflare's deployment record back. `branch` picks what to build; OMITTING it builds the project's production branch.\n\nA body it cannot parse is IGNORED rather than refused — the deployment falls back to the production branch — which is the one rule to get right here and the reason this is not a typed op: a typed request would answer 400 where this deploys. Requires ORG ADMIN (403 otherwise), and 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"project","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagesDeploy"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{}}},"description":"Success"}},"x-app":"cloudflare"}},"/v1/cloudflare/pages/projects/{project}/domains":{"post":{"operationId":"post_v1_cloudflare_pages_projects_by_project_domains","summary":"Attaches a custom domain to a Cloudflare Pages project.","description":"Attaches a custom domain to a Cloudflare Pages project. Requires\norg admin. Cloudflare owns validation and certificate issuance from here on.","tags":["cloudflare"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the Pages project name, from the path.","schema":{"type":"string"},"example":"marketing-site"}],"requestBody":{"content":{"application/json":{"example":{"name":"www.acme.com","project":"marketing-site"},"schema":{"$ref":"#/components/schemas/domainAddIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/pages/projects/{project}/domains/{domain}":{"delete":{"operationId":"delete_v1_cloudflare_pages_projects_by_project_domains_by_domain","summary":"Detaches a custom domain from a Cloudflare Pages project.","description":"Detaches a custom domain from a Cloudflare Pages project.\nRequires org admin.","tags":["cloudflare"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the Pages project name.","schema":{"type":"string"},"example":"marketing-site"},{"name":"domain","in":"path","required":true,"description":"Domain is the attached custom domain to detach.","schema":{"type":"string"},"example":"www.acme.com"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/r2/buckets":{"get":{"operationId":"get_v1_cloudflare_r2_buckets","summary":"Lists the R2 buckets on the org's Cloudflare account.","description":"Lists the R2 buckets on the org's Cloudflare account. Any org\nmember may read.","tags":["cloudflare"],"parameters":[{"name":"per_page","in":"query","required":false,"description":"PerPage is how many buckets one page holds.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"Cursor continues from the position a previous page returned.","schema":{"type":"string"}},{"name":"name_contains","in":"query","required":false,"description":"NameContains filters to buckets whose name contains this substring.","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order names the field to sort by, and Direction sorts asc or desc.","schema":{"type":"string"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"post":{"operationId":"post_v1_cloudflare_r2_buckets","summary":"Creates an R2 bucket on the org's Cloudflare account.","description":"Creates an R2 bucket on the org's Cloudflare account. Requires\norg admin.","tags":["cloudflare"],"requestBody":{"content":{"application/json":{"example":{"name":"assets"},"schema":{"$ref":"#/components/schemas/bucketCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/r2/buckets/{bucket}":{"delete":{"operationId":"delete_v1_cloudflare_r2_buckets_by_bucket","summary":"Deletes an R2 bucket.","description":"Deletes an R2 bucket. Requires org admin. Cloudflare refuses a\nbucket that still holds objects, and that refusal is relayed.","tags":["cloudflare"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the R2 bucket name.","schema":{"type":"string"},"example":"assets"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/scripts":{"get":{"operationId":"get_v1_cloudflare_workers_scripts","summary":"Lists the Worker scripts on the org's Cloudflare account.","description":"Lists the Worker scripts on the org's Cloudflare account. Any\norg member may read.","tags":["cloudflare"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/scripts/{script}":{"delete":{"operationId":"delete_v1_cloudflare_workers_scripts_by_script","summary":"Removes a Worker script from the org's Cloudflare account.","description":"Removes a Worker script from the org's Cloudflare account.\nRequires org admin. Routes bound to the script stop serving it.","tags":["cloudflare"],"parameters":[{"name":"script","in":"path","required":true,"description":"Script is the Worker script name.","schema":{"type":"string"},"example":"edge-router"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"put":{"operationId":"put_v1_cloudflare_workers_scripts_by_script","summary":"Upload or replace a module Worker script","description":"Publishes a module Worker to the org's OWN Cloudflare account under the name in the path, replacing whatever was there, and relays Cloudflare's result. `script` carries the module SOURCE; the optional compatibility date, compatibility flags and bindings are packed into the multipart upload Cloudflare expects.\n\nThe path names the script and the body field named `script` is its source — two different things that share a name, which is exactly why this cannot be a typed op: a binder that gives the URL the last word would overwrite the source with the script's name. Requires ORG ADMIN (403 otherwise); an unparseable body or empty source is 400; 503 if the org has never connected a Cloudflare token.","tags":["cloudflare"],"parameters":[{"name":"script","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerScriptPut"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{}}},"description":"Success"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/scripts/{script}/subdomain":{"post":{"operationId":"post_v1_cloudflare_workers_scripts_by_script_subdomain","summary":"Publishes or withdraws one Worker script on the account's workers.dev subdomain.","description":"Publishes or withdraws one Worker script on the\naccount's workers.dev subdomain. Requires org admin.","tags":["cloudflare"],"parameters":[{"name":"script","in":"path","required":true,"description":"Script is the Worker script name, from the path.","schema":{"type":"string"},"example":"edge-router"}],"requestBody":{"content":{"application/json":{"example":{"enabled":true,"script":"edge-router"},"schema":{"$ref":"#/components/schemas/subdomainSetIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/subdomain":{"get":{"operationId":"get_v1_cloudflare_workers_subdomain","summary":"Reads the org account's workers.dev subdomain — the name under which every subdomain-enabled script is served.","description":"Reads the org account's workers.dev subdomain — the name\nunder which every subdomain-enabled script is served. Any org member may read.","tags":["cloudflare"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/zones/{zone}/routes":{"get":{"operationId":"get_v1_cloudflare_workers_zones_by_zone_routes","summary":"Lists the Worker routes bound within one zone — the URL patterns that dispatch to a script.","description":"Lists the Worker routes bound within one zone — the URL\npatterns that dispatch to a script. Any org member may read. Routes are\nzone-scoped, so no account is resolved.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"},"post":{"operationId":"post_v1_cloudflare_workers_zones_by_zone_routes","summary":"Binds a URL pattern in a zone to a Worker script.","description":"Binds a URL pattern in a zone to a Worker script. Requires\norg admin — a route is what puts a script in front of live traffic.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id, from the path.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"}],"requestBody":{"content":{"application/json":{"example":{"pattern":"acme.com/api/*","script":"edge-router","zone":"0123456789abcdef0123456789abcdef"},"schema":{"$ref":"#/components/schemas/routeCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/workers/zones/{zone}/routes/{route}":{"delete":{"operationId":"delete_v1_cloudflare_workers_zones_by_zone_routes_by_route","summary":"Unbinds a Worker route, so its pattern stops dispatching to a script.","description":"Unbinds a Worker route, so its pattern stops dispatching to a\nscript. Requires org admin.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"},{"name":"route","in":"path","required":true,"description":"Route is the 32-hex Cloudflare route id.","schema":{"type":"string"},"example":"fedcba9876543210fedcba9876543210"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/zones":{"get":{"operationId":"get_v1_cloudflare_zones","summary":"Lists the Cloudflare zones the org's connected API token can see, paged and filtered by the query parameters Cloudflare itself accepts.","description":"Lists the Cloudflare zones the org's connected API token can see,\npaged and filtered by the query parameters Cloudflare itself accepts. Zones are\ntoken-scoped by Cloudflare, so no account is resolved. Any org member may read.\n\nZone and DNS-record MANAGEMENT is not here: it stays on the Hanzo DNS plane\n(/v1/dns). This only surfaces the Cloudflare zone objects the asset plane needs\n— a zone id is what addresses a Worker route or an analytics read.","tags":["cloudflare"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page of zones to return.","schema":{"type":"string"}},{"name":"per_page","in":"query","required":false,"description":"PerPage is how many zones one page holds.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name filters to the zone with this domain name.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Status filters by zone status (active, pending, initializing, …).","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order names the field to sort by, and Direction sorts asc or desc.","schema":{"type":"string"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/zones/{zone}":{"get":{"operationId":"get_v1_cloudflare_zones_by_zone","summary":"Reads one Cloudflare zone the org's token can see.","description":"Reads one Cloudflare zone the org's token can see. Any org member may\nread. A zone id the token cannot see is Cloudflare's own not-found, relayed.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/zones/{zone}/analytics":{"get":{"operationId":"get_v1_cloudflare_zones_by_zone_analytics","summary":"Reads a zone's Cloudflare traffic dashboard — requests, bandwidth, threats and pageviews over the since/until window.","description":"Reads a zone's Cloudflare traffic dashboard — requests, bandwidth,\nthreats and pageviews over the since/until window. Any org member may read.\n\nA zone whose Cloudflare plan does not serve this endpoint yields Cloudflare's\nOWN error, never a fabricated success.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"},{"name":"since","in":"query","required":false,"description":"Since and Until bound the window, in the form Cloudflare accepts — an RFC 3339\ntime or a negative number of minutes from now (\"-1440\" is the last day).","schema":{"type":"string"},"example":"-1440"},{"name":"until","in":"query","required":false,"schema":{"type":"string"},"example":"0"},{"name":"continuous","in":"query","required":false,"description":"Continuous asks Cloudflare for only fully-aggregated buckets.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/cloudflare/zones/{zone}/purge":{"post":{"operationId":"post_v1_cloudflare_zones_by_zone_purge","summary":"Drops a zone's Cloudflare edge cache — either the whole zone (purge_everything) or exactly the listed file URLs.","description":"Drops a zone's Cloudflare edge cache — either the whole zone\n(purge_everything) or exactly the listed file URLs. Requires org admin.\n\nPurging is the one zone-scoped WRITE this plane owns. It is not DNS — no record\nchanges — so it does not belong on /v1/dns, and it is not a connection, so it does\nnot belong on the integrations plane. It is a cache operation on a zone, which is\nwhat this asset plane is for. It takes the admin gate because dropping a zone's\ncache sends every subsequent request to the origin: on a site fronting a small\norigin that is a self-inflicted load spike, so it is a change, not a look.\n\nExactly one selector is required. Cloudflare treats a body with neither as a\nno-op and answers 200, which reads as \"purged\" to a caller that never purged\nanything — the failure we refuse to pass through.","tags":["cloudflare"],"parameters":[{"name":"zone","in":"path","required":true,"description":"Zone is the 32-hex Cloudflare zone id, from the path.","schema":{"type":"string"},"example":"0123456789abcdef0123456789abcdef"}],"requestBody":{"content":{"application/json":{"example":{"purge_everything":true,"zone":"0123456789abcdef0123456789abcdef"},"schema":{"$ref":"#/components/schemas/purgeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"cloudflare"}},"/v1/clusters":{"get":{"operationId":"listClusters","summary":"Returns the caller org's clusters from both sources: the managed clusters projected from Visor's node pools, and the BYO clusters attached to the caller's project.","description":"Returns the caller org's clusters from both sources: the managed\nclusters projected from Visor's node pools, and the BYO clusters attached to the\ncaller's project. A Visor outage costs the managed half only — the BYO half\nstill lists, because a page that 502s on an optional provider is worse than a\npage that shows what it can.","tags":["clusters"],"responses":{"200":{"content":{"application/json":{"example":{"clusters":[{"doksClusterId":"cl-1","kind":"managed","name":"prod","nodeCount":2,"nodePools":[{"count":2,"name":"gpu","poolId":"p-1","size":"gpu-h100x8-640gb"}],"nodeSize":"gpu-h100x8-640gb","status":"running"}]},"schema":{"$ref":"#/components/schemas/clusterList"}}},"description":"ok"}},"x-app":"visor"},"post":{"operationId":"attachCluster","summary":"Attaches a BYO cluster to the caller's org — the kubeconfig is validated, KMS-sealed and added to the fleet — and answers 201 with the cluster as it now appears on GET /v1/clusters.","description":"Attaches a BYO cluster to the caller's org — the kubeconfig is\nvalidated, KMS-sealed and added to the fleet — and answers 201 with the cluster\nas it now appears on GET /v1/clusters. Billed the nominal management fee: the\ncustomer brings the compute, Hanzo meters the management plane.","tags":["clusters"],"requestBody":{"content":{"application/json":{"example":{"default":false,"kubeconfig":"apiVersion: v1\nkind: Config\n...","name":"lab","provider":"on-prem"},"schema":{"$ref":"#/components/schemas/clusterAttach"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"kind":"byo","name":"lab","nodeCount":3,"nodePools":[],"nvidiaGpu":2,"region":"on-prem","status":"attached"},"schema":{"$ref":"#/components/schemas/clusterView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/clusters/{clusterId}/pools":{"post":{"operationId":"createNodePool","summary":"Adds a node pool to one of the caller org's clusters and answers 201 with the created pool.","description":"Adds a node pool to one of the caller org's clusters and answers 201\nwith the created pool. Only the CreateNodePoolSpec fields are forwarded;\nowner/provider/clusterId ride in the query exactly as Visor expects them.","tags":["clusters"],"parameters":[{"name":"clusterId","in":"path","required":true,"description":"ClusterID is the cluster to add the pool to, from the URL path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"autoScale":false,"count":2,"name":"gpu","provider":"digitalocean","size":"gpu-h100x8-640gb"},"schema":{"$ref":"#/components/schemas/poolCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"count":2,"name":"gpu","poolId":"p-1","size":"gpu-h100x8-640gb"},"schema":{"$ref":"#/components/schemas/nodePoolView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/clusters/{clusterId}/pools/{poolId}":{"delete":{"operationId":"deleteNodePool","summary":"Removes a node pool from one of the caller org's clusters.","description":"Removes a node pool from one of the caller org's clusters. The owner\nscopes the delete to the caller's tenant; provider+clusterId drive the\nprovider-side removal. Answers 204.","tags":["clusters"],"parameters":[{"name":"clusterId","in":"path","required":true,"description":"ClusterID and PoolID address the pool, from the URL path.","schema":{"type":"string"}},{"name":"poolId","in":"path","required":true,"schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"description":"Provider is the cloud the cluster lives on, from ?provider=. Required.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"visor"}},"/v1/clusters/{clusterId}/pools/{poolId}/scale":{"post":{"operationId":"scaleNodePool","summary":"Resizes a node pool to an absolute node count and returns the pool as Visor reports it after the change.","description":"Resizes a node pool to an absolute node count and returns the pool as\nVisor reports it after the change.","tags":["clusters"],"parameters":[{"name":"clusterId","in":"path","required":true,"description":"ClusterID and PoolID address the pool, from the URL path.","schema":{"type":"string"}},{"name":"poolId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"count":4,"provider":"digitalocean"},"schema":{"$ref":"#/components/schemas/poolScale"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"count":4,"name":"gpu","poolId":"p-1","size":"gpu-h100x8-640gb"},"schema":{"$ref":"#/components/schemas/nodePoolView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/clusters/{id}":{"delete":{"operationId":"detachCluster","summary":"Removes a BYO cluster from the caller org's fleet.","description":"Removes a BYO cluster from the caller org's fleet. It only ever\ntouches BYO clusters — a managed cluster's nodes are removed through the node-pool\nroutes — and answers 404 when the name is not in this org's fleet.","tags":["clusters"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cluster's fleet name (the `name` it was attached under), matched\nlower-cased.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"detached":"lab"},"schema":{"$ref":"#/components/schemas/clusterDetached"}}},"description":"ok"}},"x-app":"visor"}},"/v1/code/ask":{"get":{"operationId":"get_v1_code_ask","summary":"Answers a question about the caller org's code with a CITED answer: retrieval packs grounding context, then the synthesizer writes the answer over exactly those spans, which come back alongside it.","description":"Answers a question about the caller org's code with a CITED answer:\nretrieval packs grounding context, then the synthesizer writes the answer over\nexactly those spans, which come back alongside it. It never answers without\ngrounding — with no matched code the answer is empty and says so, and with no\nsynthesizer available the citations still come back with \"degraded\": true so\nthe caller can reason over the spans itself.","tags":["code"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q is the question to answer. Required, max 4000 bytes.","schema":{"type":"string"},"example":"where is the per-org SQLite file opened"},{"name":"repo","in":"query","required":false,"description":"Repo narrows retrieval to one repository. Empty searches every repo the org\nhas indexed.","schema":{"type":"string"},"example":"cloud"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskAnswer"}}},"description":"ok"}},"x-app":"code"},"post":{"operationId":"post_v1_code_ask","summary":"Is askGet with the question in the request BODY, for a question too long or too awkward to put in a URL.","description":"Is askGet with the question in the request BODY, for a question too\nlong or too awkward to put in a URL. `query` and `repo` in the body take\nprecedence over `?q=` and `?repo=`; either source works alone.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"query":"where is the per-org SQLite file opened","repo":"cloud"},"schema":{"$ref":"#/components/schemas/askPostIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskAnswer"}}},"description":"ok"}},"x-app":"code"}},"/v1/code/context":{"post":{"operationId":"post_v1_code_context","summary":"Packs the most relevant code for a query into a token budget — THE primitive for a coding agent that has to decide what to put in a prompt.","description":"Packs the most relevant code for a query into a token budget — THE\nprimitive for a coding agent that has to decide what to put in a prompt. It\nretrieves seed spans, expands each with the definitions it calls and its key\ncallers, then greedily fills the budget, so the answer is a coherent slice of\nthe codebase rather than a list of disconnected matches. The top match is\nalways included, truncated if it alone overflows, so a matched query never\ncomes back empty. A retrieval outage answers 200 with an empty bundle rather\nthan a 5xx.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"budgetTokens":4000,"query":"how does the store open a per-org database","repo":"cloud"},"schema":{"$ref":"#/components/schemas/contextIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextBundle"}}},"description":"ok"}},"x-app":"code"}},"/v1/code/file":{"get":{"operationId":"get_v1_code_file","summary":"Returns the INDEXED content of one file — read_file over the chunks the search tiers hold, for pulling up code an agent just found.","description":"Returns the INDEXED content of one file — read_file over the chunks the\nsearch tiers hold, for pulling up code an agent just found. It is NOT\nbyte-verbatim: the git object plane is the source of record for exact bytes,\nhistory and blame. A file absent from the index is a 404, so an agent can tell\n\"not indexed\" from \"empty file\".","tags":["code"],"parameters":[{"name":"path","in":"query","required":false,"description":"Path is the file's repo-relative path. Required.","schema":{"type":"string"},"example":"apps/code/store.go"},{"name":"repo","in":"query","required":false,"description":"Repo is the repository the file belongs to. REQUIRED.","schema":{"type":"string"},"example":"cloud"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/fileContent"}}},"description":"ok"}},"x-app":"code"}},"/v1/code/index":{"post":{"operationId":"post_v1_code_index","summary":"(re)indexes a repository for the caller's org, incrementally: files whose content hash is unchanged are skipped, so re-sending a whole tree is cheap.","description":"(re)indexes a repository for the caller's org, incrementally: files whose\ncontent hash is unchanged are skipped, so re-sending a whole tree is cheap.\nEach file is parsed for symbols, split at AST boundaries and — when the\nsemantic tier is available — embedded, which is what makes it searchable across\nall three retrieval tiers. Pass `prune` to also DELETE indexed files absent\nfrom the request, which turns the call into a full sync; without it the call is\nan upsert. The index is written to the caller org's own physically separate\ndatabase.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"files":[{"content":"package main\n","path":"main.go"}],"prune":true,"repo":"cloud"},"schema":{"$ref":"#/components/schemas/indexIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexResult"}}},"description":"ok"}},"x-app":"code"}},"/v1/code/lsp/complete":{"post":{"operationId":"post_v1_code_lsp_complete","summary":"Offers the candidates a language server has at a position, typed and resolved through the repository's dependencies rather than guessed from text.","description":"Offers the candidates a language server has at a position, typed and\nresolved through the repository's dependencies rather than guessed from text.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"character":18,"line":120,"path":"apps/lsp/lsp.go","repo":"cloud"},"schema":{"$ref":"#/components/schemas/Query"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Answer"}}},"description":"ok"}},"x-app":"lsp"}},"/v1/code/lsp/diagnostics":{"post":{"operationId":"post_v1_code_lsp_diagnostics","summary":"Reports every problem the language server finds in one file — compile errors, type errors and lints, each with its span and its severity (1 error, 2 warning, 3 information, 4 hint).","description":"Reports every problem the language server finds in one file —\ncompile errors, type errors and lints, each with its span and its severity (1\nerror, 2 warning, 3 information, 4 hint). The position is ignored.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"path":"apps/lsp/lsp.go","repo":"cloud"},"schema":{"$ref":"#/components/schemas/Query"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Answer"}}},"description":"ok"}},"x-app":"lsp"}},"/v1/code/lsp/hover":{"post":{"operationId":"post_v1_code_lsp_hover","summary":"Renders the type and documentation of the symbol at a position, as the language server itself renders it.","description":"Renders the type and documentation of the symbol at a position, as the\nlanguage server itself renders it.\n\nPositions are the LSP's: line and character are 0-BASED and character counts\nUTF-16 code units, so an editor's 1-based line must have 1 subtracted before it\nis sent. The repository is named by slug and is always one in the caller's own\norg; rev pins a branch, tag or commit sha, and empty means the default branch.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"character":18,"line":120,"path":"apps/lsp/lsp.go","repo":"cloud"},"schema":{"$ref":"#/components/schemas/Query"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Answer"}}},"description":"ok"}},"x-app":"lsp"}},"/v1/code/lsp/locate":{"post":{"operationId":"post_v1_code_lsp_locate","summary":"Finds where a symbol lives: its definition, its references, its type or its implementations, chosen by relation (definition, reference, type, implementation — empty means definition).","description":"Finds where a symbol lives: its definition, its references, its type or\nits implementations, chosen by relation (definition, reference, type,\nimplementation — empty means definition).\n\nIt resolves THROUGH dependencies. An answer whose external flag is set left the\nrepository, and its path is then the module coordinate it landed in — which is\nthe question a static index cannot answer and this service exists for.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"character":18,"line":120,"path":"apps/lsp/lsp.go","relation":"definition","repo":"cloud"},"schema":{"$ref":"#/components/schemas/Query"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Answer"}}},"description":"ok"}},"x-app":"lsp"}},"/v1/code/lsp/symbols":{"post":{"operationId":"post_v1_code_lsp_symbols","summary":"Outlines one file: every declaration in it, with its kind and its span.","description":"Outlines one file: every declaration in it, with its kind and its span.\nThe position is ignored — the answer is the whole file.","tags":["code"],"requestBody":{"content":{"application/json":{"example":{"path":"apps/lsp/lsp.go","repo":"cloud"},"schema":{"$ref":"#/components/schemas/Query"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Answer"}}},"description":"ok"}},"x-app":"lsp"}},"/v1/code/search":{"get":{"operationId":"get_v1_code_search","summary":"Finds code in the caller org's index across three orthogonal retrieval tiers fused by reciprocal-rank fusion: lexical (FTS5 trigram over code-tokenized text), symbolic (real definition and reference edges), and semantic (embedding cosine over AST-boundary chunks).","description":"Finds code in the caller org's index across three orthogonal retrieval\ntiers fused by reciprocal-rank fusion: lexical (FTS5 trigram over\ncode-tokenized text), symbolic (real definition and reference edges), and\nsemantic (embedding cosine over AST-boundary chunks). Pick one tier with\n`type`, or leave it to run all three as hybrid, which is what a coding agent\nusually wants. It is FAIL-HONEST: a retrieval outage answers 200 with an empty\nresult set and \"degraded\": true rather than a 5xx, so an agent degrades instead\nof stalling. A malformed regex is a 400.","tags":["code"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q is the search query. Required, max 4000 bytes. For type=regex it is a\nregular expression; for type=symbol it is a symbol name.","schema":{"type":"string"},"example":"func openStore"},{"name":"type","in":"query","required":false,"description":"Type selects the retrieval tier: \"text\" (FTS5 trigram), \"regex\",\n\"symbol\" (definitions), \"semantic\" (embeddings) or \"hybrid\". Anything\nelse — including empty — reads as hybrid.","schema":{"type":"string"},"example":"hybrid"},{"name":"repo","in":"query","required":false,"description":"Repo narrows to one repository. Empty searches every repo the org has indexed.","schema":{"type":"string"},"example":"cloud"},{"name":"limit","in":"query","required":false,"description":"Limit caps how many spans come back: default 20, maximum 100. A value that\nis not a positive integer reads as the default.","schema":{"type":"integer"},"example":20}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/searchResults"}}},"description":"ok"}},"x-app":"code"}},"/v1/code/tree":{"get":{"operationId":"get_v1_code_tree","summary":"Returns one repository's file structure with a per-file symbol count — get_repo_structure over the org's own index, with no git checkout involved.","description":"Returns one repository's file structure with a per-file symbol count —\nget_repo_structure over the org's own index, with no git checkout involved. A\nrepository that has not been indexed answers an empty tree rather than an\nerror, so an agent can tell \"nothing here\" without handling a failure.","tags":["code"],"parameters":[{"name":"repo","in":"query","required":false,"description":"Repo is the repository to walk. REQUIRED — a tree is repo-scoped.","schema":{"type":"string"},"example":"cloud"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/repoTree"}}},"description":"ok"}},"x-app":"code"}},"/v1/coding":{"post":{"operationId":"post_v1_coding","summary":"Start one autonomous coding run against a repo in the caller's org","tags":["coding"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodingStartIn"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodingStarted"}}},"description":"accepted"}},"x-app":"agents"}},"/v1/collections":{"delete":{"operationId":"delete_v1_collections","summary":"The org's Base content types","description":"Lists the content types in the org's managed Base, and creates one. This is what the console's Bases manager reads to render the schema.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"x-app":"base"},"get":{"operationId":"get_v1_collections","summary":"The org's Base content types","description":"Lists the content types in the org's managed Base, and creates one. This is what the console's Bases manager reads to render the schema.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"x-app":"base"},"patch":{"operationId":"patch_v1_collections","summary":"The org's Base content types","description":"Lists the content types in the org's managed Base, and creates one. This is what the console's Bases manager reads to render the schema.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"x-app":"base"},"post":{"operationId":"post_v1_collections","summary":"The org's Base content types","description":"Lists the content types in the org's managed Base, and creates one. This is what the console's Bases manager reads to render the schema.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"x-app":"base"},"put":{"operationId":"put_v1_collections","summary":"The org's Base content types","description":"Lists the content types in the org's managed Base, and creates one. This is what the console's Bases manager reads to render the schema.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"x-app":"base"}},"/v1/collections/{wildcard1}":{"delete":{"operationId":"delete_v1_collections_by_wildcard1","summary":"One Base content type, and its records","description":"Reads and writes below the collections root: `meta/scaffolds` is the field-template palette a new content type is built from, `\u003cname\u003e` is one content type (view, update, delete), `\u003cname\u003e/records` is that type's rows (list, create) and `\u003cname\u003e/records/\u003cid\u003e` is one row (get, update, delete). This is the data plane behind the console's Records browser.\n\nAny other shape below /v1/collections is refused with 404 before it is forwarded, so the wildcard admits exactly those five addresses and nothing more.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"base"},"get":{"operationId":"get_v1_collections_by_wildcard1","summary":"One Base content type, and its records","description":"Reads and writes below the collections root: `meta/scaffolds` is the field-template palette a new content type is built from, `\u003cname\u003e` is one content type (view, update, delete), `\u003cname\u003e/records` is that type's rows (list, create) and `\u003cname\u003e/records/\u003cid\u003e` is one row (get, update, delete). This is the data plane behind the console's Records browser.\n\nAny other shape below /v1/collections is refused with 404 before it is forwarded, so the wildcard admits exactly those five addresses and nothing more.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"base"},"patch":{"operationId":"patch_v1_collections_by_wildcard1","summary":"One Base content type, and its records","description":"Reads and writes below the collections root: `meta/scaffolds` is the field-template palette a new content type is built from, `\u003cname\u003e` is one content type (view, update, delete), `\u003cname\u003e/records` is that type's rows (list, create) and `\u003cname\u003e/records/\u003cid\u003e` is one row (get, update, delete). This is the data plane behind the console's Records browser.\n\nAny other shape below /v1/collections is refused with 404 before it is forwarded, so the wildcard admits exactly those five addresses and nothing more.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"base"},"post":{"operationId":"post_v1_collections_by_wildcard1","summary":"One Base content type, and its records","description":"Reads and writes below the collections root: `meta/scaffolds` is the field-template palette a new content type is built from, `\u003cname\u003e` is one content type (view, update, delete), `\u003cname\u003e/records` is that type's rows (list, create) and `\u003cname\u003e/records/\u003cid\u003e` is one row (get, update, delete). This is the data plane behind the console's Records browser.\n\nAny other shape below /v1/collections is refused with 404 before it is forwarded, so the wildcard admits exactly those five addresses and nothing more.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"base"},"put":{"operationId":"put_v1_collections_by_wildcard1","summary":"One Base content type, and its records","description":"Reads and writes below the collections root: `meta/scaffolds` is the field-template palette a new content type is built from, `\u003cname\u003e` is one content type (view, update, delete), `\u003cname\u003e/records` is that type's rows (list, create) and `\u003cname\u003e/records/\u003cid\u003e` is one row (get, update, delete). This is the data plane behind the console's Records browser.\n\nAny other shape below /v1/collections is refused with 404 before it is forwarded, so the wildcard admits exactly those five addresses and nothing more.\n\nThe path is forwarded to the managed Base unchanged and its answer comes back verbatim, so the schema, the records and every refusal are the managed Base's own.\n\nAUTH is one credential, forwarded and never minted: cloud validates the caller's hanzo.id bearer and passes THAT SAME token on, because the managed Base scopes each row by the token's own subject. A caller with no validated principal is refused here, before the request leaves the process, and the org header that rides along is the one cloud validated — a client-forged org was stripped upstream.\n\nThis is a COLLECTIONS proxy, not a Base tunnel: only the collections data plane is admitted, and everything else the managed Base mounts — settings, backups, logs — is 404 here whatever the caller's rights on that deployment are.\n\nOne registration owns this address for every method, so which methods answer is the managed Base's decision, not this edge's.","tags":["collections"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"base"}},"/v1/commands":{"get":{"operationId":"get_v1_commands","summary":"Every operation this API answers, as a command","description":"The command projection of the OpenAPI document at /v1/openapi.json — each operation reduced to what running it by name needs: its service and command token, its method and path, the prose lifted from the handler, its path parameters as positional arguments and its remaining inputs as typed flags.\n\nIt is a separate address for one measured reason: the fleet document is megabytes and a command palette cannot load it, while this projection of the same operations is several times smaller because it carries no schemas, responses or components.\n\nUnauthenticated by design, exactly as the document it derives from: a client has to be able to read the contract before it holds a credential, and a list of operation names grants nothing. The list is TOTAL and is never filtered by caller — what you may run is decided per request by the authorizer, on the decoded input, so a filtered list would be a second claim about permission that is free to be wrong.\n\nRendered once and served as bytes thereafter, under a strong ETag.","tags":["commands"],"x-app":"openapi"}},"/v1/commerce/admin/catalog":{"get":{"operationId":"get_v1_commerce_admin_catalog","summary":"The catalog projection with cost and margin included","description":"Returns the brand-scoped catalog carrying the administrative economics the public projection withholds — upstream cost and margin percentage — for the margin surface the platform console administrates. The brand comes from the query and defaults to hanzo. PLATFORM admin only, enforced by the handler on top of the route's IAM gate: an ORG-level admin is refused 403 precisely so upstream cost and margin never reach a tenant.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/catalog":{"get":{"operationId":"get_v1_commerce_catalog","summary":"The public product catalog projection for a brand","description":"Returns the brand's published catalog — the shared source docs, the console sidebar and the pricing pages all read — with the brand taken from the query and defaulting to hanzo. It is public and cacheable, and it is the projection that deliberately omits cost and margin; those live only on the platform-admin projection.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/collection/":{"get":{"operationId":"get_v1_commerce_collection","summary":"List your org's collections, as a page","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the slug and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or the Collection list scope.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_collection","summary":"Create a collection","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteCollection.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/collection/{collectionid}":{"delete":{"operationId":"delete_v1_commerce_collection_by_collectionid","summary":"Delete a collection, keeping a recoverable copy","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteCollection.","tags":["commerce"],"parameters":[{"name":"collectionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_collection_by_collectionid","summary":"Fetch one collection","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or ReadCollection.","tags":["commerce"],"parameters":[{"name":"collectionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_collection_by_collectionid","summary":"Change part of a collection","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadCollection and WriteCollection together.","tags":["commerce"],"parameters":[{"name":"collectionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_collection_by_collectionid","summary":"Method-override tunnel for a collection — for clients that cannot send PUT, PATCH or DELETE","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"collectionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_collection_by_collectionid","summary":"Replace a collection outright","description":"A collection is a merchandising group a storefront renders — a slug and name, copy and media, flat lists of the product and variant ids it holds, published, preorder and out-of-stock flags, and an availability window. Membership lives on the collection as those id lists rather than as a join, so putting a product into a collection is a write here and not on the product. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadCollection and WriteCollection together.","tags":["commerce"],"parameters":[{"name":"collectionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/currencies":{"get":{"operationId":"get_v1_commerce_currencies","summary":"The reference currency list the price and settings pickers render","description":"Returns every reference currency as one global list, so a store settings form or a product price picker binds real rows instead of a hardcoded array. It is a default-namespace read shared by every tenant rather than per-org data, and it is public and cacheable.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/disclosure/":{"get":{"operationId":"get_v1_commerce_disclosure","summary":"List your org's disclosures, as a page","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_disclosure","summary":"Create a disclosure","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/disclosure/{disclosureid}":{"delete":{"operationId":"delete_v1_commerce_disclosure_by_disclosureid","summary":"Delete a disclosure, keeping a recoverable copy","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"disclosureid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_disclosure_by_disclosureid","summary":"Fetch one disclosure","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"disclosureid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_disclosure_by_disclosureid","summary":"Change part of a disclosure","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"disclosureid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_disclosure_by_disclosureid","summary":"Method-override tunnel for a disclosure — for clients that cannot send PUT, PATCH or DELETE","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"disclosureid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_disclosure_by_disclosureid","summary":"Replace a disclosure outright","description":"A disclosure is a published-document record — a publication body, a content hash, a type and a named receiver. The hash LOOKS like a field you set and is in fact derived, but only on update: a freshly created disclosure keeps whatever hash the caller sent until the first replace or patch recomputes it, so a new row's hash attests to nothing. This kind lives in commerce's demo tree — a live writable resource in your tenant's real store that nothing else in commerce reads. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for disclosure, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"disclosureid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/discount/":{"get":{"operationId":"get_v1_commerce_discount","summary":"List your org's discounts, as a page","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_discount","summary":"Create a discount","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/discount/{discountid}":{"delete":{"operationId":"delete_v1_commerce_discount_by_discountid","summary":"Delete a discount, keeping a recoverable copy","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"discountid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_discount_by_discountid","summary":"Fetch one discount","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"discountid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_discount_by_discountid","summary":"Change part of a discount","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"discountid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_discount_by_discountid","summary":"Method-override tunnel for a discount — for clients that cannot send PUT, PATCH or DELETE","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"discountid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_discount_by_discountid","summary":"Replace a discount outright","description":"A discount is a price rule: a type (flat, percent, free-shipping, free-item or bulk), a window, a scope naming the store, collection, product or variant it applies to, a target, and rules pairing a trigger — a price or quantity threshold — with an action, an amount off or a percentage. It is ENABLED BY DEFAULT, so a bare create makes a live discount rather than a draft. The rule engine caches per replica for about thirty seconds, so a discount switched off here can keep applying briefly on other replicas. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for discount, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"discountid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/movie/":{"get":{"operationId":"get_v1_commerce_movie","summary":"List your org's movies, as a page","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the slug and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_movie","summary":"Create a movie","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/movie/{movieid}":{"delete":{"operationId":"delete_v1_commerce_movie_by_movieid","summary":"Delete a movie, keeping a recoverable copy","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"movieid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_movie_by_movieid","summary":"Fetch one movie","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"movieid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_movie_by_movieid","summary":"Change part of a movie","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"movieid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_movie_by_movieid","summary":"Method-override tunnel for a movie — for clients that cannot send PUT, PATCH or DELETE","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"movieid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_movie_by_movieid","summary":"Replace a movie outright","description":"A movie is a film catalog record — a slug plus EIDR and IMDB ids, all three required, with title and synopsis copy, artwork, screenshots, trailers, cast and crew, and available and hidden flags. It carries NO price: the money for a film lives on the product that sells it. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for movie, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"movieid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/note/":{"get":{"operationId":"get_v1_commerce_note","summary":"List your org's notes, as a page","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_note","summary":"Create a note","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/note/{noteid}":{"delete":{"operationId":"delete_v1_commerce_note_by_noteid","summary":"Delete a note, keeping a recoverable copy","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"noteid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_note_by_noteid","summary":"Fetch one note","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"noteid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_note_by_noteid","summary":"Change part of a note","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"noteid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_note_by_noteid","summary":"Method-override tunnel for a note — for clients that cannot send PUT, PATCH or DELETE","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"noteid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_note_by_noteid","summary":"Replace a note outright","description":"A note is a timestamped free-text log line — a caller-supplied time, a source, a message and an enabled flag. That time is the caller's own field and is distinct from the row's creation stamp; the note search filters on it, so a note written without one is a zero-time note the ops log will never surface. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for note, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"noteid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/product/":{"get":{"operationId":"get_v1_commerce_product","summary":"List your org's products, as a page","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the slug and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or the Product list scope.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_product","summary":"Create a product","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteProduct.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/product/{productid}":{"delete":{"operationId":"delete_v1_commerce_product_by_productid","summary":"Delete a product, keeping a recoverable copy","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteProduct.","tags":["commerce"],"parameters":[{"name":"productid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_product_by_productid","summary":"Fetch one product","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or ReadProduct.","tags":["commerce"],"parameters":[{"name":"productid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_product_by_productid","summary":"Change part of a product","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadProduct and WriteProduct together.","tags":["commerce"],"parameters":[{"name":"productid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_product_by_productid","summary":"Method-override tunnel for a product — for clients that cannot send PUT, PATCH or DELETE","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"productid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_product_by_productid","summary":"Replace a product outright","description":"A product is a sellable catalog item: slug, SKU and UPC, name and copy, media, availability and preorder flags, a reservation block, and its money — currency, price, MSRP, list price and inventory cost in minor units, inventory count, taxability, and the subscription interval when it is subscribeable. Its variants and options are carried as a denormalized JSON snapshot inside the product, separate from the standalone variant rows, and nothing keeps the two in step for you. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadProduct and WriteProduct together.","tags":["commerce"],"parameters":[{"name":"productid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/return/":{"get":{"operationId":"get_v1_commerce_return","summary":"List your org's returns, as a page","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The token must also carry Admin or the Return list scope.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_return","summary":"Create a return","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The token must also carry Admin or WriteReturn.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/return/{returnid}":{"delete":{"operationId":"delete_v1_commerce_return_by_returnid","summary":"Delete a return, keeping a recoverable copy","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The token must also carry Admin or WriteReturn.","tags":["commerce"],"parameters":[{"name":"returnid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_return_by_returnid","summary":"Fetch one return","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The token must also carry Admin or ReadReturn.","tags":["commerce"],"parameters":[{"name":"returnid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_return_by_returnid","summary":"Change part of a return","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The token must also carry Admin, or ReadReturn and WriteReturn together.","tags":["commerce"],"parameters":[{"name":"returnid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_return_by_returnid","summary":"Method-override tunnel for a return — for clients that cannot send PUT, PATCH or DELETE","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"returnid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_return_by_returnid","summary":"Replace a return outright","description":"A return is an RMA — the store, user and order it belongs to, the line items coming back, a fulfillment block carrying its own type, status and pricing, a summary, and eight lifecycle timestamps from submitted through delivered and processed. Its status is a FREE STRING with no enumeration behind it, and there is no refund amount on the return itself: the money sits inside the line items and the fulfillment pricing. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The token must also carry Admin, or ReadReturn and WriteReturn together.","tags":["commerce"],"parameters":[{"name":"returnid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/saleschannel/":{"get":{"operationId":"get_v1_commerce_saleschannel","summary":"List your org's sales channels, as a page","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_saleschannel","summary":"Create a sales channel","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/saleschannel/{saleschannelid}":{"delete":{"operationId":"delete_v1_commerce_saleschannel_by_saleschannelid","summary":"Delete a sales channel, keeping a recoverable copy","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"saleschannelid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_saleschannel_by_saleschannelid","summary":"Fetch one sales channel","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"saleschannelid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_saleschannel_by_saleschannelid","summary":"Change part of a sales channel","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"saleschannelid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_saleschannel_by_saleschannelid","summary":"Method-override tunnel for a sales channel — for clients that cannot send PUT, PATCH or DELETE","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"saleschannelid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_saleschannel_by_saleschannelid","summary":"Replace a sales channel outright","description":"A sales channel is a named selling surface — a name, a description, a disabled flag and metadata. The flag is NEGATIVE, so a channel created from an empty body is enabled. Nothing on this row links products, prices or stock to the channel; here it is a label other surfaces scope themselves by. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for saleschannel, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"saleschannelid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/stocklocation/":{"get":{"operationId":"get_v1_commerce_stocklocation","summary":"List your org's stock locations, as a page","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_stocklocation","summary":"Create a stock location","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/stocklocation/{stocklocationid}":{"delete":{"operationId":"delete_v1_commerce_stocklocation_by_stocklocationid","summary":"Delete a stock location, keeping a recoverable copy","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"stocklocationid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_stocklocation_by_stocklocationid","summary":"Fetch one stock location","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"stocklocationid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_stocklocation_by_stocklocationid","summary":"Change part of a stock location","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"stocklocationid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_stocklocation_by_stocklocationid","summary":"Method-override tunnel for a stock location — for clients that cannot send PUT, PATCH or DELETE","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"stocklocationid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_stocklocation_by_stocklocationid","summary":"Replace a stock location outright","description":"A stock location is a physical address inventory can be held at — a name, street lines, city, province, country, postal code and a phone. None of it is validated, there are no coordinates, and the row carries no enabled flag and no inventory link, so deleting it is the only way to retire one. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The per-kind permission table has no entry for stocklocation, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"stocklocationid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/submission/":{"get":{"operationId":"get_v1_commerce_submission","summary":"List your org's submissions, as a page","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_submission","summary":"Create a submission","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/submission/{submissionid}":{"delete":{"operationId":"delete_v1_commerce_submission_by_submissionid","summary":"Delete a submission, keeping a recoverable copy","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"submissionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_submission_by_submissionid","summary":"Fetch one submission","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"submissionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_submission_by_submissionid","summary":"Change part of a submission","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"submissionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_submission_by_submissionid","summary":"Method-override tunnel for a submission — for clients that cannot send PUT, PATCH or DELETE","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"submissionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_submission_by_submissionid","summary":"Replace a submission outright","description":"A submission is one filled-in form from a site visitor — an email, an optional user id, the client details the server observed (user agent, referer, geography) and the form's own fields as free metadata. It carries no form id, so the link back to the form that produced it is not stored on the row. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for submission, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"submissionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/subscriber/":{"get":{"operationId":"get_v1_commerce_subscriber","summary":"List your org's subscribers, as a page","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The token must also carry Admin or the Subscriber list scope.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_subscriber","summary":"Create a subscriber","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The token must also carry Admin or WriteSubscriber.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/subscriber/{subscriberid}":{"delete":{"operationId":"delete_v1_commerce_subscriber_by_subscriberid","summary":"Delete a subscriber, keeping a recoverable copy","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The token must also carry Admin or WriteSubscriber.","tags":["commerce"],"parameters":[{"name":"subscriberid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_subscriber_by_subscriberid","summary":"Fetch one subscriber","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The token must also carry Admin or ReadSubscriber.","tags":["commerce"],"parameters":[{"name":"subscriberid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_subscriber_by_subscriberid","summary":"Change part of a subscriber","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The token must also carry Admin, or ReadSubscriber and WriteSubscriber together.","tags":["commerce"],"parameters":[{"name":"subscriberid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_subscriber_by_subscriberid","summary":"Method-override tunnel for a subscriber — for clients that cannot send PUT, PATCH or DELETE","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"subscriberid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_subscriber_by_subscriberid","summary":"Replace a subscriber outright","description":"A subscriber is a mailing-list member — name, email, the form id that captured them, unsubscribed state and date, client details, tags and metadata. Writing one FIRES A WEBHOOK: subscriber.created on create and subscriber.updated on replace or patch, emitted BEFORE the write is known to have succeeded and carrying the row as sent, so the payload holds the raw email rather than the normalized one that gets stored. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The token must also carry Admin, or ReadSubscriber and WriteSubscriber together.","tags":["commerce"],"parameters":[{"name":"subscriberid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/tenant":{"get":{"operationId":"get_v1_commerce_tenant","summary":"The public tenant configuration a checkout page boots from","description":"Answers the branding, identity issuer and client id, identity-verification config, enabled payment providers, return-URL allowlist and public payment application config for the tenant the request HOST resolves to. It is genuinely public and unauthenticated — a checkout page calls it before anyone has signed in — and it carries the same public payment config the authenticated config read does, so the card iframe can never initialize against a different application than the one that will be charged. Only ENABLED providers are listed and no credential path is ever projected. An unresolvable host answers a constant 404 that does not echo the host, so the endpoint cannot be used to enumerate tenants; a successful answer is cacheable for a minute.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/tokentransaction/":{"get":{"operationId":"get_v1_commerce_tokentransaction","summary":"List your org's token transactions, as a page","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_tokentransaction","summary":"Create a token transaction","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/tokentransaction/{tokentransactionid}":{"delete":{"operationId":"delete_v1_commerce_tokentransaction_by_tokentransactionid","summary":"Delete a token transaction, keeping a recoverable copy","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"tokentransactionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_tokentransaction_by_tokentransactionid","summary":"Fetch one token transaction","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"tokentransactionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_tokentransaction_by_tokentransactionid","summary":"Change part of a token transaction","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"tokentransactionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_tokentransaction_by_tokentransactionid","summary":"Method-override tunnel for a token transaction — for clients that cannot send PUT, PATCH or DELETE","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"tokentransactionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_tokentransaction_by_tokentransactionid","summary":"Replace a token transaction outright","description":"A token transaction records a transfer between two identified parties — amount and fees, a timestamp, sending and receiving addresses, names, user ids, states and countries, a flag per side, a protocol name and a transaction hash. Nothing here touches a chain: the hash is an unvalidated string and the flags are plain writable booleans with no screening behind them. Amounts are floating-point rather than the exact minor units every real money field in commerce uses, and there is no currency field at all — this kind lives in commerce's demo tree, so it is a live writable resource in your tenant's store that nothing else in commerce reads, and it must never carry real money. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for tokentransaction, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"tokentransactionid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/transfer/":{"get":{"operationId":"get_v1_commerce_transfer","summary":"List your org's transfers, as a page","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_transfer","summary":"Create a transfer","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/transfer/{transferid}":{"delete":{"operationId":"delete_v1_commerce_transfer_by_transferid","summary":"Delete a transfer, keeping a recoverable copy","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"transferid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_transfer_by_transferid","summary":"Fetch one transfer","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"transferid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_transfer_by_transferid","summary":"Change part of a transfer","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"transferid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_transfer_by_transferid","summary":"Method-override tunnel for a transfer — for clients that cannot send PUT, PATCH or DELETE","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. The token must carry the ADMIN permission; an ordinary access token is refused.","tags":["commerce"],"parameters":[{"name":"transferid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_transfer_by_transferid","summary":"Replace a transfer outright","description":"A transfer records that a payable WAS PAID — the annotation a human writes after paying out of band. Commerce executes no payout: creating one moves no money, and it marks the referenced payable settled. It carries the payable and payee ids, the amount it settles and the amount actually sent (which may be a different asset), a type of eth, wire or other, the transaction hash or wire reference, when it was paid and who recorded it; amounts are exact decimal strings with an asset, not cents. It is admin-gated because writing one settles money we owe, and nothing enforces uniqueness on the reference — so posting the same transfer twice settles the payable twice. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for transfer, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"transferid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/variant/":{"get":{"operationId":"get_v1_commerce_variant","summary":"List your org's variants, as a page","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the SKU and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or the Variant list scope.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_variant","summary":"Create a variant","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteVariant.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/variant/{variantid}":{"delete":{"operationId":"delete_v1_commerce_variant_by_variantid","summary":"Delete a variant, keeping a recoverable copy","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or WriteVariant.","tags":["commerce"],"parameters":[{"name":"variantid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_variant_by_variantid","summary":"Fetch one variant","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin or ReadVariant.","tags":["commerce"],"parameters":[{"name":"variantid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_variant_by_variantid","summary":"Change part of a variant","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadVariant and WriteVariant together.","tags":["commerce"],"parameters":[{"name":"variantid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_variant_by_variantid","summary":"Method-override tunnel for a variant — for clients that cannot send PUT, PATCH or DELETE","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through.","tags":["commerce"],"parameters":[{"name":"variantid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_variant_by_variantid","summary":"Replace a variant outright","description":"A variant is one purchasable SKU of a product — its product id, SKU and UPC, name, media, availability, the option name and value pairs that distinguish it, a sold counter, and its own money and stock: currency, price, MSRP, inventory cost, inventory count and taxability. Inventory and sold are plain writable numbers with no decrement logic behind them here. The same variant also exists as a JSON copy inside its product, and writing one does not update the other. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The org must also be entitled to the commerce admin: the paywall answers 402 subscription_required unless the org holds an active or trialing pro subscription, a live trial credit or a redeemed invite, and 503 when that entitlement cannot be read rather than admitting on an unknown. The internal service token and a platform superadmin pass straight through. The token must also carry Admin, or ReadVariant and WriteVariant together.","tags":["commerce"],"parameters":[{"name":"variantid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/wallet/":{"get":{"operationId":"get_v1_commerce_wallet","summary":"List your org's wallets, as a page","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_wallet","summary":"Create a wallet","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/wallet/{walletid}":{"delete":{"operationId":"delete_v1_commerce_wallet_by_walletid","summary":"Delete a wallet, keeping a recoverable copy","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"walletid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_wallet_by_walletid","summary":"Fetch one wallet","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"walletid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_wallet_by_walletid","summary":"Change part of a wallet","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"walletid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_wallet_by_walletid","summary":"Method-override tunnel for a wallet — for clients that cannot send PUT, PATCH or DELETE","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. The token must carry the ADMIN permission; an ordinary access token is refused.","tags":["commerce"],"parameters":[{"name":"walletid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_wallet_by_walletid","summary":"Replace a wallet outright","description":"A wallet is a container of custodial blockchain accounts, and its only field is that account list — each account carrying a name, an address, a chain type, and the ENCRYPTED private key with its salt. Creating a wallet through this table generates NO KEYS: key generation lives on the account routes, so a wallet made here is an empty shell and an account posted into one is stored exactly as sent, with no key generation and no validation behind it. Know what a read renders: the plaintext private key is never marshalled and never stored, but the encrypted blob and its salt ARE returned, so whoever can read a wallet can attack it offline down to the strength of the owner's passphrase. That is why this kind is admin-gated. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for wallet, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"walletid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/watchlist/":{"get":{"operationId":"get_v1_commerce_watchlist","summary":"List your org's watchlists, as a page","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_watchlist","summary":"Create a watchlist","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/watchlist/{watchlistid}":{"delete":{"operationId":"delete_v1_commerce_watchlist_by_watchlistid","summary":"Delete a watchlist, keeping a recoverable copy","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"watchlistid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_watchlist_by_watchlistid","summary":"Fetch one watchlist","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"watchlistid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_watchlist_by_watchlistid","summary":"Change part of a watchlist","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"watchlistid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_watchlist_by_watchlistid","summary":"Method-override tunnel for a watchlist — for clients that cannot send PUT, PATCH or DELETE","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. Any valid access token reaches it.","tags":["commerce"],"parameters":[{"name":"watchlistid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_watchlist_by_watchlistid","summary":"Replace a watchlist outright","description":"A watchlist is a viewer's saved list of movies — a user id, an email, and the movies themselves. It stores WHOLE MOVIE SNAPSHOTS rather than movie ids, so a list goes stale the moment a film record changes and grows without bound as it fills. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. Any valid access token reaches it. The per-kind permission table has no entry for watchlist, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"watchlistid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/commerce/webhook/":{"get":{"operationId":"get_v1_commerce_webhook","summary":"List your org's webhooks, as a page","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Answers a pagination envelope — the page and display echoed back, the rows under models, a total count and a facets array — read from the caller org's own namespaced store, so one tenant can never list another's. Sorting defaults to the last-updated time and is overridable with sort. display is the page size and page applies only alongside it; either one that is not a positive integer is refused with 500 rather than silently ignored, and the limit query overrides the reported COUNT only, never the rows returned. No search backend is wired, so the datastore is the one and only list path and facets is always empty. A request resolving no org namespace is served an EMPTY page rather than an unscoped scan: the namespace IS the tenant filter, so without one there is nothing safe to return. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_webhook","summary":"Create a webhook","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Decodes the body into a new row in the caller org's own namespaced store — isolated to that tenant from its first write — and answers the stored row at 201 with a Location header naming its id. The id is assigned by the store, not taken from the body. A body that fails to decode is 400 and a store that refuses the write is 500. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"x-app":"commerce"}},"/v1/commerce/webhook/{webhookid}":{"delete":{"operationId":"delete_v1_commerce_webhook_by_webhookid","summary":"Delete a webhook, keeping a recoverable copy","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Removes the addressed row and answers 204 with no body. Before the live row goes it is written once more under a deleted tombstone kind, so a deletion leaves a recoverable copy rather than destroying the record outright — and a tombstone that cannot be written fails the call with 500 before anything is removed. The id is resolved inside the caller org's own namespace, so an absent or foreign id is 404. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"webhookid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_commerce_webhook_by_webhookid","summary":"Fetch one webhook","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Reads the addressed row from the caller org's own namespaced store. An id that is not there is 404 — and another tenant's id is not there by construction, so it reads exactly like a typo instead of confirming the row exists somewhere else. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"webhookid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_commerce_webhook_by_webhookid","summary":"Change part of a webhook","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Loads the stored row and decodes the body OVER it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged row. An id absent from the caller org's namespace is 404 and a body that fails to decode is 400. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"webhookid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_commerce_webhook_by_webhookid","summary":"Method-override tunnel for a webhook — for clients that cannot send PUT, PATCH or DELETE","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header. PUT replaces the row, PATCH changes part of it, DELETE removes it, and anything else is 405. The trap is the DEFAULT: naming no override at all leaves the method POST, which this tunnel maps to the PARTIAL UPDATE — it is never a create, and creating is the collection root's job. Behaviour and authorization are the underlying operation's, since the real handler runs. The token must carry the ADMIN permission; an ordinary access token is refused.","tags":["commerce"],"parameters":[{"name":"webhookid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_commerce_webhook_by_webhookid","summary":"Replace a webhook outright","description":"A webhook is a merchant-registered endpoint that receives commerce event callbacks — a name, a URL, live and all flags, a per-event map, an enabled flag, and the shared access token each delivery posts IN THE BODY. Two things to know before registering one: that token is a plainly readable field, so anyone who may read webhooks reads every endpoint's secret, and delivery consults only the all flag and the event map — it does NOT consult enabled or live, so setting enabled false does not stop delivery and deleting the row is the only thing that does. Delivery is a single POST with a twenty-second timeout and no retry. This is a true REPLACEMENT, not a merge: the stored row's key is preserved, but the body is decoded onto a FRESH entity, so every field the body omits is written back as its ZERO value. Patch is the verb for changing part of a row. The id is resolved inside the caller org's own namespace and an absent one is 404 before anything is written; a body that fails to decode is 400. Answers the stored result. The token must carry the ADMIN permission; an ordinary access token is refused. The per-kind permission table has no entry for webhook, so the scaffold skips that second check with a warning and the gate above is the whole authorization story.","tags":["commerce"],"parameters":[{"name":"webhookid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/company":{"get":{"operationId":"get_v1_company","summary":"Get returns the caller org's formation and the stages reachable from it, or 404 when the org has not begun one.","description":"Get returns the caller org's formation and the stages reachable from it, or 404\nwhen the org has not begun one.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"},"post":{"operationId":"post_v1_company","summary":"Begin starts the org's one formation and returns it with the stages reachable from it.","description":"Begin starts the org's one formation and returns it with the stages reachable\nfrom it. It is idempotent: an org that already has a formation gets that one\nback with 200, while a first call creates it and answers 201.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"jurisdiction":"DE","name":"Acme Inc.","structure":"c-corp"},"schema":{"$ref":"#/components/schemas/beginIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/advance":{"post":{"operationId":"post_v1_company_advance","summary":"Advance runs the ONE guarded transition of the formation machine.","description":"Advance runs the ONE guarded transition of the formation machine. It is the\nonly door between stages: the actions populate data, this decides ordering.\n\nAn edge the machine does not define answers 409; an edge whose guard is not yet\nsatisfied answers 422 naming what is missing. Reaching the terminal `company`\nstage also records the incorporation on the canonical cap table, and that must\nsucceed before the transition is persisted.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"to":"founders"},"schema":{"$ref":"#/components/schemas/advanceIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/documents":{"post":{"operationId":"post_v1_company_documents","summary":"Renders the formation documents for the chosen structure and jurisdiction, ingests each into the org's data room, and submits the state filing through the filing seam.","description":"Renders the formation documents for the chosen structure and\njurisdiction, ingests each into the org's data room, and submits the state\nfiling through the filing seam.\n\nWith no filing partner wired the filing is recorded honestly as \"manual\" — no\nfiling id is fabricated. Available only at the documents stage.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/esign":{"post":{"operationId":"post_v1_company_esign","summary":"Sends the generated formation documents for signature by every founder and records the provider's reference on the formation.","description":"Sends the generated formation documents for signature by every\nfounder and records the provider's reference on the formation. Available only\nat the esign stage.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignOut"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/esign/complete":{"post":{"operationId":"post_v1_company_esign_complete","summary":"Records whether the formation documents have been signed.","description":"Records whether the formation documents have been signed. It\nconsults the e-signature provider, which a real provider's webhook drives; the\nsignal is idempotent.\n\nAn explicit `signed` in the request overrides the provider's answer, which is\nthe manual path for the stub provider that never self-completes.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"signed":true},"schema":{"$ref":"#/components/schemas/esignCompleteIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/founders":{"post":{"operationId":"post_v1_company_founders","summary":"Replaces the formation's founders.","description":"Replaces the formation's founders. Each founder needs a name, an\nemail and an equity share in basis points; every founder is (re)set to pending\nKYC, so a previously settled decision does not survive a change of the list.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"founders":[{"email":"ada@acme.com","equityBps":10000,"name":"Ada"}]},"schema":{"$ref":"#/components/schemas/foundersIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/fundraise/deck":{"post":{"operationId":"post_v1_company_fundraise_deck","summary":"Share a pitch deck in the org's data room","description":"Stores the request body as a document in the caller org's data room and answers with the data room id to reference it by. The deck is RAW BYTES of whatever content type is sent — a PDF, a slide export — not a JSON document: the Content-Type header is carried through to the data room as given, and `?name=` names the document, defaulting to `pitch-deck`.\n\nScoped to the caller's validated org, and only after incorporation: a formation still short of stage `company` is refused 409 and an org that never began one is 404. The route is registered AHEAD of the surface's JSON body cap deliberately, so a deck's size ceiling is the edge's rather than the cap meant for small structured records. An empty body is 400; a data room that will not take the bytes is 502.","tags":["company"],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deckOut"}}},"description":"Success"}},"x-app":"company"}},"/v1/company/fundraise/round":{"post":{"operationId":"post_v1_company_fundraise_round","summary":"Records a fundraising round on the org's canonical cap table.","description":"Records a fundraising round on the org's canonical cap table.\nAvailable only after incorporation (stage company); roundType defaults to\nPRICED.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"name":"Seed","roundType":"PRICED","targetAmount":2000000},"schema":{"$ref":"#/components/schemas/RoundInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/roundOut"}}},"description":"created"}},"x-app":"company"}},"/v1/company/fundraise/safe":{"post":{"operationId":"post_v1_company_fundraise_safe","summary":"Raises an e-signature request over documents already in the org's data room — a SAFE, a convertible note, or any other fundraising paper.","description":"Raises an e-signature request over documents already in the org's\ndata room — a SAFE, a convertible note, or any other fundraising paper.\nAvailable only after incorporation (stage company).","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"documentIds":["doc_safe"],"signers":[{"email":"ada@acme.com","name":"Ada"}]},"schema":{"$ref":"#/components/schemas/safeIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/safeOut"}}},"description":"created"}},"x-app":"company"}},"/v1/company/genesis":{"post":{"operationId":"post_v1_company_genesis","summary":"Seeds the canonical cap table with the founding allocation (stakeholders, a common share class, issued shares) and anchors the deterministic equity-genesis root on-chain.","description":"Seeds the canonical cap table with the founding allocation\n(stakeholders, a common share class, issued shares) and anchors the\ndeterministic equity-genesis root on-chain.\n\nIt is idempotent: once a root is recorded the cap table is NOT re-seeded, which\nwould double-issue founder share certificates. The root is persisted even when\nthe on-chain submit fails, because the root is the tamper-evident witness and\nmust not be recomputed on retry. Available only at the genesis stage.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/import/captable":{"post":{"operationId":"post_v1_company_import_captable","summary":"Reads an existing company's cap table from a Google Sheet and adds its stakeholders to the canonical cap table.","description":"Reads an existing company's cap table from a Google Sheet and\nadds its stakeholders to the canonical cap table.\n\nThe first row is a header and columns are matched by name (case-insensitive):\nname and email are required, type/relationship/institution optional. A sheet\nwithout name and email columns, or with no usable data rows, is refused with\n400. Available only at the import stage.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"range":"Cap Table!A1:E100","spreadsheetId":"1AbCdEfGhIjKlMnOpQrStUvWxYz"},"schema":{"$ref":"#/components/schemas/importCapTableIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/importCapTableOut"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/import/documents":{"post":{"operationId":"post_v1_company_import_documents","summary":"Ingests an existing company's corporate documents from a Google Drive folder into the org's data room.","description":"Ingests an existing company's corporate documents from a Google\nDrive folder into the org's data room. The import is shallow — sub-folders are\nskipped, not walked — and available only at the import stage.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"folderId":"1AbCdEfGhIjKlMnOpQrStUvWxYz"},"schema":{"$ref":"#/components/schemas/importDocumentsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/importDocumentsOut"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/kyc":{"post":{"operationId":"post_v1_company_kyc","summary":"StartKYC opens an identity-verification session for every founder with the wired provider and records each session's reference on the formation.","description":"StartKYC opens an identity-verification session for every founder with the\nwired provider and records each session's reference on the formation.\n\nA start is never a decision: any terminal status the provider reports at\ninquiry time is clamped back to pending, so the payment gate can never open\nhere. A terminal status arrives only from POST /v1/company/kyc/refresh (the\nprovider) or POST /v1/company/kyc/decision (a Hanzo platform reviewer).","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kycStartOut"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/kyc/decision":{"post":{"operationId":"post_v1_company_kyc_decision","summary":"DecideKYC records a privileged reviewer's MANUAL decision on a founder's KYC — the human-in-the-loop path, and the ONLY route to a pass when no real provider is wired.","description":"DecideKYC records a privileged reviewer's MANUAL decision on a founder's KYC —\nthe human-in-the-loop path, and the ONLY route to a pass when no real provider\nis wired. It produces a DISTINCT reviewer_confirmed, never a provider\n\"verified\".\n\nBecause Hanzo forms the entity and carries the formation KYC/AML obligation,\nthe reviewer is a HANZO platform reviewer (SuperAdmin), and the decision is\nATTRIBUTED to them.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"email":"ada@acme.com","status":"reviewer_confirmed"},"schema":{"$ref":"#/components/schemas/decisionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/kyc/refresh":{"post":{"operationId":"post_v1_company_kyc_refresh","summary":"RefreshKYC reconciles each pending founder's KYC with the WIRED provider — the PULL path to a provider-reported terminal status.","description":"RefreshKYC reconciles each pending founder's KYC with the WIRED provider — the\nPULL path to a provider-reported terminal status. For the manual provider the\ncheck stays pending; for a real provider it reflects the settled decision,\nATTRIBUTED to the provider.\n\nIt NEVER trusts a client-asserted status — the status comes from the provider\nseam — so a client cannot force a pass here, and an already-passing founder\n(e.g. a reviewer confirmation) is left untouched.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kycRefreshOut"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/payment":{"post":{"operationId":"post_v1_company_payment","summary":"Charge the one-time formation fee and mark the formation paid","description":"Bills the caller's own org the one-time Hanzo Company formation fee — $999 unless the deployment sets another — and answers with the formation record carrying its paid flag and the charge reference. Takes no body: the org is the validated tenant and the amount is the platform's, never the caller's to assert.\n\nIDEMPOTENT on the formation rather than on the request: an already-paid formation answers 200 with the same record and is not charged again, so a retry or a double-clicked button costs nothing. Available only at the `payment` stage (409 anywhere else) and only for an org that has begun a formation (404 otherwise).\n\nA refused charge answers the fleet-wide billing contract, not a formation error — 402 when the org cannot pay, 503 when metering is unavailable — which is exactly why this route is not a typed op.","tags":["company"],"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"Success"}},"x-app":"company"}},"/v1/company/register":{"get":{"operationId":"get_v1_company_register","summary":"Returns the platform's whole formation register, newest activity first — every org's formation, not the caller's.","description":"Returns the platform's whole formation register, newest activity\nfirst — every org's formation, not the caller's. It is a Hanzo platform\noperation: a caller who is not a platform reviewer gets 403.\n\nFilter by stage and structure, page with limit and offset. An unknown stage is\nrefused with 400 rather than returning a silently empty page.","tags":["company"],"parameters":[{"name":"stage","in":"query","required":false,"description":"Stage keeps only formations at that stage. Empty means any.","schema":{"type":"string"},"example":"founders"},{"name":"structure","in":"query","required":false,"description":"Structure keeps only formations of that entity kind. Empty means any.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page; 0 or less means the default of 200.","schema":{"type":"integer"},"example":50},{"name":"offset","in":"query","required":false,"description":"Offset skips that many rows.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registerPage"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/register/summary":{"get":{"operationId":"get_v1_company_register_summary","summary":"Counts the platform's formations by stage — the register's shape in one read, so a queue that is growing is visible as a number rather than inferred by paging the list.","description":"Counts the platform's formations by stage — the register's\nshape in one read, so a queue that is growing is visible as a number rather\nthan inferred by paging the list. A Hanzo platform operation: a caller who is\nnot a platform reviewer gets 403.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registerCounts"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/review":{"get":{"operationId":"get_v1_company_review","summary":"Reports the founders whose KYC is not yet settled, oldest formation first, so the queue drains in the order founders have been waiting.","description":"Reports the founders whose KYC is not yet settled, oldest formation\nfirst, so the queue drains in the order founders have been waiting. A Hanzo\nplatform operation: a caller who is not a platform reviewer gets 403.\n\nIt only says who is waiting; the decision itself is POST\n/v1/company/kyc/decision.","tags":["company"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit bounds how many formations are scanned; 0 or less means the default of 200.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reviewQueue"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/skip":{"post":{"operationId":"post_v1_company_skip","summary":"Skip marks the org as already incorporated and moves it onto the import path, so an existing company brings its documents and cap table in instead of forming a new entity.","description":"Skip marks the org as already incorporated and moves it onto the import path,\nso an existing company brings its documents and cap table in instead of forming\na new entity. Available only at the structure stage.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/company/structure":{"put":{"operationId":"put_v1_company_structure","summary":"Records the entity kind, the state of formation and the proposed name.","description":"Records the entity kind, the state of formation and the proposed\nname. Available only at the structure stage; an unknown structure or\njurisdiction, or an empty name, is refused with 400.","tags":["company"],"requestBody":{"content":{"application/json":{"example":{"jurisdiction":"DE","name":"Acme Inc.","structure":"c-corp"},"schema":{"$ref":"#/components/schemas/structureIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company"}},"/v1/completions":{"post":{"operationId":"post_v1_completions","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["completions"],"x-app":"github.com/hanzoai/ai"}},"/v1/compliance/accreditation":{"get":{"operationId":"get_v1_compliance_accreditation","summary":"Returns the org's tracked accreditation-state records, newest first — evidence entries the org keeps, never a platform certification.","description":"Returns the org's tracked accreditation-state records, newest\nfirst — evidence entries the org keeps, never a platform certification.","tags":["compliance"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; non-positive means the server default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accList"}}},"description":"ok"}},"x-app":"compliance"},"post":{"operationId":"post_v1_compliance_accreditation","summary":"Records an ASSERTED accreditation state for a subject — the subject's own assertion, with no verifier.","description":"Records an ASSERTED accreditation state for a subject — the\nsubject's own assertion, with no verifier. Every CONFIRMED state\n(provider_verified, reviewer_confirmed) and every rejected/expired state is a\nDECISION recorded via the decision endpoint, attributed to the reviewer — a\ncreate can never stamp a confirmation. The underlying figures (income, net\nworth) are never stored; only the method, category, and state.","tags":["compliance"],"requestBody":{"content":{"application/json":{"example":{"basis":"income","method":"self_attested","subjectId":"sub_1"},"schema":{"$ref":"#/components/schemas/accreditationReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accView"}}},"description":"created"}},"x-app":"compliance"}},"/v1/compliance/accreditation/{id}":{"get":{"operationId":"get_v1_compliance_accreditation_by_id","summary":"Returns one tracked accreditation record.","description":"Returns one tracked accreditation record.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the accreditation record to read, from the path.","schema":{"type":"string"},"example":"acc_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/accreditation/{id}/decision":{"post":{"operationId":"post_v1_compliance_accreditation_by_id_decision","summary":"Records an org reviewer's decision on an accreditation record — a reviewer confirmation, a provider verification the reviewer has evidence of (a CPA/attorney letter, a verifier report), a rejection, or an expiry.","description":"Records an org reviewer's decision on an accreditation\nrecord — a reviewer confirmation, a provider verification the reviewer has\nevidence of (a CPA/attorney letter, a verifier report), a rejection, or an\nexpiry. ROLE-GATED (an org admin or platform reviewer) and ATTRIBUTED: the\nreviewer's identity is recorded as ReviewerSub and audited. Human-in-the-loop:\nthe platform never confirms on its own, and even a provider_verified state\ncarries the reviewer who recorded it.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the accreditation record to decide, from the path.","schema":{"type":"string"},"example":"acc_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"acc_1","status":"reviewer_confirmed"},"schema":{"$ref":"#/components/schemas/accreditationDecision"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/audit":{"get":{"operationId":"get_v1_compliance_audit","summary":"AuditRead is the compliance-scoped read of the SHARED tamper-evident audit plane — the SOC 2 posture surface (privileged actions: who started/decided what, when).","description":"AuditRead is the compliance-scoped read of the SHARED tamper-evident audit plane —\nthe SOC 2 posture surface (privileged actions: who started/decided what, when). The\norg is PINNED to the caller's validated org and the rows are narrowed to\ncompliance.* actions. Fail-closed: no principal is a 403, no configured audit\nstore a 501.","tags":["compliance"],"parameters":[{"name":"result","in":"query","required":false,"description":"Result filters rows by outcome result: success, deny, or error; empty means all.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/auditList"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/health":{"get":{"operationId":"get_v1_compliance_health","summary":"Health reports subsystem liveness and the wired verification provider.","description":"Health reports subsystem liveness and the wired verification provider. Fail-open\non purpose: it never probes the external provider, so a provider outage cannot\nfail liveness.","tags":["compliance"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/records":{"get":{"operationId":"get_v1_compliance_records","summary":"ListRecords is the unified compliance-record view for the org: its verifications and accreditation records together, each provider-reported or tracked, never platform-asserted.","description":"ListRecords is the unified compliance-record view for the org: its verifications\nand accreditation records together, each provider-reported or tracked, never\nplatform-asserted. PII stays in the subject store; records carry only opaque ids\nand statuses.","tags":["compliance"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; non-positive means the server default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/recordList"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/status":{"get":{"operationId":"get_v1_compliance_status","summary":"Status is the org's honest posture read: the wired provider and the per-status tally of its verifications.","description":"Status is the org's honest posture read: the wired provider and the per-status\ntally of its verifications. It is deliberately NOT a boolean \"compliant\" — it\nreports counts of provider-reported states and carries the boundary disclaimer.","tags":["compliance"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/statusView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/subjects":{"get":{"operationId":"get_v1_compliance_subjects","summary":"Returns the org's subjects as PII-MINIMIZED summaries — no name or email, only whether an email is on file.","description":"Returns the org's subjects as PII-MINIMIZED summaries — no name or\nemail, only whether an email is on file. The full record is returned only by the\nexplicit single-subject read.","tags":["compliance"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; non-positive means the server default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/subjectList"}}},"description":"ok"}},"x-app":"compliance"},"post":{"operationId":"post_v1_compliance_subjects","summary":"Records a party the org is verifying as part of its own onboarding/compliance — a team member, vendor, customer, or counterparty.","description":"Records a party the org is verifying as part of its own\nonboarding/compliance — a team member, vendor, customer, or counterparty. The\nsubject's contact PII (name/email) is sealed at rest and returned only to the\nowning org; downstream records reference the subject by opaque id.","tags":["compliance"],"requestBody":{"content":{"application/json":{"example":{"email":"founder@example.com","kind":"individual","name":"Ada"},"schema":{"$ref":"#/components/schemas/subjectReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subject"}}},"description":"created"}},"x-app":"compliance"}},"/v1/compliance/subjects/{id}":{"get":{"operationId":"get_v1_compliance_subjects_by_id","summary":"Returns one subject WITH its contact PII — the only surface that returns it, and only to the owning org.","description":"Returns one subject WITH its contact PII — the only surface that\nreturns it, and only to the owning org. The response is never cached by any\nintermediary.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the subject to read, from the path.","schema":{"type":"string"},"example":"sub_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subject"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/verifications":{"get":{"operationId":"get_v1_compliance_verifications","summary":"Returns the org's KYC/KYB verifications, newest first — opaque subject references and provider-reported statuses only, no subject PII.","description":"Returns the org's KYC/KYB verifications, newest first — opaque\nsubject references and provider-reported statuses only, no subject PII.","tags":["compliance"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; non-positive means the server default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/checkList"}}},"description":"ok"}},"x-app":"compliance"},"post":{"operationId":"post_v1_compliance_verifications","summary":"Begins a KYC/KYB verification of a subject through the wired provider — an existing subject by id, or one created inline from the request.","description":"Begins a KYC/KYB verification of a subject through the wired\nprovider — an existing subject by id, or one created inline from the request.\nThe returned status is provider-reported and never terminal on a fresh start:\nstarting a verification can never yield a verified record, and a provider error\nis a 502, never a verification.","tags":["compliance"],"requestBody":{"content":{"application/json":{"example":{"subjectId":"sub_1"},"schema":{"$ref":"#/components/schemas/verificationReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/checkView"}}},"description":"created"}},"x-app":"compliance"}},"/v1/compliance/verifications/webhook":{"post":{"operationId":"post_v1_compliance_verifications_webhook","summary":"Provider push that settles a verification, authenticated by HMAC signature","description":"The external PUSH reconcile: a verification provider (or a Hanzo relay) signals that a check settled, and the reconciled check comes back. It authenticates by an HMAC SIGNATURE over the RAW body bytes rather than by a principal — an external caller has no validated org — and the org is then resolved FROM the record the signed provider reference matches, so a call can only ever touch the one tenant that owns that reference.\n\nThe body carries NO trusted decision. A valid signature cannot force a status: the reference only says WHICH check to re-read, and the status is then pulled from the wired provider, which stays the source of truth. With no real provider configured a check stays pending, and the only route to a passing status is the role-gated, attributed reviewer decision.\n\nAn unknown reference is a benign 200 `{\"ignored\": ...}` no-op, not an error, so a provider replaying stale events neither retry-storms nor learns whether a reference exists in some other tenant. Fails closed otherwise: 501 unless a webhook secret is configured, 401 on a signature that does not verify, 400 with no provider reference, 413 over 1 MiB, and 502 if the secret or the provider is unreachable.","tags":["compliance"],"x-app":"compliance"}},"/v1/compliance/verifications/{id}":{"get":{"operationId":"get_v1_compliance_verifications_by_id","summary":"Returns one verification — its opaque subject reference and provider-reported status, no subject PII.","description":"Returns one verification — its opaque subject reference and\nprovider-reported status, no subject PII.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the verification to act on, from the path.","schema":{"type":"string"},"example":"chk_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/checkView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/verifications/{id}/decision":{"post":{"operationId":"post_v1_compliance_verifications_by_id_decision","summary":"Records a privileged reviewer's MANUAL decision on a verification — the human-in-the-loop path, and the ONLY route to a passing status when no real provider is wired.","description":"Records a privileged reviewer's MANUAL decision on a\nverification — the human-in-the-loop path, and the ONLY route to a passing status\nwhen no real provider is wired. It produces a DISTINCT reviewer_confirmed, never\na provider_verified (a provider decision is the provider's to report, via the\nwebhook or a reconcile), and it is ROLE-GATED (an org admin or platform reviewer)\nAND ATTRIBUTED (the reviewer's user id is DecidedBy), so a manual pass is always\naccountable.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the verification to decide, from the path.","schema":{"type":"string"},"example":"chk_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"chk_1","status":"reviewer_confirmed"},"schema":{"$ref":"#/components/schemas/verificationDecision"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/checkView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compliance/verifications/{id}/refresh":{"post":{"operationId":"post_v1_compliance_verifications_by_id_refresh","summary":"Polls the wired provider for its current decision and records it, ATTRIBUTED to the provider — the internal PULL reconcile.","description":"Polls the wired provider for its current decision and\nrecords it, ATTRIBUTED to the provider — the internal PULL reconcile. For the\nManual provider the check stays pending; for a hosted provider it reflects the\nprovider's settled status. A poll error is a 502, never a verification.","tags":["compliance"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the verification to act on, from the path.","schema":{"type":"string"},"example":"chk_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/checkView"}}},"description":"ok"}},"x-app":"compliance"}},"/v1/compute/bots":{"get":{"operationId":"listBots","summary":"Returns the caller org's bot machines — the kind=bot machines — each joined with the agent binding that says which cloud Agent it runs.","description":"Returns the caller org's bot machines — the kind=bot machines — each\njoined with the agent binding that says which cloud Agent it runs.\n\nThe bindings are read ONCE and joined by machine id, so the list is O(1) upstream\ncalls, not N+1. A bindings read that fails only costs the reconciled status: a bot\nstill lists without it.","tags":["compute"],"responses":{"200":{"content":{"application/json":{"example":{"bots":[{"agent":"bot-a","binding":{"agentName":"bot-a","machineId":"drop-a","status":"running"},"id":"drop-a","name":"bot-a","status":"running"}]},"schema":{"$ref":"#/components/schemas/botList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/compute/bots/launch":{"post":{"operationId":"post_v1_compute_bots_launch","summary":"Launch a bot machine — an agent plus the machine that runs it — or price one","description":"Creates BOTH halves of a bot in one call and answers 201 with the bot: the cloud agent it runs, then a bot-kind machine bootstrapped with the bot runtime, then the binding between them, so a launched bot is immediately messageable. Send `dryRun: true` for a price quote instead — 200 with the upstream quote verbatim, no agent created, no machine launched, nothing spent.\n\nThe agent is created FIRST and on purpose: it is create-if-absent (an agent that already exists is reused, so a relaunch is fine and several bots may share one explicit `agent`), and doing it before the machine means a bad request — a model that is not in the catalog, say — fails with the real reason BEFORE any metered machine is provisioned. `agent` defaults to the bot's name and an empty `model` takes the deployment default.\n\nOrg-scoped and fails closed: a validated principal is required (403 without one), the owning org is that principal's and never a body field, `size` is required (400), and `name` is required for a real launch though not for a quote.","tags":["compute"],"x-app":"visor"}},"/v1/compute/bots/{id}":{"delete":{"operationId":"deleteBot","summary":"Tears down both halves of a bot: it unbinds the agent (best-effort — a bot with no binding still deletes), then terminates the machine.","description":"Tears down both halves of a bot: it unbinds the agent (best-effort — a\nbot with no binding still deletes), then terminates the machine. Answers 204.","tags":["compute"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the bot machine's id — the same id the machines surface addresses it\nby. Scoped to the caller's org upstream, so another tenant's id is 404.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"visor"},"get":{"operationId":"getBot","summary":"Returns one of the caller org's bot machines with its agent binding.","description":"Returns one of the caller org's bot machines with its agent binding.\n\nA machine counts as a Bot if it carries the hanzo-kind:bot tag OR has an agent\nbinding — either signal is authoritative, so a bot resolves even before its\ncloud-init has stamped every tag. A machine that is neither is 404: this route\nanswers for bots, not for machines.","tags":["compute"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the bot machine's id — the same id the machines surface addresses it\nby. Scoped to the caller's org upstream, so another tenant's id is 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"agent":"bot-a","binding":{"agentName":"bot-a","machineId":"drop-a","status":"running"},"id":"drop-a","name":"bot-a","status":"running"},"schema":{"$ref":"#/components/schemas/botView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/compute/bots/{id}/{action}":{"post":{"operationId":"post_v1_compute_bots_by_id_by_action","summary":"Message a bot, or stop it, by naming the action in the path","description":"Dispatches one verb against a bot the caller's org owns. `message` runs the bot's bound agent with the request body as the message and streams the agent's answer back VERBATIM — the upstream body, its content type and its status — so a message is a real agent run, recorded, billed and traced exactly like any other, under the caller's own identity rather than a fabricated one. `stop` and `pause` are the same single honest capability: they halt the runtime by unbinding the agent while LEAVING THE MACHINE UP, so the bot stops answering but keeps costing — rebind to resume, or delete the bot to tear it down. Stopping is idempotent; a bot with no binding still reports stopped.\n\nOrg-scoped and fails closed: a validated principal is required (403 without one) and the bot is addressed under the caller's OWN org, so another tenant's id is not reachable. An unknown action is a clean 400 naming the three it accepts, never a silent no-op, and messaging a bot with no bound agent is a 400.","tags":["compute"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"action","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"visor"}},"/v1/compute/regions":{"get":{"operationId":"get_v1_compute_regions","summary":"The regions a machine or GPU can be launched into","description":"Lists the launch regions the compute catalog offers, passed through verbatim from the provider so the shape stays the provider's single source of truth. The catalog is GLOBAL, not per-tenant: no owner is forwarded and every org sees the same list. It is still gated — a validated principal is required, 403 without one — because the catalog is what backs the launch drawer, not public marketing copy.","tags":["compute"],"x-app":"visor"}},"/v1/compute/sizes":{"get":{"operationId":"get_v1_compute_sizes","summary":"The machine and GPU sizes that can be launched","description":"Lists the instance sizes the compute catalog offers, passed through verbatim from the provider so the shape stays the provider's single source of truth. These are the values `size` accepts on a launch. The catalog is GLOBAL, not per-tenant: no owner is forwarded and every org sees the same list. It is still gated — a validated principal is required, 403 without one.","tags":["compute"],"x-app":"visor"}},"/v1/connector/github/webhook":{"post":{"operationId":"post_v1_connector_github_webhook","summary":"GitHub App webhook","description":"The address the GitHub App delivers events to. A push is handed to the repository sync engine, and an issue or issue-comment event is mirrored into the native tracker — idempotently, so the same issue re-syncs to one row however many times it is edited, closed or reopened.\n\nIt answers a benign 200 for everything it does not act on — the ping, other event types, an unknown installation — deliberately, so GitHub does not enter a retry storm over events that were never going to do anything. Only a bad signature and a genuine sync failure are non-200, and an oversized payload is refused outright.\n\nTwo sync rules are worth stating because neither is guessable. EVERY ref syncs, tags as well as branches, because releases are cut by tag and filtering them would stop publishing with nothing reporting a failure. And a delete is NEVER propagated: the native side is canonical, so an inbound delete never removes a native ref.\n\nThe payload is verified by HMAC against the webhook secret before it is parsed.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.","tags":["connector"],"x-app":"integrations"}},"/v1/connectors":{"get":{"operationId":"get_v1_connectors","summary":"Lists the caller's OWN connectors across every provider — the set `hanzo connector ls` prints.","description":"Lists the caller's OWN connectors across every provider — the set\n`hanzo connector ls` prints. Rows are keyed (org,user), so this can never\nsurface another user's connector, and no secret is in the view.","tags":["connectors"],"responses":{"200":{"content":{"application/json":{"example":{"connectors":[{"account":"me@acme.com","connectedAt":"2026-07-01T10:00:00Z","expiresAt":"2026-07-01T11:00:00Z","externalId":"u-42","id":"openai:default","label":"default","provider":"openai","scopes":["api"]}]},"schema":{"$ref":"#/components/schemas/connectorsOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/providers":{"get":{"operationId":"get_v1_connectors_providers","summary":"Lists the user-scoped provider cards — the catalog of what a user can connect, and how.","description":"Lists the user-scoped provider cards — the catalog of what a\nuser can connect, and how. Methods derive from capabilities (Device/Adopt/Verify\n— Mount asserts at least one), never from a parallel kind enum.","tags":["connectors"],"responses":{"200":{"content":{"application/json":{"example":{"providers":[{"category":"AI","description":"Use your own OpenAI account.","id":"openai","methods":["device","token"],"name":"OpenAI","scopes":["api"]}]},"schema":{"$ref":"#/components/schemas/connectorProvidersOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{id}":{"delete":{"operationId":"delete_v1_connectors_by_id","summary":"Forgets a connector: every custodied secret, then the row.","description":"Forgets a connector: every custodied secret, then the row.\nIdempotent — dropping a never-connected id still answers {disconnected:true}\n(disconnect() parity). No provider Revoke: none of the user-plane providers\nexposes a revoke endpoint.","tags":["connectors"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the connector id, provider + \":\" + label (\"openai:default\") — the\nauth-profile-id shape. Another user's id is simply no row, so 404.","schema":{"type":"string"},"example":"openai:work"}],"responses":{"200":{"content":{"application/json":{"example":{"disconnected":true},"schema":{"$ref":"#/components/schemas/disconnectOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{id}/refresh":{"post":{"operationId":"post_v1_connectors_by_id_refresh","summary":"Forces a token rotation for a connected connector, ahead of the automatic rotation a token read would do inside the expiry window.","description":"Forces a token rotation for a connected connector, ahead of the\nautomatic rotation a token read would do inside the expiry window. Only\nproviders that declare a Refresh support it.","tags":["connectors"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the connector id, provider + \":\" + label (\"openai:default\") — the\nauth-profile-id shape. Another user's id is simply no row, so 404.","schema":{"type":"string"},"example":"openai:work"}],"responses":{"200":{"content":{"application/json":{"example":{"connector":{"account":"me@acme.com","connectedAt":"2026-07-01T10:00:00Z","expiresAt":"2026-07-01T12:00:00Z","externalId":"u-42","id":"openai:work","label":"work","provider":"openai","scopes":["api"]},"refreshed":true},"schema":{"$ref":"#/components/schemas/refreshOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{id}/token":{"get":{"operationId":"get_v1_connectors_by_id_token","summary":"Hands the custodied access token to its owner — the ONE place custody exits.","description":"Hands the custodied access token to its owner — the ONE place\ncustody exits. The (org,user)-keyed row IS the same-user gate: another user's\nid is simply \"no row\" → 404. fresh() auto-rotates within the refreshSkew\nwindow; static providers degenerate to a plain kmsGet of Secrets[0]. Refresh\ntokens are NEVER returned — custody keeps the sink. The token is never logged.","tags":["connectors"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the connector id, provider + \":\" + label (\"openai:default\") — the\nauth-profile-id shape. Another user's id is simply no row, so 404.","schema":{"type":"string"},"example":"openai:work"}],"responses":{"200":{"content":{"application/json":{"example":{"expiresAt":"2026-07-01T11:00:00Z","label":"work","provider":"openai","token":"\u003cthe access token\u003e"},"schema":{"$ref":"#/components/schemas/connectorTokenOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{provider}/credential":{"post":{"operationId":"post_v1_connectors_by_provider_credential","summary":"Is the direct intake path: a customer-held token/setup-token (Verify) or an externally obtained OAuth bundle from the CLI's local PKCE (Adopt).","description":"Is the direct intake path: a customer-held token/setup-token\n(Verify) or an externally obtained OAuth bundle from the CLI's local PKCE\n(Adopt). ALWAYS verify-before-store: a bad credential is refused and NOTHING\nis persisted (connectByCredential's fail-closed order).","tags":["connectors"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the user-scoped provider's registry id, from the path.","schema":{"type":"string"},"example":"openai"}],"requestBody":{"content":{"application/json":{"example":{"label":"work","provider":"openai","token":"sk-user-owned-key"},"schema":{"$ref":"#/components/schemas/credentialIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"connected":true,"connector":{"account":"me@acme.com","connectedAt":"2026-07-01T10:00:00Z","expiresAt":"","externalId":"u-42","id":"openai:work","label":"work","provider":"openai","scopes":["api"]}},"schema":{"$ref":"#/components/schemas/credentialOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{provider}/device":{"post":{"operationId":"post_v1_connectors_by_provider_device","summary":"Begins a device sign-in and returns the code to show the user plus how to poll for completion.","description":"Begins a device sign-in and returns the code to show the user plus\nhow to poll for completion. KMS readiness is checked NOW rather than dead-ending\nthe user at poll-done (connect() parity), and the per-provider connector cap is\nchecked before the provider is called. The provider's device code is persisted\nonly in the encrypted grants table and is NEVER returned.","tags":["connectors"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the user-scoped provider's registry id, from the path.","schema":{"type":"string"},"example":"openai"}],"requestBody":{"content":{"application/json":{"example":{"label":"work","provider":"openai"},"schema":{"$ref":"#/components/schemas/deviceStartIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"expiresAt":"2026-07-01T10:15:00Z","flow":"g_7f2c","interval":5,"userCode":"WDJB-MJHT","verifyUrl":"https://example.com/device"},"schema":{"$ref":"#/components/schemas/deviceStartOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/connectors/{provider}/device/{flow}/poll":{"post":{"operationId":"post_v1_connectors_by_provider_device_by_flow_poll","summary":"Advances a device sign-in.","description":"Advances a device sign-in. Terminal outcomes are DATA, not errors\n(verifyConn {active:false} discipline) — the status set is closed:\npending|connected|denied|expired. pollSlow collapses to \"pending\" on the\nwire; the raised cadence rides interval.","tags":["connectors"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the user-scoped provider's registry id, from the path.","schema":{"type":"string"},"example":"openai"},{"name":"flow","in":"path","required":true,"description":"Flow is the id deviceStartOut returned. Expired or another user's flow is\nindistinguishable from an unknown one: 404.","schema":{"type":"string"},"example":"g_7f2c"}],"responses":{"200":{"content":{"application/json":{"example":{"connector":{"account":"me@acme.com","connectedAt":"2026-07-01T10:05:00Z","expiresAt":"2026-07-01T11:00:00Z","externalId":"u-42","id":"openai:work","label":"work","provider":"openai","scopes":["api"]},"status":"connected"},"schema":{"$ref":"#/components/schemas/devicePollOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/content/board":{"get":{"operationId":"get_v1_content_board","summary":"Aggregates the caller org's marketing content across every publishable content type into ONE queue board — the cross-type read the framework's per-DocType list cannot give.","description":"Aggregates the caller org's marketing content across every publishable\ncontent type into ONE queue board — the cross-type read the framework's\nper-DocType list cannot give. It never fails on a partial outage: a content type\nthe org has not installed, or one whose search errors, is skipped and logged\nrather than failing the whole board.","tags":["content"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status keeps only items in one lifecycle state (draft, in_review, approved,\nqueued, published, archived). An undefined state is refused.","schema":{"type":"string"},"example":"queued"},{"name":"project","in":"query","required":false,"description":"Project keeps only items in one brand/site sub-scope.","schema":{"type":"string"}},{"name":"doctype","in":"query","required":false,"description":"DocType keeps only one content type; omitted, the board spans every\npublishable type. An unknown type is refused.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned, clamped to 1000. Defaults to 200, which is also\nwhat a non-positive or unparseable value takes.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/boardPage"}}},"description":"ok"}},"x-app":"content"}},"/v1/content/channels":{"get":{"operationId":"get_v1_content_channels","summary":"Lists the distribution channels the caller's org has connected — the social integrations a publish can target.","description":"Lists the distribution channels the caller's org has connected — the\nsocial integrations a publish can target. A deployment with no distribution edge\nwired answers 503 rather than an empty list that would read as \"no channels\".","tags":["content"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/channelList"}}},"description":"ok"}},"x-app":"content"}},"/v1/content/generate":{"post":{"operationId":"post_v1_content_generate","summary":"Draft a piece of marketing content and file it in the CMS as a draft.","description":"Draft a piece of marketing content and file it in the CMS as a draft.\n\nAnswers 201 with the created draft's identity — {doctype, name, status} — and the\ndocument itself lands in the CMS through the SAME validate and lifecycle-hook\npipeline an ordinary create runs. This is a WRITE, not a preview: there is no\ndry-run, and every call that succeeds leaves a document behind.\n\n`doctype` picks which of two generation planes runs, and they are the only two.\nCampaign and SocialPost are drafted as brand COPY on the platform AI plane (zen5 by\ndefault, overridable per request with `model` or per deployment); Asset is a studio\nimage render the AI plane never sees. Everything else about the call is identical.\n\nMONEY, metered in exactly one place per mode and never both. Copy rides the\nplatform's own inference meter — the org's balance is authorised before the model\ncall and debited at the exact token cost after — so content never re-bills it. A\nstudio render is invisible to that meter, so content is the sole meter for it: the\norg is gated BEFORE the GPU compute and refused 402 when out of funds or over its\nspend cap, and the debit is recorded only once the render actually returns, because\nthe billable event is the consumed compute and not the CMS row. `project` rides the\nBODY rather than a server-minted identity claim, so it attributes spend but a\nproject-scoped cap stays soft on it — the org is the value that is enforced.\n\nThe org is the caller's own, resolved once from the validated principal and never\nread from the body; a caller without one is refused 403. Status is not the\ngenerator's to choose: a generated item is ALWAYS a draft, and the storage-boundary\nhook enforces that a second time.\n\nIt fails closed rather than inventing anything. An unknown content type is 404 and a\ndeployment whose marketing module is not installed is 409 naming the install call.\nAn AI plane or studio that is unconfigured or unreachable, a graph the studio\nrejects, and a render that does not return in time all degrade to 503 — never\nfabricated copy, never a fake render. A `source_media` that fails the SSRF and\ntraversal validator is 400 raised before the billing gate and before the studio is\ncontacted, so a hostile source never costs the caller anything.","tags":["content"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateResult"}}},"description":"created"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateResult"}}},"description":"payment required"}},"x-app":"content"}},"/v1/content/lifecycle":{"get":{"operationId":"get_v1_content_lifecycle","summary":"Returns the ONE marketing-content state machine: the ordered lifecycle states, which state a fresh document starts in, which one is publicly live, and the legal successors of every state.","description":"Returns the ONE marketing-content state machine: the ordered\nlifecycle states, which state a fresh document starts in, which one is publicly\nlive, and the legal successors of every state. The console builds its board\ncolumns and its per-item action buttons from this single answer, so the UI and\nthe write-time enforcement hook can never disagree about what is legal.","tags":["content"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/stateGraph"}}},"description":"ok"}},"x-app":"content"}},"/v1/content/publish":{"post":{"operationId":"post_v1_content_publish","summary":"Publish distributes one CMS content item to the channels recorded on it and returns the honest per-channel outcome.","description":"Publish distributes one CMS content item to the channels recorded on it and\nreturns the honest per-channel outcome. The item names itself — its caption,\nmedia and channel list are read from the stored document, not from this request.\nIt is idempotent per channel (a channel already posted for this item is skipped),\nand a publish that loses the per-item lease to a live publisher answers status\n\"in_progress\" having posted nothing.","tags":["content"],"requestBody":{"content":{"application/json":{"example":{"doctype":"SocialPost","name":"spring-teaser"},"schema":{"$ref":"#/components/schemas/PublishInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResult"}}},"description":"ok"}},"x-app":"content"}},"/v1/content/{doctype}/{name}/transition":{"post":{"operationId":"post_v1_content_by_doctype_by_name_transition","summary":"Moves one content item to a new lifecycle state and, on the move to published, fans it out to the item's channels.","description":"Moves one content item to a new lifecycle state and, on the move to\npublished, fans it out to the item's channels. The edge must be legal for the\nitem's current state — an illegal move is refused with 409 — and the status write\nre-validates it at the storage boundary. Distribution is best effort: its honest\nstate is reported on the result and a distribution failure never rolls the status\nchange back.","tags":["content"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the content type to act on, from the path.","schema":{"type":"string"},"example":"SocialPost"},{"name":"name","in":"path","required":true,"description":"Name is the document to act on, from the path.","schema":{"type":"string"},"example":"spring-teaser"}],"requestBody":{"content":{"application/json":{"example":{"doctype":"SocialPost","name":"spring-teaser","to":"published"},"schema":{"$ref":"#/components/schemas/transitionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionResult"}}},"description":"ok"}},"x-app":"content"}},"/v1/crawl":{"post":{"operationId":"post_v1_crawl","summary":"Fetch one URL and read it back as markdown","description":"Fetches a single URL from inside the cluster and answers with the page's title, its content rendered to markdown, and whatever metadata the document carried.\n\nA page that could not be fetched is a NORMAL outcome, not a fault: an unreachable host, a refused address or a non-document content type all answer 200 with `success:false` and the reason in `error`. Non-2xx is reserved for a caller problem — 401 for a bad key, 400 for a missing url, 503 when the surface is unconfigured — so error handling can trust the status.\n\nAdmission is either a validated principal or the shared service key, presented as X-API-Key or a Bearer; neither is refused, and an unset key fails closed rather than opening the fetcher to the private network. Crawled pages are archived under the scope of the VERIFIED principal, never a scope named in the body; a service caller has no org and its pages land in the shared corpus. One URL per call, and the request body is bounded at 1 MiB.","tags":["crawl"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crawlRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crawlResult"}}},"description":"Success"}},"x-app":"crawl"}},"/v1/crm/applications":{"get":{"operationId":"get_v1_crm_applications","summary":"Returns the org's Startup Program applications, newest first.","description":"Returns the org's Startup Program applications, newest first.\nEach carries its AI screen and its stage history; a stage narrows the page to\none pipeline stage.","tags":["crm"],"parameters":[{"name":"stage","in":"query","required":false,"description":"Stage returns only the applications at that pipeline stage when set:\napplied, screened, qualified, credits-offered, onboarded or rejected.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned: 200 by default, 1000 at most.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/applicationList"}}},"description":"ok"}},"x-app":"crm"},"post":{"operationId":"post_v1_crm_applications","summary":"Apply to the Startup Program from the public form","description":"Files an application to the Startup Program and answers the id and pipeline stage it landed at.\n\nThis is the ONE unauthenticated route in crm. It takes no principal and never reads a caller org: the application is filed against the DEPLOYMENT's own program org — the brand, hanzo unless white-labelled — so there is no tenant to name and none to leak. Reading the application back is staff-only and lives elsewhere.\n\ncompany, contactName and a parseable email are required; everything else is optional context. Re-submitting the same (email, company) REFRESHES the existing application instead of filing a second one, so an impatient applicant cannot duplicate their own lead — that is a 200 where a first submission is a 201. A filled `hp` honeypot field is answered exactly like a success and stored nowhere, so a bot cannot tell a drop from an accept.\n\nFiling is not screening: the application lands at stage `applied` with its AI screen still pending, and the screen runs afterwards on its own clock. A company and contact are also projected into the program org's ordinary CRM lists, best-effort — that projection failing does not fail the application. Bodies over 64 KiB are refused, and submissions are rate-limited.","tags":["crm"],"x-app":"crm"}},"/v1/crm/applications/{id}":{"get":{"operationId":"get_v1_crm_applications_by_id","summary":"Returns one Startup Program application with its AI screen and stage history.","description":"Returns one Startup Program application with its AI screen and stage history.\nAn id belonging to another org reads as not found.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"appl_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgramApplication"}}},"description":"ok"}},"x-app":"crm"},"patch":{"operationId":"patch_v1_crm_applications_by_id","summary":"Moves one Startup Program application through the pipeline.","description":"Moves one Startup Program application through the pipeline. The\nmove is recorded on the application's timeline, attributed to the calling\nstaff user: it may advance exactly one stage, go back to any earlier stage,\nreject from any non-rejected stage, or reopen a rejected application to\n`applied`; anything else is refused. Rejecting requires a reason. A note with\nno stage change is still recorded.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the application to move, from the path.","schema":{"type":"string"},"example":"appl_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"appl_1","reason":"not a fit this round","stage":"rejected"},"schema":{"$ref":"#/components/schemas/patchApplicationIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgramApplication"}}},"description":"ok"}},"x-app":"crm"}},"/v1/crm/companies":{"get":{"operationId":"get_v1_crm_companies","summary":"Returns the caller org's companies, most recently updated first.","description":"Returns the caller org's companies, most recently updated first.","tags":["crm"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned: 200 by default, 1000 at most.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/companyList"}}},"description":"ok"}},"x-app":"crm"},"post":{"operationId":"post_v1_crm_companies","summary":"Adds a company to the caller's org and answers 201 with the stored record.","description":"Adds a company to the caller's org and answers 201 with the stored record.\nA name is required; an empty currency defaults to USD.","tags":["crm"],"requestBody":{"content":{"application/json":{"example":{"domainName":"maxpower.ai","employees":42,"name":"MaxPower Inc"},"schema":{"$ref":"#/components/schemas/companyReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}},"description":"created"}},"x-app":"crm"}},"/v1/crm/companies/{id}":{"delete":{"operationId":"delete_v1_crm_companies_by_id","summary":"Removes one of the caller org's companies and answers 204.","description":"Removes one of the caller org's companies and answers 204. Any\ncontact or opportunity in the org that referenced it keeps existing with the\nreference cleared, so nothing is left pointing at a company that is gone.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"comp_1"}],"responses":{"204":{"description":"no content"}},"x-app":"crm"},"get":{"operationId":"get_v1_crm_companies_by_id","summary":"Returns one of the caller org's companies.","description":"Returns one of the caller org's companies. An id belonging to\nanother org reads as not found.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"comp_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}},"description":"ok"}},"x-app":"crm"},"put":{"operationId":"put_v1_crm_companies_by_id","summary":"Replaces one of the caller org's companies.","description":"Replaces one of the caller org's companies. Every writable\nfield is taken from the request, so a field the request omits is CLEARED —\nsend the whole record. A name is required.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID names the company to update and comes from the path. A create ignores\nit: the server mints the id.","schema":{"type":"string"},"example":"comp_1"}],"requestBody":{"content":{"application/json":{"example":{"employees":64,"id":"comp_1","name":"MaxPower Inc"},"schema":{"$ref":"#/components/schemas/companyReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Company"}}},"description":"ok"}},"x-app":"crm"}},"/v1/crm/contacts":{"get":{"operationId":"get_v1_crm_contacts","summary":"Returns the caller org's contacts, most recently updated first.","description":"Returns the caller org's contacts, most recently updated first.\nA companyId narrows the page to the people at that company.","tags":["crm"],"parameters":[{"name":"companyId","in":"query","required":false,"description":"CompanyID returns only the contacts at that company when set.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned: 200 by default, 1000 at most.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/contactList"}}},"description":"ok"}},"x-app":"crm"},"post":{"operationId":"post_v1_crm_contacts","summary":"Adds a person to the caller's org and answers 201 with the stored record.","description":"Adds a person to the caller's org and answers 201 with the stored record.\nOne of firstName, lastName or email is required, and a companyId must name a\ncompany in the same org.","tags":["crm"],"requestBody":{"content":{"application/json":{"example":{"email":"dave@maxpower.ai","firstName":"Dave","lastName":"Lorenzini"},"schema":{"$ref":"#/components/schemas/contactReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}},"description":"created"}},"x-app":"crm"}},"/v1/crm/contacts/{id}":{"delete":{"operationId":"delete_v1_crm_contacts_by_id","summary":"Removes one of the caller org's contacts and answers 204.","description":"Removes one of the caller org's contacts and answers 204. Any\nopportunity in the org that named it point of contact keeps existing with\nthat reference cleared.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"cont_1"}],"responses":{"204":{"description":"no content"}},"x-app":"crm"},"get":{"operationId":"get_v1_crm_contacts_by_id","summary":"Returns one of the caller org's contacts.","description":"Returns one of the caller org's contacts. An id belonging to\nanother org reads as not found.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"cont_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}},"description":"ok"}},"x-app":"crm"},"put":{"operationId":"put_v1_crm_contacts_by_id","summary":"Replaces one of the caller org's contacts.","description":"Replaces one of the caller org's contacts. Every writable field\nis taken from the request, so a field the request omits is CLEARED — send the\nwhole record. One of firstName, lastName or email is required.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID names the contact to update and comes from the path. A create ignores\nit: the server mints the id.","schema":{"type":"string"},"example":"cont_1"}],"requestBody":{"content":{"application/json":{"example":{"firstName":"Dave","id":"cont_1","jobTitle":"CTO"},"schema":{"$ref":"#/components/schemas/contactReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}},"description":"ok"}},"x-app":"crm"}},"/v1/crm/opportunities":{"get":{"operationId":"get_v1_crm_opportunities","summary":"Returns the caller org's deals, most recently updated first.","description":"Returns the caller org's deals, most recently updated first.\nA stage narrows the page to one pipeline stage.","tags":["crm"],"parameters":[{"name":"stage","in":"query","required":false,"description":"Stage returns only the opportunities at that pipeline stage when set\n(NEW, SCREENING, MEETING, PROPOSAL or CUSTOMER; case-insensitive).","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned: 200 by default, 1000 at most.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/oppList"}}},"description":"ok"}},"x-app":"crm"},"post":{"operationId":"post_v1_crm_opportunities","summary":"Adds a deal to the caller's org and answers 201 with the stored record.","description":"Adds a deal to the caller's org and answers 201 with the stored record.\nA name is required; the stage defaults to NEW; companyId and pointOfContactId\nmust name records in the same org.","tags":["crm"],"requestBody":{"content":{"application/json":{"example":{"amount":5000000,"name":"Enterprise Deal","stage":"PROPOSAL"},"schema":{"$ref":"#/components/schemas/oppReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}},"description":"created"}},"x-app":"crm"}},"/v1/crm/opportunities/{id}":{"delete":{"operationId":"delete_v1_crm_opportunities_by_id","summary":"Removes one of the caller org's deals and answers 204.","description":"Removes one of the caller org's deals and answers 204.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"oppo_1"}],"responses":{"204":{"description":"no content"}},"x-app":"crm"},"get":{"operationId":"get_v1_crm_opportunities_by_id","summary":"Returns one of the caller org's deals.","description":"Returns one of the caller org's deals. An id belonging to\nanother org reads as not found.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the record to act on, from the path.","schema":{"type":"string"},"example":"oppo_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}},"description":"ok"}},"x-app":"crm"},"put":{"operationId":"put_v1_crm_opportunities_by_id","summary":"Replaces one of the caller org's deals.","description":"Replaces one of the caller org's deals. Every writable\nfield is taken from the request, so a field the request omits is CLEARED —\nsend the whole record. A name is required and the stage must be a pipeline\nstage.","tags":["crm"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID names the opportunity to update and comes from the path. A create\nignores it: the server mints the id.","schema":{"type":"string"},"example":"oppo_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"oppo_1","name":"Enterprise Deal","stage":"CUSTOMER"},"schema":{"$ref":"#/components/schemas/oppReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}},"description":"ok"}},"x-app":"crm"}},"/v1/crm/summary":{"get":{"operationId":"get_v1_crm_summary","summary":"Summary counts the caller org's CRM records: companies, contacts, opportunities.","description":"Summary counts the caller org's CRM records: companies, contacts, opportunities.","tags":["crm"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crmSummary"}}},"description":"ok"}},"x-app":"crm"}},"/v1/csrf":{"get":{"operationId":"get_v1_csrf","summary":"IssueCSRFToken mints the anti-CSRF token a browser echoes as X-CSRF-Token on every money write (mint/revoke a key, top up, onboard, and the billing/commerce write verbs).","description":"IssueCSRFToken mints the anti-CSRF token a browser echoes as X-CSRF-Token on\nevery money write (mint/revoke a key, top up, onboard, and the billing/commerce\nwrite verbs). The token is bound to the caller's validated identity and expires,\nso one minted for one identity cannot authorize a write as another.\n\nIt is answered no-store, so it is never cached by a shared proxy. This is the\nsame-origin endpoint the embedded console reads — the Same-Origin Policy is what\nstops a cross-site page from reading the response and forging a write.","tags":["csrf"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/csrfResp"}}},"description":"ok"}},"x-app":"account"}},"/v1/dataroom/analytics/dataroom/{dataroomId}":{"get":{"operationId":"get_v1_dataroom_analytics_dataroom_by_dataroomid","summary":"Rolls up every share link pointing at one data room: session and page-view totals for the room, plus the per-page breakdown for each link beneath it.","description":"Rolls up every share link pointing at one data room:\nsession and page-view totals for the room, plus the per-page breakdown for each\nlink beneath it.\n\nA room id outside the caller's own tenant store is not found. Only links that\nNAME the room are counted — a link created over a single document contributes\nnothing here, even when that document also sits in the room.","tags":["dataroom"],"parameters":[{"name":"dataroomId","in":"path","required":true,"description":"DataroomID is the room to report on. It is the path segment, resolved in\nthe caller's own tenant store.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomStats"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/analytics/link/{linkId}":{"get":{"operationId":"get_v1_dataroom_analytics_link_by_linkid","summary":"Reports how one share link was actually read: total viewing sessions, total page views, and per page the view count, the summed dwell measure and its average.","description":"Reports how one share link was actually read: total viewing\nsessions, total page views, and per page the view count, the summed dwell\nmeasure and its average.\n\nThe link is resolved in the caller's OWN tenant store, so another org's link id\nis not found — knowing a link id is enough to OPEN the room it shares, and\nnever enough to read who has been reading it.","tags":["dataroom"],"parameters":[{"name":"linkId","in":"path","required":true,"description":"LinkID is the link to report on. It is the path segment, resolved in the\ncaller's own tenant store.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomLinkStats"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/datarooms":{"get":{"operationId":"get_v1_dataroom_datarooms","summary":"Returns every data room in the caller org's own store, newest first, with its short public id, name, description and timestamps.","description":"Returns every data room in the caller org's own store, newest\nfirst, with its short public id, name, description and timestamps.\n\nDocuments are not included — a room's contents come from reading the single\nroom.","tags":["dataroom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomRooms"}}},"description":"ok"}},"x-app":"dataroom"},"post":{"operationId":"post_v1_dataroom_datarooms","summary":"Opens a new data room for the caller org and answers with it, including the short public id it is addressed by.","description":"Opens a new data room for the caller org and answers with it,\nincluding the short public id it is addressed by.\n\n`name` is required; without it the call is refused and the tenant store is\nuntouched, because a dispatch answering 4xx rolls its transaction back. A new\nroom holds no documents and is reachable by NOBODY until a share link is\ncreated over it — opening a room and granting access are two separate acts, so\na room cannot leak by existing.","tags":["dataroom"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomRoomOne"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/datarooms/{id}":{"get":{"operationId":"get_v1_dataroom_datarooms_by_id","summary":"Reads one of the caller org's data rooms together with every document in it, each carrying its membership id and order index.","description":"Reads one of the caller org's data rooms together with every\ndocument in it, each carrying its membership id and order index.\n\nThe documents are sorted by that index with unordered ones last and creation\ntime breaking ties — the SAME order a link's visitor sees, so this is what the\nroom looks like from the outside. A room id outside the caller's own tenant\nstore is not found.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the room to read. It is the path segment: the URL is the addressing\nauthority, and the org it is resolved in comes from the caller's principal,\nso an id from another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomRoomDetailOne"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/datarooms/{id}/documents":{"post":{"operationId":"post_v1_dataroom_datarooms_by_id_documents","summary":"Puts an already-uploaded document into one of the caller org's data rooms and answers with the new membership id.","description":"Puts an already-uploaded document into one of the caller\norg's data rooms and answers with the new membership id.\n\nIt ATTACHES, it never uploads: the bytes must already be stored, so the usual\norder is upload the document, then add it to the room. Both the room and the\ndocument must exist in the caller's own store — either missing is not found —\nand a document already in the room is refused as a conflict rather than\nduplicated.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the room to add to. It is the path segment: the URL is the addressing\nauthority, and the org it is resolved in comes from the caller's principal,\nso an id from another tenant is simply not found.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomAddDocument"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomMembership"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/documents":{"get":{"operationId":"get_v1_dataroom_documents","summary":"Returns every document in the caller org's own store, newest first — name, opaque storage key, content type, page count, size and timestamps.","description":"Returns every document in the caller org's own store, newest\nfirst — name, opaque storage key, content type, page count, size and\ntimestamps.\n\nTenant isolation is the per-org store itself: there is one SQLite file per org\nand the org is never a parameter, so no input the caller controls can address\nanother tenant's documents. Metadata only — the bytes come from the file route.","tags":["dataroom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomDocuments"}}},"description":"ok"}},"x-app":"dataroom"},"post":{"operationId":"post_v1_dataroom_documents","summary":"Upload a document's bytes and record it","description":"Takes the file ITSELF as the raw request body — not a JSON envelope, not multipart — stores it on the object-storage seam, and records the metadata row, answering with the new document. `?name=` names it (default \"document\"), the request's Content-Type becomes the recorded mime type, and `?numPages=` is optional.\n\nRequires a validated principal; 403 without one. An empty body is 400 and anything over 64 MiB is 413 — a data room holds decks and PDFs, not a media library.\n\nThe storage key is 128 random bits under the tenant's own key prefix, minted before the bytes are written: if the system's randomness is unavailable the upload fails 500 rather than fall back to a predictable key that could overwrite another document's bytes. A storage write that fails is 502 and no metadata row is recorded, so a document never exists without its file.","tags":["dataroom"],"x-app":"dataroom"}},"/v1/dataroom/documents/{id}":{"get":{"operationId":"get_v1_dataroom_documents_by_id","summary":"Reads one of the caller org's documents — its name, opaque storage key, content type, page count, size and timestamps.","description":"Reads one of the caller org's documents — its name, opaque storage\nkey, content type, page count, size and timestamps.\n\nThe lookup runs in the caller's own tenant store, so an id belonging to another\norg is not found exactly like one that never existed. Metadata only: the bytes\nare a separate read.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to read. It is the path segment: the URL is the\naddressing authority, and the org it is resolved in comes from the caller's\nprincipal, so an id from another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomDocumentOne"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/documents/{id}/file":{"get":{"operationId":"get_v1_dataroom_documents_by_id_file","summary":"Download a document's bytes as its owner","description":"Streams the stored file back under its recorded content type, falling back to application/octet-stream when none was recorded.\n\nRequires a validated principal; 403 without one, and the document is resolved in the caller's own tenant store, so another org's id is a 404. This is the OWNER's path and applies no link gate at all — the per-link password, email and download controls live on the viewer surface, not here. Bytes that cannot be fetched from object storage are 502, never a truncated or empty file.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom"}},"/v1/dataroom/health":{"get":{"operationId":"get_v1_dataroom_health","summary":"Liveness of the dataroom subsystem","description":"Answers {service, status} unconditionally — no principal, no tenant. It is registered BEFORE the bundle, the link index and the object-storage seam are wired, so it keeps answering when any of those fail and the subsystem degrades to health-only. That is the point, and the limit: a 200 here says the process is alive, never that a data room can be read or written.","tags":["dataroom"],"x-app":"dataroom"}},"/v1/dataroom/links":{"get":{"operationId":"get_v1_dataroom_links","summary":"Returns every live share link in the caller org's own store, newest first, with the controls a visitor will meet: whether an address is required, whether a password is set, the allow and deny lists, whether download is permitted, and when the link expires.","description":"Returns every live share link in the caller org's own store,\nnewest first, with the controls a visitor will meet: whether an address is\nrequired, whether a password is set, the allow and deny lists, whether download\nis permitted, and when the link expires.\n\nArchived links are omitted entirely. A link reports only THAT a password is\nset — the stored form is a bcrypt hash and no route returns it.","tags":["dataroom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomLinks"}}},"description":"ok"}},"x-app":"dataroom"},"post":{"operationId":"post_v1_dataroom_links","summary":"Grants access: it mints a public share link over one data room (`dataroomId`) or one document (`documentId`) — one of the two is required — and answers with the link, whose `id` is the token a visitor opens it with.","description":"Grants access: it mints a public share link over one data\nroom (`dataroomId`) or one document (`documentId`) — one of the two is\nrequired — and answers with the link, whose `id` is the token a visitor opens\nit with.\n\nThis is how a party is let in. The controls are declared HERE and enforced on\nthe viewer surface: `password` is hashed with bcrypt before storage and is\nnever readable back, `emailProtected` (on by default) makes a visitor state an\naddress, `allowList`/`denyList` narrow which addresses pass, `allowDownload`\n(off by default) governs downloads, and `expiresAt` closes the link. The target\nroom or document must exist in the caller's own store or it is not found.\n\nCreating a link also writes dataroom's ONE cross-tenant row: the link id to\nowning org mapping an anonymous visitor is routed through. That write is part\nof the operation — if it fails the call is 500 — so a link that no visitor\ncould open is never handed back as usable.\n\nThe address a visitor later states is recorded UNVERIFIED, so a link gated only\nby email is openable by anyone the link reaches. Use a password for a link that\nmust not travel.","tags":["dataroom"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomLinkCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomLinkOne"}}},"description":"ok"}},"x-app":"dataroom"}},"/v1/dataroom/view/{linkId}":{"get":{"operationId":"get_v1_dataroom_view_by_linkid","summary":"What a share link's visitor sees before authenticating","description":"Answers the pre-auth face of a link to anyone holding its id: name and type, which gates apply (whether an address is required, whether a password is set), whether download is permitted, whether it has expired, and the name and description of the room behind it — or, for a single-document link, that document's name and page count.\n\nNo principal is involved: the owning org is resolved from the link id through dataroom's one cross-tenant routing table, and an unknown or archived link is a 404.\n\nIt is metadata only — a room's document list and every file stay behind the authenticate step. An expired link is REPORTED as expired here rather than refused, so a visitor learns why the next step will fail; nothing about the password beyond its existence is disclosed.","tags":["dataroom"],"parameters":[{"name":"linkId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom"}},"/v1/dataroom/view/{linkId}/authenticate":{"post":{"operationId":"post_v1_dataroom_view_by_linkid_authenticate","summary":"Pass a share link's gates and open a viewing session","description":"Clears the link's access controls and answers with the viewing session — a `viewId`, whether download is permitted, and the documents behind the link — which every later viewer call is authorised by.\n\nNo principal: the visitor is whoever holds the link id, and the org is resolved from it. The gates run in a fixed order and each is a flat refusal, never a hint. An archived or unknown link is 404 and an expired one 403. A missing address on an email-protected link is 401. An address on the deny list is 403, checked BEFORE the allow list so deny always wins. An address the allow list does not admit is 403 — an EMPTY allow list admits everyone, so a link with no list enforces the email gate alone. A wrong or absent password is 401, decided against the stored bcrypt hash.\n\nThe address is taken as stated and recorded UNVERIFIED: it names a viewer for analytics and repeat visits from it reuse one viewer record, but it proves nothing about who is on the other end. A link gated only by email is openable by anyone the link reaches.","tags":["dataroom"],"parameters":[{"name":"linkId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom"}},"/v1/dataroom/view/{linkId}/document/{documentId}/file":{"get":{"operationId":"get_v1_dataroom_view_by_linkid_document_by_documentid_file","summary":"Read a document's bytes as an authorised link visitor","description":"Streams a document's bytes under its recorded content type to a visitor holding an open viewing session.\n\nNo principal: `?viewId=` from the authenticate step is the authorisation and must belong to this link, or the call is 403 — holding the link id alone gets no bytes. The document must be reachable THROUGH this link (a member of the room the link opens, or the single document the link names), so a visitor cannot walk to an unrelated document by guessing an id; anything else is a 404, as is an unknown or archived link. Bytes that cannot be fetched from object storage are 502.\n\n`?download=1` additionally requires the link's `allowDownload` and is 403 when the owner did not permit it. Read that flag precisely: it gates the DOWNLOAD intent, not access to the bytes — without the parameter an authorised visitor is served the file for in-place viewing whether or not downloads are allowed.","tags":["dataroom"],"parameters":[{"name":"linkId","in":"path","required":true,"schema":{"type":"string"}},{"name":"documentId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom"}},"/v1/dataroom/view/{linkId}/pageview":{"post":{"operationId":"post_v1_dataroom_view_by_linkid_pageview","summary":"Record one page-view against an open viewing session","description":"Appends a single per-page analytics event — {viewId, pageNumber, documentId, versionNumber, duration} — and answers with its id. These events are what the owner's analytics count.\n\nNo principal: the `viewId` from the authenticate step IS the authorisation, and it must belong to THIS link or the call is 404, so a session opened on one link cannot write events onto another. `pageNumber` is required (400 without it); `documentId` falls back to the document the session was opened on, and `duration` is the caller's own dwell measure, summed per page by analytics.\n\nEvents are additive: the same page reported twice is two views, which is the metric's whole point.","tags":["dataroom"],"parameters":[{"name":"linkId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom"}},"/v1/datastore":{"get":{"operationId":"get_v1_datastore","summary":"Lists the caller org's Hanzo Datastore warehouses.","description":"Lists the caller org's Hanzo Datastore warehouses. Each one is\na DEDICATED analytical instance the org alone runs, so the host is that\ninstance's own in-cluster Service and the port is its HTTP port, 8123.","tags":["datastore"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_datastore","summary":"Provision a Hanzo Datastore instance for your org","description":"Launches your org's OWN Hanzo Datastore instance and answers with its `datastore://` connection string. The instance is yours alone: a deployment in your own tenant namespace, so its admin credential is naturally scoped to you and no other tenant shares the process. Off-cluster, where there is no orchestrator to launch one, this fails closed with 503 rather than handing back a shared one.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["datastore"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/datastore/{name}":{"delete":{"operationId":"delete_v1_datastore_by_name","summary":"Deprovisions one Hanzo Datastore warehouse.","description":"Deprovisions one Hanzo Datastore warehouse. It reverts any app\ninstance bound to it back to Base BEFORE tearing down the org's dedicated\ninstance, then deletes the sealed credential and removes the metadata row.\nAnswers 204 with no body; a second call is a 404.","tags":["datastore"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"warehouse"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_datastore_by_name","summary":"Returns one Hanzo Datastore warehouse's metadata.","description":"Returns one Hanzo Datastore warehouse's metadata. It carries the\nwarehouse's status, its instance address and the admin user the instance\nbooted with — never the password. A still-booting instance reads\n\"provisioning\", reconciled from the operator's live view rather than the row.","tags":["datastore"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"warehouse"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/deploy/account/can-i/{wildcard1}":{"get":{"operationId":"get_v1_deploy_account_can-i_by_wildcard1","summary":"Compatibility answer the console UI asks before enabling its buttons","description":"Always answers `yes`, whatever resource, action or subresource the path names. It exists for the ArgoCD-compatible console, which asks this before enabling a control, and it is NOT the authorization decision: nothing downstream consults it, and every route that returns fleet data or mutates a CR carries its own gate. Reaching it at all already requires SuperAdmin, so a caller who can read the `yes` is one for whom it is true.","tags":["deploy"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"deploy"}},"/v1/deploy/applications":{"get":{"operationId":"get_v1_deploy_applications","summary":"Returns the fleet as an argocd ApplicationList: one projected Application per operator App CR, carrying the image tag the CR DECLARES, the tag actually RUNNING in the cluster's Deployment, the reconciled health, and the sync verdict those two produce (declared == running ⇒ Synced, both known and different ⇒ OutOfSync, either unknown ⇒ Unknown).","description":"Returns the fleet as an argocd ApplicationList: one\nprojected Application per operator App CR, carrying the image tag the CR\nDECLARES, the tag actually RUNNING in the cluster's Deployment, the reconciled\nhealth, and the sync verdict those two produce (declared == running ⇒ Synced,\nboth known and different ⇒ OutOfSync, either unknown ⇒ Unknown).\n\nIt is TENANT-SCOPED: a platform SuperAdmin reads every platform namespace, a\nvalidated org member reads only its own org's tenant namespace and only the App\nCRs labelled with its org, and anyone else is refused. A cross-tenant CR is\nnever projected into an answer.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoAppList"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/applications/{name}":{"get":{"operationId":"get_v1_deploy_applications_by_name","summary":"Returns ONE projected argocd Application by name, with status.resources filled in from its reconciled resource tree — which is what makes it the detail view rather than a row of the list.","description":"Returns ONE projected argocd Application by name, with\nstatus.resources filled in from its reconciled resource tree — which is what\nmakes it the detail view rather than a row of the list.\n\nIt is TENANT-SCOPED, and a name that belongs to another org is reported NOT\nFOUND rather than refused: a 403 would confirm the application exists, so the\nroute would become a cross-tenant existence oracle. A name that is not a\nDNS-1123 label is a 400 before any cluster read.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the application to read, from the path. It must be a DNS-1123 label\n(lowercase alphanumerics and hyphens, starting and ending alphanumeric) —\nevery operator App CR's metadata.name satisfies that, and anything else is a\n400 rather than a lookup.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoApp"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/applications/{name}/resource-tree":{"get":{"operationId":"get_v1_deploy_applications_by_name_resource-tree","summary":"Returns one application's argocd ApplicationTree: the objects the operator reconciled from its App CR, reached by ownerRef — the Deployment and, under it, the ReplicaSet and Pods, plus the Service, Ingress, HorizontalPodAutoscaler, PodDisruptionBudget and ConfigMaps it owns — each node carrying its parent edges and its health.","description":"Returns one application's argocd ApplicationTree: the\nobjects the operator reconciled from its App CR, reached by ownerRef — the\nDeployment and, under it, the ReplicaSet and Pods, plus the Service, Ingress,\nHorizontalPodAutoscaler, PodDisruptionBudget and ConfigMaps it owns — each node\ncarrying its parent edges and its health.\n\nSecrets are DELIBERATELY not walked, so no materialized environment can ever\nappear in the tree. Tenant-scoped exactly like the application read: another\norg's name is not found, a malformed name is a 400.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the application to read, from the path. It must be a DNS-1123 label\n(lowercase alphanumerics and hyphens, starting and ending alphanumeric) —\nevery operator App CR's metadata.name satisfies that, and anything else is a\n400 rather than a lookup.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoTree"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/applications/{name}/revisions/{revision}/metadata":{"get":{"operationId":"get_v1_deploy_applications_by_name_revisions_by_revision_metadata","summary":"Returns the argocd RevisionMetadata for one revision of one application — what the detail view shows beside a revision.","description":"Returns the argocd RevisionMetadata for one revision\nof one application — what the detail view shows beside a revision.\n\nAn App CR is IMAGE-pinned rather than commit-pinned: the deploy names an image\ntag, and the git source this projection reports is the display-only manifest\nrepo, not the application's own source. Nothing in this process can read a\ncommit's author or message for an arbitrary revision. So rather than 404 (which\nthe SPA turns into an error toast) or invent a git author, it answers the\nHONEST minimum: date is when the App CR was created, message is the revision\nasked for — with the empty revision and \"HEAD\" resolving to the image tag the\nCR declares — and author is empty. An over-long revision is truncated before it\nis echoed back.\n\nTenant-scoped exactly like the application read.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the application to read, from the path. It must be a DNS-1123 label.","schema":{"type":"string"}},{"name":"revision","in":"path","required":true,"description":"Revision is the revision to describe, from the path. The empty revision and\n\"HEAD\" both mean \"whatever this application currently declares\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoRevisionMetadata"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/applications/{name}/rollback":{"post":{"operationId":"post_v1_deploy_applications_by_name_rollback","summary":"The console's rollback control — today it requests a reconcile, nothing more","description":"Performs exactly what the sync action performs: it stamps the sync-requested timestamp onto the application's App CR and answers the application re-projected. It does NOT select, pin or revert to a prior image tag, and that is the one thing to know before wiring anything to it — the name is the console's, the behaviour is the sync. Pinning a previous release rides the release seam, which this address does not call yet.\n\nSuperAdmin-only and fail-closed, reading no request body, with an unknown application name a 404 and no cluster client a 503 — the same gate and the same failures as the sync it shares a handler with.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"deploy"}},"/v1/deploy/applications/{name}/sync":{"post":{"operationId":"post_v1_deploy_applications_by_name_sync","summary":"Ask the operator to reconcile one application now","description":"Requests an immediate reconcile of one application by stamping a sync-requested timestamp onto its App CR, which the operator's watch observes, and answers the application re-projected. It ASKS, it does not apply: the operator performs the reconcile on its own clock, so a 200 means the request landed, not that the rollout finished — the returned row's running version still lags until it does. The CR is the desired source today, so this is a nudge; when git becomes the source the same address becomes apply-from-git.\n\nSuperAdmin-only and fail-closed — a non-SuperAdmin is refused before any cluster object is read or patched, and the write surface stays admin-only while the tenant surface is read-only reflection. It reads no request body. An unknown application name is a 404; no cluster client configured is a 503.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"deploy"}},"/v1/deploy/applications/{name}/syncwindows":{"get":{"operationId":"get_v1_deploy_applications_by_name_syncwindows","summary":"Returns one application's argocd ApplicationSyncWindowState — the answer to \"is anything blocking a sync of this application right now?\".","description":"Returns one application's argocd\nApplicationSyncWindowState — the answer to \"is anything blocking a sync of this\napplication right now?\".\n\nThis platform runs NO sync windows, so the answer is always the permissive\nempty one: canSync true, with no active and no assigned windows. The\napplication is still resolved first, so a name that is not the caller's is not\nfound rather than handed the static body — the endpoint discloses nothing about\nanother tenant's fleet.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the application to read, from the path. It must be a DNS-1123 label\n(lowercase alphanumerics and hyphens, starting and ending alphanumeric) —\nevery operator App CR's metadata.name satisfies that, and anything else is a\n400 rather than a lookup.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoSyncWindows"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/callback":{"get":{"operationId":"get_v1_deploy_callback","summary":"Finish the sign-in round trip and mint the console session","description":"Completes the redirect from IAM: it validates `state` against the single-use flow cookie in constant time, redeems the authorization code with the PKCE verifier, and then VERIFIES the resulting token exactly as this deployment's identity boundary will on every later request — so a token that would be refused next request fails here with the real reason instead of producing a sign-in loop. On success it sets the session cookie, bounded by the token's own expiry, and redirects to the validated return path.\n\nIt fails closed, and closes on the ADMIN ORG: a principal whose verified owner claim is not the reserved admin org is told plainly that it lacks the role (403) and no cookie is minted for it. That check is not the authorization decision — every gated route re-derives SuperAdmin from the verified JWT — it exists so nobody is handed a session that silently 403s everything. No flow in progress, or a mismatched `state`, is a 400; a refused or unexchangeable code is a 401.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/clusters":{"get":{"operationId":"get_v1_deploy_clusters","summary":"Returns the argocd ClusterList of the destinations the caller's applications reconcile into: one entry per distinct destination server, carrying the count of applications reconciling into it.","description":"Returns the argocd ClusterList of the destinations the\ncaller's applications reconcile into: one entry per distinct destination\nserver, carrying the count of applications reconciling into it. The in-cluster\ndestination is always present, so an empty fleet still answers one cluster, and\nno cluster credential can appear — the projected type physically has no config\nfield.\n\nIt is TENANT-SCOPED and reads the SAME App CRs the applications list reads: a\nplatform SuperAdmin counts the whole fleet, a validated org member counts only\nits own org's applications, anyone else is refused.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoClusterList"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/gitops":{"get":{"operationId":"get_v1_deploy_gitops","summary":"Lists every Hanzo CD Application in the cluster: the git source each one polls, the commit it last APPLIED, how its last sync operation ended, and its recent deploy history — newest deploy first, ordered by namespace then name.","description":"Lists every Hanzo CD Application in the cluster: the git source\neach one polls, the commit it last APPLIED, how its last sync operation ended,\nand its recent deploy history — newest deploy first, ordered by namespace then\nname.\n\nThis is the layer ABOVE the application board, and the two disagree in exactly\nthe case an operator most needs to see: main carries a new image pin, CD has\nnot applied that commit yet, so every App CR still declares the old tag and the\napplication board is legitimately \"Synced\" while the deploy has not landed.\nOnly the applied revision here can show that.\n\ninstalled is false — with a reason and an empty list — when the CD CRD is not\nserved in this cluster. That is a FACT about the cluster rather than a failure\nof the request, so the caller can say \"no CD plane here\" instead of rendering\nan error it cannot act on; a genuine transport or RBAC failure still errors.\n\nRead-only, and platform SuperAdmin only: the CD plane is fleet infrastructure\nwith no tenant dimension. This view observes CD and never drives it — the sync\npolicy is automated with self-heal, and the actionable verb an operator has is\nthe per-application reconcile at POST /v1/deploy/applications/{name}/sync.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GitOpsPlane"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/health":{"get":{"operationId":"get_v1_deploy_health","summary":"Whether this control plane can actually reach the cluster it deploys to","description":"Reports the plane's real reachability: 200 only when the Kubernetes API server answers AND the App CRD is served, 503 with the same body shape otherwise, so a caller reads the same `k8s` and `crd` booleans either way rather than parsing an error envelope. It is a genuine dependency probe, not a process liveness ping — a running plane with no cluster behind it reports degraded.\n\nThis is the ONE unauthenticated route that reports state, because liveness must be probe-able without a JWT. It therefore discloses booleans only: the underlying failure — the API server address, an RBAC refusal — is logged server-side and never put on the wire.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/login":{"get":{"operationId":"get_v1_deploy_login","summary":"Start the sign-in round trip for this console","description":"Redirects the browser to IAM's authorize endpoint, having minted a nonce and a PKCE verifier into a short-lived, single-use flow cookie. The nonce comes back as `state` and is what proves the code belongs to the round trip THIS browser started; the verifier never appears in the address bar.\n\nNecessarily public — this is how a browser gets a principal for this host in the first place — and it grants nothing by itself. An optional `returnTo` names where to land afterwards and is run through the open-redirect guard, so only a same-host path survives. A deployment with no sign-in configured answers 503 rather than redirecting nowhere.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/logout":{"post":{"operationId":"post_v1_deploy_logout","summary":"End the console session on this host","description":"Clears this console's session cookie and answers the signed-out state with the sign-in URL to start again. IAM's own session is untouched — this ends the console session only, so signing back in may not prompt for credentials.\n\nIt is a POST because it changes state. As a GET it was reachable by a cross-site top-level navigation, which a SameSite=Lax cookie still rides, so any page could sign a SuperAdmin out; a POST is not carried cross-site by that cookie.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/projects":{"get":{"operationId":"get_v1_deploy_projects","summary":"Returns the argocd AppProjectList this console groups and filters applications by.","description":"Returns the argocd AppProjectList this console groups and\nfilters applications by. Projects are owned by Hanzo IAM rather than by argocd,\nso they are REFLECTED read-only from the IAM project store and nothing is\npersisted here: a validated org member gets its own organization's projects and\na platform SuperAdmin gets every organization's.\n\nA SuperAdmin whose IAM store is not reachable falls back to the real\nargoproj.io AppProject CRs when that CRD is served, and otherwise to one\npermissive synthesized project per distinct project name the App CRs declare.\nA project named \"default\" is always present, because that is what an App CR\ncarrying no project label projects to.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/argoProjectList"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/reconcile":{"post":{"operationId":"post_v1_deploy_reconcile","summary":"Render the configured git source and apply it to the cluster, once","description":"Runs one full GitOps sync through the embedded engine — render the configured repo, ref and path, then three-way server-side apply with scoped prune — and answers the revision it applied, the source it came from, the declared/synced/pruned/failed counts and a per-resource result. This is the WRITE half of the plane: it mutates live cluster objects and, with prune enabled, deletes objects the source no longer declares.\n\nSuperAdmin-only and fail-closed — a non-SuperAdmin is refused before any cluster object is read or touched. The git source is read AS THE CALLER, so the source plane scopes the answer itself rather than trusting this one to have scoped it. It reads no request body; the source is configuration, not a parameter. A deployment with the engine switched off, or with no usable cluster config, answers 503; a failure to start, render or sync is a 502.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/session/userinfo":{"get":{"operationId":"get_v1_deploy_session_userinfo","summary":"Answers \"is this browser signed in, and if not where does it sign in?\" — the dashboard SPA's bootstrap question, and the only route on this plane that answers for an anonymous caller.","description":"Answers \"is this browser signed in, and if not where does it\nsign in?\" — the dashboard SPA's bootstrap question, and the only route on this\nplane that answers for an anonymous caller.\n\nThe anonymous answer carries loggedIn:false and a URL and NOTHING else: no\nusername, no org, no groups, no issuer, no hint about who the caller might be or\nwhat exists in the cluster. Answering it costs nothing (the caller already knows\nwhether it holds a cookie) and withholding it costs the whole sign-in journey.\n\nThe predicate is the platform SuperAdmin fact — the SAME one every other route\nhere gates on, minted from a validated principal whose org is the reserved admin\norg — so a validated-but-not-SuperAdmin caller is reported as NOT signed in,\nwhich is the truth as this console defines it: they cannot use it.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sessionUser"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/settings":{"get":{"operationId":"get_v1_deploy_settings","summary":"Returns the argocd AuthSettings object the dashboard SPA awaits before its first render.","description":"Returns the argocd AuthSettings object the dashboard SPA\nawaits before its first render.\n\nEvery value is a CONSTANT of this projection rather than configuration read\nfrom anywhere: the SPA's own login form is reported disabled and its OIDC\nconfig null because Hanzo IAM owns identity at the edge and this console's\nsign-in is GET /v1/deploy/login, and every argocd feature the projection does\nnot implement — status badges, Dex connectors, config-management plugins,\nkustomize versions, the exec terminal, apps-in-any-namespace, the hydrator,\nsync-with-replace — is reported off. Platform SuperAdmin only.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/consoleSettings"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/deploy/stream/applications":{"get":{"operationId":"get_v1_deploy_stream_applications","summary":"Live application fleet updates as Server-Sent Events","description":"Holds the connection open as text/event-stream and pushes one watch event per application change. It opens with an `ADDED` frame for every application currently present — the same projection the applications list serves, so a client renders a complete fleet from the stream alone — and then forwards `ADDED`, `MODIFIED` and `DELETED` as they happen, with a keep-alive every 25 seconds that is also how a vanished client is noticed and its watch torn down.\n\nRead-only and TENANT-SCOPED, fail-closed: a platform SuperAdmin streams the whole fleet, a validated org member streams only its own org's applications, anyone else gets 403 and no stream. No cluster client configured is 503. If the deployment is not granted the watch verb the stream degrades to keep-alives only — the initial state still renders, it simply stops updating — rather than failing the connection.","tags":["deploy"],"x-app":"deploy"}},"/v1/deploy/stream/applications/{name}/resource-tree":{"get":{"operationId":"get_v1_deploy_stream_applications_by_name_resource-tree","summary":"Live resource tree for one application, as Server-Sent Events","description":"Holds the connection open as text/event-stream and pushes the application's whole resource tree — its live child objects and each one's derived health — once immediately and again on every keep-alive tick, so a client always has a current picture without polling. The refresh IS the keep-alive: it is a cheap rebuild rather than a watch, so there is no multi-resource watch to leak.\n\nTENANT-SCOPED and fail-closed BEFORE the stream opens, which is the rule that matters: the caller's scope and the application's namespace are resolved first, so an unvalidated caller gets a plain 403 and an application belonging to another tenant gets a plain 404 — never an opened stream that emits nothing. A SuperAdmin reaches the whole fleet, an org member only its own org's applications. No cluster client configured is 503.","tags":["deploy"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"deploy"}},"/v1/deploy/version":{"get":{"operationId":"get_v1_deploy_version","summary":"Returns the argocd VersionMessage the dashboard SPA reads at bootstrap.","description":"Returns the argocd VersionMessage the dashboard SPA reads at\nbootstrap. There is no argocd binary behind this plane — it is a projection\nover operator App CRs — so the fields say so rather than describing a build:\nVersion names the projection, BuildDate is the moment this response was\ngenerated, and Compiler/Platform/GoVersion are the constants the SPA tolerates\nrather than facts about this process. Platform SuperAdmin only.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/versionMessage"}}},"description":"ok"}},"x-app":"deploy"}},"/v1/destinations":{"get":{"operationId":"get_v1_destinations","summary":"Reports every destination this deployment can forward to, each with the caller org's connection state: whether it is connected, whether it is enabled, whether a credential resolves right now, and the config fields the console renders for it.","description":"Reports every destination this deployment can forward to, each with the\ncaller org's connection state: whether it is connected, whether it is enabled,\nwhether a credential resolves right now, and the config fields the console\nrenders for it.","tags":["destinations"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationList"}}},"description":"ok"}},"x-app":"destinations"}},"/v1/destinations/{platform}":{"delete":{"operationId":"delete_v1_destinations_by_platform","summary":"Forgets a destination for the caller's org: every credential held in KMS, then the stored config.","description":"Forgets a destination for the caller's org: every credential held in\nKMS, then the stored config. Idempotent, and it requires org admin.","tags":["destinations"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | posthog | umami.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationDisconnected"}}},"description":"ok"}},"x-app":"destinations"},"get":{"operationId":"get_v1_destinations_by_platform","summary":"Reports one destination's card for the caller's org — its config fields, its connection state, and whether a credential resolves right now.","description":"Reports one destination's card for the caller's org — its config fields,\nits connection state, and whether a credential resolves right now. A platform\nthis deployment does not carry is not found.","tags":["destinations"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | posthog | umami.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationStatus"}}},"description":"ok"}},"x-app":"destinations"},"post":{"operationId":"post_v1_destinations_by_platform","summary":"Connect one conversion destination for your org, or update the one you have","description":"Stores the addressed platform's non-secret ids (its measurement, pixel or dataset ids) and seals its API credential into KMS under a path scoped to the caller's own org, then answers the same status card the read routes do — with live telling you whether the credential actually resolves right now. The body's property NAMES are the platform's own: each field the platform declares, plus each secret under its camelCase name, so the accepted keys differ per platform and a missing REQUIRED field is refused. Connecting is an ORG ADMIN action — a validated member without the admin bit gets 403 — and it fails closed with 503 when the KMS master key is unavailable rather than persisting a destination whose secret was never sealed. The secret itself never appears in the response, in the store, or in a log line; only its NAME is ever published. Set enabled to false to keep the connection but stop the analytics fan-out to it.","tags":["destinations"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationStatus"}}},"description":"Success"}},"x-app":"destinations"}},"/v1/destinations/{platform}/test":{"post":{"operationId":"post_v1_destinations_by_platform_test","summary":"Sends ONE synthetic pageview through the connected destination end to end and reports what the platform said.","description":"Sends ONE synthetic pageview through the connected destination end to end\nand reports what the platform said. A send the platform refuses is reported as\ndata — {\"ok\": false, \"error\": …} at 200 — so the console shows the platform's\nown words rather than an error about Hanzo. It requires org admin.","tags":["destinations"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | posthog | umami.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationTest"}}},"description":"ok"}},"x-app":"destinations"}},"/v1/dev-bridge":{"get":{"operationId":"get_v1_dev-bridge","summary":"Upgrades to WebSocket and bridges JSON-RPC messages between the browser and a hanzo-app-server instance.","description":"Upgrades to WebSocket and bridges JSON-RPC messages between\nthe browser and a hanzo-app-server instance.\n\nIn local mode (default): spawns hanzo-app-server as a child process.\nIn remote mode (?remote=host:port): proxies to a remote app-server via TCP.\n\nGET /api/dev-bridge?cwd=/path/to/project\nGET /api/dev-bridge?remote=host:port\u0026cwd=/path/to/project","tags":["dev-bridge"],"x-app":"github.com/hanzoai/ai"}},"/v1/dns/{wildcard1}":{"delete":{"operationId":"delete_v1_dns_by_wildcard1","summary":"Delete a DNS zone or record","description":"Removes a DNS zone or record from the Hanzo DNS control plane. The plane owns the authoritative zone and record store behind every name pointed at Hanzo; this head keeps none of it. The sub-path after /v1/dns and the query string ARE the plane's own API address, relayed verbatim, and the plane's answer comes back unchanged — its status code, its Content-Type, and its Location on a redirect this head never follows.\n\nIt travels under the CALLER'S OWN identity and substitutes no service credential, which would collapse tenants: the caller's validated session bearer goes upstream as Authorization and the server-validated org as X-Org-Id, so a caller in one org reaches only that org's zones, exactly as if it had called the plane directly. The upstream host comes only from deployment config, never from the request, so no path can re-target another host.\n\nFails closed before a byte leaves cloud: no validated principal is 403; an API key is 401, because a pk-/sk- key is not a JWT the OIDC-gated plane can validate and there is no substitute credential to send in its place; a path that normalizes outside /v1/dns, or still carries a percent-escape or a `..` after one decode, is 400; an unconfigured plane is 503 and an unreachable one 502.","tags":["dns"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dns"},"get":{"operationId":"get_v1_dns_by_wildcard1","summary":"Read your org's DNS zones and records","description":"Reads DNS state — a zone, a record, a listing — from the Hanzo DNS control plane. The plane owns the authoritative zone and record store behind every name pointed at Hanzo; this head keeps none of it. The sub-path after /v1/dns and the query string ARE the plane's own API address, relayed verbatim, and the plane's answer comes back unchanged — its status code, its Content-Type, and its Location on a redirect this head never follows.\n\nIt travels under the CALLER'S OWN identity and substitutes no service credential, which would collapse tenants: the caller's validated session bearer goes upstream as Authorization and the server-validated org as X-Org-Id, so a caller in one org reaches only that org's zones, exactly as if it had called the plane directly. The upstream host comes only from deployment config, never from the request, so no path can re-target another host.\n\nFails closed before a byte leaves cloud: no validated principal is 403; an API key is 401, because a pk-/sk- key is not a JWT the OIDC-gated plane can validate and there is no substitute credential to send in its place; a path that normalizes outside /v1/dns, or still carries a percent-escape or a `..` after one decode, is 400; an unconfigured plane is 503 and an unreachable one 502.","tags":["dns"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dns"},"patch":{"operationId":"patch_v1_dns_by_wildcard1","summary":"Amend a DNS zone or record","description":"Amends a DNS zone or record on the Hanzo DNS control plane. The plane owns the authoritative zone and record store behind every name pointed at Hanzo; this head keeps none of it. The sub-path after /v1/dns and the query string ARE the plane's own API address, relayed verbatim, and the plane's answer comes back unchanged — its status code, its Content-Type, and its Location on a redirect this head never follows.\n\nIt travels under the CALLER'S OWN identity and substitutes no service credential, which would collapse tenants: the caller's validated session bearer goes upstream as Authorization and the server-validated org as X-Org-Id, so a caller in one org reaches only that org's zones, exactly as if it had called the plane directly. The upstream host comes only from deployment config, never from the request, so no path can re-target another host.\n\nFails closed before a byte leaves cloud: no validated principal is 403; an API key is 401, because a pk-/sk- key is not a JWT the OIDC-gated plane can validate and there is no substitute credential to send in its place; a path that normalizes outside /v1/dns, or still carries a percent-escape or a `..` after one decode, is 400; an unconfigured plane is 503 and an unreachable one 502.","tags":["dns"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dns"},"post":{"operationId":"post_v1_dns_by_wildcard1","summary":"Create a DNS zone or record","description":"Creates DNS state — a zone, a record — on the Hanzo DNS control plane. The plane owns the authoritative zone and record store behind every name pointed at Hanzo; this head keeps none of it. The sub-path after /v1/dns and the query string ARE the plane's own API address, relayed verbatim, and the plane's answer comes back unchanged — its status code, its Content-Type, and its Location on a redirect this head never follows.\n\nIt travels under the CALLER'S OWN identity and substitutes no service credential, which would collapse tenants: the caller's validated session bearer goes upstream as Authorization and the server-validated org as X-Org-Id, so a caller in one org reaches only that org's zones, exactly as if it had called the plane directly. The upstream host comes only from deployment config, never from the request, so no path can re-target another host.\n\nFails closed before a byte leaves cloud: no validated principal is 403; an API key is 401, because a pk-/sk- key is not a JWT the OIDC-gated plane can validate and there is no substitute credential to send in its place; a path that normalizes outside /v1/dns, or still carries a percent-escape or a `..` after one decode, is 400; an unconfigured plane is 503 and an unreachable one 502.","tags":["dns"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dns"},"put":{"operationId":"put_v1_dns_by_wildcard1","summary":"Replace a DNS zone or record","description":"Replaces a DNS zone or record on the Hanzo DNS control plane. The plane owns the authoritative zone and record store behind every name pointed at Hanzo; this head keeps none of it. The sub-path after /v1/dns and the query string ARE the plane's own API address, relayed verbatim, and the plane's answer comes back unchanged — its status code, its Content-Type, and its Location on a redirect this head never follows.\n\nIt travels under the CALLER'S OWN identity and substitutes no service credential, which would collapse tenants: the caller's validated session bearer goes upstream as Authorization and the server-validated org as X-Org-Id, so a caller in one org reaches only that org's zones, exactly as if it had called the plane directly. The upstream host comes only from deployment config, never from the request, so no path can re-target another host.\n\nFails closed before a byte leaves cloud: no validated principal is 403; an API key is 401, because a pk-/sk- key is not a JWT the OIDC-gated plane can validate and there is no substitute credential to send in its place; a path that normalizes outside /v1/dns, or still carries a percent-escape or a `..` after one decode, is 400; an unconfigured plane is 503 and an unreachable one 502.","tags":["dns"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dns"}},"/v1/docdb":{"get":{"operationId":"get_v1_docdb","summary":"ListDocDB lists the caller org's Hanzo DocDB document databases.","description":"ListDocDB lists the caller org's Hanzo DocDB document databases. Each one is\na DEDICATED FerretDB instance the org alone runs, speaking the MongoDB wire\nprotocol, so the host is that instance's own in-cluster Service and the port\nis 27017.","tags":["docdb"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_docdb","summary":"Provision a document database for your org","description":"Launches your org's OWN document-database instance — it speaks the MongoDB wire protocol, so existing MongoDB drivers connect unchanged — and answers with its `mongodb://` connection string. The instance is yours alone: a deployment in your own tenant namespace, so its admin credential is naturally scoped to you and no other tenant shares the process. Off-cluster, where there is no orchestrator to launch one, this fails closed with 503 rather than handing back a shared one.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["docdb"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/docdb/{name}":{"delete":{"operationId":"delete_v1_docdb_by_name","summary":"DropDocDB deprovisions one Hanzo DocDB database.","description":"DropDocDB deprovisions one Hanzo DocDB database. It reverts any app instance\nbound to it back to Base BEFORE tearing down the org's dedicated FerretDB\ninstance, then deletes the sealed credential and removes the metadata row.\nAnswers 204 with no body; a second call is a 404.","tags":["docdb"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"sessions"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_docdb_by_name","summary":"GetDocDB returns one Hanzo DocDB database's metadata.","description":"GetDocDB returns one Hanzo DocDB database's metadata. It carries the\ndatabase's status, its instance address and the SCRAM user the instance was\nset up with — never the password. A still-booting instance reads\n\"provisioning\", reconciled from the operator's live view.","tags":["docdb"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"sessions"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/docs/ingest":{"post":{"operationId":"post_v1_docs_ingest","summary":"Unified RAG ingest: parse + chunk + embed documents and pipe them to BOTH Hanzo Vector (semantic) AND Hanzo Search (keyword) under the tenant index {owner}-{store}-docs — the same index /v1/chat retrieval reads.","description":"Unified RAG ingest: parse + chunk + embed documents and pipe them\nto BOTH Hanzo Vector (semantic) AND Hanzo Search (keyword) under the tenant\nindex {owner}-{store}-docs — the same index /v1/chat retrieval reads. The\nsource is pluggable: \"upload\" (inline files/documents), \"github\" (index a\nrepo), \"crawl\" (web), or \"s3\" (the store's object-storage space). The owner\nis bound to the authenticated principal; the client-supplied owner is never\ntrusted.","tags":["docs"],"x-app":"github.com/hanzoai/ai"}},"/v1/documents":{"delete":{"operationId":"delete_v1_documents","summary":"Handles DELETE /v1/documents — a JSON array of file_ids.","description":"Handles DELETE /v1/documents — a JSON array of file_ids.","tags":["documents"],"x-app":"github.com/hanzoai/ai"}},"/v1/documents/{file_id}/context":{"get":{"operationId":"get_v1_documents_by_file_id_context","summary":"Handles GET /v1/documents/:file_id/context — every chunk of a file, as LangChain Documents (used when RAG_USE_FULL_CONTEXT is on).","description":"Handles GET /v1/documents/:file_id/context — every chunk of\na file, as LangChain Documents (used when RAG_USE_FULL_CONTEXT is on).","tags":["documents"],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/domain/availability":{"get":{"operationId":"get_v1_domain_availability","summary":"Availability and price for names you already have in mind","description":"Checks exact names rather than searching for them, and answers the same quote shape search does — purchasable, premium, first-term and renewal price in cents. Pass `domain` with one name or several comma-separated to check them in one call; names are lowercased. An empty `domain` is 400.\n\nRequires a validated principal; 403 without one. Nothing is charged and nothing is held. A deployment with no registrar credentials answers 503.","tags":["domain"],"x-app":"domain"}},"/v1/domain/domains":{"get":{"operationId":"get_v1_domain_domains","summary":"The domains your org has bought here","description":"Lists the caller org's domains, newest registration first, each carrying the name, when it was registered, when it expires, what the org paid, the registrar order id and the nameservers it points at. Scoped to the validated principal's org — 403 without one, and there is no parameter that reaches another org's holdings.\n\nThis is the deployment's OWN ownership record, not a query to the registrar: it lists what was bought THROUGH this surface, so a domain the org holds elsewhere is not here. The default store is in-process, so a deployment that has not swapped in a durable store answers from what this process registered.","tags":["domain"],"x-app":"domain"}},"/v1/domain/health":{"get":{"operationId":"get_v1_domain_health","summary":"Whether this deployment can actually sell domains, and why not when it cannot","description":"Reports registrar reachability honestly: `ok` only when the wholesale credentials are present AND name.com accepted them on a live call made while you waited. Missing credentials or an unreachable registrar is 503 carrying `configured`, `reachable` and the reason, so an operator reads the blocker instead of guessing at it. Takes no principal, like every subsystem health probe. The answer also names the registrar `env`, which is the fact that decides whether money moves: only `prod` reaches the live, billable registrar — anything else, including unset, is the sandbox.","tags":["domain"],"x-app":"domain"}},"/v1/domain/register":{"post":{"operationId":"post_v1_domain_register","summary":"Buy a domain for your org — charged only once the registrar confirms","description":"Buys `domain` for `years` (default 1) and answers the ownership record together with the quote it was bought at. The order of operations is the product guarantee: quote, refuse anything unpurchasable or unpriced, AUTHORIZE the org's prepaid balance, provision the authoritative zone in Hanzo DNS, register at the registrar already pointing at Hanzo's nameservers, and only then CAPTURE the charge and record ownership. A registrar failure therefore leaves the balance untouched — the org is never billed for a domain it did not get.\n\nRequires a validated principal; that principal's org owns the domain and is the ledger the charge lands on. Re-buying a name the org already holds is 409, not a second purchase. `contacts` is optional — omit it and the registrar uses the reseller account's default WHOIS contacts.\n\nRefusals are distinct on purpose: 402 when the prepaid balance cannot cover the quoted price, 409 when the name is not available, 503 when the deployment has no registrar credentials, and the registrar's own message with its own 4xx — or 502 for its 5xx — when it rejects the purchase. Zone provisioning is best-effort: if the zone service is down the domain is still registered against Hanzo's nameservers and the zone reconciles afterwards, rather than the purchase failing.","tags":["domain"],"x-app":"domain"}},"/v1/domain/renew":{"post":{"operationId":"post_v1_domain_renew","summary":"Extend a domain your org already owns","description":"Renews `domain` for `years` (default 1) and answers the updated record with its new expiry alongside what was paid. Ownership is the gate: a name the caller's org does not hold is 404, so a renewal can never reach another tenant's domain.\n\nThe price is re-quoted at the CURRENT renewal rate rather than the one paid at purchase. If the registrar returns no renewal price the org's original price is charged instead, so a renewal is never accidentally free. Balance is authorized before the registrar is called and captured after it confirms — 402 when the prepaid balance cannot cover it, 503 when the deployment has no registrar credentials. Requires a validated principal.","tags":["domain"],"x-app":"domain"}},"/v1/domain/search":{"get":{"operationId":"get_v1_domain_search","summary":"Buyable names for a keyword, priced","description":"Searches the registrar for names built from the keyword `q`, plus its alternate-TLD suggestions, and answers a quote for each: the name, whether it is purchasable, whether it is premium, the first-term and renewal price in cents, and the TLD. Prices are RETAIL — this deployment's markup is already applied and the wholesale cost is never on the wire. Narrow the TLDs with a comma-separated `tld`; `q` is required and its absence is 400.\n\nRequires a validated principal; 403 without one. Nothing is charged and nothing is held — a quote is not a reservation, and the price is re-quoted at purchase, so a name quoted here can be gone or dearer by the time you buy it. A deployment with no registrar credentials answers 503.","tags":["domain"],"x-app":"domain"}},"/v1/domain/transfer":{"post":{"operationId":"post_v1_domain_transfer","summary":"Move a domain you own at another registrar onto your org here","description":"Transfers `domain` in using its `authCode` — both required, 400 otherwise — for `years` (default 1), and answers the same record-plus-quote a purchase does. It is priced and charged exactly like a registration: authorize the org's prepaid balance, ask the registrar for the transfer, capture only after the registrar accepts. A name the registrar will not price is 409, an insufficient balance is 402, and a deployment with no registrar credentials is 503.\n\nRequires a validated principal; the ownership record is written under that org as soon as the registrar ACCEPTS the request, which is not the same instant the transfer completes at the losing registrar. Unlike a registration this does not provision a zone, so the record carries this deployment's configured nameservers.","tags":["domain"],"x-app":"domain"}},"/v1/download/{wildcard1}":{"get":{"operationId":"get_v1_download_by_wildcard1","summary":"Download a file from a session","description":"Fetches one file's BYTES from a session, addressed as {session_id}/{fileId} — a plot, a generated CSV, whatever a run wrote. The content type is derived from the name and defaults to application/octet-stream.\n\nThis is the one address whose success body is not JSON, which is why it is not a typed operation: a typed operation always marshals a Go value.","tags":["download"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"exec"}},"/v1/embed":{"get":{"operationId":"get_v1_embed","summary":"Reports whether one of this brand's shared embedded apps (cms, erp, help) may be framed by the caller and is actually running, so a console module can choose between the embed and the provision panel.","description":"Reports whether one of this brand's shared embedded apps (cms, erp,\nhelp) may be framed by the caller and is actually running, so a console module\ncan choose between the embed and the provision panel.\n\nIt answers two questions the browser cannot answer for itself. ENTITLEMENT is\nserver-authoritative: each app is a single shared per-BRAND instance, so only a\nmember of the owning brand org — or a SuperAdmin — is given the embed URL; every\nother caller gets phase \"not-entitled\" and no URL. REACHABILITY is a probe of\nthat origin, which a cross-origin page cannot read for itself.\n\nThe probed host is always \u003capp\u003e.\u003cthis deployment's own brand domain\u003e: no part of\nit comes from the request, so this can never be steered into probing an\narbitrary origin.","tags":["embed"],"parameters":[{"name":"app","in":"query","required":false,"description":"App is the embedded app to report on: cms (Content Studio), erp or help.","schema":{"type":"string"},"example":"cms"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/embedStatusResp"}}},"description":"ok"}},"x-app":"account"}},"/v1/embeddings":{"post":{"operationId":"post_v1_embeddings","summary":"Implements POST /v1/embeddings (OpenAI-compatible).","description":"Implements POST /v1/embeddings (OpenAI-compatible).\n\nBody: {\"model\": \"...\", \"input\": \"...\"|[\"...\", ...], \"encoding_format\"?, \"dimensions\"?}\nIt authenticates the caller, resolves the model to its upstream provider via\nthe shared routing table, rewrites the user-facing model name to the upstream\nid, and proxies the request to the provider's /embeddings endpoint verbatim.","tags":["embeddings"],"x-app":"github.com/hanzoai/ai"}},"/v1/enablement":{"get":{"operationId":"get_v1_enablement","summary":"Returns what the caller's org can actually use: every managed item with its global state, whether it is effective here, whether this org is already opted into its beta, and whether it may still opt in.","description":"Returns what the caller's org can actually use: every managed\nitem with its global state, whether it is effective here, whether this org is\nalready opted into its beta, and whether it may still opt in. Read-only and\nsafe for any caller — one without a validated principal simply sees the\ngenerally-available items and no opt-in affordance, never another org's state.","tags":["enablement"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/enablementBoard"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/enablement/optin":{"post":{"operationId":"post_v1_enablement_optin","summary":"Opts the caller's OWN org into a beta item.","description":"Opts the caller's OWN org into a beta item. The org is the\ncaller's validated one, so this can never target another org, and the registry\nrefuses anything not in beta — so it can neither re-open an item an operator\nturned off nor touch one that is already generally available. Requires a\nsigned-in caller with an org.","tags":["enablement"],"requestBody":{"content":{"application/json":{"example":{"id":"labs","kind":"feature"},"schema":{"$ref":"#/components/schemas/enablementOptRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/userEnablementItem"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/enablement/optout":{"post":{"operationId":"post_v1_enablement_optout","summary":"Removes the caller's OWN org from a beta item's grant list, the reverse of OptIntoBeta and idempotent.","description":"Removes the caller's OWN org from a beta item's grant list, the\nreverse of OptIntoBeta and idempotent. The org is the caller's validated one,\nso this can never revoke another org's grant. Requires a signed-in caller with\nan org.","tags":["enablement"],"requestBody":{"content":{"application/json":{"example":{"id":"labs","kind":"feature"},"schema":{"$ref":"#/components/schemas/enablementOptRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/userEnablementItem"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/engine/model":{"get":{"operationId":"engineModel","summary":"Read one model's load state on the serving runtime","description":"Model reads one model's load state — loaded, unloading, or not_found, as\nthe engine itself reports it.","tags":["engine"],"parameters":[{"name":"model","in":"query","required":false,"description":"Model is the model id to inspect, exactly as the model list reports it.","schema":{"type":"string"},"example":"Qwen/Qwen3-4B"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"engine"}},"/v1/engine/models":{"get":{"operationId":"engineModels","summary":"List the models the serving runtime holds, with each one's load state","description":"Models lists the models the engine serves, each with its load state — the\nserver's own model table (its standard list envelope, load status\nincluded), relayed verbatim.","tags":["engine"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"engine"}},"/v1/engine/status":{"get":{"operationId":"engineStatus","summary":"Whether the serving runtime is reachable, and which build it runs","description":"Status reports whether the engine deployment is reachable and which build\nrevision it runs — an honest lens for \"is the serving runtime up\", never a\nfabricated ok.","tags":["engine"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/engineStatus"}}},"description":"ok"}},"x-app":"engine"}},"/v1/engine/system":{"get":{"operationId":"engineSystem","summary":"The serving host's own inventory: devices, memory and build capabilities","description":"System reads the engine host's inventory: OS, CPU, memory, every accelerator\ndevice with its VRAM and compute capability, and the build's capabilities\n(CUDA/Metal/flash-attention) — the real hardware under the serving runtime,\nrelayed verbatim.","tags":["engine"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"engine"}},"/v1/entitlements":{"get":{"operationId":"get_v1_entitlements","summary":"Projection reports which console apps the CALLER's org may open, and the plan slug that decides it.","description":"Projection reports which console apps the CALLER's org may open, and the plan slug\nthat decides it. It is the READ side of the unified paywall: the org's plan tier\nresolved from commerce, which is a different authority from the enablement store\nbehind GET /v1/orgs/{org}/entitlements (that one is the org's own on/off intent).\n\nIt fails SAFE-TO-LOCKED, never 500: an unvalidated principal is a 403, but a\ncommerce outage reports every app locked at 200 rather than breaking the shell.\nThe ENFORCEMENT path still fails open, so functionality survives the same outage\neven while the UI conservatively shows locked.","tags":["entitlements"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectionView"}}},"description":"ok"}},"x-app":"entitlements"}},"/v1/environments":{"get":{"operationId":"get_v1_environments","summary":"Returns your deploy targets, and what is running on each.","description":"Returns your deploy targets, and what is running on each.\n\nIt returns the org's environments — the distinct deploy targets its applications\nname, `production` for anything that names none — each aggregating the apps that\ntarget it, a rolled-up status and when it last changed.\n\nAn environment is DERIVED, not stored: there is nothing to create or delete here,\nand an environment exists exactly as long as an app points at it. Requires a\nvalidated principal; 403 without one.","tags":["environments"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentBoard"}}},"description":"ok"}},"x-app":"platform"}},"/v1/errors":{"get":{"operationId":"get_v1_errors","summary":"Errors returns the caller org's most recently captured errors, newest first.","description":"Errors returns the caller org's most recently captured errors, newest first. The\nerror-tracking read view over event.error — the plane table the write core's error\nfacts land in (errors are DELIBERATELY not on event.event) — each with its captured\nexception surfaced from the attributes map as a first-class field.\n\nThe org is the validated principal's — never a parameter — and this read requires a\nreal bearer, NEVER the write-only publishable key: pk- can attribute a write and can\nread nothing. 403 without a validated bearer, 503 when the warehouse is unreachable.","tags":["errors"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit is how many rows to return, newest first. Default 50, maximum 200; a\nvalue at or below zero, or one that is not a number, takes the default.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorList"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/esign/documents":{"get":{"operationId":"get_v1_esign_documents","summary":"Your org's documents, newest first","description":"Lists the caller org's documents with their status, recipients and timestamps, newest first, capped at 200 — there is no paging, so treat it as the recent window rather than a complete export. Requires a validated principal (403 without one) and reads the caller's own tenant store, so no other org's documents can appear in it.","tags":["esign"],"x-app":"esign"},"post":{"operationId":"post_v1_esign_documents","summary":"Upload a PDF and open a draft ready for recipients and fields","description":"Creates a document from a base64 PDF and answers 201 with it in `DRAFT` — the state where recipients and fields may still be added, and the only state they may. `title` and `pdfBase64` are required; `signingOrder` chooses `PARALLEL` (the default, everyone may sign at once) or `SEQUENTIAL`, and that choice is fixed for the document's life.\n\nThe bytes go to object storage, not into the tenant database, and the ORIGINAL is kept under its own key so it survives sealing untouched — a completed document can always be compared against what was uploaded. Creation is recorded on the audit trail.\n\nThis is the sender's door: a validated principal is required (403 without one) and the document lands in that principal's OWN org. Isolation is physical rather than a filter — each tenant has its own store — so another org's document id is simply not there. Bodies over 32 MiB are refused with 413.","tags":["esign"],"x-app":"esign"}},"/v1/esign/documents/{id}":{"get":{"operationId":"get_v1_esign_documents_by_id","summary":"One document with its recipients and field layout","description":"Answers the document, its recipients with each one's read and signing status, and every field with its type, page and position — the view a sender's UI renders, and where the field ids come from. Requires a validated principal (403 without one) and resolves the id in the caller's OWN tenant store, so another org's document id is a 404 rather than a refusal that would confirm it exists.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/documents/{id}/audit":{"get":{"operationId":"get_v1_esign_documents_by_id_audit","summary":"The document's full audit trail, oldest first","description":"Answers every recorded event for the document in order — created, recipient added, field created, sent, opened, each field inserted, each recipient completed or rejected, and completion — with the actor and timestamp on each. This is the evidence record behind a signature, so it is append-only and nothing in the surface edits it.\n\nRequires a validated principal (403 without one) and resolves the id in the caller's OWN tenant store, so another org's document id is a 404.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/documents/{id}/download":{"get":{"operationId":"get_v1_esign_documents_by_id_download","summary":"Download the document — the sealed PDF once it is complete","description":"Answers the document's current PDF as base64 with a `sealed` flag and a filename. Before completion that is the original upload; once every signer has finished it is the SEALED artifact — the field values rendered onto the page and a real x509 PKCS#7 digital signature applied — and `sealed` is true. There is one `pdfBase64` field either way, so `sealed` is what tells you which you are holding.\n\nRequires a validated principal (403 without one) and resolves the id in the caller's OWN tenant store, so another org's document id is a 404.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/documents/{id}/fields":{"post":{"operationId":"post_v1_esign_documents_by_id_fields","summary":"Place a field on the page for one recipient to fill","description":"Adds a field — a signature, date, name, email or text box — at a page and position for ONE named recipient, and answers 201 with its id. `recipientId` and a valid `type` are required, and the recipient must belong to this document (400 otherwise); page defaults to 1 and position defaults to the origin.\n\nFields are what make a recipient signable: a document cannot be sent while any signing recipient has none. Only while DRAFT — adding a field to a sent document is a 409. Requires a validated principal (403 without one), acts only on the caller's own tenant, and an unknown document is a 404. The addition is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/documents/{id}/recipients":{"post":{"operationId":"post_v1_esign_documents_by_id_recipients","summary":"Add someone to a draft and mint their signing token","description":"Adds a recipient and answers 201 with their id and their signing TOKEN — the crypto-random capability that is the only credential the signer's door accepts, so this response is where the signing link is built from. `email` is required; `role` defaults to `SIGNER`, and a `CC` recipient is recorded as already complete because they are never asked to sign. `signingOrder` sets this recipient's position for a sequential document.\n\nOnly while DRAFT: adding a recipient to a document already sent is a 409, because the field layout and the turn order were fixed when it went out. Requires a validated principal (403 without one), acts only on the caller's own tenant, and an unknown document is a 404. The addition is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/documents/{id}/send":{"post":{"operationId":"post_v1_esign_documents_by_id_send","summary":"Send the document out and get each signer's link","description":"Moves the document from `DRAFT` to `PENDING` and answers the signing tokens — one per signing recipient, with the path to hand them — which is how the links reach the people who must sign. Nothing is emailed by this call; delivering the links is the caller's.\n\nIt refuses to send an unsignable document: no recipients at all is a 400, and so is any signing recipient with no fields to fill, named in the error. Re-sending an already-pending document is allowed and re-issues the same links rather than restarting anything; a completed document is a 409. Requires a validated principal (403 without one) and acts only on the caller's own tenant; an unknown document is a 404. The send is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/health":{"get":{"operationId":"get_v1_esign_health","summary":"Whether the e-signature surface is mounted","description":"Answers ok whenever the subsystem is mounted. It is unauthenticated and takes no tenant, and it is deliberately shallow: it is registered before the document host is built, so it still answers on a deployment that came up WITHOUT object storage and therefore serves nothing else. Read it as reachability, never as a promise that documents can be stored.","tags":["esign"],"x-app":"esign"}},"/v1/esign/o/{org}/sign/{token}":{"get":{"operationId":"get_v1_esign_o_by_org_sign_by_token","summary":"Open a document you were asked to sign, using your signing link","description":"Answers the document, the recipient it identifies, the fields THAT recipient must fill, and the PDF to display. The first open also marks the recipient as having opened it and records that on the audit trail, so this read has a side effect by design.\n\nThis is the signer's door and it takes NO account: the signing token is the entire credential, and it names the recipient, so a signer sees only their own fields and never the other recipients' tokens. The `:org` segment selects which tenant's store is opened, and the token is then looked up inside it — so a token presented under the wrong org simply does not resolve. An unknown or wrong-org token is a 401, never a hint that some other document exists.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/o/{org}/sign/{token}/complete":{"post":{"operationId":"post_v1_esign_o_by_org_sign_by_token_complete","summary":"Finish signing — and seal the document if you were the last","description":"Marks this recipient as done and answers whether the DOCUMENT sealed with it. When every signing recipient has completed, sealing happens right here in the same call: the collected values are rendered onto the PDF, a real x509 PKCS#7 signature is applied, the sealed bytes are stored beside the untouched original, and the document moves to `COMPLETED`. Until then the answer is the recipient's own completion with the document still pending.\n\nIt refuses to complete a half-filled signature: a recipient with any unfilled field is a 400 naming how many remain. A document not out for signature is a 409, as is a recipient who has already completed, and under SEQUENTIAL order a signer out of turn is a 403. The token is the whole credential — no account, and a token that does not resolve under `:org` is a 401. Sealing and completion are one transaction, so a failure anywhere leaves the document exactly as it was.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/o/{org}/sign/{token}/fields/{fieldId}":{"post":{"operationId":"post_v1_esign_o_by_org_sign_by_token_fields_by_fieldid","summary":"Fill in one of your fields","description":"Records a value for one field and marks it inserted. A signature field takes `value` with `isBase64` true for drawn image bytes, or false for a typed signature; a date, name or email field falls back to today, the recipient's name or their email when `value` is omitted; any other type requires one.\n\nNothing is sealed here — filling every field still leaves the document pending until the completion call. The token is the whole credential and it bounds what can be written: a field belonging to another recipient is refused with 401 even under a valid token, an unknown field is a 404, and a field already filled is a 409. A document not out for signature is a 409, as is a recipient who has already completed or rejected. Under SEQUENTIAL order a signer whose turn has not come is refused 403 until every earlier signer has signed. Each insertion is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"fieldId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/esign/o/{org}/sign/{token}/reject":{"post":{"operationId":"post_v1_esign_o_by_org_sign_by_token_reject","summary":"Decline to sign, with an optional reason","description":"Records this recipient's refusal and moves the WHOLE DOCUMENT to `REJECTED` — one declining signer ends it for everyone, and there is no route back: the document cannot then be signed or completed. An optional `reason` is stored and written onto the audit trail with the rejection, which is what the sender sees.\n\nA document not out for signature is a 409, and so is a recipient who has already signed or already rejected — a refusal cannot be taken back or repeated. The token is the whole credential; one that does not resolve under `:org` is a 401.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"esign"}},"/v1/evals/datasets":{"get":{"operationId":"get_v1_evals_datasets","summary":"The datasets your org has","description":"Lists the caller org's datasets as `{data:[…]}`, each with its name, description, metadata and timestamps. `limit` defaults to 100 and is capped at 500; an unparseable or non-positive value falls back to the default rather than failing.\n\nRequires a validated principal; 403 without one. Every row is filtered on the validated org, so there is no parameter that reaches another tenant's datasets. The `items` count is NOT populated here — read one dataset to get it.","tags":["evals"],"x-app":"evals"},"post":{"operationId":"post_v1_evals_datasets","summary":"Create a dataset, or edit the one with that name","description":"Writes a dataset — the named set of graded examples a run scores a model against — under the caller's org and answers 201 with it. The NAME is the key, not an id: posting a name the org already has updates that dataset's description and metadata and keeps its original creation time, so this is create-or-edit and never a duplicate. Its items are untouched.\n\nRequires a validated principal; 403 without one. The org comes from the validated owner claim, never from a client `X-Org-Id`, so a dataset can only ever be written under the caller's own tenant. `name` is required and must match `^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$`; a description over 64 KiB is 400.","tags":["evals"],"x-app":"evals"}},"/v1/evals/datasets/{name}":{"delete":{"operationId":"delete_v1_evals_datasets_by_name","summary":"Delete a dataset and every example in it","description":"Removes the named dataset of the caller's org AND all of its items, in one transaction, and answers 204. This is not a detach: the examples are gone with the set, so a dataset cannot be resurrected by re-creating the name.\n\nA name this org does not have is 404 — never a silent success — and a name belonging to another tenant is the same 404, because the delete is predicated on the validated org. Requires a validated principal; 403 without one. Runs and scores already recorded against the dataset are telemetry events and are NOT deleted with it.","tags":["evals"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"evals"},"get":{"operationId":"get_v1_evals_datasets_by_name","summary":"One dataset, with how many examples it holds","description":"Returns a single dataset of the caller's org by name, together with its live item count — the one read that answers how big the set actually is. A name this org does not have is 404, which is also what another tenant's dataset looks like from here. Requires a validated principal; 403 without one.","tags":["evals"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"evals"}},"/v1/evals/datasets/{name}/items":{"get":{"operationId":"get_v1_evals_datasets_by_name_items","summary":"The examples in one of your datasets","description":"Lists the examples of ONE dataset as `{data:[…]}` — the set is named in the path, because this collection only exists inside one. Archived examples are included, so the caller sees the whole set rather than only what a run would use. `limit` defaults to 100 and is capped at 500.\n\nRequires a validated principal; 403 without one, and the read is filtered on the validated org, so naming another tenant's dataset returns nothing rather than its contents.","tags":["evals"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"evals"},"post":{"operationId":"post_v1_evals_datasets_by_name_items","summary":"Add a graded example to one of your datasets","description":"Writes one example — its `input`, its `expectedOutput`, free-form metadata and a status — into the dataset named in the path, and answers 201 with it. That dataset MUST already exist for this org: an unknown one is 404, never a silent create, so an example can never be attached to a set the caller does not own.\n\nSupply `id` to make the write idempotent — re-posting the same id replaces that example in place — or omit it and one is generated. An id that already exists in a DIFFERENT dataset is 409 rather than a move. `status` is `ACTIVE` (the default) or `ARCHIVED`; only ACTIVE examples are fed to a run, which is how an example is retired without deleting it. `input` and `expectedOutput` are stored as raw JSON exactly as sent. Requires a validated principal; 403 without one.","tags":["evals"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"evals"}},"/v1/evals/evaluators":{"get":{"operationId":"get_v1_evals_evaluators","summary":"The judges your org has defined","description":"Lists the caller org's evaluators as `{data:[…]}`, each with its judge model, criteria and the score name it writes under. `limit` defaults to 100 and is capped at 500. Requires a validated principal; 403 without one, and the listing is filtered on the validated org.","tags":["evals"],"x-app":"evals"},"post":{"operationId":"post_v1_evals_evaluators","summary":"Define a judge: a model plus the criteria it grades by","description":"Saves a reusable evaluator for the caller's org — the judge model and the written criteria it grades against — and answers 201 with it. Like a dataset, the NAME is the key: re-posting a name edits that evaluator rather than adding a second one.\n\n`scoreName` is the name the resulting scores are filed under and defaults to the evaluator's own name; both must match `^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$`. Criteria over 64 KiB is 400. Requires a validated principal; 403 without one.","tags":["evals"],"x-app":"evals"}},"/v1/evals/metrics":{"get":{"operationId":"get_v1_evals_metrics","summary":"Your org's AI overview board","description":"Returns the whole observability board for the caller's org over a window: totals (generations, prompt and completion tokens, cost in cents, errors, success rate, distinct models and users), a gap-filled time series, a per-model breakdown with the long tail folded into `other`, and latency percentiles read from the GenAI spans.\n\n`range` is `24h` (the default), `7d` or `30d`, and anything else normalises to `24h` rather than failing; `interval` overrides the bucket with `hour` or `day`. The window the answer was actually computed over is echoed back, so a client never has to infer it. A platform admin sees the board across ALL orgs; everyone else sees their own.\n\nThe board is HONEST-EMPTY where it cannot be computed: with no datastore wired, or under a named project scope the usage ledger does not yet carry, it answers a valid board with zero totals and a flat series rather than a fabricated number or a 500. Requires a validated principal; 403 without one.","tags":["evals"],"x-app":"evals"}},"/v1/evals/rubrics":{"get":{"operationId":"get_v1_evals_rubrics","summary":"The score shapes your org has declared","description":"Lists the caller org's rubrics as `{data:[…]}` — each name's data type, its numeric bounds and its allowed categories. `limit` defaults to 100 and is capped at 500. Requires a validated principal; 403 without one, and the listing is filtered on the validated org.","tags":["evals"],"x-app":"evals"},"post":{"operationId":"post_v1_evals_rubrics","summary":"Declare what a score named X is allowed to be","description":"Defines the shape of one score name for the caller's org — `NUMERIC` (the default, optionally bounded by `minValue`/`maxValue`), `CATEGORICAL` (a closed set of `categories`) or `BOOLEAN` — and answers 201 with it. The NAME is the key, so re-posting a name replaces its rules.\n\nThis is the integrity contract, not documentation: once a config exists for a name, every score recorded under that name is checked against it and the config's data type is AUTHORITATIVE — a caller cannot claim a different one. Out-of-range values, unlisted labels and non-finite numbers are refused at write time.\n\nA `CATEGORICAL` config with no categories is 400, as is a non-finite bound or a `minValue` above `maxValue`. Requires a validated principal; 403 without one.","tags":["evals"],"x-app":"evals"}},"/v1/evals/runs":{"get":{"operationId":"get_v1_evals_runs","summary":"Past runs and how they scored","description":"Lists the caller org's durable run records as `{data:[…]}` — the dataset and model, the judge model, how many examples were attempted and how many scored, the average score, and when it happened. Narrow to one dataset with `datasetName`; `limit` defaults to 100 and is capped at 500.\n\nRequires a validated principal; 403 without one, and rows are filtered on the validated org. These records come from the metastore rather than the datastore, so they are readable on a deployment with no telemetry wired — but a run's traces and scores are not.","tags":["evals"],"x-app":"evals"},"post":{"operationId":"post_v1_evals_runs","summary":"Score a dataset through a model and a judge, now","description":"Runs a real evaluation and answers the summary when it is finished — this is synchronous work, not a job id. For each ACTIVE example in the dataset it calls the model under test, records a trace, calls the LLM-as-judge, and records the judge's score with its reasoning. The answer carries the per-item results (item id, trace id, score, output or error) alongside `items`, `scored` and `avgScore`.\n\n`dataset` and `model` are required; the dataset must belong to the caller's org (404 otherwise) and must have at least one ACTIVE example (422 otherwise). `judge` is optional — omitted, the model under test grades itself against a default correctness criterion under the score name `llm-judge`. `limit` defaults to 20 and anything above 100 falls back to the default. `runName` is generated from the clock when omitted.\n\nIt runs as YOU: the caller's own `Authorization` bearer drives the model gateway, so a request without one is 401 rather than a run made anonymously or under a service identity. Only a non-reversible hash of that credential is recorded on the traces.\n\nBounded and honest about it: an org may have at most 4 runs in flight and the fifth is 429 rather than queued, and the whole run is capped at 10 minutes — items past the deadline come back with an error instead of a score, and `scored` counts only real successes. A run where NOTHING scored answers 502, not a 200 that looks like an evaluation. A run must be able to persist what it produces, so a deployment with no datastore wired is 503 up front. Requires a validated principal; 403 without one.","tags":["evals"],"x-app":"evals"}},"/v1/evals/scores":{"get":{"operationId":"get_v1_evals_scores","summary":"Score events, filtered","description":"Lists the caller org's score events as `{data:[…]}`, narrowed by any of `name`, `runName` and `traceId`; an absent filter simply does not narrow. `limit` defaults to 100 and is capped at 500.\n\nThe org is bound as an authoritative predicate on the query, never taken from a header, so a filter can narrow the caller's own scores but can never widen past them. Requires a validated principal; 403 without one. Scores live in the datastore, so a deployment with none wired answers 503 rather than an empty page that would read as 'no scores'.","tags":["evals"],"x-app":"evals"},"post":{"operationId":"post_v1_evals_scores","summary":"Record a score against a trace, a run or an example","description":"Files one score event for the caller's org and answers 201 with it. This is how human review and out-of-band graders land beside the automatic ones: name the score, give it a `value` (or a `stringValue` for a categorical label), and attach it to a `traceId`, a `runName`, a `datasetName`/`datasetItemId`, or any combination.\n\nScores are validated fail-closed. A value must be FINITE — NaN and Inf are 400 — and if the org has declared a score config for this name, that config decides the type and the value must satisfy it: inside the numeric bounds, or one of the allowed categories. A caller cannot override the declared type by sending a different `dataType`. Comments are truncated at 2000 characters.\n\nA score is TELEMETRY, not metadata, so it needs the datastore: a deployment with no datastore wired answers 503 rather than accepting a score it cannot persist. Requires a validated principal; 403 without one, and the org is stamped from the validated claim rather than read off the body.","tags":["evals"],"x-app":"evals"}},"/v1/evals/traces":{"get":{"operationId":"get_v1_evals_traces","summary":"The traces behind your evaluations","description":"Lists the caller org's traces as `{data:[…]}` — one per model call an evaluation made, carrying its input, output, model and timing — narrowed by any of `sessionId`, `runName` and `datasetName`. `limit` defaults to 100 and is capped at 500.\n\nScoped by org AND by project: the project is the caller's server-minted scope, not a parameter, so it cannot be widened by asking. Requires a validated principal; 403 without one. Traces live in the datastore, so a deployment with none wired answers 503 rather than an empty page.","tags":["evals"],"x-app":"evals"}},"/v1/event":{"post":{"operationId":"post_v1_event","summary":"Capture product events into your org's warehouse","description":"Stores pageviews, browser errors, identifies and custom commerce events as rows in the caller's own tenant, and answers a receipt {accepted, dropped} that always totals what was sent — a beacon is never silently discarded.\n\nTHE STATUS SAYS WHETHER ANYTHING LANDED, so a green check can never mean an empty warehouse. 200 means at least one event was stored (or that nothing was sent), and a nonzero `dropped` beside a nonzero `accepted` is a PARTIAL batch, never a failed one — a batch is not refused whole for its worst element. If NOTHING was stored the request is an error, and it names the one thing that fixes it: 401 `ingest_key_required` when every event was refused for want of a credential (the same events land with a key), and 400 `unroutable_events` when the caller HAD capability and the body still named nothing storable.\n\nONE door for every wire a Hanzo surface emits, dispatched by the SHAPE of the body and never by a second path: a bare event object, a bare array of them, the {batch:[…]} / {events:[…]} envelope, the team console's snake_case array, and the PostHog wire (spelled `distinct_id`/`api_key`, which the canonical wire never uses). BATCH IS A BODY, NOT A PATH — there is no /v1/event/batch, because an array already is one.\n\nWHAT THE CALLER PRESENTS DECIDES WHAT IT MAY WRITE, and the door itself grants nothing. A validated bearer or an org API key writes the full event at full fidelity. A PUBLISHABLE key (pk-, on Authorization: Bearer, x-hanzo-ingest-key, or ?ingest_key= for navigator.sendBeacon, which cannot set headers) does the same, and is the credential a browser bundle ships: it is deliberately NOT a secret, it resolves WHICH tenant a beacon belongs to and nothing more. A pk- never authenticates and can READ NOTHING — not this org's errors, not a lens, not any other route on this API — so a leaked one lets a stranger write into your stream, and never lets one read out of it. Reading these rows back always takes a real bearer. A Hanzo Team workspace token resolves its org at REDUCED capability: the signed account names the person, so a `distinctId` in the body cannot pin events on a colleague.\n\nNO CREDENTIAL IS REFUSED: a write the server cannot attribute to a project is 401 `ingest_key_required`, and a credential that IS presented but resolves to no project is 403 `ingest_key_unknown`. Nothing is filed under a shared tenant — events nobody can read are worse than events nobody sent, because the caller is told it succeeded. A browser bundle therefore always ships a pk-, which is what /v1/event.js takes.\n\nA REDUCED principal — a Hanzo Team workspace token — writes through the PROJECTION into its own org: narrowed to what the SERVER can name (pageviews and errors, plus the closed autocapture vocabulary $click, $input, $change, $submit, $view), where every one of those names is resolved through a server-owned table and stored as that table's value, so the name on the wire is never the name in the row. Stripped, too, to the fields the projection names, so revenue, personId, groupId and every property but the element annotation cannot reach a row — and an exception is carried only on an error, never on an interaction, so a click cannot ship a stack trace into a row's attributes. It does NOT name the person: the signed account is the identity, so a `distinctId` in the body cannot pin events on a colleague. Everything refused is counted in `dropped`.\n\nThe projected lane alone is bounded: 413 over 64 KiB, 400 over 50 events, 429 on the per-client-IP and per-peer caps, and a DNT:1 or Sec-GPC:1 request stores nothing and says so in the receipt. Two stored values carry their own bounds on top, because a request cap does not bound one value: an element annotation over 2 KiB (or a trail over 32 steps) and an exception class over 256 bytes are dropped from the row, which still lands. Authenticated bodies are offered to the observability plane first, which claims LLM-observability ingestion batches and declines everything else.","tags":["event"],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Event"},{"items":{"$ref":"#/components/schemas/Event"},"type":"array"},{"$ref":"#/components/schemas/CaptureBatch"},{"$ref":"#/components/schemas/insightsBody"}]}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureResult"}}},"description":"Success"}},"x-app":"analytics"}},"/v1/event.js":{"get":{"operationId":"get_v1_event.js","summary":"The Hanzo event tag — the one-line install for a surface with no bundler","description":"Serves the browser tag that autocaptures pageviews (initial and SPA) and uncaught errors onto the canonical wire at POST /v1/event.\n\nInstall is one line, and it is the same line for a Hanzo property and for a customer's own page:\n\n    \u003cscript defer src=\"https://api.hanzo.ai/v1/event.js\" data-key=\"pk-…\"\u003e\u003c/script\u003e\n\n`data-key` is the publishable key the project mints; `data-product` optionally names the emitting surface. The key may also ride the src as `?key=` for a host that strips data attributes.\n\nWITHOUT A KEY THE TAG SENDS NOTHING. A keyless beacon is accepted 200 into $public, a reserved tenant the owning org cannot read — so silence is the honest failure, and the tag picks it rather than reporting success into a tenant nobody reads.","responses":{"2XX":{"content":{"application/javascript":{"schema":{"format":"binary","type":"string"}}},"description":"Success"}},"x-app":"analytics"}},"/v1/event/{project}/envelope":{"post":{"operationId":"post_v1_event_by_project_envelope","summary":"Sentry SDK envelope ingest — errors and traces from an unmodified Sentry client","description":"Accepts the CURRENT Sentry wire — the framed envelope a modern SDK posts, carrying its items in one request — so an application already instrumented with Sentry reports into Hanzo's error tracking by pointing its DSN here and changing nothing else.\n\nCLOUD ROUTES IT AND READS NONE OF IT. The body is relayed byte-for-byte to the observability plane, which parses the wire, verifies the credential and answers; this door declares no response shape because it does not know one. A deployment with no observability plane mounted answers 503.\n\nTHE CREDENTIAL IS A SENTRY DSN KEY, NOT A HANZO PRINCIPAL. This is one of the few writes on the platform that carries no bearer and no org header by design — a Sentry SDK has neither — and it is exempt from the principal gate for that reason. The observability plane verifies the DSN key itself, fail-closed: a request without a valid one is refused there, never admitted here. Presenting a Hanzo bearer instead does nothing.\n\n`project` IS THE DSN'S PROJECT ID — the identifier in the DSN the SDK was configured with, and what the tenant is derived from. It is NOT a Hanzo IAM project and NOT a tracker project key. Only these two ingest paths map through: no observability READ API is reachable by any other suffix under this prefix.","tags":["event"],"parameters":[{"name":"project","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"analytics"}},"/v1/event/{project}/store":{"post":{"operationId":"post_v1_event_by_project_store","summary":"Sentry SDK store ingest — the legacy single-event wire","description":"Accepts the LEGACY Sentry wire: one event per request, what an SDK predating envelopes sends. Same door, same credential, same destination as the envelope endpoint — kept open so an old client reports without being upgraded first. New instrumentation has no reason to choose it.\n\nCLOUD ROUTES IT AND READS NONE OF IT. The body is relayed byte-for-byte to the observability plane, which parses the wire, verifies the credential and answers; this door declares no response shape because it does not know one. A deployment with no observability plane mounted answers 503.\n\nTHE CREDENTIAL IS A SENTRY DSN KEY, NOT A HANZO PRINCIPAL. This is one of the few writes on the platform that carries no bearer and no org header by design — a Sentry SDK has neither — and it is exempt from the principal gate for that reason. The observability plane verifies the DSN key itself, fail-closed: a request without a valid one is refused there, never admitted here. Presenting a Hanzo bearer instead does nothing.\n\n`project` IS THE DSN'S PROJECT ID — the identifier in the DSN the SDK was configured with, and what the tenant is derived from. It is NOT a Hanzo IAM project and NOT a tracker project key. Only these two ingest paths map through: no observability READ API is reachable by any other suffix under this prefix.","tags":["event"],"parameters":[{"name":"project","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"analytics"}},"/v1/exec":{"post":{"operationId":"post_v1_exec","summary":"Run a code snippet in a sandboxed interpreter","description":"Executes a program in a throwaway sandbox and answers with what it printed\nand what it left behind.\n\n`lang` names one of the thirteen the sandbox image carries — py, js, ts, bash, r,\nphp, go, rs, c, cpp, java, d, f90 — and `code` is the whole program, not a\nfragment: a compiled language is compiled and then run, an interpreted one is\ninterpreted, and `args` becomes the program's own argv either way. Nothing is\ninstalled for you; the image is the environment.\n\nA PROGRAM THAT FAILS IS A SUCCESSFUL CALL. A non-zero exit answers 200 with the\ndiagnostics on `stderr`, because \"the code threw\" and \"the interpreter is down\"\nare different facts a caller renders differently. Only the second is an error\nstatus.\n\nRuns are stateful through `session_id`. Omit it and the run gets a fresh sandbox\nwhose id comes back on the answer; pass that id again and the next run sees the\nsame filesystem, so a program can write a file one call and read it the next.\n`files` names bytes already uploaded to a session (POST /v1/upload), copied in\nbefore the program starts. `files` on the ANSWER is what the program created or\nchanged, by comparison against a marker taken at start — so it is the run's real\noutput, not a listing of the directory — and each is fetched from\nGET /v1/download/{session}/{name}.\n\nThe tenant is the caller's, never the body's, at every door. A typed op is also\nan MCP tool and an op-plane op; MCP's tools/call invokes it directly, with no\nroute and therefore no middleware, so nothing there could have checked a\ncredential. tenantOf refuses a context carrying neither a validated principal nor\nexec's own admission marker, so those doors fail closed without a second gate to\nkeep in step.","tags":["exec"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeRun"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeResult"}}},"description":"ok"}},"x-app":"exec"}},"/v1/exec/programmatic":{"post":{"operationId":"post_v1_exec_programmatic","summary":"Programmatic tool calling (not served here)","description":"Answers 501. This address belongs to a DIFFERENT protocol from /v1/exec: the server suspends a program on each tool call, returns the pending calls with a continuation token, and resumes when the client posts results back. Serving it means implementing suspension and resumption, so it refuses in the open rather than answering with a shape the caller's parser cannot read.","tags":["exec"],"x-app":"exec"}},"/v1/experiments":{"get":{"operationId":"get_v1_experiments","summary":"Every experiment in the caller's org, with its variants, status and decision.","description":"Returns {data, total} ordered by project then id. Scoped to the org resolved from the validated principal — a distinct org is a distinct physical store, so no query here can reach another tenant's rows — and further narrowed to the caller's project scope when the credential carries one. A principal with NO project scope sees the org's experiments across all of its projects, which is the answer a reader most often expects to be filtered and is not.\n\nRequires a validated principal; refuses without one rather than answering an empty list.","tags":["experiments"],"x-app":"experiments"},"post":{"operationId":"post_v1_experiments","summary":"Create a controlled experiment and put its assignment flag live.","description":"Registers the experiment AND writes its multivariate assignment flag, in that order, so the arms start bucketing subjects the moment this returns 201 — the flag is created active at 100% rollout, with each variant weighted as declared. There is no separate start call; creating IS starting.\n\nThe body names the experiment (`id`, a slug that is claimed once), what it measures (`metricEvent`, required; `exposureEvent` defaults to the SDK's `$feature_flag_called` marker), the unit it assigns (`subjectKind`: user, org, session or audience — user by default), and at least two `variants`. A variant carries an opaque `payload` this primitive never interprets: a feature config, an ad-creative id, a subject line, a model id. Weights that are all zero become an even split; otherwise they must sum to 100. At most one variant may be flagged `control`; with none, the first arm is the baseline. `flagKey` defaults to `exp_\u003cid\u003e`.\n\nRequires a validated principal, and refuses without one. The org and project are taken from that principal and the creator is stamped from the credential — none of the three is a body field, so an experiment cannot be filed against another tenant. An id already used in this project is a conflict, never a silent overwrite: re-creating would stomp the assignment flag of a run in progress.\n\nIt fails closed on the flag write. An experiment whose assignment flag does not exist would assign nobody, so if that write fails nothing is registered.","tags":["experiments"],"x-app":"experiments"}},"/v1/experiments/health":{"get":{"operationId":"get_v1_experiments_health","summary":"Whether the experiments subsystem is mounted and serving in this process.","description":"Answers {\"ok\":true,\"subsystem\":\"experiments\"} unconditionally. It proves exactly one thing — that this binary registered the experiments routes and is dispatching them — and deliberately no more: it reads no principal, opens no per-org registry, and touches neither the flags engine nor the analytics plane, so a 200 here says nothing about whether a given tenant's store will open or whether an analysis can run. It is the only route on this surface that needs no org.\n\nThe static path is registered ahead of the /:id read, so it always wins the first-match scan. `health` is a legal experiment id, which means an experiment created under that id can never be fetched by id — pick another.","tags":["experiments"],"x-app":"experiments"}},"/v1/experiments/{id}":{"get":{"operationId":"get_v1_experiments_by_id","summary":"One experiment's definition and lifecycle: variants, weights, control arm, status and winner.","description":"Reads the registry row only — the definition and the decision, never live measurements. Assignment lives in the flags plane and outcomes in analytics; this is the value that names both.\n\nScoped to the caller's org and project from the validated principal, so another tenant's experiment of the same id is simply not found. An id that is not a legal slug is answered the same way, without a store read — the shape check and the existence check are one answer, so neither leaks the other.","tags":["experiments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"experiments"}},"/v1/experiments/{id}/analyze":{"post":{"operationId":"post_v1_experiments_by_id_analyze","summary":"Per-variant conversion, lift and statistical significance against the control arm.","description":"Reads per-subject outcomes from the analytics plane over a window, folds them into per-variant samples, and returns each arm's exposed count, conversions, rate, lift versus control, two-proportion z, two-tailed p-value and whether it clears alpha. Arms with no data still appear with zero exposed, so the read is complete over the experiment's declared arms; the control arm sorts first. The pooled-variance estimator is used and the p-value is exact; a degenerate comparison (an empty arm, no variance) answers z 0 and p 1 — not significant, never an error.\n\nThe window is `start`/`end` in RFC3339 if given, otherwise the last `days` (1 to 365, 30 by default) up to now. `alpha` overrides the 0.05 two-tailed threshold when it lies strictly between 0 and 1; anything else leaves the default in place.\n\nOnly EXPOSED subjects are counted, and each is joined to its arm by re-evaluating the assignment flag AT ANALYSIS TIME — not from what was in force during the window. That is the one rule to get right: analyzing an experiment after its winner has been promoted re-buckets every subject into the promoted arm, collapsing the control to zero exposed and making the result meaningless. Read the analysis before deciding. A subject the flag cannot place is dropped rather than allowed to poison the fold.\n\n`winner` in the response is ADVISORY — the significant, control-beating arm with the highest rate, or empty when inconclusive. It promotes nothing; the decision is a separate, explicit act.\n\nEvery plane read is scoped to the caller's org. Per-variant samples are also written to the research evidence plane as immutable `ab` rows, best-effort: the analysis is still returned if that write fails, because the samples are recomputable, and the failure is logged rather than swallowed.","tags":["experiments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"experiments"}},"/v1/experiments/{id}/assign":{"get":{"operationId":"get_v1_experiments_by_id_assign","summary":"The variant one subject is bucketed into, and the payload that variant carries.","description":"Evaluates the experiment's assignment flag for the `subject` in the query and answers {experiment, subject, variant, on, payload}. The bucketing is a deterministic hash of the subject, so the same subject gets the same arm on every call for as long as the flag definition is unchanged — and this is a pure READ: it records nothing. In particular it does NOT record an exposure. The caller's SDK must emit the experiment's exposure event itself, or the analysis has an empty denominator and every arm measures zero.\n\n`subject` is required. `props` may carry a JSON object of person properties for targeting; a `props` value that is not valid JSON is dropped silently rather than refused, so a malformed one changes the bucketing without saying so.\n\nAn empty `variant` with `on` false is not an error — it means the flag returned nothing for this subject, so the subject is not enrolled. A flags engine that is unavailable refuses rather than defaulting to an arm. Requires a validated principal, and the experiment must exist in the caller's org and project.","tags":["experiments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"experiments"}},"/v1/experiments/{id}/decide":{"post":{"operationId":"post_v1_experiments_by_id_decide","summary":"Promote one variant to the whole rollout and record who decided.","description":"Rewrites the assignment flag so the named `winner` serves 100% of the rollout and every other arm 0%, preserving the flag's targeting groups and payloads, then stamps the experiment decided with the winner, the deciding credential and the time. This is a production behaviour change that takes effect immediately for every subject the flag evaluates.\n\nRequires an ORG ADMIN of the caller's own org — a stricter gate than the rest of this surface, matching the flags write plane, because promoting is a flag write. The admin check runs AFTER the experiment is found, so a caller from another tenant is answered not-found rather than forbidden and learns nothing about what exists.\n\n`winner` is required and must name one of the experiment's own variants. An experiment whose assignment flag has gone missing is a conflict rather than a silent no-op — there is nothing to promote.\n\nDeciding is NOT terminal. A second call re-promotes a different variant and re-stamps the row; the status stays decided and the previous winner is overwritten with no record that it was ever chosen. Nothing here reverts the flag to its original weights either, so an experiment cannot be un-decided through this route — restoring a split means writing the flag definition back through the flags plane.","tags":["experiments"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"experiments"}},"/v1/feedback":{"post":{"operationId":"post_v1_feedback","summary":"Attaches a per-request outcome reward to the routing decision that served request_id — the enso training loop's quality signal.","description":"Attaches a per-request outcome reward to the routing decision\nthat served request_id — the enso training loop's quality signal. Org-scoped via\nthe same session-OR-Bearer principal the usage read uses (RequirePrincipal): the\nreward lands only on the caller's OWN org's event, so a request_id from another\norg (or unknown) is a 404 — cross-org writes are impossible and unknown ids are\nindistinguishable from foreign ones. Idempotent: a repeat overwrites. The body\ncarries NO prompt text — only {request_id, reward|rating}.","tags":["feedback"],"x-app":"github.com/hanzoai/ai"}},"/v1/files/{sid}":{"get":{"operationId":"get_v1_files_by_sid","summary":"List the files in an execution session","description":"Lists what a session's sandbox holds — the uploads a run can read and the artifacts it produced — each then fetched from /v1/download.\n\nIt answers a BARE JSON ARRAY of {name, lastModified}, where `name` is the same {session_id}/{fileId} identifier download takes, because that is what the client matches on. An object wrapper would be a wire change, which is why this is not a typed operation.","tags":["files"],"parameters":[{"name":"sid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"exec"}},"/v1/finance/accounts":{"get":{"operationId":"get_v1_finance_accounts","summary":"Returns the ledger accounts the caller may see, with their balances.","description":"Returns the ledger accounts the caller may see, with their\nbalances. It is tenant-isolated SERVER-SIDE: an ordinary caller sees ONLY\naccounts under its own \"org:\u003ctenant\u003e:\" prefix, never house accounts and never\nanother tenant's. A SuperAdmin may widen with ?scope=house (the reserve,\nrevenue and payout house accounts) or ?org=\u003ctenant\u003e — the only way to cross the\ntenant boundary, and only for platform sudo. The answer is honestly empty until\na tenant has ledger postings.","tags":["finance"],"parameters":[{"name":"scope","in":"query","required":false,"description":"Scope is \"house\" to read the reserve/revenue/payout house accounts. SuperAdmin only.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org names another tenant to read. SuperAdmin only; ignored when scope=house.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accountsOut"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/finance/balance":{"get":{"operationId":"get_v1_finance_balance","summary":"Answers the org's spendable prepaid balance typed for the finance surfaces: `availableCents`, `pendingCents`, `dueCents` and the `asOf` instant it was read.","description":"Answers the org's spendable prepaid balance typed for the\nfinance surfaces: `availableCents`, `pendingCents`, `dueCents` and the `asOf`\ninstant it was read.\n\nIt is the SAME wallet read /v1/billing/balance answers — one function, called\nby both, so the two surfaces cannot drift into disagreeing about a customer's\nmoney. Reshaped, never re-metered. Co-resident the number comes straight out\nof the org's own double-entry ledger file.\n\n`dueCents` is a structural 0: this is a PREPAID wallet with no open-invoice\ndebt, so nothing is ever owed and a non-zero value here would be an invention.\n`pendingCents` is 0 on the co-resident ledger, where authorization holds are\nnever posted; only a split-deploy upstream reports holds, and there spendable\nis the balance NET of them, floored at 0 — a fully-held wallet reports 0\nrather than money the gate would refuse.\n\nCents are ROUNDED from the ledger's exact 18-decimal USD. Scoped to the\ncaller's own org from the validated IAM owner claim; 401 without a validated\nprincipal, and a balance that cannot be read is 502 — never 0, because unknown\nis not broke.","tags":["finance"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/financeBalanceView"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/balance.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finance/credits":{"get":{"operationId":"get_v1_finance_credits","summary":"Answers the money PUT IN to the org's wallet — each staff grant, promo and settled top-up as a positive row with its id, label, cents and grant time.","description":"Answers the money PUT IN to the org's wallet — each staff\ngrant, promo and settled top-up as a positive row with its id, label, cents\nand grant time.\n\nSpend is not a credit. A posting counts here only when it moved money IN;\ndebits belong to /v1/finance/usage (aggregated) and /v1/finance/ledger\n(signed). All three project ONE read of the same ledger through ONE vocabulary\nfor what a posting means, so they cannot disagree about a row — nor silently\ndrop one, which is what an empty credits page against a funded wallet was.\n\n`label` falls back through the posting's notes, then its tags, then a bare\nCredit — it is a description, never an identifier. `remainingCents` is\nOMITTED: the wallet is one running balance, not per-grant buckets, so no grant\nhas a remainder to report and spend cannot be attributed to the credit that\nfunded it.\n\nCents are ROUNDED from the ledger's exact 18-decimal USD. Scoped to the\ncaller's own org; 401 without a validated principal. An org with no grants\ngets an empty array — honest, never a fabricated figure.","tags":["finance"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/financeCredit"},"type":"array"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/credits.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finance/invoices":{"get":{"operationId":"get_v1_finance_invoices","summary":"Answers an empty typed array, always.","description":"Answers an empty typed array, always. The fleet bills a\nPREPAID wallet — money in, metered debits out — and issues no customer\ninvoices, so there is no invoice ledger to project. Nothing here is a\nfabricated figure and nothing is hidden behind a filter.\n\nThe shape is fixed, so the finance UI renders this lane today and the day an\ninvoice ledger exists it fills with ZERO client change. Spend that actually\nhappened is /v1/finance/usage; money in and out is /v1/finance/ledger; what is\nleft to spend is /v1/finance/balance.\n\nThe gate is real even though the body is empty: 401 without a validated\nprincipal. It is the only finance read that touches no store, so it is also\nthe only one that cannot 502.","tags":["finance"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/financeInvoice"},"type":"array"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/invoices.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finance/ledger":{"get":{"operationId":"get_v1_finance_ledger","summary":"Answers the org's own postings inside `range=`, each as a signed entry: a DEPOSIT CREDITS the wallet (positive, account `credits:\u003corg\u003e`) and every other posting DEBITS it (negative, account `usage:\u003corg\u003e`), described by its notes or its tags.","description":"Answers the org's own postings inside `range=`, each as a signed\nentry: a DEPOSIT CREDITS the wallet (positive, account `credits:\u003corg\u003e`) and\nevery other posting DEBITS it (negative, account `usage:\u003corg\u003e`), described by\nits notes or its tags. The sign is the posting's own meaning, read through ONE\nvocabulary shared with the ledger that wrote it — a reader with its own\nspelling for `deposit` rendered a customer's grant as a charge.\n\nThis is the closest projection of the truth. The org's double-entry postings\nare the source of record — balanced, only ever appended, one file per org —\nand this lane is that list, widest of the three: /v1/finance/credits is its\ndeposit half and /v1/finance/usage is its withdrawal half rolled up. All three\ncome from ONE read, which is why they cannot contradict each other, and all\nthree answer 501 where no commerce link is configured rather than reporting an\nempty wallet.\n\nA row whose timestamp will not parse is KEPT rather than dropped — a malformed\ndate must show up in a money list, not vanish from it. `balanceCents` is\nomitted: these are MOVEMENTS, and the standing balance is /v1/finance/balance.\n\nCents are ROUNDED from the ledger's exact 18-decimal USD. Scoped to the\ncaller's own org, where the org's ledger file is the tenant boundary; 401\nwithout a validated principal.","tags":["finance"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the window: 24h, 7d, 30d or 90d. Anything else — including\nabsent — is 30d, so a typo silently widens the window to a month rather\nthan failing.","schema":{"type":"string"},"example":"30d"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/financeLedgerEntry"},"type":"array"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/ledger.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finance/payment-methods":{"get":{"operationId":"get_v1_finance_payment-methods","summary":"Answers the masked card descriptors for the caller's resolved WALLET — id, brand, last four, expiry, default flag — reshaped into the finance contract.","description":"Answers the masked card descriptors for the caller's resolved\nWALLET — id, brand, last four, expiry, default flag — reshaped into the\nfinance contract.\n\nIt re-masks defensively: whatever the upstream sends, at most the trailing\nfour DIGITS survive into `last4`. No card number, no security code and no\nprocessor token exists in this shape at all, so an over-returning upstream\nstill cannot leak one through this lane.\n\nRead the sibling difference before trusting a mismatch. This keys the store on\nthe resolved wallet; /v1/billing/methods keys it on the org SLUG, which is\nalso the key a card is SAVED under — identical for an org paying from its\nshared pool, different wherever the payer is a person. When the two lists\ndisagree, the billing one is what was saved.\n\n401 without a validated principal. An upstream that answers non-2xx or cannot\nbe reached is 502 — never an empty list, because no cards and could not ask\nmust not look alike.","tags":["finance"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/financePaymentMethod"},"type":"array"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/payment-methods.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finance/treasury":{"get":{"operationId":"get_v1_finance_treasury","summary":"Returns the reserve fund's health and the current revenue-share policy for any validated caller.","description":"Returns the reserve fund's health and the current revenue-share\npolicy for any validated caller. It is a TRANSPARENCY view — a partner or\nauthor can see that the pool backing their payouts is solvent — and NOT per-org\nmoney, which is the customer's own commerce balance at /v1/billing/balance. The\npolicy is read-only here; only a SuperAdmin sets it.","tags":["finance"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreasuryReport"}}},"description":"ok"}},"x-app":"treasury"}},"/v1/finance/usage":{"get":{"operationId":"get_v1_finance_usage","summary":"Answers metered spend inside `range=`: the window total, a time series to plot, and one line per usage TAG.","description":"Answers metered spend inside `range=`: the window total, a time\nseries to plot, and one line per usage TAG. Aggregated from the same charged\nledger the balance comes off — projected, never re-metered.\n\nOnly DEBIT postings count; deposits are credits and are excluded. Buckets are\nhourly at 24h and daily otherwise, in UTC; a posting whose timestamp will not\nparse is dropped rather than mis-bucketed.\n\nLines group by the posting's tag (`Usage` where it carries none) and `units`\ncounts POSTINGS, not tokens. The dimensions here are time and tag. For\nper-request rows and a per-PRODUCT breakdown, read /v1/billing/usage instead —\nthe same money, cut a different way.\n\nCents are ROUNDED from the ledger's exact 18-decimal USD, so a window made of\nsub-cent token calls totals LOW here. Scoped to the caller's own org; 401\nwithout a validated principal.","tags":["finance"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the window: 24h, 7d, 30d or 90d. Anything else — including\nabsent — is 30d, so a typo silently widens the window to a month rather\nthan failing.","schema":{"type":"string"},"example":"24h"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/financeUsageView"}}},"description":"ok","headers":{"Cache-Control":{"description":"Set by GET /v1/finance/usage.","schema":{"type":"string"}}}}},"x-app":"billing"}},"/v1/finetune/cancel":{"post":{"operationId":"post_v1_finetune_cancel","summary":"Deletes the TrainJob CR, meters the GPU-hours used so far, and marks the job cancelled.","description":"Deletes the TrainJob CR, meters the GPU-hours used so far, and\nmarks the job cancelled. ?id= or ?name=","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/deploy":{"post":{"operationId":"post_v1_finetune_deploy","summary":"Serves a completed job's checkpoints and registers the result as a routable model on api.hanzo.ai.","description":"Serves a completed job's checkpoints and registers the result\nas a routable model on api.hanzo.ai. ?id= or ?name=","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/hf/datasets":{"get":{"operationId":"get_v1_finetune_hf_datasets","summary":"Proxies a HuggingFace dataset search (dataset picker).","description":"Proxies a HuggingFace dataset search (dataset picker).","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/hf/models":{"get":{"operationId":"get_v1_finetune_hf_models","summary":"Proxies a HuggingFace model search (base-model picker).","description":"Proxies a HuggingFace model search (base-model picker).","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/hf/repo":{"get":{"operationId":"get_v1_finetune_hf_repo","summary":"Returns a repo's detail (files, gated/private state).","description":"Returns a repo's detail (files, gated/private state). ?id=\u0026kind=model|dataset","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/job":{"get":{"operationId":"get_v1_finetune_job","summary":"Returns one job with refreshed live status.","description":"Returns one job with refreshed live status. ?id=owner/name or ?name=","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/jobs":{"get":{"operationId":"get_v1_finetune_jobs","summary":"Returns the org's jobs, refreshing live status for active ones.","description":"Returns the org's jobs, refreshing live status for active ones.","tags":["finetune"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_finetune_jobs","summary":"Validates the request, resolves efficient defaults, persists the job, and submits a real TrainJob CR.","description":"Validates the request, resolves efficient defaults, persists the\njob, and submits a real TrainJob CR. A submit failure (e.g. no cluster wired) is\nsurfaced honestly: the job is saved with status \"failed\" + the reason, never faked.","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/finetune/presets":{"get":{"operationId":"get_v1_finetune_presets","summary":"Returns the new-job catalog plus, when a selection is passed (?baseModel\u0026method\u0026task\u0026preset[\u0026datasetExamples]), the recommended config so the console can render \"Recommended\" as a one-click, ready-to-run default.","description":"Returns the new-job catalog plus, when a selection is passed\n(?baseModel\u0026method\u0026task\u0026preset[\u0026datasetExamples]), the recommended config so the\nconsole can render \"Recommended\" as a one-click, ready-to-run default.","tags":["finetune"],"x-app":"github.com/hanzoai/ai"}},"/v1/flags":{"post":{"operationId":"post_v1_flags","summary":"Evaluate runs the caller's flag definitions for one identity and returns the flag verdict: which flags are on (or which variant), their payloads, and whether any definition failed to compute.","description":"Evaluate runs the caller's flag definitions for one identity and returns the\nflag verdict: which flags are on (or which variant), their payloads,\nand whether any definition failed to compute. Evaluation is in-process over the\ncaller's own (org, project) definitions — no network hop, no shared KV — so a\ntenant can only ever evaluate its own flags.","tags":["flags"],"requestBody":{"content":{"application/json":{"example":{"distinct_id":"u1","groups":{"0":{"key":"acme"}},"person_properties":{"plan":"pro"}},"schema":{"$ref":"#/components/schemas/evaluateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"errorsWhileComputingFlags":false,"featureFlagPayloads":{},"featureFlags":{"new-editor":true}},"schema":{}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/activity":{"get":{"operationId":"get_v1_flags_activity","summary":"Returns the caller's flag change log newest-first: every create, update and delete, with the actor and the time.","description":"Returns the caller's flag change log newest-first: every\ncreate, update and delete, with the actor and the time.","tags":["flags"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. 1–500; anything else takes the default 100.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/activityOut"}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/decide":{"post":{"operationId":"post_v1_flags_decide","summary":"Evaluate runs the caller's flag definitions for one identity and returns the flag verdict: which flags are on (or which variant), their payloads, and whether any definition failed to compute.","description":"Evaluate runs the caller's flag definitions for one identity and returns the\nflag verdict: which flags are on (or which variant), their payloads,\nand whether any definition failed to compute. Evaluation is in-process over the\ncaller's own (org, project) definitions — no network hop, no shared KV — so a\ntenant can only ever evaluate its own flags.","tags":["flags"],"requestBody":{"content":{"application/json":{"example":{"distinct_id":"u1","groups":{"0":{"key":"acme"}},"person_properties":{"plan":"pro"}},"schema":{"$ref":"#/components/schemas/evaluateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"errorsWhileComputingFlags":false,"featureFlagPayloads":{},"featureFlags":{"new-editor":true}},"schema":{}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/defs":{"get":{"operationId":"get_v1_flags_defs","summary":"Returns every flag definition in the caller's (org, project) store, by key, with its version and who last changed it.","description":"Returns every flag definition in the caller's (org,\nproject) store, by key, with its version and who last changed it.","tags":["flags"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/defsOut"}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/defs/{key}":{"delete":{"operationId":"delete_v1_flags_defs_by_key","summary":"Removes one flag definition by key and records the deletion in the change log.","description":"Removes one flag definition by key and records the\ndeletion in the change log. A key the caller's store does not hold is a 404.","tags":["flags"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the flag key to act on, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deletedOut"}}},"description":"ok"}},"x-app":"flags"},"get":{"operationId":"get_v1_flags_defs_by_key","summary":"Returns one flag definition by key, or 404 when the caller's store has none under that key.","description":"Returns one flag definition by key, or 404 when the caller's\nstore has none under that key.","tags":["flags"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the flag key to act on, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefRow"}}},"description":"ok"}},"x-app":"flags"},"put":{"operationId":"put_v1_flags_defs_by_key","summary":"Creates or replaces the flag definition at the path's key and returns the stored row.","description":"Creates or replaces the flag definition at the path's key and\nreturns the stored row. The BODY IS THE DEFINITION DOCUMENT — the flag-definition\nJSON object the evaluator consumes — and it is stored verbatim except that its\n\"key\" is forced to the key in the URL, so a document can never be filed under a\nname other than the one it was addressed by. Every write bumps the version and\nappends to the change log under the caller's identity.","tags":["flags"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the flag key to write, from the path.","schema":{"type":"string"},"example":"new-editor"}],"requestBody":{"content":{"application/json":{"example":{"active":true,"filters":{"groups":[{"rollout_percentage":25}]},"key":"new-editor"},"schema":{}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DefRow"}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/health":{"get":{"operationId":"get_v1_flags_health","summary":"Health reports that the flag engine is serving.","description":"Health reports that the flag engine is serving. It is not gated: liveness must\nbe probe-able without a token.","tags":["flags"],"responses":{"200":{"content":{"application/json":{"example":{"engine":"hanzo-flags","ok":true},"schema":{"$ref":"#/components/schemas/healthOut"}}},"description":"ok"}},"x-app":"flags"}},"/v1/flags/waitlist":{"get":{"operationId":"get_v1_flags_waitlist","summary":"Reports whether ONE host is currently gated by the launch waitlist.","description":"Reports whether ONE host is currently gated by the launch waitlist.\nIt resolves the host to the service that governs it and reads that service's\nwaitlist switch, so a guard sitting in front of a hosted surface can decide in one\ncall whether to show the waitlist or the product. It answers for the ONE host\nasked about and never enumerates the registry, which is why it needs no\ncredential. It FAILS OPEN: an unregistered host, an unmounted registry and a store\nfault all answer known=false with mode=false, so a request is never gated pre-boot\nor on a registry fault.","tags":["flags"],"parameters":[{"name":"host","in":"query","required":false,"description":"Host is the host to resolve, e.g. \"chat.hanzo.ai\". Defaults to the request's\nown Host header when omitted, which is what lets a guard running on the\ngoverned host ask about itself with no argument.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/waitlistModeView"}}},"description":"ok"}},"x-app":"admission"}},"/v1/fleet":{"get":{"operationId":"listFleet","summary":"Returns every compute unit the caller's org has, from every source, each carrying its latest utilization: agent run-targets, the BYO machines that dialed in, attached BYO clusters and Visor-provisioned machines.","description":"Returns every compute unit the caller's org has, from every source, each\ncarrying its latest utilization: agent run-targets, the BYO machines that dialed\nin, attached BYO clusters and Visor-provisioned machines.\n\nA unit with a live snapshot of its own keeps it; the rest are overlaid from the\nutilization series, and only when the sample agrees about the SOURCE — two planes\ncould mint the same unit id, and a board must never show one machine's load on\nanother's row. BYO GPU units also carry their gpu-jobs queue depth. Every source\nis folded in independently: a broken one costs its own rows and nothing else.","tags":["fleet"],"responses":{"200":{"content":{"application/json":{"example":{"units":[{"host":"spark","kind":"worker","label":"spark","metrics":{"at":"2026-07-27T09:00:00Z","gpuUtil":0.42},"queued":2,"running":1,"sessions":0,"source":"byo","spec":{"arch":"arm64","cpus":20,"gpuModel":"NVIDIA GB10","gpus":1,"os":"linux"},"status":"online","unit":"spark"}]},"schema":{"$ref":"#/components/schemas/fleetBoard"}}},"description":"ok"}},"x-app":"visor"}},"/v1/fleet/jobs":{"get":{"operationId":"listFleetJobs","summary":"Returns the caller org's gpu-jobs render queue, each row tagged with the GPU it targets (empty = the shared any-GPU lane) and the node claiming it, optionally narrowed to one GPU's queue and/or one status.","description":"Returns the caller org's gpu-jobs render queue, each row tagged with\nthe GPU it targets (empty = the shared any-GPU lane) and the node claiming it,\noptionally narrowed to one GPU's queue and/or one status.\n\nA job whose worker died — STARTED with an elapsed lease and not yet reclaimed —\nreads \"stalled\", not \"running\". Fail-soft: an unavailable tasks engine yields an\nempty queue rather than an error.","tags":["fleet"],"parameters":[{"name":"gpu","in":"query","required":false,"description":"GPU selects one node's lane: jobs TARGETED at it (gpu:\u003cnode\u003e) or CLAIMED by\nit. The literal \"shared\" selects the any-GPU lane — no target, no claimant.\nMatched case-insensitively.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Status selects one lifecycle state: queued, running, stalled, completed,\nfailed or canceled.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"jobs":[{"attempt":1,"gpu":"spark","id":"job-1","label":"hero","runId":"job-1","status":"running","type":"studio.render","worker":"spark"}]},"schema":{"$ref":"#/components/schemas/jobList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/fleet/jobs/{id}/cancel":{"post":{"operationId":"cancelFleetJob","summary":"Cancels a queued or running render in the caller's org.","description":"Cancels a queued or running render in the caller's org. The engine\ncancel is org-scoped, so a tenant can only ever cancel its OWN job: a job in\nanother tenant's shard is 404, exactly like one that never existed. An\nalready-finished job is 409.","tags":["fleet"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the job (activity) id, from the URL path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"reason":"superseded"},"schema":{"$ref":"#/components/schemas/jobCancel"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"canceled":"job-1","run":"job-1"},"schema":{"$ref":"#/components/schemas/jobCanceled"}}},"description":"ok"}},"x-app":"visor"}},"/v1/fleet/samples":{"get":{"operationId":"listFleetSamples","summary":"Returns the caller org's utilization series, oldest first.","description":"Returns the caller org's utilization series, oldest first.\n\nA rejected narrower is a 400 carrying its own reason (the vocabulary is ours and\nsafe to echo); a warehouse failure is logged and answered 503 \"unavailable\",\nbecause a chart that silently reads \"no load\" when the truth is \"we cannot tell\"\nis worse than one that says so. An ABSENT warehouse is different again: it returns\nan empty series, which renders honestly as \"no samples yet\".","tags":["fleet"],"parameters":[{"name":"unit","in":"query","required":false,"description":"Unit selects one compute unit's series by its source-local id.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source selects one plane: \"agent\", \"byo\" or \"visor\".","schema":{"type":"string"}},{"name":"range","in":"query","required":false,"description":"Range is the lookback window (e.g. \"1h\", \"24h\", \"7d\"); empty takes the\nwarehouse default.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"samples":[{"at":"2026-07-27T09:00:00Z","gpuModel":"GB10","gpuUtil":0.42,"gpus":1,"host":"spark","kind":"worker","source":"byo","unit":"spark"}]},"schema":{"$ref":"#/components/schemas/sampleList"}}},"description":"ok"}},"x-app":"visor"},"post":{"operationId":"recordFleetSample","summary":"Records a BYO worker's live GPU utilization into the SAME series the fleet board overlays.","description":"Records a BYO worker's live GPU utilization into the SAME series the\nfleet board overlays. The org is the validated principal and source/kind are fixed\nserver-side, so a worker names only its own metrics — never another tenant or\nanother source. Answers 202: the warehouse write is DETACHED (its own bounded\ncontext, never in the response path), so a slow or absent warehouse cannot stall a\nheartbeat.","tags":["fleet"],"requestBody":{"content":{"application/json":{"example":{"gpuModel":"GB10","gpuUtil":0.42,"gpus":1,"host":"spark","memFree":200,"memUsed":100,"unit":"spark"},"schema":{"$ref":"#/components/schemas/sampleIngest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"recorded":true},"schema":{"$ref":"#/components/schemas/sampleAccepted"}}},"description":"ok"}},"x-app":"visor"}},"/v1/fleet/workers":{"get":{"operationId":"listFleetWorkers","summary":"Returns the caller org's BYO machines — the ones that dialed in via `hanzo link` — with everything each host reported about itself.","description":"Returns the caller org's BYO machines — the ones that dialed in\nvia `hanzo link` — with everything each host reported about itself. The Machines\nand GPUs pages fold the same data into their normalized shapes; this is the\ncanonical raw list a fleet view (or the CLI's `status`) reads.","tags":["fleet"],"responses":{"200":{"content":{"application/json":{"example":{"workers":[{"arch":"arm64","cpus":20,"gpus":[{"memoryTotal":"122880 MiB","name":"NVIDIA GB10"}],"hostname":"spark","id":"spark","location":"on-prem","provider":"byo","status":"online"}]},"schema":{"$ref":"#/components/schemas/workerList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/flow/runs":{"get":{"operationId":"get_v1_flow_runs","summary":"Runs reads one workflow's recorded runs: every component build with its result, keyed by component.","description":"Runs reads one workflow's recorded runs: every component build with its\nresult, keyed by component. Ownership is verified first — run records never\ncross the org boundary.","tags":["flow"],"parameters":[{"name":"workflow","in":"query","required":false,"description":"Workflow is the UUID of the workflow whose run records to read. It rides\nthe query string.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"},"post":{"operationId":"post_v1_flow_runs","summary":"Run executes one of the caller's workflows synchronously: the graph runs in the flow service and the response carries the run's session and outputs.","description":"Run executes one of the caller's workflows synchronously: the graph runs in\nthe flow service and the response carries the run's session and outputs. A\ngraph whose components fail reports the product's own error. Runs are\nbounded by the product's five-minute sync ceiling.","tags":["flow"],"requestBody":{"content":{"application/json":{"example":{"input":"summarize today's tickets","workflow":"8f14e45f-…"},"schema":{"$ref":"#/components/schemas/flowRun"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"}},"/v1/flow/status":{"get":{"operationId":"get_v1_flow_status","summary":"Status reports whether the flow service is reachable and which version it runs.","description":"Status reports whether the flow service is reachable and which version it\nruns. It is the product's own /health and /v1/version composed — an honest\nlens for \"is the workflow plane up\", never a fabricated ok.","tags":["flow"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/flowStatus"}}},"description":"ok"}},"x-app":"flow"}},"/v1/flow/workflows":{"get":{"operationId":"get_v1_flow_workflows","summary":"Workflows lists the caller's workflows, paged.","description":"Workflows lists the caller's workflows, paged. The list is scoped\nserver-side to the org's project — the page can only ever hold the caller's\nown workflows.","tags":["flow"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page of workflows to return.","schema":{"type":"string"}},{"name":"size","in":"query","required":false,"description":"Size is how many workflows one page holds (the product caps it at 100).","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"},"post":{"operationId":"post_v1_flow_workflows","summary":"Creates a workflow in the caller's org.","description":"Creates a workflow in the caller's org. The org's project id\nis pinned server-side from the validated principal — there is no field by\nwhich a caller could place a workflow in another org.","tags":["flow"],"requestBody":{"content":{"application/json":{"example":{"description":"route tickets","name":"support-triage"},"schema":{"$ref":"#/components/schemas/flowCreate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"}},"/v1/flow/workflows/{workflow}":{"delete":{"operationId":"delete_v1_flow_workflows_by_workflow","summary":"Deletes one of the caller's workflows and its runs.","description":"Deletes one of the caller's workflows and its runs. Ownership\nis verified first; a foreign id answers 404 and deletes nothing.","tags":["flow"],"parameters":[{"name":"workflow","in":"path","required":true,"description":"Workflow is the workflow's UUID, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"},"get":{"operationId":"get_v1_flow_workflows_by_workflow","summary":"Workflow reads one of the caller's workflows — the full record, graph included.","description":"Workflow reads one of the caller's workflows — the full record, graph\nincluded. A workflow outside the caller's org answers 404, indistinguishable\nfrom one that does not exist.","tags":["flow"],"parameters":[{"name":"workflow","in":"path","required":true,"description":"Workflow is the workflow's UUID, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"},"patch":{"operationId":"patch_v1_flow_workflows_by_workflow","summary":"Patches one of the caller's workflows: name, description, graph, or the locked flag — only the stated fields move.","description":"Patches one of the caller's workflows: name, description,\ngraph, or the locked flag — only the stated fields move. Ownership is\nverified before the patch reaches the product.","tags":["flow"],"parameters":[{"name":"workflow","in":"path","required":true,"description":"Workflow is the workflow's UUID, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"description":"route tickets to the right queue"},"schema":{"$ref":"#/components/schemas/flowUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"flow"}},"/v1/framework/doctypes":{"get":{"operationId":"get_v1_framework_doctypes","summary":"Returns every DocType defined in the caller's org.","description":"Returns every DocType defined in the caller's org. Another\ntenant's definitions are never included: the org is part of the store key.","tags":["framework"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/docTypeList"}}},"description":"ok"}},"x-app":"framework"},"post":{"operationId":"post_v1_framework_doctypes","summary":"Defines a DocType in the caller's org: the metadata that gives a document surface its fields, its naming rule, whether it has a submit/cancel lifecycle, and which role may do what to it.","description":"Defines a DocType in the caller's org: the metadata that gives a\ndocument surface its fields, its naming rule, whether it has a submit/cancel\nlifecycle, and which role may do what to it. Manager-only — on a fresh org the\nfirst caller to administer it is seeded as its System Manager, after which\nonly a System Manager (or a platform admin) may define. Answers 201.","tags":["framework"],"requestBody":{"content":{"application/json":{"example":{"autoname":"TASK-.#####","fields":[{"fieldname":"subject","fieldtype":"Data","reqd":true}],"name":"Task"},"schema":{"$ref":"#/components/schemas/DocType"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocType"}}},"description":"created"}},"x-app":"framework"}},"/v1/framework/doctypes/{name}":{"delete":{"operationId":"delete_v1_framework_doctypes_by_name","summary":"Removes a DocType and every document stored under it.","description":"Removes a DocType and every document stored under it. The\ndefinition and its data go together — a document with no schema can be neither\nvalidated nor read back — so there is no undo. Manager-only. Answers 204.","tags":["framework"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the DocType's name, from the path. A name containing a space\n(\"Sales Invoice\") arrives percent-encoded and is decoded before it is\nmatched against the stored one.","schema":{"type":"string"},"example":"Task"}],"responses":{"204":{"description":"no content"}},"x-app":"framework"},"get":{"operationId":"get_v1_framework_doctypes_by_name","summary":"Returns one DocType definition — its fields, naming rule, permissions and lifecycle flags.","description":"Returns one DocType definition — its fields, naming rule,\npermissions and lifecycle flags. Scoped to the caller's org, so another\ntenant's DocType of the same name is simply not found.","tags":["framework"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the DocType's name, from the path. A name containing a space\n(\"Sales Invoice\") arrives percent-encoded and is decoded before it is\nmatched against the stored one.","schema":{"type":"string"},"example":"Task"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocType"}}},"description":"ok"}},"x-app":"framework"},"put":{"operationId":"put_v1_framework_doctypes_by_name","summary":"Replaces a DocType definition wholesale (PUT semantics): the stored definition becomes the body.","description":"Replaces a DocType definition wholesale (PUT semantics): the\nstored definition becomes the body. The name in the URL is authoritative over\nthe body's, and documents already stored under the DocType are left intact.\nManager-only.","tags":["framework"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"},"example":"Task"}],"requestBody":{"content":{"application/json":{"example":{"fields":[{"fieldname":"subject","fieldtype":"Data"}],"name":"Task"},"schema":{"$ref":"#/components/schemas/DocType"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocType"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/modules":{"get":{"operationId":"get_v1_framework_modules","summary":"Returns every app lane compiled into this deployment and the DocTypes each one installs.","description":"Returns every app lane compiled into this deployment and the\nDocTypes each one installs. It describes the BINARY, not the org: what a given\norg has actually installed is the per-module state below.","tags":["framework"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/moduleList"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/modules/{module}":{"get":{"operationId":"get_v1_framework_modules_by_module","summary":"Returns one app lane's install state for the caller's org: the DocTypes the lane declares, and which of them already exist in the org.","description":"Returns one app lane's install state for the caller's org: the\nDocTypes the lane declares, and which of them already exist in the org. That\nis the honest \"set up\" versus \"installed\" answer a console renders.","tags":["framework"],"parameters":[{"name":"module","in":"path","required":true,"description":"Module is the lane's registered name (\"cms\", \"erp\"), from the path.","schema":{"type":"string"},"example":"cms"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModuleState"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/modules/{module}/install":{"post":{"operationId":"post_v1_framework_modules_by_module_install","summary":"Creates an app lane's DocTypes in the caller's org.","description":"Creates an app lane's DocTypes in the caller's org. Idempotent\nand create-if-absent: a DocType the org already has is reported as existing\nand never replaced, so re-installing cannot clobber a definition the org has\nsince edited. Manager-only.","tags":["framework"],"parameters":[{"name":"module","in":"path","required":true,"description":"Module is the lane's registered name (\"cms\", \"erp\"), from the path.","schema":{"type":"string"},"example":"cms"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Install"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/roles":{"get":{"operationId":"get_v1_framework_roles","summary":"Returns every (user, role) assignment in the caller's org.","description":"Returns every (user, role) assignment in the caller's org. Roles are\nwhat DocType permissions are written against, so this is the grant table the\npermission calculus resolves a member's rights from.","tags":["framework"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/roleList"}}},"description":"ok"}},"x-app":"framework"},"post":{"operationId":"post_v1_framework_roles","summary":"Grants one user one role in the caller's org — how a member gains rights on a DocType, since permissions name roles and never users.","description":"Grants one user one role in the caller's org — how a member gains\nrights on a DocType, since permissions name roles and never users.\nManager-only. Answers 201.","tags":["framework"],"requestBody":{"content":{"application/json":{"example":{"role":"System Manager","user":"u_alice"},"schema":{"$ref":"#/components/schemas/RoleAssignment"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleAssignment"}}},"description":"created"}},"x-app":"framework"}},"/v1/framework/roles/{user}/{role}":{"delete":{"operationId":"delete_v1_framework_roles_by_user_by_role","summary":"Removes one (user, role) grant in the caller's org.","description":"Removes one (user, role) grant in the caller's org. Manager-only.\nAnswers 204; a grant that does not exist is not found.","tags":["framework"],"parameters":[{"name":"user","in":"path","required":true,"description":"User is the assignee whose grant is being revoked, from the path.","schema":{"type":"string"},"example":"u_alice"},{"name":"role","in":"path","required":true,"description":"Role is the role to revoke, from the path. A role name containing a space\n(\"System Manager\") arrives percent-encoded and is decoded before it is\nmatched against the stored assignment.","schema":{"type":"string"},"example":"System Manager"}],"responses":{"204":{"description":"no content"}},"x-app":"framework"}},"/v1/framework/summary":{"get":{"operationId":"get_v1_framework_summary","summary":"Reports how much of the DocType surface the caller's org uses: how many DocTypes it has defined, and how many documents exist across them.","description":"Reports how much of the DocType surface the caller's org uses: how\nmany DocTypes it has defined, and how many documents exist across them.","tags":["framework"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/summaryView"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/{doctype}":{"get":{"operationId":"get_v1_framework_by_doctype","summary":"Returns the caller org's documents of one DocType, filtered, ordered and projected by the query.","description":"Returns the caller org's documents of one DocType, filtered,\nordered and projected by the query. The DocType is resolved FIRST — through\nthe same permission gate the list itself uses — because the query is validated\nagainst its schema: a filter, sort or field name the DocType does not declare\nis refused rather than reaching the store.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the DocType to list, from the path.","schema":{"type":"string"},"example":"Task"},{"name":"filters","in":"query","required":false,"description":"Filters is a JSON object of equality matches, e.g. {\"priority\":\"High\"}.\nEvery key must be a field the DocType declares (or the managed name /\ndocstatus); an undeclared one is refused rather than silently ignored.","schema":{"type":"string"},"example":"{\"priority\":\"High\"}"},{"name":"fields","in":"query","required":false,"description":"Fields projects the response to a subset — a JSON array [\"a\",\"b\"] or a\ncomma list \"a,b\". The envelope keys are always returned.","schema":{"type":"string"}},{"name":"order_by","in":"query","required":false,"description":"OrderBy is \"\u003cfield\u003e [asc|desc]\". Empty means most-recently-updated first.","schema":{"type":"string"},"example":"estimate asc"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. Anything that is not a positive integer\nleaves the engine's default in place.","schema":{"type":"string"},"example":"20"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/documentList"}}},"description":"ok"}},"x-app":"framework"},"post":{"operationId":"post_v1_framework_by_doctype","summary":"Create one document of a DocType, from that DocType's own fields.","description":"The body is the DOCUMENT'S field data: a flat JSON object whose properties are the fieldnames the DocType declares, not a fixed envelope. That is why this operation publishes no request schema — the shape is metadata the DocType defines at run time, and no Go struct both accepts it verbatim and describes it, so nothing is asserted rather than something false.\n\nThe engine validates and coerces every field against the DocType, runs the before_insert and before_save hooks (either may reject the write), stores the document, then runs the after hooks. It answers 201 with the stored document: the field data plus the managed envelope — `name`, `doctype`, `docstatus`, `createdAt`, `updatedAt`. A Password field comes back as a fixed redaction marker and is dropped when empty; its stored value is never returned by this or any other read on this surface.\n\n`name` in the body is the REQUESTED DOCUMENT NAME, not a data field. A DocType with an autoname rule names the document itself and ignores it; a prompt-named DocType takes it. This collision is also why the two path segments cannot be folded into the body, and so why the route stays untyped.\n\nScoped to the org of the validated principal, and the engine's own permission calculus decides the rest: the caller needs create rights on this DocType through a role it holds, or a platform admin bit. A caller with no validated principal reaches the engine as the zero Caller and is refused before any store is opened — a forged org header alone buys nothing.\n\nA DocType declared Single has exactly ONE document per org, so this writes that one instance instead of adding a row. The body is size-bounded by the engine, the same bound on every host.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"framework"}},"/v1/framework/{doctype}/{name}":{"delete":{"operationId":"delete_v1_framework_by_doctype_by_name","summary":"Removes one document, after its on_trash hooks agree.","description":"Removes one document, after its on_trash hooks agree. A\nSUBMITTED document cannot be deleted — cancel it first. Answers 204.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the document's DocType, from the path.","schema":{"type":"string"},"example":"Task"},{"name":"name","in":"path","required":true,"description":"Name is the document's name — its key within the DocType — from the path.\nA name containing a space arrives percent-encoded and is decoded before it\nis matched against the stored one.","schema":{"type":"string"},"example":"TASK-00001"}],"responses":{"204":{"description":"no content"}},"x-app":"framework"},"get":{"operationId":"get_v1_framework_by_doctype_by_name","summary":"Returns one document by name, with Password fields redacted.","description":"Returns one document by name, with Password fields redacted.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the document's DocType, from the path.","schema":{"type":"string"},"example":"Task"},{"name":"name","in":"path","required":true,"description":"Name is the document's name — its key within the DocType — from the path.\nA name containing a space arrives percent-encoded and is decoded before it\nis matched against the stored one.","schema":{"type":"string"},"example":"TASK-00001"}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"framework"},"put":{"operationId":"put_v1_framework_by_doctype_by_name","summary":"Replace a draft document's field data wholesale.","description":"PUT semantics: the stored field data BECOMES the body, so a field the body omits is not left at its previous value. The body is the document's own field data — the same metadata-defined open object the create takes, and the same reason this operation publishes no request schema.\n\nOnly a DRAFT can be edited. A document that has been submitted or cancelled is immutable and the write is refused as a conflict, so the submit lifecycle cannot be bypassed by a plain update — cancel it first, and note that a cancelled document can be deleted but never re-submitted or re-edited. The engine validates the new data against the DocType, runs before_save (which may reject), writes, then runs the after hooks, and answers 200 with the stored document plus its managed envelope, Password fields redacted.\n\nThe document name in the path is percent-decoded before it is matched, so a name containing a space is addressed as it is stored. An unknown DocType or document is not found, and the same answer covers a document that exists in another tenant: the org comes from the validated principal and is part of the store key, so a caller cannot learn that another org's document exists. Write rights on the DocType are required, decided by the engine's permission calculus.\n\nFor a Single DocType the path name is ignored — there is one instance per org and this writes it.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"framework"}},"/v1/framework/{doctype}/{name}/cancel":{"post":{"operationId":"post_v1_framework_by_doctype_by_name_cancel","summary":"Moves a submitted document to cancelled (docstatus 1 → 2) after its on_cancel hooks agree.","description":"Moves a submitted document to cancelled (docstatus 1 → 2) after\nits on_cancel hooks agree. Cancelling is terminal — a cancelled document\ncannot be re-submitted — but it CAN then be deleted.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the document's DocType, from the path.","schema":{"type":"string"},"example":"Task"},{"name":"name","in":"path","required":true,"description":"Name is the document's name — its key within the DocType — from the path.\nA name containing a space arrives percent-encoded and is decoded before it\nis matched against the stored one.","schema":{"type":"string"},"example":"TASK-00001"}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"framework"}},"/v1/framework/{doctype}/{name}/submit":{"post":{"operationId":"post_v1_framework_by_doctype_by_name_submit","summary":"Moves a draft to submitted (docstatus 0 → 1) after its on_submit hooks agree.","description":"Moves a draft to submitted (docstatus 0 → 1) after its\non_submit hooks agree. A submitted document is IMMUTABLE: further writes and\ndeletes are refused until it is cancelled. Only a submittable DocType has this\nlifecycle; any other docstatus is an illegal transition.","tags":["framework"],"parameters":[{"name":"doctype","in":"path","required":true,"description":"DocType is the document's DocType, from the path.","schema":{"type":"string"},"example":"Task"},{"name":"name","in":"path","required":true,"description":"Name is the document's name — its key within the DocType — from the path.\nA name containing a space arrives percent-encoded and is decoded before it\nis matched against the stored one.","schema":{"type":"string"},"example":"TASK-00001"}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"framework"}},"/v1/functions":{"get":{"operationId":"get_v1_functions","summary":"Every serverless function the caller's org has published, with its real 7-day rollup","description":"A row carries the function's runtime, resource limits, deployment target and its invoke endpoint, plus envCount — how many secrets it mounts. The registry holds secret NAMES only; a value never enters this store and is never returned.\n\nThe rollup (invocations7d, errors7d, successRate, avgDurationMs) is counted from real invocation rows over the trailing 7 days and is OMITTED for a function with no calls in that window rather than sent as zero, so a consumer must render absence as unknown, not as an idle function. Ordered most-recently-deployed first.\n\nScoped to the caller's own org — one store per org, with the org column on every query. Requires a validated principal: an org claim with no verified credential behind it is refused, never answered with an empty list.","tags":["functions"],"x-app":"functions"},"post":{"operationId":"post_v1_functions","summary":"Publish a function, or redeploy an existing one under the same name","description":"Org and name together identify a function, so a second call for a name the org already owns is a REDEPLOY: the spec is replaced, the deploy version advances, and the original creation time is kept. There is no separate update call, and no way to take over a name another org owns.\n\nWhat is accepted is a closed set. runtime is one of node, python, go, deno, bash or container; name must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ and may not be one of the reserved static names (metrics, triggers, deployments, secrets); source is capped at 256 KiB. timeoutSec is CLAMPED to the 900s ceiling rather than rejected, and an absent one defaults to 30s with 256Mi of memory. target=fleet runs the function on the org's own linked GPU fleet and is accepted for runtime=python only; everything else runs on the shared sandbox.\n\nenvNames declares which secrets the function mounts BY NAME — values live in KMS and are resolved sandbox-side at run time, so no secret value is sent here or stored here. Scoped to the caller's org; requires a validated principal.","tags":["functions"],"x-app":"functions"}},"/v1/functions/deployments":{"get":{"operationId":"get_v1_functions_deployments","summary":"The live deployment of every function in the caller's org","description":"A function's current record IS its deployment, so this answers in the same shape the function list does — runtime, resource limits, target, endpoint, and when it was last deployed.\n\nTwo things not to assume. The invocation rollup is never populated here, even for a function that has run: those fields are omitted unconditionally, and the function list is where they are filled in. And this is an inventory of what is live, not a history — there is exactly one entry per function, and a redeploy replaces it rather than appending to it.\n\nScoped to the caller's org; requires a validated principal.","tags":["functions"],"x-app":"functions"}},"/v1/functions/metrics":{"get":{"operationId":"get_v1_functions_metrics","summary":"Invocation chart and status breakdown across every function in the caller's org","description":"One series per function that actually ran in the window, bucketed, plus a success/timeout/error donut over the same rows. Every point is a COUNT of real invocation rows that fell in that bucket — nothing is interpolated, and a function with no invocations in the window has no series at all.\n\nThe `range` query selects the window and its bucket count: 1H, 6H, 24H, 7D or 30D. An absent or unrecognized value falls back to 24H rather than failing. At most the 5000 newest rows are read, so a very busy org's oldest buckets in a wide range can undercount.\n\ncostCents is always null: this view has no per-invocation cost source, and reports nothing rather than a fabricated figure. Scoped to the caller's org; requires a validated principal.","tags":["functions"],"x-app":"functions"}},"/v1/functions/secrets":{"get":{"operationId":"get_v1_functions_secrets","summary":"The names of the secrets mounted by the caller's org's functions","description":"NAMES only. A secret's value is not held by this subsystem and is not read on this path — values live in KMS and are resolved sandbox-side when a function runs — so nothing in this answer is a credential.\n\nThe list is derived from the mount declarations on the function records and deduplicated by namespace and name, so a name mounted by several functions appears ONCE: mountedBy names the first function that claimed it in deploy order, not every function that mounts it. Read it as a hint about origin, not as a complete usage map.\n\nScoped to the caller's org; requires a validated principal.","tags":["functions"],"x-app":"functions"}},"/v1/functions/triggers":{"get":{"operationId":"get_v1_functions_triggers","summary":"Every trigger attached to the caller's org's functions","description":"A function has exactly ONE trigger today and it is derived, not stored: an always-enabled HTTP trigger whose target is that function's own invoke endpoint, listed once per function.\n\nThere is no trigger table behind this and no call that creates, disables or deletes one. The list is a projection of the function registry, so it changes only when a function is published or deleted.\n\nScoped to the caller's org; requires a validated principal.","tags":["functions"],"x-app":"functions"}},"/v1/functions/{name}":{"delete":{"operationId":"delete_v1_functions_by_name","summary":"Delete a function and its entire invocation history","description":"One transaction removes the function record and every invocation row recorded against its name, so that history also leaves the metrics chart and the invocation list. This is not a soft delete and there is no restore.\n\nDeletion is keyed on (org, name): a name owned by another org is not found here, exactly like a name that never existed, so the call cannot be used to probe for or destroy another tenant's function. A successful delete answers with no body.\n\nRequires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"functions"},"get":{"operationId":"get_v1_functions_by_name","summary":"One function in full: spec, trailing-7-day rollup, trigger, latest runs and mounted secret names","description":"Extends the list row with the function's single derived HTTP trigger, its 20 most recent invocations (newest first, metadata only — no captured output), and `secrets`, the NAMES of the secrets it mounts. No secret value is stored or returned.\n\nLookup is keyed on (org, name), so a function that exists but belongs to another org answers exactly as one that never existed — not found, never a signal that the name is taken elsewhere. The 7-day rollup fields are omitted rather than zeroed when the function has not run in the window.\n\nRequires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"functions"}},"/v1/functions/{name}/invocations":{"get":{"operationId":"get_v1_functions_by_name_invocations","summary":"Recent invocation history for one function, newest first","description":"Each entry is invocation METADATA — id, status, HTTP status code, wall-clock duration and when it ran. The captured stdout/stderr is not on this path; the logs call returns it, for the latest run only.\n\n`limit` defaults to 100 and is clamped: at or below zero, above 500, or not a number at all, it falls back to 100. An unknown function name is NOT an error here — nothing has ever run under it, so the answer is an empty list rather than a not-found, and a caller testing existence must ask for the function itself.\n\nScoped to the caller's org, so it can only ever return the calling tenant's own runs. Requires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"functions"}},"/v1/functions/{name}/invoke":{"post":{"operationId":"post_v1_functions_by_name_invoke","summary":"Run a function and get back the recorded invocation","description":"The body's `input` is handed to the function on stdin. Execution NEVER happens in this process: the runtime and source go to the sandboxed code executor, or, for a function published with target=fleet, to the org's own linked GPU fleet as an fn.run job this call blocks on until it finishes. Either way it is bounded by the function's own timeout, itself capped at 900s.\n\nThe answer is the invocation record — id, status, duration — and its HTTP status is about the RUN, not about this API: a function whose own code fails answers 502 with a recorded `error` invocation, which is a successful invocation of a failing program. The captured output is not in this reply; the logs call returns it.\n\nMONEY. The caller's org ledger is gated BEFORE any compute runs, so an org out of credit or over its spend cap is refused 402 and nothing executes, and a billing plane that cannot answer refuses rather than granting free compute. A run that actually executed is then debited twice — a flat per-invocation fee, and GB-seconds of compute derived from the measured duration and the function's configured memory. A run that never reached its executor (unreachable, or timed out in transport) consumed nothing and is not charged; a run whose code exited non-zero DID consume compute and is. An operator who prices either half at zero makes it a no-op, and a zero request fee removes the balance gate with it.\n\nWhen the sandbox is not configured on this deployment, a non-fleet function fails closed before anything is recorded — no execution and no fabricated output. Scoped to the caller's org; requires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"functions"}},"/v1/functions/{name}/logs":{"get":{"operationId":"get_v1_functions_by_name_logs","summary":"The captured output of a function's most recent invocation","description":"One string, from the LATEST invocation only. This is not a log stream and carries no history; the invocations list is where earlier runs are enumerated.\n\nWhen that run failed, the string is its ERROR text rather than its stdout — the two share one field, so success cannot be told from failure by this value alone and the invocation's status is what answers that. Output was truncated to 64 KiB when the run was recorded, error text to 16 KiB.\n\nA function that has never run — or a name that does not exist in the caller's org — answers with an empty string, not a not-found. Scoped to the caller's org; requires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"functions"}},"/v1/gateway/config":{"get":{"operationId":"get_v1_gateway_config","summary":"Read returns the EFFECTIVE edge policy the caller is subject to: the platform CORS allowlist and pre-auth per-IP flood cap in force, plus the caller's own authenticated rate ceiling, edge-cache TTLs and accepted-method allowlist.","description":"Read returns the EFFECTIVE edge policy the caller is subject to: the platform CORS\nallowlist and pre-auth per-IP flood cap in force, plus the caller's own authenticated\nrate ceiling, edge-cache TTLs and accepted-method allowlist. A SuperAdmin may inspect\na specific tenant's effective policy with ?org=\u003cslug\u003e.","tags":["gateway"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}},"description":"ok"}},"x-app":"gateway"},"put":{"operationId":"put_v1_gateway_config","summary":"Write updates one policy scope and returns the policy in force after the write.","description":"Write updates one policy scope and returns the policy in force after the write.\nA body carrying any PLATFORM field (cors_origins, per_ip_rpm, window_sec) is a\nplatform write and requires SuperAdmin; otherwise it is a per-org write (org_rpm,\ncache_ttl_sec, cache_paths, methods) scoped to the caller's own org — or, for a\nSuperAdmin, the tenant named by ?org=\u003cslug\u003e. A body that sets nothing is a 400.\nThe abuse gate's mode is an OPERATOR field: setting it requires SuperAdmin,\nwhichever organization it lands on. updated_at and updated_by are\nserver-stamped; a client-supplied value is ignored.","tags":["gateway"],"requestBody":{"content":{"application/json":{"example":{"cache_ttl_sec":30,"methods":["GET","POST"],"org_rpm":120},"schema":{"$ref":"#/components/schemas/Policy"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}},"description":"ok"}},"x-app":"gateway"}},"/v1/gateway/traffic":{"get":{"operationId":"gatewayTraffic","summary":"Report who is calling this org's API right now","description":"Traffic reports who is calling this organization's API right now: the request\ncount for the last minute split by AGENCY LANE — agent, human, bot, unknown —\nand the busiest callers behind it, each with its request count, its\nauthentication-failure count, how many distinct paths it touched, and any\nverdict currently held against it.\n\nThe lane split is the answer to the question a generic bot filter cannot\nanswer: which of this traffic is the customer's own automation and which is\nsomebody working through a list. It is computed from credentials we issued, not\nfrom the client's self-description, so a scraper cannot move itself into the\nagent lane by editing a header.\n\nA validated caller appears as a FINGERPRINT — a one-way, per-process digest. It\nis stable enough to recognise the same caller across a minute and cannot be\nturned back into a key, so this report is safe to read, screenshot and paste.\n\nIt also reports what the sensor's own ceilings are doing (strain, tracked,\nceiling, refused) and how many screens the scorer did not answer (unscored), so\na control that has stopped measuring or a judge that has stopped answering is a\nnumber here rather than a quiet day.\n\nScoped to the caller's own validated organization. A SuperAdmin may inspect a\nspecific tenant with ?org=\u003cslug\u003e, or the lane that has no tenant — every caller\nthe identity boundary could not validate — with an empty ?org=.","tags":["gateway"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrafficView"}}},"description":"ok"}},"x-app":"gateway"}},"/v1/generate-text-to-speech-audio":{"post":{"operationId":"post_v1_generate-text-to-speech-audio","summary":"Convert text to speech","description":"Convert text to speech","tags":["generate-text-to-speech-audio"],"x-app":"github.com/hanzoai/ai"}},"/v1/generate-text-to-speech-audio-stream":{"get":{"operationId":"get_v1_generate-text-to-speech-audio-stream","summary":"Convert text to speech with streaming","description":"Convert text to speech with streaming","tags":["generate-text-to-speech-audio-stream"],"x-app":"github.com/hanzoai/ai"}},"/v1/git/keys":{"get":{"operationId":"get_v1_git_keys","summary":"Returns the SSH public keys registered to the caller's org — the keys that authenticate `git clone git@\u003chost\u003e:\u003corg\u003e/\u003crepo\u003e.git`.","description":"Returns the SSH public keys registered to the caller's org — the keys\nthat authenticate `git clone git@\u003chost\u003e:\u003corg\u003e/\u003crepo\u003e.git`. Keys are org-scoped\non read even though the fingerprint index is global, so one org never sees\nanother's.","tags":["git"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"createdAt":"2026-07-01T10:00:00Z","fingerprint":"SHA256:9pQ…","id":"gitkey_4a1b","publicKey":"ssh-ed25519 AAAAC3Nz…","title":"laptop"}]},"schema":{"$ref":"#/components/schemas/keyList"}}},"description":"ok"}},"x-app":"git"},"post":{"operationId":"post_v1_git_keys","summary":"Registers an SSH public key so it can authenticate `git clone git@\u003chost\u003e:\u003corg\u003e/\u003crepo\u003e.git` for the caller's org.","description":"Registers an SSH public key so it can authenticate `git clone\ngit@\u003chost\u003e:\u003corg\u003e/\u003crepo\u003e.git` for the caller's org. The key line is parsed and\ncanonicalized before storage, its SHA256 fingerprint becomes the auth lookup\nhandle, and the full public key round-trips (it is public). Answers 201.\nFingerprints are globally unique, so a key already registered — to this org or\nany other — is a 409: one key belongs to exactly one org.","tags":["git"],"requestBody":{"content":{"application/json":{"example":{"publicKey":"ssh-ed25519 AAAAC3Nz… z@hanzo.ai","title":"laptop"},"schema":{"$ref":"#/components/schemas/registerKeyReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/keyView"}}},"description":"created"}},"x-app":"git"}},"/v1/git/keys/{id}":{"delete":{"operationId":"delete_v1_git_keys_by_id","summary":"Removes a registered SSH key, scoped to the caller's org: an org can only delete its own, and a key id it does not own is not found.","description":"Removes a registered SSH key, scoped to the caller's org: an org can\nonly delete its own, and a key id it does not own is not found. Answers 204\nwith no body. Once removed the key no longer authenticates any SSH git access.","tags":["git"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the key's identifier (\"gitkey_…\"), from the :id path segment.","schema":{"type":"string"},"example":"gitkey_4a1b"}],"responses":{"204":{"description":"no content"}},"x-app":"git"}},"/v1/git/repos":{"get":{"operationId":"get_v1_git_repos","summary":"Returns the repos in the caller's scope, most recently updated first.","description":"Returns the repos in the caller's scope, most recently updated\nfirst. The scope is the request principal's — the gateway-minted org and its\noptional project — never anything off the wire, so a caller only ever sees its\nown. Rows carry no branches or HEAD; read one repo for those.","tags":["git"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"cloneUrl":"https://api.hanzo.ai/v1/git/acme/widgets.git","createdAt":"2026-07-01T10:00:00Z","defaultBranch":"main","id":"repo_9f3c","name":"widgets","org":"acme","public":false,"sizeBytes":4096,"sshUrl":"git@git.hanzo.ai:acme/widgets.git"}]},"schema":{"$ref":"#/components/schemas/repoList"}}},"description":"ok"}},"x-app":"git"},"post":{"operationId":"post_v1_git_repos","summary":"Provisions an empty bare repository in the caller's scope and returns it with its clone URLs.","description":"Provisions an empty bare repository in the caller's scope and\nreturns it with its clone URLs. Answers 201. The name must be unique within\nthe scope — a repeat is a 409, never a silent overwrite of an existing repo.\nThe org comes from the validated principal, so a repo is always born owned by\nthe caller's own tenant.","tags":["git"],"requestBody":{"content":{"application/json":{"example":{"description":"the widget service","name":"widgets"},"schema":{"$ref":"#/components/schemas/createReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/repoView"}}},"description":"created"}},"x-app":"git"}},"/v1/git/repos/{name}":{"delete":{"operationId":"delete_v1_git_repos_by_name","summary":"Removes a repo's metadata and purges its storage.","description":"Removes a repo's metadata and purges its storage. Answers 204 with\nno body. The metadata row is the source of truth for existence, so a storage\npurge that fails is logged and the delete still succeeds — and a second call\nis a 404, not a second delete.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"204":{"description":"no content"}},"x-app":"git"},"get":{"operationId":"get_v1_git_repos_by_name","summary":"Returns one repo with its live ref state: every branch name and the resolved HEAD commit.","description":"Returns one repo with its live ref state: every branch name and the\nresolved HEAD commit. Both are read from the object store on each call, so an\nempty repo reports no branches and an empty head rather than failing. A repo\noutside the caller's scope is not found.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/repoView"}}},"description":"ok"}},"x-app":"git"},"patch":{"operationId":"patch_v1_git_repos_by_name","summary":"Flips a repo's public bit, the one mutable repo setting today.","description":"Flips a repo's public bit, the one mutable repo setting today.\nPublic grants ANONYMOUS fetch only; push and the whole control plane stay\norg-authed. Returns the updated repo.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to update, from the :name path segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"name":"widgets","public":true},"schema":{"$ref":"#/components/schemas/patchIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/repoView"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/blob":{"get":{"operationId":"get_v1_git_repos_by_name_blob","summary":"Returns one file's bytes at one revision.","description":"Returns one file's bytes at one revision. Text comes back verbatim,\nbinary comes back base64, and a file past the 1 MiB view cap comes back marked\ntruncated with NO content — the client is expected to clone instead.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to read, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"ref","in":"query","required":false,"description":"Ref is a branch, tag or commit; empty means the repo's HEAD.","schema":{"type":"string"},"example":"main"},{"name":"path","in":"query","required":false,"description":"Path is repo-relative; empty is the tree root. Traversal is stripped.","schema":{"type":"string"},"example":"go.mod"}],"responses":{"200":{"content":{"application/json":{"example":{"binary":false,"content":"module widgets\n","encoding":"utf8","path":"go.mod","size":42,"truncated":false},"schema":{"$ref":"#/components/schemas/blobJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/commits":{"get":{"operationId":"get_v1_git_repos_by_name_commits","summary":"Walks a ref's history newest first, or one path's history when a path is given.","description":"Walks a ref's history newest first, or one path's history when a\npath is given. There is no cursor: the page is the newest `limit` commits.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to read, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"ref","in":"query","required":false,"description":"Ref is the branch, tag or commit to walk back from; empty means HEAD.","schema":{"type":"string"},"example":"main"},{"name":"path","in":"query","required":false,"description":"Path narrows the history to commits touching it; empty walks the whole ref.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the page. Anything not positive means 50; the cap is 100.","schema":{"type":"integer"},"example":2}],"responses":{"200":{"content":{"application/json":{"example":{"commits":[{"authorEmail":"ada@hanzo.ai","authorName":"Ada","date":"2026-07-01T10:00:00Z","message":"add the widget service","sha":"a1b2c3d4e5f6","shortSha":"a1b2c3d"}]},"schema":{"$ref":"#/components/schemas/commitsJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/files":{"get":{"operationId":"get_v1_git_repos_by_name_files","summary":"Returns every file a glob selects at one revision, WITH its bytes and the revision they came from.","description":"Returns every file a glob selects at one revision, WITH its bytes\nand the revision they came from. It is the read a delivery generator makes:\none call answers \"what is the inventory at this commit, and what does it say\",\nwhere listing and then fetching would be a request per file.\n\nReturning the resolved revision matters as much as the bytes. A generator that\nlists at `main` and then reads at `main` can straddle a push and assemble half\nits inventory from one commit and half from the next; resolving once makes the\nwhole read consistent by construction.\n\nA file past the read cap comes back Truncated with no content rather than\nbeing dropped. A caller building a desired set has to know the difference\nbetween \"this file is empty\" and \"this file was not read\" — silently omitting\nit is how a pruning reconcile deletes what the missing file declared.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to read, from the :name path segment.","schema":{"type":"string"},"example":"universe"},{"name":"ref","in":"query","required":false,"description":"Ref is a branch, tag or commit; empty means the repo's HEAD.","schema":{"type":"string"},"example":"main"},{"name":"glob","in":"query","required":false,"description":"Glob selects files, matched segment by segment so `*` never crosses a `/`.\n`**` matches zero or more whole segments.","schema":{"type":"string"},"example":"charts/app/values/*/*.yaml"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/filesJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/gc":{"post":{"operationId":"post_v1_git_repos_by_name_gc","summary":"Repacks a repo into one bitmapped pack and rewrites its commit-graph, so the next clone reuses the bitmap instead of walking the whole object graph.","description":"Repacks a repo into one bitmapped pack and rewrites its commit-graph, so\nthe next clone reuses the bitmap instead of walking the whole object graph.\nIdempotent, and safe to interrupt — git swaps both artifacts atomically. It\nruns under one pack slot with the same memory bounds as a clone, so it can\nblock behind heavy pack traffic rather than compete with it. Storage usage is\nre-measured afterwards, since a repack reclaims space.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"maintained":true,"repo":"widgets","sizeBytes":3072},"schema":{"$ref":"#/components/schemas/gcOut"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/mirror":{"post":{"operationId":"post_v1_git_repos_by_name_mirror","summary":"Imports an external git repository into the caller's repo, provisioning it on first use.","description":"Imports an external git repository into the caller's repo, provisioning\nit on first use. Fetch is FORCED and covers every ref, so a first call clones\nthe source and a repeat call re-syncs it — the endpoint is idempotent by mirror\nsemantics. Mirrored bytes are metered exactly like a push, and a push.landed\nevent is emitted for the default branch so the code index picks the repo up.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the local repo to mirror into, from the :name path segment. It is\nCREATED on first use.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"name":"widgets","source":"https://github.com/acme/widgets.git"},"schema":{"$ref":"#/components/schemas/mirrorReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/repoView"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/mirrors":{"get":{"operationId":"get_v1_git_repos_by_name_mirrors","summary":"Returns a repo's outbound mirror targets — the downstream remotes the mirror reactor pushes to whenever a push lands here.","description":"Returns a repo's outbound mirror targets — the downstream remotes\nthe mirror reactor pushes to whenever a push lands here.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"createdAt":"2026-07-01T10:00:00Z","host":"github.com","id":"mir_2d90","repo":"widgets","url":"https://github.com/acme/widgets.git"}]},"schema":{"$ref":"#/components/schemas/mirrorList"}}},"description":"ok"}},"x-app":"git"},"post":{"operationId":"post_v1_git_repos_by_name_mirrors","summary":"Registers a downstream remote the repo's advanced refs are pushed to whenever a push lands here.","description":"Registers a downstream remote the repo's advanced refs are pushed to\nwhenever a push lands here. Answers 201. The URL must be https to a host on the\nmirror allowlist (github.com / gitlab.com): the same set the mirror credential\nmay be sent to, so a target can never capture the shared token or point the push\nat an internal service. Any embedded userinfo is stripped — credentials ride\nenv-only at push time and never enter the stored URL. One mirror per host per\nrepo; a second is a 409.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo whose advanced refs are pushed downstream, from the :name\npath segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"name":"widgets","url":"https://github.com/acme/widgets.git"},"schema":{"$ref":"#/components/schemas/mirrorTargetReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mirrorTargetView"}}},"description":"created"}},"x-app":"git"}},"/v1/git/repos/{name}/mirrors/{id}":{"delete":{"operationId":"delete_v1_git_repos_by_name_mirrors_by_id","summary":"Removes one outbound mirror target; later pushes stop being forwarded to it.","description":"Removes one outbound mirror target; later pushes stop being\nforwarded to it. Answers 204 with no body. Nothing is done to the downstream\nremote itself — only this repo's intent to push there is dropped.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"id","in":"path","required":true,"description":"ID is the row to remove, from the :id path segment.","schema":{"type":"string"},"example":"mir_2d90"}],"responses":{"204":{"description":"no content"}},"x-app":"git"}},"/v1/git/repos/{name}/push":{"post":{"operationId":"post_v1_git_repos_by_name_push","summary":"Lands a set of files as one commit without a git client — the hanzo.app builder's push.","description":"Lands a set of files as one commit without a git client — the\nhanzo.app builder's push. The repo is CREATED on first push, the files are\nmerged onto the branch tip (unlisted files survive), and the same\npush-to-deploy hook a real receive-pack fires is fired, so downstream this is\nindistinguishable from a `git push`.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to push into, from the :name path segment. It is CREATED\non first push if it does not exist.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"branch":"main","files":[{"content":"\u003ch1\u003ehi\u003c/h1\u003e","path":"index.html"}],"message":"generated build","name":"widgets"},"schema":{"$ref":"#/components/schemas/pushReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"branch":"main","cloneUrl":"https://api.hanzo.ai/v1/git/acme/widgets.git","commit":"a1b2c3d4e5f6","sshUrl":"git@git.hanzo.ai:acme/widgets.git"},"schema":{"$ref":"#/components/schemas/pushResp"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/readme":{"get":{"operationId":"get_v1_git_repos_by_name_readme","summary":"Returns the README at the tree root as plain text — unrendered, so the caller decides how to present it.","description":"Returns the README at the tree root as plain text — unrendered, so\nthe caller decides how to present it. A repo with no README is not found.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to read, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"ref","in":"query","required":false,"description":"Ref is a branch, tag or commit; empty means the repo's HEAD.","schema":{"type":"string"},"example":"main"}],"responses":{"200":{"content":{"application/json":{"example":{"content":"# widgets\n","encoding":"utf8","path":"README.md"},"schema":{"$ref":"#/components/schemas/readmeJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/refs":{"get":{"operationId":"get_v1_git_repos_by_name_refs","summary":"Lists a repo's branches, tags and default branch — what a branch picker needs in one call.","description":"Lists a repo's branches, tags and default branch — what a branch\npicker needs in one call. Unlike the other read ops it tolerates a repo with no\ncommits: the ref sets come back empty and the default branch is still named.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"branches":[{"name":"main","sha":"a1b2c3d4"}],"default":"main","tags":[]},"schema":{"$ref":"#/components/schemas/refsJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/repos/{name}/subscriptions":{"get":{"operationId":"get_v1_git_repos_by_name_subscriptions","summary":"Returns a repo's Slack subscriptions — which channels the lifecycle notifier posts this repo's push and deploy events to.","description":"Returns a repo's Slack subscriptions — which channels the\nlifecycle notifier posts this repo's push and deploy events to.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo's org-unique handle, from the :name path segment. A\ntrailing \".git\" is stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"channel":"#builds","createdAt":"2026-07-01T10:00:00Z","events":["push.landed"],"id":"sub_7c2e","repo":"widgets"}]},"schema":{"$ref":"#/components/schemas/subscriptionList"}}},"description":"ok"}},"x-app":"git"},"post":{"operationId":"post_v1_git_repos_by_name_subscriptions","summary":"Binds a Slack channel to a repo, so the lifecycle notifier posts that repo's push and deploy events there.","description":"Binds a Slack channel to a repo, so the lifecycle notifier posts\nthat repo's push and deploy events there. Answers 201. The same channel twice\non one repo is a 409; a repo outside the caller's scope is a 404, exactly as\nreading it is.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to subscribe, from the :name path segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"channel":"#builds","events":["push.landed"],"name":"widgets"},"schema":{"$ref":"#/components/schemas/subscribeReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/subscriptionView"}}},"description":"created"}},"x-app":"git"}},"/v1/git/repos/{name}/subscriptions/{id}":{"delete":{"operationId":"delete_v1_git_repos_by_name_subscriptions_by_id","summary":"Removes one Slack subscription from a repo; the notifier stops posting that repo's events to that channel.","description":"Removes one Slack subscription from a repo; the notifier stops\nposting that repo's events to that channel. Answers 204 with no body. An id\nthat is not this repo's subscription is not found.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"id","in":"path","required":true,"description":"ID is the row to remove, from the :id path segment.","schema":{"type":"string"},"example":"sub_7c2e"}],"responses":{"204":{"description":"no content"}},"x-app":"git"}},"/v1/git/repos/{name}/tree":{"get":{"operationId":"get_v1_git_repos_by_name_tree","summary":"Lists the immediate children of one directory at one revision, directories before files.","description":"Lists the immediate children of one directory at one revision,\ndirectories before files. It does not recurse — walk down a level at a time.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo to read, from the :name path segment.","schema":{"type":"string"},"example":"widgets"},{"name":"ref","in":"query","required":false,"description":"Ref is a branch, tag or commit; empty means the repo's HEAD.","schema":{"type":"string"},"example":"main"},{"name":"path","in":"query","required":false,"description":"Path is repo-relative; empty is the tree root. Traversal is stripped.","schema":{"type":"string"},"example":"cmd"}],"responses":{"200":{"content":{"application/json":{"example":{"entries":[{"mode":"040000","name":"server","path":"cmd/server","size":0,"type":"tree"}]},"schema":{"$ref":"#/components/schemas/treeJSON"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/usage":{"get":{"operationId":"get_v1_git_usage","summary":"Returns per-repo and total storage bytes for the caller's org — the queryable, per-tenant number commerce and o11y meter on.","description":"Returns per-repo and total storage bytes for the caller's org — the\nqueryable, per-tenant number commerce and o11y meter on. It spans EVERY\nproject sub-scope, unlike the repo list, so a billing consumer sees the whole\ntenant footprint in one call. Sizes are last-measured values (create, push,\nmirror and gc each re-measure), not a live walk of the disk.","tags":["git"],"responses":{"200":{"content":{"application/json":{"example":{"org":"acme","repos":[{"name":"widgets","sizeBytes":4096},{"name":"site","project":"web","sizeBytes":8192}],"totalBytes":12288},"schema":{"$ref":"#/components/schemas/usageView"}}},"description":"ok"}},"x-app":"git"}},"/v1/git/webhook":{"post":{"operationId":"post_v1_git_webhook","summary":"Retired — forge pushes build via platform.hanzo.ai","description":"GONE (410). This was the canonical forge's push-to-deploy door, and it never dispatched a build in its life.\n\nIt handed each verified push to cloud.OnGitPush, a single-registrant seam whose only registrant lives in apps/platform. cloud runs each app as its own OS process, so in the git process that builder is nil forever — and this handler answered 204 either way. Delivered, signature valid, green on the forge's hook page, and nothing built.\n\nPush-to-deploy now belongs to POST https://platform.hanzo.ai/v1/git-webhook, which owns the build system-of-record and dispatches BuildKit Jobs. git.hanzo.ai delivers there through ONE forge-wide system webhook covering every repository; a repo opts in by committing hanzo.yml, not by owning a hook of its own.\n\nThe route is kept, and answers 410 naming that address, precisely so a misdirected delivery says what is wrong. Deleting it would 404, and a 404 here reads as 'the API is switched off' — the wrong conclusion this estate has already drawn twice.","tags":["git"],"x-app":"git"}},"/v1/git/zap/createRepo":{"post":{"operationId":"post_v1_git_zap_createrepo","summary":"Create a repository over the ZAP transport","description":"Creates a repository in the caller's org and project scope and answers with its record. `name` is required and `description` is optional; `project` narrows the scope within the org. A name already taken in that scope is a 409 envelope and an invalid name a 400.\n\nA ZAP PROCEDURE, not a REST resource. It answers the bridge's {status, msg, data} envelope rather than the raw view the /v1 route returns — which is a wire shape a typed op cannot produce, and the reason this stays a raw handler — and it calls the SAME core function the REST route calls, so the two transports cannot diverge in behaviour. Org and project scope come from the request identity and NEVER from the body: the body cannot widen the caller's scope. Without a validated org the answer is a 403 envelope.","tags":["git"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/zapProcReq"}}}},"x-app":"git"}},"/v1/git/zap/deleteRepo":{"post":{"operationId":"post_v1_git_zap_deleterepo","summary":"Delete a repository over the ZAP transport","description":"Deletes the repository named by `name` and answers with the deleted name. A repository outside the caller's org and project scope is a 404 envelope, so a delete can never reach another tenant's repository.\n\nA ZAP PROCEDURE, not a REST resource. It answers the bridge's {status, msg, data} envelope rather than the raw view the /v1 route returns — which is a wire shape a typed op cannot produce, and the reason this stays a raw handler — and it calls the SAME core function the REST route calls, so the two transports cannot diverge in behaviour. Org and project scope come from the request identity and NEVER from the body: the body cannot widen the caller's scope. Without a validated org the answer is a 403 envelope.","tags":["git"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/zapProcReq"}}}},"x-app":"git"}},"/v1/git/zap/getRepo":{"post":{"operationId":"post_v1_git_zap_getrepo","summary":"Read one repository over the ZAP transport","description":"Answers a single repository's record, named by `name`. A repository outside the caller's org and project scope is a 404 envelope, the same answer one that does not exist gets.\n\nA ZAP PROCEDURE, not a REST resource. It answers the bridge's {status, msg, data} envelope rather than the raw view the /v1 route returns — which is a wire shape a typed op cannot produce, and the reason this stays a raw handler — and it calls the SAME core function the REST route calls, so the two transports cannot diverge in behaviour. Org and project scope come from the request identity and NEVER from the body: the body cannot widen the caller's scope. Without a validated org the answer is a 403 envelope.","tags":["git"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/zapProcReq"}}}},"x-app":"git"}},"/v1/git/zap/listRepos":{"post":{"operationId":"post_v1_git_zap_listrepos","summary":"List your repositories over the ZAP transport","description":"Answers every repository in the caller's org and project scope. It reads NO body — the scope is entirely the caller's identity — so a request with an empty object is correct.\n\nA ZAP PROCEDURE, not a REST resource. It answers the bridge's {status, msg, data} envelope rather than the raw view the /v1 route returns — which is a wire shape a typed op cannot produce, and the reason this stays a raw handler — and it calls the SAME core function the REST route calls, so the two transports cannot diverge in behaviour. Org and project scope come from the request identity and NEVER from the body: the body cannot widen the caller's scope. Without a validated org the answer is a 403 envelope.","tags":["git"],"x-app":"git"}},"/v1/git/zap/usage":{"post":{"operationId":"post_v1_git_zap_usage","summary":"Report your org's git storage footprint over the ZAP transport","description":"Answers every repository in the caller's org with its size in bytes, plus the org's total — what git storage is actually being used, and by which repository. It reads NO body, and it is scoped to the caller's own org, so it is that org's footprint and never the fleet's.\n\nA ZAP PROCEDURE, not a REST resource. It answers the bridge's {status, msg, data} envelope rather than the raw view the /v1 route returns — which is a wire shape a typed op cannot produce, and the reason this stays a raw handler — and it calls the SAME core function the REST route calls, so the two transports cannot diverge in behaviour. Org and project scope come from the request identity and NEVER from the body: the body cannot widen the caller's scope. Without a validated org the answer is a 403 envelope.","tags":["git"],"x-app":"git"}},"/v1/git/{org}/{project}/{repo}/git-receive-pack":{"post":{"operationId":"post_v1_git_by_org_by_project_by_repo_git-receive-pack","summary":"Accept a push, and turn it into a build","description":"The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero — the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed under the API prefix, with the PROJECT as a middle path segment: project scope otherwise rides a header a git client cannot send, so this path is the only usable remote for a project-scoped repository. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/v1/git/{org}/{project}/{repo}/git-upload-pack":{"post":{"operationId":"post_v1_git_by_org_by_project_by_repo_git-upload-pack","summary":"Serve a clone or fetch","description":"The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol, streamed straight through git itself — request body to git's stdin, git's stdout to the response — so a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type other than `application/x-git-upload-pack-request` is 400. Addressed under the API prefix, with the PROJECT as a middle path segment: project scope otherwise rides a header a git client cannot send, so this path is the only usable remote for a project-scoped repository. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/v1/git/{org}/{project}/{repo}/info/refs":{"get":{"operationId":"get_v1_git_by_org_by_project_by_repo_info_refs","summary":"Advertise a repository's refs to a git client","description":"The ref-advertisement phase of git's smart-HTTP protocol — the first request a clone, a fetch and a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push, and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private repository reached without its org is 404, indistinguishable from one that does not exist. Addressed under the API prefix, with the PROJECT as a middle path segment: project scope otherwise rides a header a git client cannot send, so this path is the only usable remote for a project-scoped repository. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/v1/git/{org}/{repo}/git-receive-pack":{"post":{"operationId":"post_v1_git_by_org_by_repo_git-receive-pack","summary":"Accept a push, and turn it into a build","description":"The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero — the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed under the API prefix, so `git clone https://\u003chost\u003e/v1/git/\u003corg\u003e/\u003crepo\u003e.git` works on any host the binary serves. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/v1/git/{org}/{repo}/git-upload-pack":{"post":{"operationId":"post_v1_git_by_org_by_repo_git-upload-pack","summary":"Serve a clone or fetch","description":"The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol, streamed straight through git itself — request body to git's stdin, git's stdout to the response — so a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type other than `application/x-git-upload-pack-request` is 400. Addressed under the API prefix, so `git clone https://\u003chost\u003e/v1/git/\u003corg\u003e/\u003crepo\u003e.git` works on any host the binary serves. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/v1/git/{org}/{repo}/info/refs":{"get":{"operationId":"get_v1_git_by_org_by_repo_info_refs","summary":"Advertise a repository's refs to a git client","description":"The ref-advertisement phase of git's smart-HTTP protocol — the first request a clone, a fetch and a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push, and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private repository reached without its org is 404, indistinguishable from one that does not exist. Addressed under the API prefix, so `git clone https://\u003chost\u003e/v1/git/\u003corg\u003e/\u003crepo\u003e.git` works on any host the binary serves. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/v1/gpus":{"get":{"operationId":"listGpus","summary":"Returns one row per physical accelerator the caller's org has, derived from its real GPU machines (the size slug says how many cards a node holds) and from the accelerators BYO workers report through nvidia-smi.","description":"Returns one row per physical accelerator the caller's org has, derived\nfrom its real GPU machines (the size slug says how many cards a node holds) and\nfrom the accelerators BYO workers report through nvidia-smi.\n\nLive telemetry is absent on Visor rows because Visor's machine object carries\nnone — an honest omission the console renders as \"—\", never a fabricated 0.","tags":["gpus"],"responses":{"200":{"content":{"application/json":{"example":{"gpus":[{"id":"gpu-1#0","machine":"gpu-1","model":"H100","name":"gpu-1","provider":"digitalocean","region":"nyc2","status":"running"}]},"schema":{"$ref":"#/components/schemas/gpuList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/gpus/alerts":{"get":{"operationId":"listGpuAlerts","summary":"Is an HONEST empty surface: Visor exposes no GPU alert inventory, so this returns [] rather than fabricating alerts.","description":"Is an HONEST empty surface: Visor exposes no GPU alert inventory, so\nthis returns [] rather than fabricating alerts. It stays a real, tenant-gated\nroute so the console's alerts fetch resolves (200 [], not a 404) — an honest\n\"no alerts\", the same discipline the rest of the surface follows.","tags":["gpus"],"responses":{"200":{"content":{"application/json":{"example":{"alerts":[]},"schema":{"$ref":"#/components/schemas/gpuAlertList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/guide":{"get":{"operationId":"get_v1_guide","summary":"Overview returns the caller org's launch journey: the active curriculum's version and title, every step with its state, whether it is available, what blocks it and whether the Business AI can run it, the done/total/percent progress with the next step to take, and the org's analytics funnel folded in.","description":"Overview returns the caller org's launch journey: the active curriculum's\nversion and title, every step with its state, whether it is available, what\nblocks it and whether the Business AI can run it, the done/total/percent\nprogress with the next step to take, and the org's analytics funnel folded in.\nAuto-detect runs first, so a step the org has already completed elsewhere reads\ndone without anyone marking it.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/overviewView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/actions":{"get":{"operationId":"get_v1_guide_actions","summary":"Returns the caller org's Business AI action ledger, most recent first: every \"do it for me\" tool call, the arguments it ran with, its result and whether it succeeded.","description":"Returns the caller org's Business AI action ledger, most recent\nfirst: every \"do it for me\" tool call, the arguments it ran with, its result and\nwhether it succeeded. It is the audit-visible record of what the agent did on\nthe org's behalf, and the backing state for the \"acted\" auto-detect signal.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/actionsView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/analytics":{"get":{"operationId":"get_v1_guide_analytics","summary":"Analytics returns the caller org's funnel from the analytics lens plus the GTM recommendations derived from it.","description":"Analytics returns the caller org's funnel from the analytics lens plus the GTM\nrecommendations derived from it. It is the Business AI's data-grounded read —\nwhat the funnel is doing, and the next-best action to move its weakest stage. An\nunreachable or silent warehouse answers available=false, never a fabricated\nnumber.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/analyticsView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/blueprint":{"get":{"operationId":"get_v1_guide_blueprint","summary":"Returns the FULL authored brand blueprint — every principle, section, step, strategy and template WITH its enabled flag made explicit, including the disabled items the org-facing reads never see — plus the active version number, the brand key it is stored under and the item counts.","description":"Returns the FULL authored brand blueprint — every principle,\nsection, step, strategy and template WITH its enabled flag made explicit,\nincluding the disabled items the org-facing reads never see — plus the active\nversion number, the brand key it is stored under and the item counts. It is the\nSuperAdmin authoring view of the platform blueprint, so it is refused 403 for\nanyone else, including a per-org admin: the brand blueprint is shared platform\ncontent, not a per-customer surface.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/blueprintView"}}},"description":"ok"}},"x-app":"guide"},"put":{"operationId":"put_v1_guide_blueprint","summary":"Publish a new version of the brand blueprint","description":"Replaces the deployment's brand blueprint — the shared journey, sections, strategies and templates every org starts from — as a NEW VERSION, and answers the stored document with its key and version number. The previous versions are kept, so /blueprint/versions is a real recovery trail.\n\nSuperAdmin ONLY. A per-org admin is 403: this is platform content, not a per-customer surface — the per-customer surface is /v1/guide/curriculum. The write is audited.\n\nThe body is a blueprint document accepted as YAML **or** JSON, which is the caller-visible reason it takes a raw body. It must parse AND validate — unique ids throughout, an acyclic step graph with no dangling dependencies, every step's section and every strategy's principle resolving to a real one — or it is 422 and never becomes active, leaving the version already serving authoritative. An empty body is 400 and one over 16 MiB is 413.\n\nEdits are live: the next resolve reads the newest version. A stored document that is itself corrupt or schema-drifted does not block this write — the target is resolved without parsing what is there — so a bad version can always be published over.","tags":["guide"],"x-app":"guide"}},"/v1/guide/blueprint/versions":{"get":{"operationId":"get_v1_guide_blueprint_versions","summary":"Returns the brand blueprint's version history — every stored version's number and edit time, newest first — which is the point-in-time-recovery and audit trail behind the authoring plane.","description":"Returns the brand blueprint's version history — every\nstored version's number and edit time, newest first — which is the\npoint-in-time-recovery and audit trail behind the authoring plane. Metadata\nonly: the documents are not returned. SuperAdmin only, like the rest of this\nplane. The history is listable even when the current stored document no longer\nparses, so a schema-drifted row can still be diagnosed.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/blueprintVersionsView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/blueprint/{collection}/{id}":{"patch":{"operationId":"patch_v1_guide_blueprint_by_collection_by_id","summary":"Edit — or retire — one item of the brand blueprint","description":"Edits a single item of the brand blueprint by id and saves it as a NEW VERSION, answering the whole blueprint after the edit. `collection` is one of `sections`, `steps`, `strategies` or `templates`; anything else is 400, and an id that collection does not hold is 404. This is also the retire lever: `{\"enabled\": false}` takes an item out of every org's journey without deleting it or its history.\n\nSuperAdmin ONLY, like the rest of the authoring plane; a per-org admin is 403. The write is audited.\n\nThe patch is a SHALLOW merge over the item's own top-level keys — a key you send replaces that key whole, a key you omit is left alone — and `id` is dropped from the patch before it is applied, so an edit can never rekey an item. That is why the body has no declarable shape: its keys are the patched item's, not this route's.\n\nFail-closed on the WHOLE document, not just the item: the blueprint is re-validated after the merge, so a patch that would dangle a dependency, break the step DAG or empty the journey is 422 and nothing is saved. An empty patch is 400 and one over 16 MiB is 413.","tags":["guide"],"parameters":[{"name":"collection","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"guide"}},"/v1/guide/chat":{"post":{"operationId":"post_v1_guide_chat","summary":"Chat answers a founder's question about their launch journey as the Business AI coach: it grounds the reply in the org's REAL progress, its ranked available quests and its analytics funnel, and returns those candidate quests alongside so the caller can act on one.","description":"Chat answers a founder's question about their launch journey as the Business AI\ncoach: it grounds the reply in the org's REAL progress, its ranked available\nquests and its analytics funnel, and returns those candidate quests alongside so\nthe caller can act on one. READ-ONLY — it advises and never runs a step, so it\ncannot be talked into performing an action; the only executing path is POST\n/v1/guide/steps/{id}/do. One AI completion per call, billed to the caller's own\npayer.","tags":["guide"],"requestBody":{"content":{"application/json":{"example":{"message":"what should I do next to get my first customers?"},"schema":{"$ref":"#/components/schemas/chatRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/chatResponse"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/curriculum":{"delete":{"operationId":"delete_v1_guide_curriculum","summary":"Clears the caller org's curriculum override and returns the journey it falls back to — the brand blueprint, else the embedded fixture.","description":"Clears the caller org's curriculum override and returns the\njourney it falls back to — the brand blueprint, else the embedded fixture.\nClearing an org that never set one is a no-op that answers the same default.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/curriculumView"}}},"description":"ok"}},"x-app":"guide"},"get":{"operationId":"get_v1_guide_curriculum","summary":"Returns the journey the caller's org is actually running, and whether it comes from the org's OWN override (custom) or from the platform default — the brand blueprint, else the embedded fixture.","description":"Returns the journey the caller's org is actually running, and\nwhether it comes from the org's OWN override (custom) or from the platform\ndefault — the brand blueprint, else the embedded fixture.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/curriculumView"}}},"description":"ok"}},"x-app":"guide"},"put":{"operationId":"put_v1_guide_curriculum","summary":"Replace your org's journey with a curriculum you author","description":"Sets the caller org's OWN curriculum — the per-customer override — and answers the journey now in force with `custom: true`. The body is a curriculum document, and it is accepted as YAML **or** JSON: that is the caller-visible reason this takes a raw body rather than a declared shape. Whatever the syntax, the CANONICAL parsed form is what is stored, so the document the engine runs never depends on how it was written.\n\nFail-closed: a body that does not parse, or parses but is not a valid journey (unique step ids, no dangling or cyclic dependencies), is 422 and NEVER becomes active — the org keeps the journey it had. Requires a validated org; 403 without one. An empty body is 400 and one over 256 KiB is 413.\n\nThis is tier one only. It overrides nothing but this org's own journey; the shared brand blueprint is a different surface with a different gate. DELETE the same path to drop the override and fall back to it.","tags":["guide"],"x-app":"guide"}},"/v1/guide/profile":{"get":{"operationId":"get_v1_guide_profile","summary":"Profile returns the caller org's OBSERVED growth profile — the signal set, the classified growth stage, and the org's own key metrics.","description":"Profile returns the caller org's OBSERVED growth profile — the signal set, the\nclassified growth stage, and the org's own key metrics. It is a pure READ,\nrecomputed from the org's CURRENT state each request (real-time by pull): it\nreuses the reconcile path (snapshotFor runs the detectors) for launch progress\nand runs the growth probes (observe) for the signals — it never caches, never\nruns a billable effect, never targets another org. Org-scoped on the validated\nprincipal; fail-closed without one. It PRODUCES the profile and classifies the\nstage; it decides NO recommendation (that is a later surface).","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/profileResponse"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/steps/{id}/do":{"post":{"operationId":"post_v1_guide_steps_by_id_do","summary":"Have the Business AI actually do the step for you","description":"Executes one step of the caller org's journey through that principal's OWN tool plane and answers the action log — `{step, events, state}` — so the caller sees every tool call the agent made and where the step ended up. This is the ONE executing path in guide: suggest and chat advise, this acts, and the work is charged to the calling principal's ledger.\n\nAsk for it live and the same actions arrive as Server-Sent Events instead, on either of two triggers — `Accept: text/event-stream` or `?stream=1`. The stream opens with a comment, emits one frame per action as it happens, and closes with an `end` frame carrying `ok` and the final state. The streamed run is detached and bounded at 120 seconds, so it finishes on its own clock once the response has begun.\n\nAn agent that FAILS is not a failed request: the JSON answer still comes back 200 with `error` beside the events it did manage, and the stream still ends with `ok:false`. The refusals are the ones before the agent runs — 409 with `{error, step, blockedBy}` for a step whose dependencies are unfinished, 404 for an id the journey does not contain, 403 without a validated org.","tags":["guide"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"guide"}},"/v1/guide/steps/{id}/done":{"post":{"operationId":"post_v1_guide_steps_by_id_done","summary":"Mark a step of your org's journey finished","description":"Moves one step of the caller org's journey to done and answers the whole refreshed journey, which is what unblocks everything downstream of it.\n\nDependency-GATED like start: finishing a step whose prerequisites are themselves unfinished is 409 carrying `{error, step, blockedBy}` naming what is in the way, not a silent success. A step id the org's active journey does not contain is 404. Skipping is the ungated alternative — a founder declaring a step does not apply — and it lives at /skip.\n\nRequires a validated org; 403 without one. The mark is recorded as `manual`, and /reset returns the step to todo.","tags":["guide"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"guide"}},"/v1/guide/steps/{id}/reset":{"post":{"operationId":"post_v1_guide_steps_by_id_reset","summary":"Returns one step of the caller org's journey to todo — clearing a manual mark or a skip — and returns the refreshed journey.","description":"Returns one step of the caller org's journey to todo — clearing a\nmanual mark or a skip — and returns the refreshed journey. Reset is never\ndependency-gated. Auto-detect runs on the next read, so a step the org has in\nfact completed elsewhere goes straight back to done.","tags":["guide"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the step's id, as it appears in the journey (e.g. \"gsuite\").","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/overviewView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/steps/{id}/skip":{"post":{"operationId":"post_v1_guide_steps_by_id_skip","summary":"Marks one step of the caller org's journey skipped and returns the refreshed journey.","description":"Marks one step of the caller org's journey skipped and returns the\nrefreshed journey. Skipping is never dependency-gated — the founder is\ndeclaring the step does not apply to them — so a step whose dependencies are\nunfinished can still be skipped, and a skipped step counts as terminal for\neverything downstream of it.","tags":["guide"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the step's id, as it appears in the journey (e.g. \"gsuite\").","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/overviewView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/steps/{id}/start":{"post":{"operationId":"post_v1_guide_steps_by_id_start","summary":"Mark a step of your org's journey started","description":"Moves one step of the caller org's journey to in-progress and answers the whole refreshed journey, so a console needs no second read.\n\nThe transition is dependency-GATED, and that is why the answer set is wider than a success: a step whose prerequisites are unfinished is 409 carrying `{error, step, blockedBy}`, where `blockedBy` names the exact steps in the way — enough to render the blockage rather than merely report it. A step id the org's active journey does not contain is 404.\n\nRequires a validated org; 403 without one, and the journey read and written is that org's alone. The mark is recorded as `manual`, and the journey is reconciled against the auto-detectors on every read, so a step the org has demonstrably completed elsewhere can still be moved to done underneath it.","tags":["guide"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"guide"}},"/v1/guide/strategies":{"get":{"operationId":"get_v1_guide_strategies","summary":"Strategies returns the ENABLED tactics corpus for the caller's org: the tactics library narrowed by the explicit category/workload filters AND by the org's OBSERVED growth stage and capability signals (a tactic's tags are preconditions, so it surfaces only once the org can act on it).","description":"Strategies returns the ENABLED tactics corpus for the caller's org: the tactics\nlibrary narrowed by the explicit category/workload filters AND by the org's\nOBSERVED growth stage and capability signals (a tactic's tags are\npreconditions, so it surfaces only once the org can act on it). Passing stage\nPREVIEWS the corpus at that stage instead of the observed one. The content is\nshared platform data — no org's records — and the read is never a billable\neffect.","tags":["guide"],"parameters":[{"name":"category","in":"query","required":false,"description":"Category filters to tactics in exactly this category.","schema":{"type":"string"},"example":"viral-coefficient"},{"name":"stage","in":"query","required":false,"description":"Stage previews the corpus at a chosen growth stage\n(research|formed|launched|activated|scaling), overriding the org's observed\none. An unknown value is ignored and the observed stage stands.","schema":{"type":"string"},"example":"scaling"},{"name":"workload","in":"query","required":false,"description":"Workload filters to tactics with exactly this workload.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/corpusView"}}},"description":"ok"}},"x-app":"guide"}},"/v1/guide/suggest":{"get":{"operationId":"get_v1_guide_suggest","summary":"Suggest returns the caller org's next-best quests: the available, non-terminal steps of its journey ranked by how much downstream work each unblocks, each with the grounded reason it is a good next move and whether the Business AI can run it, plus the org's funnel and the GTM recommendations derived from it.","description":"Suggest returns the caller org's next-best quests: the available, non-terminal\nsteps of its journey ranked by how much downstream work each unblocks, each with\nthe grounded reason it is a good next move and whether the Business AI can run\nit, plus the org's funnel and the GTM recommendations derived from it. A\nbest-effort AI narrative over exactly those quests and numbers is included when\nan AI plane is wired. READ-ONLY: it advises and never runs a step — the only\nexecuting path is POST /v1/guide/steps/{id}/do.","tags":["guide"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/suggestResponse"}}},"description":"ok"}},"x-app":"guide"}},"/v1/health":{"get":{"operationId":"get_v1_health","summary":"Check if the system is live","description":"Check if the system is live","tags":["health"],"x-app":"github.com/hanzoai/ai"}},"/v1/help/articles":{"get":{"operationId":"get_v1_help_articles","summary":"Returns the public knowledge base: the help center's Published, publicly-visible articles as cards.","description":"Returns the public knowledge base: the help center's Published,\npublicly-visible articles as cards. The org is server-fixed and the\nstatus/is_public filter is server-set, so neither the tenant nor the visibility\ncan be widened by the caller. A deployment with no help center answers 404.","tags":["help"],"parameters":[{"name":"category","in":"query","required":false,"description":"Category narrows the list to one knowledge-base section, matched against\nthe article's category by exact name. Empty lists every section.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many articles are returned. Anything that is not a positive\ninteger uses 50, and values above 200 are clamped to 200.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/helpArticleList"}}},"description":"ok"}},"x-app":"help"}},"/v1/help/articles/{slug}":{"get":{"operationId":"get_v1_help_articles_by_slug","summary":"Returns one public article by slug, with its body.","description":"Returns one public article by slug, with its body. A missing, Draft,\nor internal (non-public) article is 404 — fail-closed, so this route is no\nexistence oracle for anything beyond \"published and public\".","tags":["help"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the article's public identifier, from the path. It IS the document\nname in the help center's store.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/helpArticle"}}},"description":"ok"}},"x-app":"help"}},"/v1/help/categories":{"get":{"operationId":"get_v1_help_categories","summary":"Returns the knowledge-base sections for the public center's navigation — but ONLY the sections that front at least one Published, public article, so an internal (agent-only) category name or description never leaks.","description":"Returns the knowledge-base sections for the public center's\nnavigation — but ONLY the sections that front at least one Published, public\narticle, so an internal (agent-only) category name or description never leaks. A\nsection with no public article is invisible; a center with no public articles has\nno sections, which is an empty list rather than an error.","tags":["help"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/helpCategoryList"}}},"description":"ok"}},"x-app":"help"}},"/v1/help/tickets":{"post":{"operationId":"post_v1_help_tickets","summary":"Files a customer support ticket into the public help center.","description":"Files a customer support ticket into the public help center. It\ncreates the ticket (status Open, source portal) with the customer's message on\nthe description, then records that same message as the opening entry of the\nticket's conversation thread; the description carries it regardless, so failing\nto write that entry loses nothing. Answers 201 with an opaque reference.\n\nA deployment with no help center answers 404, one whose center has not installed\nthe Help model answers 503, and a body over 64 KiB answers 413 — in that order,\nwhich is the order the route has always decided them in.","tags":["help"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/helpTicketIntake"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/helpTicketFiled"}}},"description":"created"}},"x-app":"help"}},"/v1/iam/.well-known/jwks":{"get":{"operationId":"get_v1_iam_.well-known_jwks","summary":"Publishes the public keys that verify the tokens issued here — the one URL you point a service at so it can check a token itself, offline, without calling back and without holding any secret of ours.","description":"Publishes the public keys that verify the tokens issued here — the\none URL you point a service at so it can check a token itself, offline, without\ncalling back and without holding any secret of ours.\n\nKeys appear here before they start signing and stay after they stop, so a\nrotation never leaves a live token unverifiable. Nothing private is ever\npublished.","tags":["iam"],"x-app":"iam"}},"/v1/iam/.well-known/oauth-authorization-server":{"get":{"operationId":"get_v1_iam_.well-known_oauth-authorization-server","summary":"Returns the OpenID Connect discovery document — the one URL you point a standards-compliant client at so it can find every other endpoint on its own, instead of you configuring them by hand.","description":"Returns the OpenID Connect discovery document — the one URL you\npoint a standards-compliant client at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins the issuer never sees it change.","tags":["iam"],"x-app":"iam"}},"/v1/iam/.well-known/openid-configuration":{"get":{"operationId":"get_v1_iam_.well-known_openid-configuration","summary":"Returns the OpenID Connect discovery document — the one URL you point a standards-compliant client at so it can find every other endpoint on its own, instead of you configuring them by hand.","description":"Returns the OpenID Connect discovery document — the one URL you\npoint a standards-compliant client at so it can find every other endpoint on\nits own, instead of you configuring them by hand.\n\nIt advertises only what is actually implemented, so a client that reads it\ncannot ask for a flow that will fail: the authorization-code flow, PKCE with\nS256, the supported grants, and the signing algorithms whose public keys the\nJWKS really publishes.\n\nThe issuer is derived from the host you asked on and is the same value the\ntokens carry, so a client that pins the issuer never sees it change.","tags":["iam"],"x-app":"iam"}},"/v1/iam/account":{"get":{"operationId":"get_v1_iam_account","summary":"Returns the signed-in person's own account and the organization they belong to — what a console reads to draw the account menu.","description":"Returns the signed-in person's own account and the organization\nthey belong to — what a console reads to draw the account menu.\n\nPasswords, API secrets and MFA material are stripped. It answers for a session\ncookie or a bearer token alike.","tags":["iam"],"x-app":"iam"}},"/v1/iam/add-application":{"post":{"operationId":"post_v1_iam_add-application","summary":"Registers an application in your organization — one product or site your people sign in to, with its own client credentials, sign-in methods and allowed redirect URIs.","description":"Registers an application in your organization — one product or site your\npeople sign in to, with its own client credentials, sign-in methods and\nallowed redirect URIs.\n\nThe older spelling of POST /v1/iam/application. A name already used in the\norganization is refused rather than overwritten.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-membership":{"post":{"operationId":"post_v1_iam_add-membership","summary":"Lets a person or an application act in an organization.","description":"Lets a person or an application act in an organization. It is the grant\nbehind \"add someone to the team\", and it is safe to repeat — granting a\nmembership that already exists changes nothing. Granting membership IS the org's authority to give, so it takes the\nsame gate a write to that org's own registry row takes: a SuperAdmin, an admin\nof the org itself, or an org-admin-capable confidential client. One rule, one\nplace (internal/authz).","tags":["iam"],"x-app":"iam"}},"/v1/iam/add-organization":{"post":{"operationId":"post_v1_iam_add-organization","summary":"Creates an organization — the account everything else in your directory hangs from.","description":"Creates an organization — the account everything else in your directory\nhangs from. Users, applications, roles, projects and workspaces are all\nnamed inside one organization, so this is the first write in a new tenant.\n\nThe older spelling of POST /v1/iam/organizations. Both reach the same\ncreate, so a name already taken is refused here too.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.CreateOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-project":{"post":{"operationId":"post_v1_iam_add-project","summary":"Creates a project inside your organization — the scope people pick between when their work is separated by product or client rather than by team.","description":"Creates a project inside your organization — the scope people pick between\nwhen their work is separated by product or client rather than by team.\n\nThe older spelling of POST /v1/iam/projects. Creating one takes an\nadministrator of the owning organization.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-provider":{"post":{"operationId":"post_v1_iam_add-provider","summary":"Adds an identity provider your people can sign in with, or a service your applications send through — a social or enterprise login, an email or SMS sender, a storage or payment connector.","description":"Adds an identity provider your people can sign in with, or a service your\napplications send through — a social or enterprise login, an email or SMS\nsender, a storage or payment connector.\n\nA provider is configured once here and then switched on per application, so\nseveral applications can share one set of credentials.\n\nThe older spelling of POST /v1/iam/providers.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Provider"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-role":{"post":{"operationId":"post_v1_iam_add-role","summary":"Creates a role — a named group of people that permissions are granted to.","description":"Creates a role — a named group of people that permissions are granted to.\nGranting to a role rather than to each person is what keeps access correct\nas your team changes: add someone to the role and they inherit everything\nit can do.\n\nThe older spelling of POST /v1/iam/roles.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-user":{"post":{"operationId":"post_v1_iam_add-user","summary":"Adds a person to your organization and, if you send a password, sets the one they will sign in with.","description":"Adds a person to your organization and, if you send a password, sets the\none they will sign in with. The password is hashed before it is stored and\nis never returned to you or to anyone else.\n\nUsernames are checked against one rule wherever an account is created —\nthis verb, password signup, a social sign-in, or SCIM — so a name accepted\nhere is a name accepted everywhere.\n\nThe older spelling of POST /v1/iam/users, and it posts the user's fields at\nthe top level rather than wrapped in {user, password}.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.userBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/add-workspace":{"post":{"operationId":"post_v1_iam_add-workspace","summary":"Creates a workspace inside your organization — the scope a team works in, alongside projects rather than instead of them.","description":"Creates a workspace inside your organization — the scope a team works in,\nalongside projects rather than instead of them.\n\nThe older spelling of POST /v1/iam/workspaces. Creating one takes an\nadministrator of the owning organization.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/admin/applications/upsert":{"post":{"operationId":"upsertApplication","summary":"Creates an application or updates it in place, so a deployment can declare the applications it needs and run the same declaration on every environment and on every redeploy.","description":"Creates an application or updates it in place, so a\ndeployment can declare the applications it needs and run the same declaration\non every environment and on every redeploy.\n\nIt says which of the two it did. Leave the client secret out and the existing\none is kept — so re-running your deployment does not rotate a credential your\nrunning services are holding.","tags":["iam"],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.registration"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"internal server error"}},"x-app":"iam"}},"/v1/iam/admin/provision":{"post":{"operationId":"post_v1_iam_admin_provision","summary":"Sets up an account on someone's behalf — the same onboarding a person gets themselves, driven by one of your own services instead of by them.","description":"Sets up an account on someone's behalf — the same\nonboarding a person gets themselves, driven by one of your own services\ninstead of by them.\n\nIt authenticates as your service rather than as a person, which is why the\nperson to provision is named in the request. The setup it performs is\nidentical to self-service onboarding; there is one provisioning path, not\ntwo that can drift.","tags":["iam"],"x-app":"iam"}},"/v1/iam/admin/users/upsert":{"post":{"operationId":"upsertUser","summary":"Creates a person or updates them in place, so a deployment can declare the accounts it needs and re-run that declaration safely.","description":"Creates a person or updates them in place, so a deployment can\ndeclare the accounts it needs and re-run that declaration safely.\n\nPasswords are hashed before they are stored. Leave the password out and their\ncurrent one is kept, so a redeploy never locks somebody out.","tags":["iam"],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.person"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.reply"}}},"description":"internal server error"}},"x-app":"iam"}},"/v1/iam/application":{"delete":{"operationId":"delete_v1_iam_application","summary":"Removes an application.","description":"Removes an application. Anyone mid-sign-in through it is\nturned away and its client credentials stop working, so retire the integration\nbefore deleting it.","tags":["iam","compat"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteResult"}}},"description":"ok"}},"x-app":"iam"},"get":{"operationId":"get_v1_iam_application","summary":"Returns one application: its sign-in methods, its allowed redirect URIs and the client credentials your integration authenticates with.","description":"Returns one application: its sign-in methods, its allowed\nredirect URIs and the client credentials your integration authenticates with.","tags":["iam","compat"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_application","summary":"Registers an application in your organization — one product or site your people sign in to, with its own client credentials, sign-in methods and allowed redirect URIs.","description":"Registers an application in your organization — one product or site\nyour people sign in to, with its own client credentials, sign-in methods and\nallowed redirect URIs. A name already used in the organization is refused\nrather than overwritten.\n\nExported so the legacy add-application alias reuses this exact path — one\ncreate, two spellings.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"},"put":{"operationId":"put_v1_iam_application","summary":"Changes an application's display, its sign-in methods and the redirect URIs it may return to — the call that makes login work from a new host.","description":"Changes an application's display, its sign-in methods and the redirect\nURIs it may return to — the call that makes login work from a new host. Which\norganization it belongs to and what it is named are fixed when it is created\nand are not editable here.\n\nExported so the legacy update-application alias reuses this exact path — one\nupdate, two spellings.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/applications":{"get":{"operationId":"get_v1_iam_applications","summary":"Returns the applications in one organization, newest first — each product or site your people sign in to, with the sign-in methods and redirect URIs it allows.","description":"Returns the applications in one organization, newest first —\neach product or site your people sign in to, with the sign-in methods and\nredirect URIs it allows.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ApplicationListResult"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_applications","summary":"Registers an application in your organization — one product or site your people sign in to, with its own client credentials, sign-in methods and allowed redirect URIs.","description":"Registers an application in your organization — one product or site\nyour people sign in to, with its own client credentials, sign-in methods and\nallowed redirect URIs. A name already used in the organization is refused\nrather than overwritten.\n\nExported so the legacy add-application alias reuses this exact path — one\ncreate, two spellings.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/applications/delete":{"post":{"operationId":"post_v1_iam_applications_delete","summary":"Removes an application.","description":"Removes an application. Anyone mid-sign-in through it is\nturned away and its client credentials stop working, so retire the integration\nbefore deleting it.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ApplicationRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/applications/get":{"get":{"operationId":"get_v1_iam_applications_get","summary":"Returns one application: its sign-in methods, its allowed redirect URIs and the client credentials your integration authenticates with.","description":"Returns one application: its sign-in methods, its allowed\nredirect URIs and the client credentials your integration authenticates with.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/applications/update":{"post":{"operationId":"post_v1_iam_applications_update","summary":"Changes an application's display, its sign-in methods and the redirect URIs it may return to — the call that makes login work from a new host.","description":"Changes an application's display, its sign-in methods and the redirect\nURIs it may return to — the call that makes login work from a new host. Which\norganization it belongs to and what it is named are fixed when it is created\nand are not editable here.\n\nExported so the legacy update-application alias reuses this exact path — one\nupdate, two spellings.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/audit-logs":{"get":{"operationId":"get_v1_iam_audit-logs","summary":"Returns your organization's audit trail, newest first — who did what, when, and from where.","description":"Returns your organization's audit trail, newest first — who did\nwhat, when, and from where. It is the record you reach for during a security\nreview or an incident.\n\nYou see your own organization's audit trail and no one else's; which organization that\nis comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_audit-logs","summary":"Records an audit entry, so activity from your own systems lands in the same trail as everything the Hanzo Cloud records for you.","description":"Records an audit entry, so activity from your own systems lands in the\nsame trail as everything the Hanzo Cloud records for you.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.auditlogs.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/audit-logs/delete":{"post":{"operationId":"post_v1_iam_audit-logs_delete","summary":"Removes an audit entry.","description":"Removes an audit entry. Retention policy is normally what should expire\na trail; deleting by hand leaves a gap a reviewer will notice.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/audit-logs/get":{"post":{"operationId":"post_v1_iam_audit-logs_get","summary":"Returns one audit entry in full: the action, the person or key behind it, and the request it came in on.","description":"Returns one audit entry in full: the action, the person or key behind it,\nand the request it came in on.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/audit-logs/update":{"post":{"operationId":"post_v1_iam_audit-logs_update","summary":"Corrects an audit entry.","description":"Corrects an audit entry. The trail is append-only in normal operation\nand nothing in the Hanzo Cloud rewrites it — this exists for an administrator\nto correct an entry their own systems recorded wrongly.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.auditlogs.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/auth/application":{"get":{"operationId":"get_v1_iam_auth_application","summary":"Returns everything a login screen needs to draw itself for one application: its branding, and each sign-in method it offers with the provider details that method needs.","description":"Returns everything a login screen needs to draw itself for one\napplication: its branding, and each sign-in method it offers with the provider\ndetails that method needs.\n\nThe client secret is masked. Read before anyone has signed in, so it carries\nonly what is safe for a browser to see.","tags":["iam"],"parameters":[{"name":"clientId","in":"query","required":false,"description":"ClientId is the application's OAuth client id — the one field that selects\nwhich login screen this is.","schema":{"type":"string"}},{"name":"responseType","in":"query","required":false,"description":"ResponseType is the OAuth response type the screen will ask for. Only \"code\"\nis served; anything else is refused here rather than at the authorize leg,\nwhere the person has already typed a password.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"}},"/v1/iam/auth/methods":{"get":{"operationId":"get_v1_iam_auth_methods","summary":"Returns the sign-in methods one application actually has switched on, so a login screen can render the right buttons for it without you hard-coding a list that drifts the moment you add a provider.","description":"Returns the sign-in methods one application actually has switched\non, so a login screen can render the right buttons for it without you\nhard-coding a list that drifts the moment you add a provider.\n\nPublic by design: it is read before anyone has signed in, and it exposes only\nwhich methods exist, never their credentials.","tags":["iam"],"parameters":[{"name":"clientId","in":"query","required":false,"description":"ClientId is the application's OAuth client id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"}},"/v1/iam/certs":{"get":{"operationId":"get_v1_iam_certs","summary":"Returns your organization's signing certificates, newest first — the keys the tokens your applications verify are signed with.","description":"Returns your organization's signing certificates, newest first — the keys\nthe tokens your applications verify are signed with. Private key material is\nmasked.\n\nYou see your own organization's certificates and no one else's; which\norganization that is comes from your credentials, not from the request, so a\nquery parameter can never widen the listing.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.certs.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_certs","summary":"Adds a signing certificate your applications can verify tokens against — the call you make to bring your own key, or to stage the next one before a rotation.","description":"Adds a signing certificate your applications can verify tokens against\n— the call you make to bring your own key, or to stage the next one before a\nrotation. A name already used in your organization is refused.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/certs/delete":{"post":{"operationId":"post_v1_iam_certs_delete","summary":"Removes a signing certificate.","description":"Removes a signing certificate. Tokens signed with it can no longer be\nverified, so retire it only once nothing is still presenting them.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.certs.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.certs.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/certs/get":{"post":{"operationId":"post_v1_iam_certs_get","summary":"Returns one signing certificate — its algorithm, its validity window and its public half.","description":"Returns one signing certificate — its algorithm, its validity window and\nits public half. The private key is masked.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.certs.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/certs/update":{"post":{"operationId":"post_v1_iam_certs_update","summary":"Changes a signing certificate's settings.","description":"Changes a signing certificate's settings. What it is called does not\nchange, and neither does when it was added.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/consent":{"get":{"operationId":"get_v1_iam_consent","summary":"Returns the calling person's own privacy and communication choices.","description":"Returns the calling person's own privacy and communication\nchoices. Somebody who has never set them gets the defaults rather than\nnothing, so a consent screen always has something to show — insights on, and\ntraining UNANSWERED, which is the state that means the screen still has to ask.","tags":["iam"],"x-app":"iam"},"put":{"operationId":"put_v1_iam_consent","summary":"Records the calling person's privacy and communication choices.","description":"Records the calling person's privacy and communication\nchoices. Only their own — there is no way to set consent for somebody else.\n\nSend only the answers you are changing. A question you leave out keeps the\nanswer it already had, so a screen that saves one switch never revokes the\nother, and two screens saving at once do not undo each other.\n\nAn answer this version does not recognize is refused here rather than stored,\nso nothing is ever persisted for a later reader to have to interpret.","tags":["iam"],"x-app":"iam"}},"/v1/iam/delete-application":{"post":{"operationId":"post_v1_iam_delete-application","summary":"Deletes an application.","description":"Deletes an application. Anyone mid-sign-in through it is turned away and\nits client credentials stop working, so retire the integration first.\n\nThe older spelling of DELETE /v1/iam/application.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-membership":{"post":{"operationId":"post_v1_iam_delete-membership","summary":"Takes away a person's or an application's right to act in an organization.","description":"Takes away a person's or an application's right to act in an\norganization. Their account survives; what ends is their access to that\norganization. Revoking a membership that is already gone reports that nothing\nwas removed rather than failing, so a retry is safe. It is the mirror of ensure and takes the SAME gate:\nrevoking membership is the org's authority to give or take, so a SuperAdmin, an\nadmin of the org itself, or an org-admin-capable confidential client. Idempotent\nthrough the store — deleting an absent membership reports removed=false, never an\nerror — so a retried revoke is safe.","tags":["iam"],"x-app":"iam"}},"/v1/iam/delete-mfa":{"post":{"operationId":"post_v1_iam_delete-mfa","summary":"Turns off the authenticator app for an account, so sign-in stops asking for a code.","description":"Turns off the authenticator app for an account, so sign-in stops\nasking for a code. People may do this for themselves; doing it for somebody\nelse takes an administrator, which is what makes it the reset path when a\nphone is lost.","tags":["iam"],"x-app":"iam"}},"/v1/iam/delete-organization":{"post":{"operationId":"post_v1_iam_delete-organization","summary":"Deletes an organization and everything named inside it — its users, applications, roles, projects and workspaces.","description":"Deletes an organization and everything named inside it — its users,\napplications, roles, projects and workspaces. There is no undo, and every\nsession issued under it stops working.\n\nThe older spelling of POST /v1/iam/organizations/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-project":{"post":{"operationId":"post_v1_iam_delete-project","summary":"Deletes a project.","description":"Deletes a project. The people and roles in your organization are unchanged;\nwhat goes is the scope itself, so anything addressed by it must move first.\n\nThe older spelling of POST /v1/iam/projects/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-provider":{"post":{"operationId":"post_v1_iam_delete-provider","summary":"Removes a provider.","description":"Removes a provider. Sign-in through it stops for every application that\nused it, so detach those applications first if they have no other method.\n\nThe older spelling of POST /v1/iam/providers/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Provider"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-role":{"post":{"operationId":"post_v1_iam_delete-role","summary":"Deletes a role.","description":"Deletes a role. Everyone in it loses the access it carried; their accounts\nand any other roles they hold are untouched.\n\nThe older spelling of POST /v1/iam/roles/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-user":{"post":{"operationId":"post_v1_iam_delete-user","summary":"Removes a person from your organization.","description":"Removes a person from your organization. Their sessions stop working and\nthe account is gone, not suspended — to keep the record and only stop\nsign-in, update the user instead.\n\nThe older spelling of POST /v1/iam/users/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.userBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/delete-workspace":{"post":{"operationId":"post_v1_iam_delete-workspace","summary":"Deletes a workspace.","description":"Deletes a workspace. The people and roles in your organization are\nunchanged; what goes is the scope itself.\n\nThe older spelling of POST /v1/iam/workspaces/delete.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/get-account":{"get":{"operationId":"get_v1_iam_get-account","summary":"Returns the signed-in person's own account and the organization they belong to — what a console reads to draw the account menu.","description":"Returns the signed-in person's own account and the organization\nthey belong to — what a console reads to draw the account menu.\n\nPasswords, API secrets and MFA material are stripped. It answers for a session\ncookie or a bearer token alike.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-app-login":{"get":{"operationId":"get_v1_iam_get-app-login","summary":"Returns everything a login screen needs to draw itself for one application: its branding, and each sign-in method it offers with the provider details that method needs.","description":"Returns everything a login screen needs to draw itself for one\napplication: its branding, and each sign-in method it offers with the provider\ndetails that method needs.\n\nThe client secret is masked. Read before anyone has signed in, so it carries\nonly what is safe for a browser to see.","tags":["iam"],"parameters":[{"name":"clientId","in":"query","required":false,"description":"ClientId is the application's OAuth client id — the one field that selects\nwhich login screen this is.","schema":{"type":"string"}},{"name":"responseType","in":"query","required":false,"description":"ResponseType is the OAuth response type the screen will ask for. Only \"code\"\nis served; anything else is refused here rather than at the authorize leg,\nwhere the person has already typed a password.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"}},"/v1/iam/get-application":{"get":{"operationId":"get_v1_iam_get-application","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-applications":{"get":{"operationId":"get_v1_iam_get-applications","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-cert":{"get":{"operationId":"get_v1_iam_get-cert","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-certs":{"get":{"operationId":"get_v1_iam_get-certs","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-global-users":{"get":{"operationId":"get_v1_iam_get-global-users","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-invitations":{"get":{"operationId":"get_v1_iam_get-invitations","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-memberships":{"get":{"operationId":"get_v1_iam_get-memberships","summary":"Answers either question about who belongs where: which organizations one person can act in, or who can act in one organization.","description":"Answers either question about who belongs where: which organizations one\nperson can act in, or who can act in one organization.\n\nBoth are org-scoped: a non-SuperAdmin may ask about ITS OWN org's roster, or\nabout a user whose home org is its own, and nothing else. The bound comes from\nthe verified credential via authz.Scope, so a request parameter can never\nwiden it — a membership row names who may act and spend in an org, so a\ncross-tenant read is a customer roster leak.","tags":["iam"],"parameters":[{"name":"user","in":"query","required":false,"description":"User is \"\u003chomeOrg\u003e/\u003cusername\u003e\" — which organizations that identity may act in.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org is an organization — who may act in it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"}},"/v1/iam/get-organization":{"get":{"operationId":"get_v1_iam_get-organization","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-organization-projects":{"get":{"operationId":"get_v1_iam_get-organization-projects","summary":"Returns one organization's projects — what a scope switcher lists so somebody can move between them.","description":"Returns one organization's projects — what a scope switcher\nlists so somebody can move between them.\n\nYou see your own organization and no other, whatever the request asks for.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-organization-workspaces":{"get":{"operationId":"get_v1_iam_get-organization-workspaces","summary":"Returns one organization's workspaces — what a scope switcher lists so somebody can move between them.","description":"Returns one organization's workspaces — what a scope\nswitcher lists so somebody can move between them.\n\nYou see your own organization and no other, whatever the request asks for.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-organizations":{"get":{"operationId":"get_v1_iam_get-organizations","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-permission":{"get":{"operationId":"get_v1_iam_get-permission","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-permissions":{"get":{"operationId":"get_v1_iam_get-permissions","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-provider":{"get":{"operationId":"get_v1_iam_get-provider","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-providers":{"get":{"operationId":"get_v1_iam_get-providers","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-records":{"get":{"operationId":"get_v1_iam_get-records","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-role":{"get":{"operationId":"get_v1_iam_get-role","summary":"Reads one record — the older spelling of the single reads on the REST surface, over the same data and the same permissions.","description":"Reads one record — the older spelling of the single reads on the\nREST surface, over the same data and the same permissions.\n\nSecrets are stripped. Naming a record in another organization does not reach\nit, however the request spells it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-roles":{"get":{"operationId":"get_v1_iam_get-roles","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-user":{"get":{"operationId":"get_v1_iam_get-user","summary":"Reads one person, two ways.","description":"Reads one person, two ways.\n\nName them and it is an ordinary read, with secrets stripped. Or hand it a\nSECRET API key and it answers with the person that key belongs to — how a\nservice of yours turns a credential on an incoming request into an identity.\n\nA publishable key resolves to nobody here, deliberately: it is safe to ship in\na browser precisely because it names an organization and never a person.\n\nget-user is handler-authorized (authz.handlerAuthorizedExact) because the key\nvariant carries no owner/name for the Guard to authorize; so the owner/name\nvariant reinstates the SAME read authorization the Guard applies, through the ONE\npolicy function (authz.Can) — identical behavior, a cross-tenant or non-self read\nstill refused 403 — then reuses the generic getHandler verbatim for resolution and\nredaction. No authz and no CRUD is reimplemented.","tags":["iam"],"x-app":"iam"}},"/v1/iam/get-users":{"get":{"operationId":"get_v1_iam_get-users","summary":"Lists one kind of record in your organization — the older spelling of the collection reads on the REST surface, over the same data and the same permissions.","description":"Lists one kind of record in your organization — the older spelling\nof the collection reads on the REST surface, over the same data and the same\npermissions.\n\nSecrets are stripped from every row. Send both a page number and a page size to\npage, and the total comes back alongside; send neither and you get the whole\nset. You see your own organization and no other, whatever the request asks for.\n\nScoping note (intentional, fail-closed): iam's ownership model is mixed —\nusers/roles/permissions are owned by their tenant org, while organizations/\napplications/providers/certs are platform-owned (Owner \"admin\"). A SuperAdmin\n(Scope → the requested owner, empty = all) therefore lists every entity, which\nis the console-admin path. A non-super is pinned by Scope to its own org, so it\nlists its tenant-owned entities correctly and is refused the platform-owned\nlists at the Guard (owner \"\" or \"admin\" both deny) — a safe 403, never another\ntenant's rows. Non-super, membership-scoped views of the platform-owned\nentities (e.g. an org console's own app list keyed on Application.Organization)\nare a separate, additive surface, not a silent behavior of this generic lister.","tags":["iam"],"x-app":"iam"}},"/v1/iam/invitations":{"get":{"operationId":"get_v1_iam_invitations","summary":"Returns your organization's invitations, newest first — who has been asked to join, on what terms, and how many seats each invitation still has left.","description":"Returns your organization's invitations, newest first — who has\nbeen asked to join, on what terms, and how many seats each invitation still\nhas left.\n\nYou see your own organization's invitations and no one else's; which organization that\nis comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_invitations","summary":"Issues an invitation to join your organization — the code or link a new member redeems, with the role they arrive holding and the date it stops working.","description":"Issues an invitation to join your organization — the code or link a new\nmember redeems, with the role they arrive holding and the date it stops\nworking. A name already used in the organization is refused.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Invitation"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/invitations/delete":{"post":{"operationId":"post_v1_iam_invitations_delete","summary":"Withdraws an invitation.","description":"Withdraws an invitation. It stops being redeemable at once; anyone who\nalready joined through it keeps their account.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/invitations/get":{"post":{"operationId":"post_v1_iam_invitations_get","summary":"Returns one invitation: who it is for, what it grants on acceptance, and when it expires.","description":"Returns one invitation: who it is for, what it grants on acceptance, and\nwhen it expires.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Invitation"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/invitations/update":{"post":{"operationId":"post_v1_iam_invitations_update","summary":"Changes an invitation's terms — the role it grants, how many may redeem it, or when it expires.","description":"Changes an invitation's terms — the role it grants, how many may redeem\nit, or when it expires. What it is called does not change.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Invitation"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/issue-user-token":{"post":{"operationId":"post_v1_iam_issue-user-token","summary":"Mints an access token for the `?id=\u003cowner\u003e/\u003cname\u003e` target user (optional `?aud=` resource, RFC 8707), issued by the authenticated + allow-listed confidential client.","description":"Mints an access token for the `?id=\u003cowner\u003e/\u003cname\u003e` target\nuser (optional `?aud=` resource, RFC 8707), issued by the authenticated +\nallow-listed confidential client. The token's subject + owner are the TARGET\nUSER's, so a resource server scopes on the validated owner claim to the user's\ntenant — indistinguishable from a token the user obtained directly. Response is\nthe camelCase `{accessToken, expiresIn}` body identity.ts consumes. Equivalent to\nthe RFC 8693 token-exchange grant, minus the subject_token proof (the console has\nthe user's id, not a token) — the reason this compat shim exists.","tags":["iam"],"x-app":"iam"}},"/v1/iam/keys":{"get":{"operationId":"get_v1_iam_keys","summary":"Returns your organization's API keys, newest first — what each is called, what it may reach, and its publishable half.","description":"Returns your organization's API keys, newest first — what each is called,\nwhat it may reach, and its publishable half. Secret halves are never listed.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListResponse"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_keys","summary":"Issues an API key.","description":"Issues an API key. A standard key comes back as a publishable half you\nmay ship in client code and a secret half you must not — the secret is shown\nonce, at creation, and cannot be retrieved afterwards. A publish-scoped key is\nissued with the publishable half only, so there is no secret to leak.\n\nA name already used in your organization is refused rather than reissued, so\ncreating twice never silently invalidates a key that is in production.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/keys/delete":{"post":{"operationId":"post_v1_iam_keys_delete","summary":"Revokes an API key.","description":"Revokes an API key. Anything still presenting it stops being authorized at\nonce, so roll the replacement out before you revoke.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.keys.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteResponse"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/keys/get":{"get":{"operationId":"get_v1_iam_keys_get","summary":"Returns one API key: what it is called, what it may reach, and when it was issued.","description":"Returns one API key: what it is called, what it may reach, and when it was\nissued.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/keys/mint":{"post":{"operationId":"post_v1_iam_keys_mint","summary":"(re)generates the target user's key of the requested TYPE and returns it once, over the shared authorizeMinter + mintTarget seam.","description":"(re)generates the target user's key of the requested TYPE and\nreturns it once, over the shared authorizeMinter + mintTarget seam. `?type=secret`\n(the default) yields the confidential sk-; `?type=publishable` yields the pk- that\nis safe to ship in client JS and resolves to an org, never a principal.\n\nIt writes the schema.Key row that the resolvers actually read. schema.User.AccessKey\nis not a credential and nothing resolves it, so a key stamped there would\nauthenticate nobody.","tags":["iam"],"x-app":"iam"}},"/v1/iam/keys/revoke":{"post":{"operationId":"post_v1_iam_keys_revoke","summary":"Clears the target user's key of the requested TYPE (immediate revoke).","description":"Clears the target user's key of the requested TYPE (immediate\nrevoke). Scoped by the same `?type` field mint takes, so revoking the browser key\nleaves the server key working. A secret key's stored value is the sk- in its\nschema.Key row.","tags":["iam"],"x-app":"iam"}},"/v1/iam/keys/update":{"post":{"operationId":"post_v1_iam_keys_update","summary":"Changes what a key is called or what it may reach.","description":"Changes what a key is called or what it may reach. The credential\nitself is not reissued — the key in your deployment keeps working.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/linked-accounts":{"get":{"operationId":"get_v1_iam_linked-accounts","summary":"Returns the sign-in identities linked to the calling person's account — every provider they can currently sign in with.","description":"Returns the sign-in identities linked to the calling\nperson's account — every provider they can currently sign in with. It is what\na security page lists next to the option to disconnect one.","tags":["iam"],"x-app":"iam"}},"/v1/iam/login":{"post":{"operationId":"post_v1_iam_login","summary":"Signs a person in with the credential they typed, and — when the request is part of an OAuth flow — hands back the one-time code that finishes it.","description":"Signs a person in with the credential they typed, and — when the\nrequest is part of an OAuth flow — hands back the one-time code that finishes\nit. A second factor, if the account has one, is asked for and required here.\n\nThe password is compared against a stored one-way hash and is never logged,\nechoed or stored as typed.","tags":["iam"],"x-app":"iam"}},"/v1/iam/memberships":{"get":{"operationId":"get_v1_iam_memberships","summary":"Answers either question about who belongs where: which organizations one person can act in, or who can act in one organization.","description":"Answers either question about who belongs where: which organizations one\nperson can act in, or who can act in one organization.\n\nBoth are org-scoped: a non-SuperAdmin may ask about ITS OWN org's roster, or\nabout a user whose home org is its own, and nothing else. The bound comes from\nthe verified credential via authz.Scope, so a request parameter can never\nwiden it — a membership row names who may act and spend in an org, so a\ncross-tenant read is a customer roster leak.","tags":["iam"],"parameters":[{"name":"user","in":"query","required":false,"description":"User is \"\u003chomeOrg\u003e/\u003cusername\u003e\" — which organizations that identity may act in.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org is an organization — who may act in it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_memberships","summary":"Lets a person or an application act in an organization.","description":"Lets a person or an application act in an organization. It is the grant\nbehind \"add someone to the team\", and it is safe to repeat — granting a\nmembership that already exists changes nothing. Granting membership IS the org's authority to give, so it takes the\nsame gate a write to that org's own registry row takes: a SuperAdmin, an admin\nof the org itself, or an org-admin-capable confidential client. One rule, one\nplace (internal/authz).","tags":["iam"],"x-app":"iam"}},"/v1/iam/mfa/disable":{"post":{"operationId":"post_v1_iam_mfa_disable","summary":"Turns off the authenticator app for an account, so sign-in stops asking for a code.","description":"Turns off the authenticator app for an account, so sign-in stops\nasking for a code. People may do this for themselves; doing it for somebody\nelse takes an administrator, which is what makes it the reset path when a\nphone is lost.","tags":["iam"],"x-app":"iam"}},"/v1/iam/mfa/preferred":{"post":{"operationId":"post_v1_iam_mfa_preferred","summary":"Picks which second factor an account is asked for first when it has more than one enrolled.","description":"Picks which second factor an account is asked for first when it\nhas more than one enrolled.","tags":["iam"],"x-app":"iam"}},"/v1/iam/mfa/setup/enable":{"post":{"operationId":"post_v1_iam_mfa_setup_enable","summary":"Finishes the enrolment: from here the account's sign-ins ask for a code from the authenticator app.","description":"Finishes the enrolment: from here the account's sign-ins ask for a code\nfrom the authenticator app. Repeating it re-enrols rather than failing.","tags":["iam"],"x-app":"iam"}},"/v1/iam/mfa/setup/initiate":{"post":{"operationId":"post_v1_iam_mfa_setup_initiate","summary":"Starts enrolling an authenticator app: it returns a fresh secret, a URL to render as a QR code, and one recovery code to keep somewhere safe.","description":"Starts enrolling an authenticator app: it returns a fresh secret, a\nURL to render as a QR code, and one recovery code to keep somewhere safe.\n\nNothing is switched on yet. The enrolment counts only once it is confirmed with\na code from the app, so abandoning this step leaves the account exactly as it\nwas. Response:\n{status:\"ok\", data:{secret, url, recoveryCodes:[code]}}.","tags":["iam"],"x-app":"iam"}},"/v1/iam/mfa/setup/verify":{"post":{"operationId":"post_v1_iam_mfa_setup_verify","summary":"Checks a six-digit code against an enrolment in progress, so somebody can confirm their authenticator app is set up correctly before it starts being required.","description":"Checks a six-digit code against an enrolment in progress, so somebody\ncan confirm their authenticator app is set up correctly before it starts being\nrequired. Clocks a step out either way are accepted.\nA valid code → {status:\"ok\"}; an invalid one → 200 {status:\"error\"} (the\ncasibase convention: clients branch on status, not the HTTP code).","tags":["iam"],"x-app":"iam"}},"/v1/iam/mint-user-keys":{"post":{"operationId":"post_v1_iam_mint-user-keys","summary":"(re)generates the target user's key of the requested TYPE and returns it once, over the shared authorizeMinter + mintTarget seam.","description":"(re)generates the target user's key of the requested TYPE and\nreturns it once, over the shared authorizeMinter + mintTarget seam. `?type=secret`\n(the default) yields the confidential sk-; `?type=publishable` yields the pk- that\nis safe to ship in client JS and resolves to an org, never a principal.\n\nIt writes the schema.Key row that the resolvers actually read. schema.User.AccessKey\nis not a credential and nothing resolves it, so a key stamped there would\nauthenticate nobody.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/authorize":{"get":{"operationId":"get_v1_iam_oauth_authorize","summary":"Starts a sign-in — the address you send a browser to, and the beginning of every OAuth and OpenID Connect flow.","description":"Starts a sign-in — the address you send a browser to, and the\nbeginning of every OAuth and OpenID Connect flow.\n\nIf the person is ALREADY signed in here, it does not ask them again: it\nreturns them to the application with a one-time code and they never see this\npage. Otherwise it shows the right way to sign in for the application they are\nsigning in to, or hands off to another identity provider if that is what they\npick.\n\nA client can say what it wants with `prompt`: `none` means answer without any\nscreen at all — with the code if a session exists, with an error if not, but\nnever with a page; `login` means ask for the password again even if a session\nexists; `select_account` means let the person choose which identity to use.\n\nIt returns only to an address the application has registered. That check\nhappens before anything else, so a request naming an unregistered address is\nrefused where the person can see it rather than being bounced onwards.","tags":["iam"],"x-app":"iam"},"post":{"operationId":"post_v1_iam_oauth_authorize","summary":"Starts a sign-in — the address you send a browser to, and the beginning of every OAuth and OpenID Connect flow.","description":"Starts a sign-in — the address you send a browser to, and the\nbeginning of every OAuth and OpenID Connect flow.\n\nIf the person is ALREADY signed in here, it does not ask them again: it\nreturns them to the application with a one-time code and they never see this\npage. Otherwise it shows the right way to sign in for the application they are\nsigning in to, or hands off to another identity provider if that is what they\npick.\n\nA client can say what it wants with `prompt`: `none` means answer without any\nscreen at all — with the code if a session exists, with an error if not, but\nnever with a page; `login` means ask for the password again even if a session\nexists; `select_account` means let the person choose which identity to use.\n\nIt returns only to an address the application has registered. That check\nhappens before anything else, so a request naming an unregistered address is\nrefused where the person can see it rather than being bounced onwards.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/callback":{"get":{"operationId":"get_v1_iam_oauth_callback","summary":"Completes the round-trip: it resolves and burns the single-use transaction (checking expiry + browser binding), exchanges and verifies the IdP response, links or provisions the local user, and mints the iam authorization code the relying party expects — then redirects to the original redirect_uri with code + state.","description":"Completes the round-trip: it resolves and burns the\nsingle-use transaction (checking expiry + browser binding), exchanges and\nverifies the IdP response, links or provisions the local user, and mints the\niam authorization code the relying party expects — then redirects to the\noriginal redirect_uri with code + state.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/device":{"post":{"operationId":"post_v1_iam_oauth_device","summary":"Starts a sign-in on a device with no browser and no keyboard — a TV, a CLI, a headless box.","description":"Starts a sign-in on a device with no browser and no keyboard —\na TV, a CLI, a headless box. It returns a short code to show the person and\nthe address to send them to on a phone or laptop.\n\nNothing is granted until a human approves it there; until then the code is\njust a pending request.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/device/info":{"post":{"operationId":"post_v1_iam_oauth_device_info","summary":"Name the application a pending device code is asking to sign in.","description":"Answers \"what am I approving?\" for a pending user_code, so the approval page can name the application a human is about to authorize. Both fields come off the pending code's OWN application — never off the portal the browser happens to be on — so the screen cannot name one application while the code belongs to another.\n\nRequires a signed-in session, resolved from the browser's session cookie exactly as the approval itself resolves it. Not signed in is not a refusal to explain: it carries the stable login-required code the approval page branches on to sign the human in first.\n\nPOST for a read, deliberately, for the same reason RFC 7662 introspection beside it is POST: the argument is a SECRET. A user_code in a request line is copied into ingress and proxy access logs, which a POST body is not.\n\nUnknown, expired, already used and already approved all get ONE opaque refusal — the same one the approval attempt would get. The user_code carries only 40 bits, so an answer that distinguished those states would be an oracle for hunting live codes; gated and opaque, this reveals strictly less than the approval the same caller could already attempt.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/federation/mfa":{"post":{"operationId":"post_v1_iam_oauth_federation_mfa","summary":"Completes a sign-in that came in through another identity provider and still owes a second factor.","description":"Completes a sign-in that came in through another identity\nprovider and still owes a second factor. The person supplies the factor here\nand the login finishes.\n\nThe account is fixed when the challenge is issued, not by the request, so no\none can redirect a half-finished login onto somebody else's account. A wrong\nfactor uses the challenge up: retrying means starting the sign-in again, the\nsame as a mistyped password.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/introspect":{"post":{"operationId":"post_v1_iam_oauth_introspect","summary":"Answers whether an access token is still good, and what it is good for — the check a resource server of yours makes before honouring a token it did not mint.","description":"Answers whether an access token is still good, and what it\nis good for — the check a resource server of yours makes before honouring a\ntoken it did not mint.\n\nA token counts as active only if it verifies AND has not been revoked, so a\nrevoked token reads as dead here immediately rather than until it expires. A\ntoken that is unknown, expired or revoked answers simply that it is not\nactive, and nothing more — the endpoint is not a way to learn about tokens you\nwere not given.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/logout":{"get":{"operationId":"get_v1_iam_oauth_logout","summary":"Ends a sign-in and sends the browser somewhere sensible.","description":"Ends a sign-in and sends the browser somewhere sensible. Accepts\nGET or POST, so it works as a plain link.\n\nIt ACTUALLY signs you out, which is worth stating because the endpoint spent a\nrelease not doing it: the whole body computed a redirect and answered\n{\"status\":\"ok\"} unconditionally — no session ended, no token revoked. A logout\nthat reports success while leaving the session live is worse than no logout at\nall, because the person on the shared machine believes it worked. Three things\nhappen here now, in this order:\n\n 1. The browser session dies — sid revoked server-side AND the cookie expired\n    (sessions.Clear). Server-side revocation is the load-bearing half: a copy\n    of the cookie taken before logout must not still resolve.\n 2. The relying party's tokens are revoked when an id_token_hint names it, so\n    the refresh token cannot mint a fresh access token after the human left.\n    Revocation state is authoritative — a JWT's `exp` still reads valid for\n    days, so expiry is necessary but never sufficient.\n 3. Only then is a redirect considered, and only to a REGISTERED uri.\n\nThe open-redirect guard is unchanged: a redirect happens only when a VERIFIED\nid_token_hint identifies the application and that application has registered\nthe target. Anything else refuses to redirect — nobody can turn your logout\nlink into a redirect to a site of their choosing.","tags":["iam"],"x-app":"iam"},"post":{"operationId":"post_v1_iam_oauth_logout","summary":"Ends a sign-in and sends the browser somewhere sensible.","description":"Ends a sign-in and sends the browser somewhere sensible. Accepts\nGET or POST, so it works as a plain link.\n\nIt ACTUALLY signs you out, which is worth stating because the endpoint spent a\nrelease not doing it: the whole body computed a redirect and answered\n{\"status\":\"ok\"} unconditionally — no session ended, no token revoked. A logout\nthat reports success while leaving the session live is worse than no logout at\nall, because the person on the shared machine believes it worked. Three things\nhappen here now, in this order:\n\n 1. The browser session dies — sid revoked server-side AND the cookie expired\n    (sessions.Clear). Server-side revocation is the load-bearing half: a copy\n    of the cookie taken before logout must not still resolve.\n 2. The relying party's tokens are revoked when an id_token_hint names it, so\n    the refresh token cannot mint a fresh access token after the human left.\n    Revocation state is authoritative — a JWT's `exp` still reads valid for\n    days, so expiry is necessary but never sufficient.\n 3. Only then is a redirect considered, and only to a REGISTERED uri.\n\nThe open-redirect guard is unchanged: a redirect happens only when a VERIFIED\nid_token_hint identifies the application and that application has registered\nthe target. Anything else refuses to redirect — nobody can turn your logout\nlink into a redirect to a site of their choosing.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/revoke":{"post":{"operationId":"post_v1_iam_oauth_revoke","summary":"Retires a token before it expires — what you call when someone signs out or a credential may have leaked.","description":"Retires a token before it expires — what you call when someone\nsigns out or a credential may have leaked.\n\nRevoking an access token kills that token. Revoking a REFRESH token kills the\nwhole chain it belongs to, so no further access tokens can be minted from it\nand every token already minted from it dies with it.\n\nA token that is not yours, or that never existed, answers success and does\nnothing — so the endpoint cannot be used to discover which tokens are real.\n\nPUBLIC clients revoke too, and must: sign-out is the only control a long-lived\nrefresh token has. hanzo-cli is a public PKCE client holding a 30-day rotating\nrefresh token, so a confidential-only revocation endpoint made `hanzo auth\nlogout` a LOCAL DELETE — the credential it dropped stayed spendable at\nhanzo.id for the rest of the month, with nothing able to kill it. Measured\n2026-08-01: revoke answered 401 invalid_client and the refresh token went on\nminting access tokens.\n\nWidening authentication does not widen authority. The caller must still POSSESS\nthe token — and possession already permits USE, of which revocation is the\nstrict opposite — and the row must belong to the client that presents it, so a\npublic client_id buys the ability to destroy exactly what its holder could\notherwise spend. RFC 6749 §3.2.1 is the same reading: a client with no\ncredentials identifies itself with client_id.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/token":{"post":{"operationId":"post_v1_iam_oauth_token","summary":"Exchanges what your application is holding for the tokens it needs — the one-time code from a finished sign-in, a refresh token, or your own client credentials when the caller is a program rather than a person.","description":"Exchanges what your application is holding for the tokens it\nneeds — the one-time code from a finished sign-in, a refresh token, or your\nown client credentials when the caller is a program rather than a person.\n\nA refresh returns a NEW refresh token and retires the one you sent. If a\nretired one is ever presented again the whole chain is revoked, on the\nassumption that a token which came back from the dead was copied — so a stolen\nrefresh token buys an attacker one use and costs them the session.\n\nResponses are never cached, by any hop.","tags":["iam"],"x-app":"iam"}},"/v1/iam/oauth/userinfo":{"get":{"operationId":"get_v1_iam_oauth_userinfo","summary":"Returns the profile claims for whoever the access token belongs to — the standard OpenID Connect way to find out who is calling you without your application storing anything itself.","description":"Returns the profile claims for whoever the access token\nbelongs to — the standard OpenID Connect way to find out who is calling you\nwithout your application storing anything itself.\n\nThe token must still be live: revoke it and this stops answering.","tags":["iam"],"x-app":"iam"},"post":{"operationId":"post_v1_iam_oauth_userinfo","summary":"Returns the profile claims for whoever the access token belongs to — the standard OpenID Connect way to find out who is calling you without your application storing anything itself.","description":"Returns the profile claims for whoever the access token\nbelongs to — the standard OpenID Connect way to find out who is calling you\nwithout your application storing anything itself.\n\nThe token must still be live: revoke it and this stops answering.","tags":["iam"],"x-app":"iam"}},"/v1/iam/onboard":{"post":{"operationId":"post_v1_iam_onboard","summary":"Finishes setting up the account of whoever is calling — it creates their organization if they have none and puts them in it, so a person who has just signed up lands somewhere they can work.","description":"Finishes setting up the account of whoever is calling — it\ncreates their organization if they have none and puts them in it, so a person\nwho has just signed up lands somewhere they can work.\n\nIt always acts on the caller and never on somebody named in the request, so\nthere is no way to onboard another person's account through it.","tags":["iam"],"x-app":"iam"}},"/v1/iam/organizations":{"get":{"operationId":"listOrganizations","summary":"Returns the organizations you can see, newest first.","description":"Returns the organizations you can see, newest first. Narrow it to one\nparent account, and set a limit and offset to page through the rest.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListOrganizationsOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"createOrganization","summary":"Makes a new organization — the account your users, applications, roles, projects and workspaces are all named inside.","description":"Makes a new organization — the account your users, applications, roles,\nprojects and workspaces are all named inside. It is the first write in a new\ntenant, and a name already in use is refused rather than taken over.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.CreateOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Organization"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/organizations/delete":{"post":{"operationId":"deleteOrganization","summary":"Removes an organization and everything named inside it.","description":"Removes an organization and everything named inside it. There is no\nundo, and every session issued under it stops working.\n\nThe built-in admin organization cannot be deleted — losing it would leave the\naccount with no way back in.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOrganizationOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/organizations/get":{"get":{"operationId":"getOrganization","summary":"Returns one organization: its display, its defaults and the sign-in rules everyone in it inherits.","description":"Returns one organization: its display, its defaults and the sign-in rules\neveryone in it inherits.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Organization"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/organizations/update":{"post":{"operationId":"updateOrganization","summary":"Changes an organization's display, its defaults and the sign-in rules everyone in it inherits.","description":"Changes an organization's display, its defaults and the sign-in rules\neveryone in it inherits. Which organization it is does not change, and neither\ndoes when it was created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.UpdateOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Organization"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/permissions":{"get":{"operationId":"get_v1_iam_permissions","summary":"Returns the permissions in one organization, newest first — each one a grant saying which people or roles may do what, and to which resources.","description":"Returns the permissions in one organization, newest first — each one a\ngrant saying which people or roles may do what, and to which resources.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.permission.ListResponse"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_permissions","summary":"Grants a permission — the call that gives a person or a role the ability to do something.","description":"Grants a permission — the call that gives a person or a role the ability to\ndo something. Adding refuses to overwrite a grant that already exists, so\nwidening an existing one is an update, never an accident.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/permissions/delete":{"post":{"operationId":"post_v1_iam_permissions_delete","summary":"Revokes a permission.","description":"Revokes a permission. Everyone who held access only through it loses\nthat access immediately; grants they hold by another route are untouched.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.permission.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.permission.DeleteResponse"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/permissions/get":{"get":{"operationId":"get_v1_iam_permissions_get","summary":"Returns one permission: who it grants to, what it allows, and the resources it covers.","description":"Returns one permission: who it grants to, what it allows, and the\nresources it covers.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/permissions/update":{"post":{"operationId":"post_v1_iam_permissions_update","summary":"Changes who a permission grants to, what it allows, or the resources it covers.","description":"Changes who a permission grants to, what it allows, or the resources it\ncovers. Access changes as soon as the write lands. What the permission is\ncalled does not change, and neither does when it was created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/preferences":{"post":{"operationId":"post_v1_iam_preferences","summary":"Saves the calling person's own settings and returns the full set afterwards.","description":"Saves the calling person's own settings and returns\nthe full set afterwards. Send only the settings you are changing — the rest\nare kept, so two screens can save at once without one undoing the other.","tags":["iam"],"x-app":"iam"}},"/v1/iam/projects":{"get":{"operationId":"get_v1_iam_projects","summary":"Returns your organization's projects, newest first — the scope people pick between when their work is separated by product or client rather than by team.","description":"Returns your organization's projects, newest first — the scope\npeople pick between when their work is separated by product or client rather\nthan by team.\n\nYou see your own organization's projects and no one else's; which organization that\nis comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_projects","summary":"Makes a project inside your organization — the scope people pick between when their work is separated by product or client rather than by team.","description":"Makes a project inside your organization — the scope people pick\nbetween when their work is separated by product or client rather than by team.\nA name already used in the organization is refused.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/projects/delete":{"post":{"operationId":"post_v1_iam_projects_delete","summary":"Removes a project.","description":"Removes a project. The people and roles in your organization are\nunchanged; what goes is the scope itself, so move anything addressed by it\nfirst.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/projects/get":{"post":{"operationId":"post_v1_iam_projects_get","summary":"Returns one project: what it is called and how it is set up.","description":"Returns one project: what it is called and how it is set up.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/projects/update":{"post":{"operationId":"post_v1_iam_projects_update","summary":"Changes a project's settings.","description":"Changes a project's settings. What it is called does not change, and\nneither does when it was created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/providers":{"get":{"operationId":"listProviders","summary":"Returns your organization's providers, newest first — the identity providers your people sign in with, and the senders and connectors your applications go through.","description":"Returns your organization's providers, newest first — the\nidentity providers your people sign in with, and the senders and connectors\nyour applications go through.\n\nYou see your own organization's providers and no one else's; which organization\nthat is comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listProvidersOut"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"addProvider","summary":"Adds an identity provider your people can sign in with, or a service your applications send through — a social or enterprise login, an email or SMS sender, a storage or payment connector.","description":"Adds an identity provider your people can sign in with, or a\nservice your applications send through — a social or enterprise login, an email\nor SMS sender, a storage or payment connector.\n\nA provider is configured once and then switched on per application, so several\napplications can share one set of credentials.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Provider"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.providerResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/providers/delete":{"post":{"operationId":"deleteProvider","summary":"Removes a provider.","description":"Removes a provider. Sign-in through it stops for every\napplication that used it, so give those applications another method first.\n\nA provider that is already gone answers \"nothing changed\" rather than an\nerror, so the call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.providerKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.mutationResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/providers/get":{"post":{"operationId":"getProvider","summary":"Returns one provider: what it connects to and how it is configured.","description":"Returns one provider: what it connects to and how it is\nconfigured. Its credentials come back masked.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.providerKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.providerResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/providers/update":{"post":{"operationId":"updateProvider","summary":"Changes a provider's settings or rotates the credentials it holds.","description":"Changes a provider's settings or rotates the credentials it\nholds. The change takes effect on the next sign-in through it — sessions\nalready issued are unaffected.\n\nA provider that is not there answers \"nothing changed\" rather than an error, so\nthe call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Provider"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.mutationResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/registry/jwks":{"get":{"operationId":"get_v1_iam_registry_jwks","summary":"Publishes the public key your registry uses to verify the tokens issued above — the one URL to configure so the registry trusts logins without holding any secret of its own.","description":"Publishes the public key your registry uses to verify the tokens issued\nabove — the one URL to configure so the registry trusts logins without holding\nany secret of its own.\n\nIf no signing key is available it refuses rather than publishing an empty set,\nbecause a registry that trusts nothing looks identical to one that trusts\neverything until somebody tries to push.","tags":["iam"],"x-app":"iam"}},"/v1/iam/registry/token":{"get":{"operationId":"get_v1_iam_registry_token","summary":"Signs a container client in to your registry.","description":"Signs a container client in to your registry. `docker login`, and every\nbuild tool that pushes or pulls images, lands here: it exchanges the\ncredential for a short-lived token scoped to exactly the repositories that\ncredential may touch.\n\nBoth of the shapes container tooling uses are accepted, so the same login works\nwhichever client your pipeline runs.","tags":["iam"],"x-app":"iam"},"post":{"operationId":"post_v1_iam_registry_token","summary":"Signs a container client in to your registry.","description":"Signs a container client in to your registry. `docker login`, and every\nbuild tool that pushes or pulls images, lands here: it exchanges the\ncredential for a short-lived token scoped to exactly the repositories that\ncredential may touch.\n\nBoth of the shapes container tooling uses are accepted, so the same login works\nwhichever client your pipeline runs.","tags":["iam"],"x-app":"iam"}},"/v1/iam/resolve-key":{"get":{"operationId":"get_v1_iam_resolve-key","summary":"Answers which organization a PUBLISHABLE key belongs to — what a service of yours calls to attribute a request that arrived carrying a key shipped in a browser.","description":"Answers which organization a PUBLISHABLE key belongs to —\nwhat a service of yours calls to attribute a request that arrived carrying a\nkey shipped in a browser.\n\nIt names an organization and never a person: no path through it can load or\nreturn a user, so a key you put in client code cannot become a way to learn\nwho anyone is. A key that is expired, secret rather than publishable, or\nsimply unknown all answer with the same sentence, and with a `code` saying\nwhich of those it was. Only a confidential service that already proved it may\nresolve keys at all ever reads that code — there is no anonymous caller here\nto probe for which keys exist — and telling it apart is what lets the holder\nbe told to re-mint an expired key instead of hunting a configuration error.","tags":["iam"],"x-app":"iam"}},"/v1/iam/revoke-user-keys":{"post":{"operationId":"post_v1_iam_revoke-user-keys","summary":"Clears the target user's key of the requested TYPE (immediate revoke).","description":"Clears the target user's key of the requested TYPE (immediate\nrevoke). Scoped by the same `?type` field mint takes, so revoking the browser key\nleaves the server key working. A secret key's stored value is the sk- in its\nschema.Key row.","tags":["iam"],"x-app":"iam"}},"/v1/iam/roles":{"get":{"operationId":"get_v1_iam_roles","summary":"Returns your organization's roles, newest first — each a named group of people that permissions are granted to.","description":"Returns your organization's roles, newest first — each a named group of\npeople that permissions are granted to.\n\nYou see your own organization's roles and no one else's; which organization\nthat is comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_roles","summary":"Makes a role — a named group of people that permissions are granted to.","description":"Makes a role — a named group of people that permissions are granted to.\nGranting to a role rather than to each person is what keeps access correct as\nyour team changes: add someone to the role and they inherit everything it can\ndo. A name already used in your organization is refused.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Role"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/roles/delete":{"post":{"operationId":"post_v1_iam_roles_delete","summary":"Removes a role.","description":"Removes a role. Everyone in it loses the access it carried; their\naccounts, and any other role they hold, are untouched.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/roles/get":{"post":{"operationId":"post_v1_iam_roles_get","summary":"Returns one role: who is in it, and the roles it includes.","description":"Returns one role: who is in it, and the roles it includes.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Role"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/roles/update":{"post":{"operationId":"post_v1_iam_roles_update","summary":"Changes who is in a role, or which roles it includes.","description":"Changes who is in a role, or which roles it includes. Access changes for\neveryone in it as soon as the write lands. What the role is called does not\nchange, and neither does when it was created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Role"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/scim/v2/ResourceTypes":{"get":{"operationId":"get_v1_iam_scim_v2_resourcetypes","summary":"Returns the kinds of record this directory provisions and the address of each, so your identity provider discovers them rather than having them configured by hand.","description":"Returns the kinds of record this directory provisions and\nthe address of each, so your identity provider discovers them rather than\nhaving them configured by hand.","tags":["iam"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listResponse"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/scim/v2/ResourceTypes/{name}":{"get":{"operationId":"get_v1_iam_scim_v2_resourcetypes_by_name","summary":"Returns one provisionable record kind in full.","description":"Returns one provisionable record kind in full.","tags":["iam"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"},"404":{"content":{"application/json":{"schema":{}}},"description":"not found"}},"x-app":"iam"}},"/v1/iam/scim/v2/Schemas":{"get":{"operationId":"get_v1_iam_scim_v2_schemas","summary":"Returns the attribute definitions this directory understands, so your identity provider knows which fields it may send and what they mean before it sends any.","description":"Returns the attribute definitions this directory understands, so\nyour identity provider knows which fields it may send and what they mean\nbefore it sends any.","tags":["iam"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listResponse"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/scim/v2/Schemas/{id}":{"get":{"operationId":"get_v1_iam_scim_v2_schemas_by_id","summary":"Returns one attribute definition in full.","description":"Returns one attribute definition in full.","tags":["iam"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"},"404":{"content":{"application/json":{"schema":{}}},"description":"not found"}},"x-app":"iam"}},"/v1/iam/scim/v2/ServiceProviderConfig":{"get":{"operationId":"get_v1_iam_scim_v2_serviceproviderconfig","summary":"Tells your identity provider which parts of SCIM this directory supports, so it configures itself instead of you filling in a form.","description":"Tells your identity provider which parts of SCIM this\ndirectory supports, so it configures itself instead of you filling in a form.\n\nFiltering and partial updates are supported. Bulk operations, sorting and\nentity tags are not — an IdP that reads this will not attempt them.","tags":["iam"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.config"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/scim/v2/Users":{"get":{"operationId":"get_v1_iam_scim_v2_users","summary":"Returns the people in your organization to your identity provider, in the standard SCIM shape, so an IdP can reconcile its directory against ours.","description":"Returns the people in your organization to your identity provider,\nin the standard SCIM shape, so an IdP can reconcile its directory against\nours. Searchable by username or email address, and paged.\n\nReading the whole list takes an administrator; an ordinary person is refused.","tags":["iam"],"x-app":"iam"},"post":{"operationId":"post_v1_iam_scim_v2_users","summary":"Provisions a person from your identity provider — how a new hire gets an account here automatically when they are added over there.","description":"Provisions a person from your identity provider — how a new hire\ngets an account here automatically when they are added over there.\n\nTakes an administrator. Making someone an administrator takes more than that,\nso an IdP integration cannot escalate anyone by setting a flag.","tags":["iam"],"x-app":"iam"}},"/v1/iam/scim/v2/Users/{owner}/{name}":{"delete":{"operationId":"delete_v1_iam_scim_v2_users_by_owner_by_name","summary":"Deprovisions a person — how removing someone in your identity provider removes their access here.","description":"Deprovisions a person — how removing someone in your identity\nprovider removes their access here. Their sessions stop working immediately.\nTakes an administrator.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"},"get":{"operationId":"get_v1_iam_scim_v2_users_by_owner_by_name","summary":"Returns one person in the standard SCIM shape.","description":"Returns one person in the standard SCIM shape. An administrator may\nread anyone in the organization; everyone else may read only themselves.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"},"patch":{"operationId":"patch_v1_iam_scim_v2_users_by_owner_by_name","summary":"Applies a partial change from your identity provider — one attribute moved, not the whole record resent.","description":"Applies a partial change from your identity provider — one attribute\nmoved, not the whole record resent.\n\nThe change is applied onto the person as they currently are, so everything you\ndid not mention keeps its value, including the parts SCIM does not describe.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"},"put":{"operationId":"put_v1_iam_scim_v2_users_by_owner_by_name","summary":"Overwrites a person's SCIM attributes with what your identity provider sends — how a change made there lands here.","description":"Overwrites a person's SCIM attributes with what your identity\nprovider sends — how a change made there lands here.\n\nOnly the attributes SCIM describes are replaced. Anything the standard does not\ncover — their multi-factor enrolment above all — survives untouched, so a\nroutine sync from your IdP can never quietly strip someone's second factor or\nbring a deleted account back.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"}},"/v1/iam/send-verification-code":{"post":{"operationId":"post_v1_iam_send-verification-code","summary":"Validates the request, mints + persists an OTP, and reports success.","description":"Validates the request, mints + persists an OTP, and\nreports success. The request fields are read via fiber's FormValue — the\nescape hatch zip exposes for form bodies (multipart or urlencoded) — since the\ntyped JSON Bind does not apply here. v1 also accepts countryCode/method/\ncheckUser/captchaType; iam ignores them (the captcha/forget/MFA flows those\ndrive are not ported), and CAPTCHA verification is likewise not enforced —\niam models no captcha provider — so the code is issued once the destination\nand application validate.","tags":["iam"],"x-app":"iam"}},"/v1/iam/service-accounts":{"get":{"operationId":"get_v1_iam_service-accounts","summary":"Returns your organization's service accounts — what each is called and when it was created.","description":"Returns your organization's service accounts — what each is called and\nwhen it was created. Never their secrets: a key's secret half exists in a\nresponse exactly once, when it is minted. Paginated in memory over the already org-scoped\nslice — the set per org is small, so a dedicated count query is overkill\n(v1 service_account.go:296-307).","tags":["iam"],"parameters":[{"name":"organization","in":"query","required":false,"description":"Organization is the organization whose service accounts to list. Required.","schema":{"type":"string"}},{"name":"p","in":"query","required":false,"description":"P is the 1-indexed page to return. Paging takes both p and pageSize —\nleave either out, or send something that is not a number, and the whole\nlist comes back.","schema":{"type":"integer"}},{"name":"pageSize","in":"query","required":false,"description":"Size is how many accounts a page holds.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"bad request"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_service-accounts","summary":"Makes a service account — an identity for a program rather than a person, for a script, a bot or a deployment that has to authenticate on its own.","description":"Makes a service account — an identity for a program rather than a\nperson, for a script, a bot or a deployment that has to authenticate on its\nown.\n\nIt comes back with its first key, and the secret half is shown ONCE, here.\nThere is no way to read it again; if you lose it, rotate.","tags":["iam"],"x-app":"iam"}},"/v1/iam/service-accounts/{name}":{"delete":{"operationId":"delete_v1_iam_service-accounts_by_name","summary":"Serves DELETE /v1/iam/service-accounts/:name.","description":"Serves DELETE /v1/iam/service-accounts/:name.","tags":["iam"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"}},"/v1/iam/service-accounts/{name}/keys":{"post":{"operationId":"post_v1_iam_service-accounts_by_name_keys","summary":"Serves POST /v1/iam/service-accounts/:name/keys: mint a fresh key, invalidating the prior one, and return the new raw secret exactly once.","description":"Serves POST /v1/iam/service-accounts/:name/keys: mint a fresh key,\ninvalidating the prior one, and return the new raw secret exactly once.","tags":["iam"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam"}},"/v1/iam/sessions/create":{"post":{"operationId":"createSession","summary":"Records a sign-in.","description":"Records a sign-in. Signing in again from another browser adds to the\nsession rather than replacing it, so one person can be signed in from a laptop\nand a phone at once.\n\nAsk for an exclusive sign-in and the opposite holds: the new sign-in is the only\none left and every other browser is signed out. That is the setting to use when\none person may hold only one live session at a time.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.CreateSessionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Session"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/sessions/delete":{"post":{"operationId":"deleteSession","summary":"Signs a person out of one application — the session ends and every browser carrying it stops being authenticated.","description":"Signs a person out of one application — the session ends and every\nbrowser carrying it stops being authenticated.\n\nA session that is already gone reports that nothing was deleted rather than an\nerror, so the call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.SessionRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteSessionOut"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/sessions/get":{"post":{"operationId":"getSession","summary":"Returns one person's session in one application — when it began and which browsers or devices are still carrying it.","description":"Returns one person's session in one application — when it began and which\nbrowsers or devices are still carrying it.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.SessionRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Session"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/sessions/list":{"post":{"operationId":"listSessions","summary":"Returns who is currently signed in to your organization, newest first, and can be narrowed to one person or one application.","description":"Returns who is currently signed in to your organization, newest first, and\ncan be narrowed to one person or one application. It is what you read before\nsigning someone out.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListSessionsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListSessionsOut"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/sessions/update":{"post":{"operationId":"updateSession","summary":"Replaces the set of browsers a session covers — signing out the ones you leave off while the session itself stays live.","description":"Replaces the set of browsers a session covers — signing out the ones you\nleave off while the session itself stays live. A session that does not exist is\nreported as missing rather than created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.UpdateSessionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Session"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/set-preferred-mfa":{"post":{"operationId":"post_v1_iam_set-preferred-mfa","summary":"Picks which second factor an account is asked for first when it has more than one enrolled.","description":"Picks which second factor an account is asked for first when it\nhas more than one enrolled.","tags":["iam"],"x-app":"iam"}},"/v1/iam/signin":{"post":{"operationId":"post_v1_iam_signin","summary":"Completes a sign-in: it exchanges the one-time code your application was handed at the end of the login flow for a live session, and returns the signed-in account.","description":"Completes a sign-in: it exchanges the one-time code your\napplication was handed at the end of the login flow for a live session, and\nreturns the signed-in account.\n\nThe code works once. This is the call that turns a finished login into\nsomething your application can act on.","tags":["iam"],"x-app":"iam"}},"/v1/iam/signup":{"post":{"operationId":"post_v1_iam_signup","summary":"Creates an account from the sign-up form and applies the application's own sign-up rules — whether self-service registration is open at all, and which fields it requires.","description":"Creates an account from the sign-up form and applies the\napplication's own sign-up rules — whether self-service registration is open at\nall, and which fields it requires.\n\nThe password is hashed before it is stored and is never returned.","tags":["iam"],"x-app":"iam"}},"/v1/iam/tokens":{"get":{"operationId":"listTokens","summary":"Returns the access tokens issued in your organization, newest first, and can be narrowed to one organization.","description":"Returns the access tokens issued in your organization, newest\nfirst, and can be narrowed to one organization. Use it to see what is currently\nauthorized before revoking anything.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}},{"name":"organization","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listTokensOut"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"addToken","summary":"Records an access token — the credential an application or integration presents on a caller's behalf.","description":"Records an access token — the credential an application or integration\npresents on a caller's behalf.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Token"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/tokens/delete":{"post":{"operationId":"deleteToken","summary":"Revokes an access token.","description":"Revokes an access token. Whatever was using it stops being\nauthorized at once.\n\nA token that is already gone answers \"nothing changed\" rather than an error, so\nthe call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenMutation"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/tokens/get":{"post":{"operationId":"getToken","summary":"Returns one access token: who and what it was issued to, and when it expires.","description":"Returns one access token: who and what it was issued to, and when it\nexpires.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/tokens/issue":{"post":{"operationId":"post_v1_iam_tokens_issue","summary":"Mints an access token for the `?id=\u003cowner\u003e/\u003cname\u003e` target user (optional `?aud=` resource, RFC 8707), issued by the authenticated + allow-listed confidential client.","description":"Mints an access token for the `?id=\u003cowner\u003e/\u003cname\u003e` target\nuser (optional `?aud=` resource, RFC 8707), issued by the authenticated +\nallow-listed confidential client. The token's subject + owner are the TARGET\nUSER's, so a resource server scopes on the validated owner claim to the user's\ntenant — indistinguishable from a token the user obtained directly. Response is\nthe camelCase `{accessToken, expiresIn}` body identity.ts consumes. Equivalent to\nthe RFC 8693 token-exchange grant, minus the subject_token proof (the console has\nthe user's id, not a token) — the reason this compat shim exists.","tags":["iam"],"x-app":"iam"}},"/v1/iam/tokens/update":{"post":{"operationId":"updateToken","summary":"Changes an access token's scope or expiry.","description":"Changes an access token's scope or expiry.\n\nA token that is not there answers \"nothing changed\" rather than an error, so\nthe call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Token"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenMutation"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/unlink":{"post":{"operationId":"post_v1_iam_unlink","summary":"Disconnects one sign-in identity from an account, so that provider can no longer be used to sign in as that person.","description":"Disconnects one sign-in identity from an account, so that provider can\nno longer be used to sign in as that person. Their account and every other way\nthey sign in are untouched. Two principals may do it, and\nonly two: the account holder itself, and a SuperAdmin (a member of the reserved\nadmin org, the one predicate). An ORG ADMIN deliberately may NOT — unlinking is\nnot tenant administration, it is unpicking someone's own sign-in method, so the\ngeneric org-admin rule is the wrong answer here.\n\nA holder unlinking itself must also be permitted by the application — the\nprovider link's CanUnlink flag — so an organization that mandates federated\nsign-in cannot have its users strand themselves. A SuperAdmin is not bound by\nthat flag; it is the platform's own recovery path. Fail-closed throughout.","tags":["iam"],"x-app":"iam"}},"/v1/iam/update-application":{"post":{"operationId":"post_v1_iam_update-application","summary":"Updates one of your applications — its display, its sign-in methods and the redirect URIs it is allowed to return to.","description":"Updates one of your applications — its display, its sign-in methods and the\nredirect URIs it is allowed to return to. Which organization and name the\napplication has are fixed when it is created and are not editable here.\n\nA redirect URI you add becomes an allowed sign-in origin, so this is the\ncall that makes login work from a new host.\n\nThe older spelling of PUT /v1/iam/application.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/update-organization":{"post":{"operationId":"post_v1_iam_update-organization","summary":"Updates your organization — its display, its default settings and the sign-in rules everyone in it inherits.","description":"Updates your organization — its display, its default settings and the\nsign-in rules everyone in it inherits.\n\nThe older spelling of POST /v1/iam/organizations/update.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.UpdateOrganizationInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/update-preferences":{"post":{"operationId":"post_v1_iam_update-preferences","summary":"Saves the calling person's own settings and returns the full set afterwards.","description":"Saves the calling person's own settings and returns\nthe full set afterwards. Send only the settings you are changing — the rest\nare kept, so two screens can save at once without one undoing the other.","tags":["iam"],"x-app":"iam"}},"/v1/iam/update-provider":{"post":{"operationId":"post_v1_iam_update-provider","summary":"Updates a provider's settings or rotates the credentials it holds.","description":"Updates a provider's settings or rotates the credentials it holds. The\nchange takes effect on the next sign-in through it — sessions already\nissued are unaffected.\n\nThe older spelling of POST /v1/iam/providers/update.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Provider"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/update-role":{"post":{"operationId":"post_v1_iam_update-role","summary":"Updates a role's members or the roles it includes.","description":"Updates a role's members or the roles it includes. Access changes for\neveryone in it as soon as the write lands.\n\nThe older spelling of POST /v1/iam/roles/update.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/update-user":{"post":{"operationId":"post_v1_iam_update-user","summary":"Updates one of your users' profile, roles or credentials.","description":"Updates one of your users' profile, roles or credentials. Send a password\nto reset it; leave it out and the current one stands.\n\nThe older spelling of POST /v1/iam/users/update, with the user's fields at\nthe top level rather than wrapped in {user, password}.","tags":["iam","compat"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.userBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Response"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/users":{"get":{"operationId":"get_v1_iam_users","summary":"Returns a page of the people in your organization, with the total so you can page through the rest.","description":"Returns a page of the people in your organization, with the total so you\ncan page through the rest. Passwords, API secrets and MFA material are stripped\nfrom every entry.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.users.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_users","summary":"Adds a person to your organization.","description":"Adds a person to your organization. Send a password and it becomes the\none they sign in with; it is hashed before it is stored and never comes back\nin any response.\n\nThe username is checked against the same rule every account in the Hanzo Cloud\nis held to, whichever way it was created — this call, password signup, a social\nsign-in, or SCIM — so a name accepted here works everywhere.\n\nA name already taken in your organization is refused rather than overwritten.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.CreateInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.User"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/users/delete":{"post":{"operationId":"post_v1_iam_users_delete","summary":"Removes a person from your organization.","description":"Removes a person from your organization. Their sessions stop working\nimmediately and the account is gone rather than suspended — to keep the record\nand only stop sign-in, update the user instead.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.users.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.users.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/users/get":{"get":{"operationId":"get_v1_iam_users_get","summary":"Returns one person in your organization, by the organization they belong to and their username.","description":"Returns one person in your organization, by the organization they belong\nto and their username. Passwords, API secrets and MFA material are stripped\nfrom the response.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.User"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/users/update":{"post":{"operationId":"post_v1_iam_users_update","summary":"Changes a person's profile, their roles, or the credentials they sign in with.","description":"Changes a person's profile, their roles, or the credentials they sign\nin with. Send a password to reset it; leave it out and their current one keeps\nworking.\n\nWho they are does not change: their organization, username and the identifier\ntheir existing sessions are keyed on all survive the write, so an update never\nsigns anyone out.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.UpdateInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.User"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/verification-codes":{"post":{"operationId":"post_v1_iam_verification-codes","summary":"Validates the request, mints + persists an OTP, and reports success.","description":"Validates the request, mints + persists an OTP, and\nreports success. The request fields are read via fiber's FormValue — the\nescape hatch zip exposes for form bodies (multipart or urlencoded) — since the\ntyped JSON Bind does not apply here. v1 also accepts countryCode/method/\ncheckUser/captchaType; iam ignores them (the captcha/forget/MFA flows those\ndrive are not ported), and CAPTCHA verification is likewise not enforced —\niam models no captcha provider — so the code is issued once the destination\nand application validate.","tags":["iam"],"x-app":"iam"}},"/v1/iam/web3/nonce":{"get":{"operationId":"get_v1_iam_web3_nonce","summary":"Starts a wallet sign-in: it returns a one-time challenge for the wallet to sign.","description":"Starts a wallet sign-in: it returns a one-time challenge for the wallet\nto sign. The challenge is good once and is tied to the site that asked for it,\nso a signature collected elsewhere cannot be replayed here.","tags":["iam"],"x-app":"iam"}},"/v1/iam/web3/verify":{"post":{"operationId":"post_v1_iam_web3_verify","summary":"Completes a wallet sign-in: it verifies the signed challenge and, if it holds, signs the wallet's owner in.","description":"Completes a wallet sign-in: it verifies the signed challenge and, if it\nholds, signs the wallet's owner in.\n\nThis IS the login — it answers exactly as a password sign-in does, so the rest\nof your flow does not branch on how somebody arrived.","tags":["iam"],"x-app":"iam"}},"/v1/iam/webauthn-credentials":{"get":{"operationId":"listWebauthnCredentials","summary":"Returns the passkeys and security keys registered in your organization, newest first — which device each belongs to and when it was last used.","description":"Returns the passkeys and security keys registered in\nyour organization, newest first — which device each belongs to and when it was\nlast used.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listWebauthnCredentialsOut"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"addWebauthnCredential","summary":"Registers a passkey or security key for a person, so they can sign in with their device instead of a password.","description":"Registers a passkey or security key for a person, so they\ncan sign in with their device instead of a password.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.WebauthnCredential"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/webauthn-credentials/delete":{"post":{"operationId":"deleteWebauthnCredential","summary":"Removes a passkey or security key — what you call when a device is lost.","description":"Removes a passkey or security key — what you call when\na device is lost. Make sure the person has another way to sign in first.\n\nA credential that is already gone answers \"nothing changed\" rather than an\nerror, so the call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialMutationResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/webauthn-credentials/get":{"post":{"operationId":"getWebauthnCredential","summary":"Returns one passkey or security key: whose it is, what device it lives on, and when it was registered.","description":"Returns one passkey or security key: whose it is, what\ndevice it lives on, and when it was registered.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/webauthn-credentials/update":{"post":{"operationId":"updateWebauthnCredential","summary":"Renames a registered passkey or security key, so a person can tell their devices apart.","description":"Renames a registered passkey or security key, so a\nperson can tell their devices apart.\n\nA credential that is not there answers \"nothing changed\" rather than an error,\nso the call is safe to repeat.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.WebauthnCredential"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialMutationResult"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/whoami":{"get":{"operationId":"get_v1_iam_whoami","summary":"Tells you who the current caller is — the lightweight check a page makes on load to decide whether to render signed-in or signed-out.","description":"Tells you who the current caller is — the lightweight check a\npage makes on load to decide whether to render signed-in or signed-out.\n\nIt answers for a session cookie or a bearer token alike, and says plainly when\nnobody is signed in rather than failing.","tags":["iam"],"x-app":"iam"}},"/v1/iam/workspaces":{"get":{"operationId":"get_v1_iam_workspaces","summary":"Returns your organization's workspaces, newest first — the scope a team works in, alongside projects rather than instead of them.","description":"Returns your organization's workspaces, newest first — the scope a\nteam works in, alongside projects rather than instead of them.\n\nYou see your own organization's workspaces and no one else's; which organization that\nis comes from your credentials, not from the request.","tags":["iam"],"parameters":[{"name":"owner","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.ListOutput"}}},"description":"ok"}},"x-app":"iam"},"post":{"operationId":"post_v1_iam_workspaces","summary":"Makes a workspace inside your organization — the scope a team works in, alongside projects rather than instead of them.","description":"Makes a workspace inside your organization — the scope a team works in,\nalongside projects rather than instead of them. A name already used in the\norganization is refused.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Workspace"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/workspaces/delete":{"post":{"operationId":"post_v1_iam_workspaces_delete","summary":"Removes a workspace.","description":"Removes a workspace. The people and roles in your organization are\nunchanged; what goes is the scope itself.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.DeleteOutput"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/workspaces/get":{"post":{"operationId":"post_v1_iam_workspaces_get","summary":"Returns one workspace: what it is called and how it is set up.","description":"Returns one workspace: what it is called and how it is set up.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Ref"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Workspace"}}},"description":"ok"}},"x-app":"iam"}},"/v1/iam/workspaces/update":{"post":{"operationId":"post_v1_iam_workspaces_update","summary":"Changes a workspace's settings.","description":"Changes a workspace's settings. What it is called does not change, and\nneither does when it was created.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Workspace"}}},"description":"ok"}},"x-app":"iam"}},"/v1/images/generations":{"post":{"operationId":"post_v1_images_generations","summary":"Implements POST /v1/images/generations (OpenAI-compatible).","description":"Implements POST /v1/images/generations (OpenAI-compatible).\n\nBody: {\"model\": \"...\", \"prompt\": \"...\", \"n\"?: int, \"size\"?: \"1024x1024\",\n\n\t\"response_format\"?: \"url\"|\"b64_json\"}\n\nIt authenticates the caller, resolves the model to its upstream provider via\nthe shared routing table (zen3-image* → do-ai fal diffusion), reserves the\nper-image budget, generates the image(s) through the do-ai async image\nclient, records usage for billing, and returns the OpenAI images response.","tags":["images"],"x-app":"github.com/hanzoai/ai"}},"/v1/index/health":{"get":{"operationId":"get_v1_index_health","summary":"Report whether the search plane can serve","description":"Answers Meilisearch's `{\"status\":\"available\"}` when the index store is readable. It FAILS CLOSED — an unreadable store answers 503 and `unavailable` — so a replica whose volume has gone bad stops taking traffic instead of answering every search with nothing found. It touches no tenant data and needs no credential.","tags":["index"],"x-app":"index"}},"/v1/index/indexes":{"get":{"operationId":"get_v1_index_indexes","summary":"List the indexes your org holds","description":"Answers every index in the caller's org with its primary key and timestamps. It is the only way to enumerate what an org holds — without it an index whose uid a caller has forgotten is unreachable. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"x-app":"index"},"post":{"operationId":"post_v1_index_indexes","summary":"Create an index","description":"Creates an index named by `uid` in the caller's org. `primaryKey` names the document field that identifies a document and defaults to `id`. Creating an index that already exists is not an error — it settles on the existing one, primary key included — so a client that creates before every write is safe to run repeatedly. A missing or over-long uid is 400 `invalid_index_uid`. A new index starts with `user` filterable, which is what lets a multi-user app narrow searches to one end user without configuring anything. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"x-app":"index"}},"/v1/index/indexes/{uid}":{"delete":{"operationId":"delete_v1_index_indexes_by_uid","summary":"Delete an index and everything in it","description":"Drops one index in the caller's org together with all of its documents. This is the only way to retire an index; without it a mistaken uid would be permanent. It is idempotent — dropping an index that is not there still succeeds. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"},"get":{"operationId":"get_v1_index_indexes_by_uid","summary":"Read one index's definition","description":"Answers a single index's uid, primary key and timestamps. An index the caller's org does not hold is 404 `index_not_found` — which is the same answer another org's index gives, since the org is a bound predicate on the read. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/indexes/{uid}/documents":{"get":{"operationId":"get_v1_index_indexes_by_uid_documents","summary":"Page through the documents in an index","description":"Answers the documents in one index with a total count. `limit` defaults to 20 and is capped at 1000, `offset` pages, and the response echoes both back so a pager knows what it actually got. An index the caller's org does not hold is 404 `index_not_found`. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"},"post":{"operationId":"post_v1_index_indexes_by_uid_documents","summary":"Add or replace documents in an index","description":"Upserts documents into one index, keyed by the index's primary key: a document whose key is already present is REPLACED, one that is not is added, and it becomes searchable immediately. Send an array, or a single object — a hand-rolled caller sending one document is accepted rather than 400'd. The index is created on demand, so a first write needs no create call.\n\nThis and the PUT on the same path are the SAME operation: both are a whole document upsert, which is what a Meilisearch client's addDocuments and updateDocuments both reduce to here. A body that is neither an array nor an object is 400. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"},"put":{"operationId":"put_v1_index_indexes_by_uid_documents","summary":"Add or update documents in an index","description":"Upserts documents into one index, keyed by the index's primary key: a document whose key is already present is REPLACED, one that is not is added, and it becomes searchable immediately. Send an array, or a single object — a hand-rolled caller sending one document is accepted rather than 400'd. The index is created on demand, so a first write needs no create call.\n\nThis and the POST on the same path are the SAME operation, served by one handler. Both exist because the Meilisearch dialect has both verbs; there is no partial-update semantics on this one — a document is replaced whole either way. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/indexes/{uid}/documents/delete-batch":{"post":{"operationId":"post_v1_index_indexes_by_uid_documents_delete-batch","summary":"Delete many documents by primary key in one call","description":"Removes every document named by an array of primary keys. Keys may be sent as strings or numbers — a number keeps its exact decimal form, so an integer key round-trips as `42` and never as scientific notation. Keys that are absent from the index are skipped rather than failing the batch, so this is idempotent. A body that is not an array is 400. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/indexes/{uid}/documents/{id}":{"delete":{"operationId":"delete_v1_index_indexes_by_uid_documents_by_id","summary":"Delete one document by its primary key","description":"Removes one document from an index. It is IDEMPOTENT: deleting a key that is not there succeeds rather than 404, so a retry after a lost response is safe. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"},"get":{"operationId":"get_v1_index_indexes_by_uid_documents_by_id","summary":"Read one document by its primary key","description":"Answers the stored document whose primary key matches, exactly as it was written. A missing document is 404 `document_not_found` and a missing index is 404 `index_not_found` — two different codes, because a client that branches on them treats the cases differently. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/indexes/{uid}/search":{"post":{"operationId":"post_v1_index_indexes_by_uid_search","summary":"Search an index, forgiving typos","description":"Answers the documents in one index matching `q`, ranked by how many of the query's terms they match, with prefix matching so a partial word still finds its document. `limit` defaults to 20 and is capped at 1000, `offset` pages; a negative value falls back to the default rather than erroring.\n\n`filter` takes a Meilisearch filter expression, or an array of them, and the `user = \"…\"` and `user IN […]` forms are honoured — that is how an app with many end users narrows results to one of them WITHIN the org. `estimatedTotalHits` is exact for the page returned, not an estimate, because every hit is materialised. An index the caller's org does not hold is 404 `index_not_found`. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/indexes/{uid}/settings":{"get":{"operationId":"get_v1_index_indexes_by_uid_settings","summary":"Read an index's filterable attributes","description":"Answers the attributes an index allows filtering on. This dialect implements the filterable-attributes setting and no other, so that is the whole of what comes back. An index the caller's org does not hold is 404 `index_not_found`. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"},"patch":{"operationId":"patch_v1_index_indexes_by_uid_settings","summary":"Set which attributes an index can be filtered on","description":"Replaces an index's filterable attributes with the list in `filterableAttributes`; omitting the field leaves them as they are. The index is CREATED ON DEMAND rather than 404'd, because a client that configures an index it has just asked for should not have to create it first — this is the one read-shaped path on the surface that writes. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers, and the task it names is already complete. A client that polls waitForTask resolves immediately rather than waiting, and a client that does not poll has still had its write committed.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/stats":{"get":{"operationId":"get_v1_index_stats","summary":"Count the documents in each of your indexes","description":"Answers a document count per index for the caller's org, plus their sum. `isIndexing` is always false, which is the honest answer here rather than a stub: writes are applied before their response, so there is never a backlog in progress to report. The tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, and every query filters on it, so two orgs may both hold an index named \"messages\" and neither can see the other's documents. Without a validated principal the answer is 403 carrying Meilisearch's `invalid_api_key` body. Errors use Meilisearch's {message, code, type, link} shape rather than cloud's, because that `code` is a wire contract a Meilisearch client branches on.","tags":["index"],"x-app":"index"}},"/v1/index/tasks/{uid}":{"get":{"operationId":"get_v1_index_tasks_by_uid","summary":"Check a write task, which has already finished","description":"Answers `succeeded` for the task id given. It ALWAYS answers succeeded, and that is honest rather than a stub: writes on this surface are applied before their response returns, so by the time any task id exists to ask about, its work is done. It exists so a Meilisearch client's waitForTask resolves at once instead of polling forever for a queue that was never there. It requires a validated principal but reads no tenant data.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"index"}},"/v1/index/version":{"get":{"operationId":"get_v1_index_version","summary":"Identify the search implementation answering","description":"Answers the version shape a Meilisearch client expects. It names THIS implementation rather than a Meilisearch release — the commit field reads `hanzo-cloud` — so a client that logs it records which server actually answered instead of implying a Meilisearch build. Needs no credential.","tags":["index"],"x-app":"index"}},"/v1/indexers":{"get":{"operationId":"get_v1_indexers","summary":"Reports the deployment's chain indexer(s) and how far each has indexed.","description":"Reports the deployment's chain indexer(s) and how far each has\nindexed. Identity and health come from the indexer's /health; the latest indexed\nblock (height + time) from its /v1/explorer/blocks. The row EXISTS if EITHER call\nreaches the indexer; when the indexer is entirely unreachable the answer degrades\nto an honest-EMPTY list at 200, not a 502. No chain HEAD is exposed by the indexer\nREST, so `lag` is honestly omitted rather than fabricated.","tags":["indexers"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexersOut"}}},"description":"ok"}},"x-app":"explorer"}},"/v1/ingress/middlewares":{"get":{"operationId":"get_v1_ingress_middlewares","summary":"Returns every edge transform the caller's org has configured, ordered by id.","description":"Returns every edge transform the caller's org has configured,\nordered by id. A route names the ones it wants, in order.","tags":["ingress"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingressMiddlewares"}}},"description":"ok"}},"x-app":"ingress"},"post":{"operationId":"post_v1_ingress_middlewares","summary":"Creates or replaces one edge transform and hot-applies it.","description":"Creates or replaces one edge transform and hot-applies it. POST\nmints an id when the body omits one; PUT takes the id from the URL, which wins\nover any id in the body. type must be one of redirectScheme, stripPrefix,\naddPrefix or headers, and stripPrefix/addPrefix each require their config key.","tags":["ingress"],"requestBody":{"content":{"application/json":{"example":{"config":{"prefixes":"/api"},"id":"strip-api","type":"stripPrefix"},"schema":{"$ref":"#/components/schemas/Middleware"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Middleware"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/middlewares/{id}":{"delete":{"operationId":"delete_v1_ingress_middlewares_by_id","summary":"Removes one of the caller org's edge transforms and hot-applies the change.","description":"Removes one of the caller org's edge transforms and hot-applies\nthe change. Routes still naming it stop being served (they compile as skipped)\nuntil they name a transform that exists. Answers 204; an id this org does not\nhold is 404.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"strip-api"}],"responses":{"204":{"description":"no content"}},"x-app":"ingress"},"get":{"operationId":"get_v1_ingress_middlewares_by_id","summary":"Returns one of the caller org's edge transforms by id.","description":"Returns one of the caller org's edge transforms by id.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"strip-api"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Middleware"}}},"description":"ok"}},"x-app":"ingress"},"put":{"operationId":"put_v1_ingress_middlewares_by_id","summary":"Creates or replaces one edge transform and hot-applies it.","description":"Creates or replaces one edge transform and hot-applies it. POST\nmints an id when the body omits one; PUT takes the id from the URL, which wins\nover any id in the body. type must be one of redirectScheme, stripPrefix,\naddPrefix or headers, and stripPrefix/addPrefix each require their config key.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID identifies the transform within the org: [A-Za-z0-9-_.], at most 128\nchars. A create that omits it gets a generated one. Routes reference it by\nthis id.","schema":{"type":"string"},"example":"strip-api"}],"requestBody":{"content":{"application/json":{"example":{"config":{"prefixes":"/api"},"id":"strip-api","type":"stripPrefix"},"schema":{"$ref":"#/components/schemas/Middleware"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Middleware"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/routes":{"get":{"operationId":"get_v1_ingress_routes","summary":"Returns every routing rule the caller's org has configured, ordered by id.","description":"Returns every routing rule the caller's org has configured, ordered\nby id. A route maps an exact Host (and optional path prefix) to a service.","tags":["ingress"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingressRoutes"}}},"description":"ok"}},"x-app":"ingress"},"post":{"operationId":"post_v1_ingress_routes","summary":"Creates or replaces one routing rule and hot-applies the new table — there is no config file and no restart.","description":"Creates or replaces one routing rule and hot-applies the new table —\nthere is no config file and no restart. POST mints an id when the body omits\none; PUT takes the id from the URL, which wins over any id in the body. A\nroute's host is a GLOBALLY unique DNS claim: a host another org's route already\nholds is refused 409, so no tenant can hijack another's hostname.","tags":["ingress"],"requestBody":{"content":{"application/json":{"example":{"host":"app.example.com","id":"web","service":"app-pool","tls":true},"schema":{"$ref":"#/components/schemas/Route"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Route"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/routes/{id}":{"delete":{"operationId":"delete_v1_ingress_routes_by_id","summary":"Removes one of the caller org's routing rules and hot-applies the shrunken table, freeing its host for another claim.","description":"Removes one of the caller org's routing rules and hot-applies the\nshrunken table, freeing its host for another claim. Answers 204; an id this org\ndoes not hold is 404.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"web"}],"responses":{"204":{"description":"no content"}},"x-app":"ingress"},"get":{"operationId":"get_v1_ingress_routes_by_id","summary":"Returns one of the caller org's routing rules by id.","description":"Returns one of the caller org's routing rules by id.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"a1b2c3d4e5f60718"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Route"}}},"description":"ok"}},"x-app":"ingress"},"put":{"operationId":"put_v1_ingress_routes_by_id","summary":"Creates or replaces one routing rule and hot-applies the new table — there is no config file and no restart.","description":"Creates or replaces one routing rule and hot-applies the new table —\nthere is no config file and no restart. POST mints an id when the body omits\none; PUT takes the id from the URL, which wins over any id in the body. A\nroute's host is a GLOBALLY unique DNS claim: a host another org's route already\nholds is refused 409, so no tenant can hijack another's hostname.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID identifies the route within the org: [A-Za-z0-9-_.], at most 128 chars.\nA create that omits it gets a generated one.","schema":{"type":"string"},"example":"web"}],"requestBody":{"content":{"application/json":{"example":{"host":"app.example.com","id":"web","service":"app-pool","tls":true},"schema":{"$ref":"#/components/schemas/Route"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Route"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/services":{"get":{"operationId":"get_v1_ingress_services","summary":"Returns every backend pool the caller's org has configured, ordered by id.","description":"Returns every backend pool the caller's org has configured,\nordered by id. A service is the weighted round-robin target a route dispatches\nto.","tags":["ingress"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingressServices"}}},"description":"ok"}},"x-app":"ingress"},"post":{"operationId":"post_v1_ingress_services","summary":"Creates or replaces one backend pool and hot-applies it.","description":"Creates or replaces one backend pool and hot-applies it. POST mints\nan id when the body omits one; PUT takes the id from the URL, which wins over\nany id in the body. A pool needs at least one backend and every backend URL\nmust be http(s)://host[:port].","tags":["ingress"],"requestBody":{"content":{"application/json":{"example":{"backends":[{"url":"http://10.0.0.7:8000","weight":1}],"id":"app-pool"},"schema":{"$ref":"#/components/schemas/Service"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/services/{id}":{"delete":{"operationId":"delete_v1_ingress_services_by_id","summary":"Removes one of the caller org's backend pools and hot-applies the change.","description":"Removes one of the caller org's backend pools and hot-applies the\nchange. Routes still pointing at it stop being served (they compile as skipped)\nuntil they name a pool that exists. Answers 204; an id this org does not hold\nis 404.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"app-pool"}],"responses":{"204":{"description":"no content"}},"x-app":"ingress"},"get":{"operationId":"get_v1_ingress_services_by_id","summary":"Returns one of the caller org's backend pools by id.","description":"Returns one of the caller org's backend pools by id.","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the object to act on, from the path.","schema":{"type":"string"},"example":"app-pool"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}},"description":"ok"}},"x-app":"ingress"},"put":{"operationId":"put_v1_ingress_services_by_id","summary":"Creates or replaces one backend pool and hot-applies it.","description":"Creates or replaces one backend pool and hot-applies it. POST mints\nan id when the body omits one; PUT takes the id from the URL, which wins over\nany id in the body. A pool needs at least one backend and every backend URL\nmust be http(s)://host[:port].","tags":["ingress"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID identifies the pool within the org: [A-Za-z0-9-_.], at most 128 chars.\nA create that omits it gets a generated one. Routes reference it by this id.","schema":{"type":"string"},"example":"app-pool"}],"requestBody":{"content":{"application/json":{"example":{"backends":[{"url":"http://10.0.0.7:8000","weight":1}],"id":"app-pool"},"schema":{"$ref":"#/components/schemas/Service"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/status":{"get":{"operationId":"get_v1_ingress_status","summary":"Status reports the ingress edge's live posture: the role this instance runs in (app or edge), whether its listeners are bound and on which addresses, the ACME posture (staging flag and certificate cache directory), how many hosts the compiled route table currently serves, and how many the ACME HostPolicy will issue a certificate for.","description":"Status reports the ingress edge's live posture: the role this instance runs in\n(app or edge), whether its listeners are bound and on which addresses, the ACME\nposture (staging flag and certificate cache directory), how many hosts the\ncompiled route table currently serves, and how many the ACME HostPolicy will\nissue a certificate for.","tags":["ingress"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingressStatus"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/ingress/tls":{"get":{"operationId":"get_v1_ingress_tls","summary":"GetTLS returns the caller org's ACME intent together with the edge-wide TLS facts it lands in: which role this instance runs in, whether its listeners are bound, every host the ACME HostPolicy will issue a certificate for (the union across ALL orgs of TLS-marked routes and configured extraHosts, because one process holds one certificate cache), and the ACME directory and account email the process was started with.","description":"GetTLS returns the caller org's ACME intent together with the edge-wide TLS\nfacts it lands in: which role this instance runs in, whether its listeners are\nbound, every host the ACME HostPolicy will issue a certificate for (the union\nacross ALL orgs of TLS-marked routes and configured extraHosts, because one\nprocess holds one certificate cache), and the ACME directory and account email\nthe process was started with.","tags":["ingress"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingressTLS"}}},"description":"ok"}},"x-app":"ingress"},"put":{"operationId":"put_v1_ingress_tls","summary":"PutTLS replaces the caller org's ACME intent and hot-applies what can be hot-applied.","description":"PutTLS replaces the caller org's ACME intent and hot-applies what can be\nhot-applied. extraHosts are normalized and validated, then feed the ACME\nHostPolicy on the reload this op performs, alongside the per-route tls flags.\nacmeEmail and staging bind an ACME account for the lifetime of an edge process,\nso they only take effect when the edge (re)starts — the returned note says so.","tags":["ingress"],"requestBody":{"content":{"application/json":{"example":{"extraHosts":["www.example.com"]},"schema":{"$ref":"#/components/schemas/TLSConfig"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TLSConfig"}}},"description":"ok"}},"x-app":"ingress"}},"/v1/insights/events":{"get":{"operationId":"get_v1_insights_events","summary":"Returns the caller org's most recent product events, newest first.","description":"Returns the caller org's most recent product events, newest first.\nThe console's raw-event view over event.event — the same table the capture doors\nfill — one row per stored event, with the row's attributes returned as the\nproperties object.\n\nThe org is the validated principal's — never a parameter — and a read requires a\nreal bearer, never the write-only publishable key. 403 without a validated bearer,\n503 when the warehouse is unreachable.","tags":["insights"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit is how many rows to return, newest first. Default 50, maximum 200; a\nvalue at or below zero, or one that is not a number, takes the default.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/eventList"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/insights/health":{"get":{"operationId":"get_v1_insights_health","summary":"Reports that the unified insights surface is serving.","description":"Reports that the unified insights surface is serving. It reads no\ntenant data and consults no dependency, so it answers 200 unconditionally and needs\nno principal — liveness must be probe-able. The warehouse-connectivity probe is a\ndifferent question and lives at GET /v1/analytics/health.","tags":["insights"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/insightsStatus"}}},"description":"ok"}},"x-app":"analytics"}},"/v1/install-patch":{"post":{"operationId":"post_v1_install-patch","summary":"Install an OS patch by patch ID (KB number or title) asynchronously","description":"Install an OS patch by patch ID (KB number or title) asynchronously","tags":["install-patch"],"x-app":"github.com/hanzoai/ai"}},"/v1/integrations":{"get":{"operationId":"get_v1_integrations","summary":"Returns every registered integration provider together with THIS org's connection status for it — the catalog the console's Integrations page renders.","description":"Returns every registered integration provider together with THIS org's\nconnection status for it — the catalog the console's Integrations page renders.\nOrg-authed: a caller with no validated principal is 403, because the status is\nper-org and there is no org-less answer. User-plane providers (the /v1/connectors\nsurface) are omitted; the two planes are disjoint.","tags":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"providers":[{"available":true,"category":"Communication","connected":true,"connection":{"account":"Acme","connectedAt":"2026-07-01T10:00:00Z","externalId":"T0231","scopes":["chat:write"]},"description":"Connect your workspace.","id":"slack","name":"Slack"}]},"schema":{"$ref":"#/components/schemas/listOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/discord/interactions":{"post":{"operationId":"post_v1_integrations_discord_interactions","summary":"Discord interactions endpoint","description":"The Interactions Endpoint URL for the Discord app. It answers Discord's PING with a PONG, and handles the `/hanzo` slash command by acknowledging with a deferred ephemeral reply and editing that reply with the answer once the agent has run. Any other interaction is acknowledged and ignored.\n\nRequests are verified by ED25519 SIGNATURE over the timestamp and body against the app's public key — not by HMAC, unlike the Slack webhooks. Interactions work over plain HTTP, so no gateway connection and no message-content intent is involved.\n\nDiscord does not retry, so this is the one bridge where being at capacity is shown to the user as an ephemeral ask-to-run-it-again rather than answered as a retriable failure — nothing is recorded either way, so the next attempt is clean.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/discord/link":{"get":{"operationId":"get_v1_integrations_discord_link","summary":"Begin linking a Hanzo account from Discord","description":"The entry point behind the connect prompt Hanzo shows in a Discord server. It starts a link session and redirects to Discord's OAuth `identify` consent — the narrowest scope that establishes which Discord user is asking, and nothing more.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/discord/link/callback":{"get":{"operationId":"get_v1_integrations_discord_link_callback","summary":"Complete the Discord account link","description":"The final leg: it binds the verified Discord user to the Hanzo account that just signed in, and answers a short confirmation page telling them to return to Discord. The Hanzo credential is sealed into the connected org's KMS namespace rather than stored beside the link.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/discord/link/discord":{"get":{"operationId":"get_v1_integrations_discord_link_discord","summary":"Discord sign-in return leg","description":"Where Discord returns the user after the identify consent. It resolves the verified Discord user, confirms the server is connected to an org, and hands the browser to the Hanzo sign-in that completes the link.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/github/issues/backfill":{"post":{"operationId":"post_v1_integrations_github_issues_backfill","summary":"Seeds the native tracker with the EXISTING issues across the org's granted repos (default state=open); the webhook keeps them live thereafter.","description":"Seeds the native tracker with the EXISTING issues across the\norg's granted repos (default state=open); the webhook keeps them live thereafter.\nOrg-scoped by the validated principal — a caller only ever backfills its OWN org.\nSynchronous + bounded (a total time budget and an issue cap) so it returns the\ncounts directly; idempotent by ExtRef, so a re-run continues where a truncated\npass left off and never duplicates.","tags":["integrations"],"requestBody":{"content":{"application/json":{"example":{"state":"all"},"schema":{"$ref":"#/components/schemas/githubBackfillIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"created":410,"failed":0,"issues":430,"repos":12,"updated":20},"schema":{"$ref":"#/components/schemas/githubBackfillResult"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/github/repos":{"get":{"operationId":"get_v1_integrations_github_repos","summary":"Lists the org's granted GitHub repositories, each annotated with its native import + sync status from the git object plane.","description":"Lists the org's granted GitHub repositories, each annotated with its\nnative import + sync status from the git object plane. Org-authed: the org comes\nfrom the validated principal, and the granted set is bounded to THAT org's\ninstallation token — an org can never enumerate another org's repos. The console\npolls it to watch an import flip a repo to imported.","tags":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"repos":[{"defaultBranch":"main","fullName":"acme/widgets","htmlUrl":"https://github.com/acme/widgets","imported":true,"lastSyncedAt":"2026-07-01T10:00:00Z","name":"widgets","private":true,"syncStatus":"synced"}]},"schema":{"$ref":"#/components/schemas/githubReposOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/github/repos/import":{"post":{"operationId":"post_v1_integrations_github_repos_import","summary":"Imports the selected (or all) granted repos into git.hanzo.ai.","description":"Imports the selected (or all) granted repos into git.hanzo.ai. The\nselection is intersected with the installation's GRANTED set, so a client can\nnever import a repo the App was not granted (org isolation + a grant check). The\nimport runs in a bounded background worker (don't block the request), so the\nanswer is 202 Accepted; poll GET /v1/integrations/github/repos for the per-repo\nstatus to flip to imported.","tags":["integrations"],"requestBody":{"content":{"application/json":{"example":{"repos":["widgets"]},"schema":{"$ref":"#/components/schemas/githubImportIn"}}},"required":true},"responses":{"202":{"content":{"application/json":{"example":{"queued":1,"repos":["widgets"]},"schema":{"$ref":"#/components/schemas/githubImportOut"}}},"description":"accepted"}},"x-app":"integrations"}},"/v1/integrations/github/repos/{repo}/pages":{"delete":{"operationId":"delete_v1_integrations_github_repos_by_repo_pages","summary":"Deletes the repo's Pages site.","description":"Deletes the repo's Pages site. 404 when there is none, so a\ncaller can tell \"turned it off\" from \"there was nothing on\".","tags":["integrations"],"parameters":[{"name":"repo","in":"path","required":true,"description":"Repo is the repository's short name within the org's installation, with no\nowner prefix (the owner is server-derived from the grant). A trailing \".git\"\nis stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"disabled":true,"repo":"widgets"},"schema":{"$ref":"#/components/schemas/githubPagesDisabledOut"}}},"description":"ok"}},"x-app":"integrations"},"get":{"operationId":"get_v1_integrations_github_repos_by_repo_pages","summary":"Returns the repo's Pages status, live URL, custom domain and build source.","description":"Returns the repo's Pages status, live URL, custom domain and build\nsource. The repo is resolved against the org installation's GRANTED set, so a\ncaller can never address a repo the App was not granted; 404 when the repo has no\nPages site.","tags":["integrations"],"parameters":[{"name":"repo","in":"path","required":true,"description":"Repo is the repository's short name within the org's installation, with no\nowner prefix (the owner is server-derived from the grant). A trailing \".git\"\nis stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"200":{"content":{"application/json":{"example":{"buildType":"legacy","cname":"docs.acme.com","custom404":false,"httpsEnforced":true,"repo":"widgets","source":{"branch":"main","path":"/docs"},"status":"built","url":"https://acme.github.io/widgets/"},"schema":{"$ref":"#/components/schemas/githubPagesView"}}},"description":"ok"}},"x-app":"integrations"},"post":{"operationId":"post_v1_integrations_github_repos_by_repo_pages","summary":"Creates the repo's Pages site and answers 201 Created with it.","description":"Creates the repo's Pages site and answers 201 Created with it.\nWith buildType \"workflow\" the site builds via GitHub Actions; otherwise it builds\nfrom a branch source, defaulting to the repo's own default branch when none is\ngiven. Only \"/\" and \"/docs\" are legal source paths (GitHub's rule).","tags":["integrations"],"parameters":[{"name":"repo","in":"path","required":true,"description":"Repo is the repository, from the :repo path segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"branch":"main","path":"/docs","repo":"widgets"},"schema":{"$ref":"#/components/schemas/githubPagesEnableReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"buildType":"legacy","custom404":false,"httpsEnforced":true,"repo":"widgets","source":{"branch":"main","path":"/docs"},"status":"building","url":"https://acme.github.io/widgets/"},"schema":{"$ref":"#/components/schemas/githubPagesView"}}},"description":"ok"}},"x-app":"integrations"},"put":{"operationId":"put_v1_integrations_github_repos_by_repo_pages","summary":"Sets or clears the custom domain (cname) and updates HTTPS enforcement, build type, or source.","description":"Sets or clears the custom domain (cname) and updates HTTPS\nenforcement, build type, or source. ONLY the provided fields are sent to GitHub,\nso an update never resets a setting the caller did not mention.","tags":["integrations"],"parameters":[{"name":"repo","in":"path","required":true,"description":"Repo is the repository, from the :repo path segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"cname":"docs.acme.com","httpsEnforced":true,"repo":"widgets"},"schema":{"$ref":"#/components/schemas/githubPagesUpdateReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"repo":"widgets","updated":true},"schema":{"$ref":"#/components/schemas/githubPagesUpdatedOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/github/repos/{repo}/pages/builds":{"post":{"operationId":"post_v1_integrations_github_repos_by_repo_pages_builds","summary":"Requests a Pages rebuild and returns the queued build's status.","description":"Requests a Pages rebuild and returns the queued build's status.\nThe build is queued AT GITHUB, not completed here, so the answer is 202 Accepted\nand its status is the one GitHub reported at queue time. 404 when the repository\nhas no Pages site, or when the org's installation was not granted it.","tags":["integrations"],"parameters":[{"name":"repo","in":"path","required":true,"description":"Repo is the repository's short name within the org's installation, with no\nowner prefix (the owner is server-derived from the grant). A trailing \".git\"\nis stripped.","schema":{"type":"string"},"example":"widgets"}],"responses":{"202":{"content":{"application/json":{"example":{"repo":"widgets","status":"queued","url":"https://api.github.com/repos/acme/widgets/pages/builds/1"},"schema":{"$ref":"#/components/schemas/githubPagesBuildOut"}}},"description":"accepted"}},"x-app":"integrations"}},"/v1/integrations/slack/commands":{"post":{"operationId":"post_v1_integrations_slack_commands","summary":"Slack slash command webhook","description":"The address Slack posts a slash command to, form-encoded. It acknowledges inside Slack's three-second budget and posts the answer afterwards to the command's own response URL, which is why the immediate reply is empty.\n\nThe body is verified against the same app signing secret as the events webhook, and a repeat of the same command invocation is absorbed rather than answered twice.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.\n\nThe answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/slack/events":{"post":{"operationId":"post_v1_integrations_slack_events","summary":"Slack Events API webhook","description":"The address a Slack app posts workspace events to. It answers Slack's url_verification handshake with the challenge, and routes an @mention or a direct message to an agent turn that replies in the same thread. A prompt beginning with `code:` is routed to the coding flow instead, which runs under its own pool.\n\nThe raw body and its timestamp are verified against the app's signing secret before anything is read from them. Hanzo's own bot messages are dropped, so a reply cannot trigger another reply.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.\n\nThe answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/slack/install":{"get":{"operationId":"get_v1_integrations_slack_install","summary":"Install the Hanzo app into a Slack workspace","description":"The address behind Slack's \"Add to Slack\" and Marketplace Install buttons. It answers a 302 to Slack's own consent screen and does nothing else — it is a redirector by design.\n\nIt exists because Slack refuses a slack.com URL in that field and requires one of ours that redirects there, which makes the field an ATTRIBUTION hook: routing the click through our own address is what lets an install be counted, and always answering the redirect is what keeps the counter from becoming a detour that never reaches consent. The destination is the same consent URL every time, built from the same scopes the console's Connect button asks for, so a workspace is asked to grant one thing however the install began.\n\nIt is PUBLIC and carries no principal, because whoever clicks Install in Slack's directory has no Hanzo session yet. It binds no org either, and that is deliberate rather than missing: the org is resolved at the shared provider callback, from the signed state a console connect minted or from the workspace's existing connection. Minting an org for an anonymous click is the one thing that would break tenant isolation, so an install begun here finishes under exactly the rules every other install obeys.\n\nWhere the app is not configured it answers 503, rather than a consent URL carrying an empty client_id that Slack would render as its own dead-end error page.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/slack/link":{"get":{"operationId":"get_v1_integrations_slack_link","summary":"Begin linking a Hanzo account from Slack","description":"The entry point behind the connect prompt Hanzo posts in Slack. It starts a link session in the browser and redirects to Slack's own sign-in, which is what proves which Slack user is asking.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/slack/link/callback":{"get":{"operationId":"get_v1_integrations_slack_link_callback","summary":"Complete the Slack account link","description":"The final leg: the user has proved both who they are in Slack and who they are in Hanzo, and this binds the two. It answers a short confirmation page telling them to return to Slack.\n\nThe Hanzo credential obtained here is sealed into the connected workspace's own KMS namespace; it is never written to a database column and never logged. A deployment whose secret store is unavailable refuses the link rather than completing it without custody of the credential.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/slack/link/slack":{"get":{"operationId":"get_v1_integrations_slack_link_slack","summary":"Slack sign-in return leg","description":"Where Slack returns the user after they sign in. It establishes the verified Slack workspace and user, confirms that workspace is connected to an org, and hands the browser on to the Hanzo sign-in that completes the link.\n\nThe verified pair is carried onward in a host-bound cookie rather than in the URL, so the identity being linked cannot be edited in transit.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/teams/events":{"post":{"operationId":"post_v1_integrations_teams_events","summary":"Microsoft Teams Bot Framework webhook","description":"The messaging endpoint for the Teams bot. A message activity is routed to an agent turn and answered proactively through the Bot Connector; anything that is not a message with text is acknowledged and ignored.\n\nAuthentication is the Bot Framework's RS256 JWT, verified against its published keys and bound BOTH to this deployment's app id and to the activity's own service URL. The service-URL binding is the part that matters: without it a token valid for one activity could point the outbound reply somewhere else.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.\n\nThe answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/teams/link":{"get":{"operationId":"get_v1_integrations_teams_link","summary":"Begin linking a Hanzo account from Teams","description":"The entry point behind the connect prompt Hanzo shows in Teams. It starts a link session and redirects to Microsoft sign-in addressed to the CHAT'S OWN tenant, not the common endpoint, so only a member of that tenant can complete it.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/teams/link/aad":{"get":{"operationId":"get_v1_integrations_teams_link_aad","summary":"Microsoft sign-in return leg","description":"Where Microsoft returns the user after sign-in. It resolves the verified directory identity and then re-checks the tenant: the signed-in user's tenant must equal the tenant of the chat the link started from, so a valid Microsoft sign-in from a different organization is refused here rather than accepted.\n\nThis is the leg Teams has and the other platforms do not, which is why the Teams flow has an extra address.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/teams/link/callback":{"get":{"operationId":"get_v1_integrations_teams_link_callback","summary":"Complete the Teams account link","description":"The final leg: it binds the verified directory identity to the Hanzo account that just signed in, and answers a short confirmation page telling them to return to Teams. The Hanzo credential is sealed into the connected org's KMS namespace.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/telegram/connect":{"post":{"operationId":"post_v1_integrations_telegram_connect","summary":"Mints a short, single-use deep-link code bound to the caller's org and returns the t.me link the console navigates to.","description":"Mints a short, single-use deep-link code bound to the caller's\norg and returns the t.me link the console navigates to. Org-authed: a caller with\nno validated principal is 403 (same gate as the framework connect). The code is\nstored as an oauth_nonce (org,telegram); the webhook's /start handler claims it to\nbind chat→org. It is short (128-bit hex) so it fits Telegram's 64-char `start`\npayload limit.","tags":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"authorizeUrl":"https://t.me/hanzo_bot?start=9f3c1d2e4b5a6c7d8e9f0a1b2c3d4e5f"},"schema":{"$ref":"#/components/schemas/authorizeOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/telegram/link":{"get":{"operationId":"get_v1_integrations_telegram_link","summary":"Begin linking a Hanzo account from Telegram","description":"The entry point behind the connect prompt Hanzo sends in Telegram. Unlike the other platforms it answers an HTML PAGE rather than a redirect: Telegram has no OAuth flow, so the page hosts Telegram's Login Widget, and the browser is sent onward only after the user signs in through it.\n\nThe widget only appears on the domain registered for the bot, so a deployment whose bot domain is unset renders a page with nothing on it.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/telegram/link/auth":{"get":{"operationId":"get_v1_integrations_telegram_link_auth","summary":"Telegram Login Widget return leg","description":"Where Telegram's Login Widget sends the user with its signed authentication data. That data is verified against the bot token — this is the identity source, and it is the widget's signature rather than a code exchange — and the chat is confirmed to be bound to an org before the browser is handed to the Hanzo sign-in.\n\nWidget data is only accepted while it is fresh, so a captured sign-in blob cannot be replayed later even though its signature stays valid.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/telegram/link/callback":{"get":{"operationId":"get_v1_integrations_telegram_link_callback","summary":"Complete the Telegram account link","description":"The final leg: it binds the verified Telegram user to the Hanzo account that just signed in, and answers a short confirmation page telling them to return to Telegram. The Hanzo credential is sealed into the connected org's KMS namespace.\n\nThis is one leg of a three-leg flow, and the legs are not interchangeable: a browser is expected to arrive here only from the leg before it. The link URL's state proves the prompt was server-minted and carries the CHAT it started from — it is provenance only, and it never decides which account gets linked. The account identity always comes from the platform's own verified sign-in and a host-bound cookie, so forwarding a link to someone else cannot bind their account, and a session lifted into another browser is refused rather than completed. Each link is single-use, and a deployment without linking configured answers 503.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/telegram/webhook":{"post":{"operationId":"post_v1_integrations_telegram_webhook","summary":"Telegram Bot API webhook","description":"The update webhook for the Telegram bot. It does two jobs: `/start \u003ccode\u003e` or `/connect \u003ccode\u003e` binds the chat it was sent from to an org, idempotently; anything else is treated as a possible agent trigger.\n\nWhat counts as a trigger differs by chat type, and it is easy to get wrong: in a private chat every message is a trigger, while in a group the message must mention the bot or use the `/hanzo` command. Non-triggers and non-message updates are acknowledged and dropped.\n\nAuthentication is the secret token Telegram echoes on every update, compared in constant time. A message in a chat that has never been bound is dropped, which is why the bind command exists.\n\nThe caller here is the PLATFORM, not a Hanzo tenant, so there is no bearer and no principal. The signature check IS the authentication, and it fails closed. The tenant is never read from the payload either: it is resolved from the verified platform identifier through the connection map, so an event from a workspace nobody connected does nothing. Refusals are written with their own status rather than being flattened to a 500, so a rejected signature reads as 401 and a malformed body as 400.\n\nThe answer is acknowledged immediately and the work happens afterwards, because every one of these platforms times out a slow webhook. Duplicate deliveries are absorbed durably, so a platform retry of an event that already ran never runs it a second time or bills for it twice. When the agent pool is full nothing at all is recorded and the delivery is refused as retriable, so the message is re-delivered later rather than being lost or half-processed.","tags":["integrations"],"x-app":"integrations"}},"/v1/integrations/{provider}":{"get":{"operationId":"get_v1_integrations_by_provider","summary":"Returns ONE provider with this org's connection status — the same view list carries, for a single id.","description":"Returns ONE provider with this org's connection status — the same view list\ncarries, for a single id. An unknown id is 404, and so is a user-plane provider:\nthe org surface never resolves one.","tags":["integrations"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the registry id of the connector — \"slack\", \"github\",\n\"cloudflare\". Unknown ids are 404, as are the user-plane (/v1/connectors)\nproviders, which this surface never resolves.","schema":{"type":"string"},"example":"slack"}],"responses":{"200":{"content":{"application/json":{"example":{"available":true,"category":"Communication","connected":false,"description":"Connect your workspace.","id":"slack","name":"Slack"},"schema":{"$ref":"#/components/schemas/providerView"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/{provider}/callback":{"get":{"operationId":"get_v1_integrations_by_provider_callback","summary":"OAuth return for any connector","description":"The single address every connector's OAuth flow returns to. It exchanges the authorization the provider granted, records the connection, and ALWAYS redirects the browser back to the console — on success and on every labeled failure alike, so a user never lands on a raw JSON dead end.\n\nIt is public and carries no principal, so the org is taken ONLY from the signed state minted when the flow began; no header is trusted here. That state is single-use and is burned BEFORE the exchange, so one authorization is one attempt and a replayed return fails instead of exchanging twice.\n\nTokens are sealed into the org's KMS namespace BEFORE the connection row is written, so a failure of the secret store leaves no half-connected integration advertising a credential that was never stored. Token values never appear in the redirect, in a log line or in an error.\n\nOne generalization is worth knowing: a GitHub App installation returns an installation identifier instead of an OAuth code, and it is accepted in the code's place so the App model needs no second address.","tags":["integrations"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"integrations"}},"/v1/integrations/{provider}/connect":{"post":{"operationId":"post_v1_integrations_by_provider_connect","summary":"Acquires the org's credential for one provider.","description":"Acquires the org's credential for one provider. It has TWO paths and the\nREQUEST picks which: a \"token\" key in the body seals that credential directly\n(verify-before-store), and its absence begins the 3-legged OAuth flow — minting a\nsingle-use nonce plus an HMAC-signed state that binds this org to this provider,\nand answering with the provider's authorize URL for the caller to redirect to.\n\nFail-closed order, unchanged: no principal → 403; unknown provider → 404; an\nAdminOnly connector without the caller's own-org admin bit → 403; not configured\n→ 503; KMS not ready → 503 (the flow WILL need to seal a token, so refuse now\nrather than dead-end at the callback).","tags":["integrations"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the connector's registry id, from the :provider path segment.","schema":{"type":"string"},"example":"cloudflare"}],"requestBody":{"content":{"application/json":{"example":{"accountId":"a1b2c3","provider":"cloudflare","token":"cf-scoped-api-token"},"schema":{"$ref":"#/components/schemas/connectIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"account":"Acme","connected":true,"externalId":"a1b2c3","provider":"cloudflare","scopes":[]},"schema":{"$ref":"#/components/schemas/connectOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/{provider}/disconnect":{"post":{"operationId":"post_v1_integrations_by_provider_disconnect","summary":"Revokes (best-effort) and forgets an org's connection: it deletes every custodied KMS secret and the connection row.","description":"Revokes (best-effort) and forgets an org's connection: it deletes\nevery custodied KMS secret and the connection row. Idempotent — disconnecting a\nprovider that was never connected still returns {disconnected:true}. Symmetric\nwith connect: an AdminOnly connector needs the caller's own-org admin bit.","tags":["integrations"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the registry id of the connector — \"slack\", \"github\",\n\"cloudflare\". Unknown ids are 404, as are the user-plane (/v1/connectors)\nproviders, which this surface never resolves.","schema":{"type":"string"},"example":"slack"}],"responses":{"200":{"content":{"application/json":{"example":{"disconnected":true},"schema":{"$ref":"#/components/schemas/disconnectOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/integrations/{provider}/verify":{"post":{"operationId":"post_v1_integrations_by_provider_verify","summary":"Re-checks a CONNECTED apikey connector's stored credential against the provider, live (`hanzo connector verify`).","description":"Re-checks a CONNECTED apikey connector's stored credential against the\nprovider, live (`hanzo connector verify`). Org-scoped (any member may check\nstatus); the credential is read from KMS, verified, and NEVER returned or logged.\nA verification failure is reported as {active:false}, not an error — the console/\nCLI renders it. Only apikey providers support verify (OAuth tokens are checked at\nuse, not re-verified here).","tags":["integrations"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the registry id of the connector — \"slack\", \"github\",\n\"cloudflare\". Unknown ids are 404, as are the user-plane (/v1/connectors)\nproviders, which this surface never resolves.","schema":{"type":"string"},"example":"cloudflare"}],"responses":{"200":{"content":{"application/json":{"example":{"account":"Acme","active":true,"externalId":"a1b2c3","provider":"cloudflare","scopes":["zone:read"]},"schema":{"$ref":"#/components/schemas/verifyOut"}}},"description":"ok"}},"x-app":"integrations"}},"/v1/k8s/clusters":{"get":{"operationId":"listKubernetesClusters","summary":"Lists the org's DOKS clusters (Visor, house account) folded with the org's BYO clusters — ONE fleet cluster view under the unified k8s noun.","description":"Lists the org's DOKS clusters (Visor, house account) folded with\nthe org's BYO clusters — ONE fleet cluster view under the unified k8s noun. A Visor\noutage is logged and skipped so a down optional provider never hides the BYO list.","tags":["k8s"],"responses":{"200":{"content":{"application/json":{"example":{"clusters":[{"doClusterId":"cl-1","doksClusterId":"cl-1","kind":"managed","name":"prod","nodeCount":0,"nodePools":[],"region":"nyc3","status":"running"}]},"schema":{"$ref":"#/components/schemas/clusterList"}}},"description":"ok"}},"x-app":"visor"},"post":{"operationId":"createKubernetesCluster","summary":"Provisions a DOKS cluster for the caller's org and answers 201.","description":"Provisions a DOKS cluster for the caller's org and answers 201.\nADMIN-GATED — a SuperAdmin, or an OrgAdmin of the caller's own org — because\nprovisioning spends real infrastructure on the house account. The request is\nvalidated at this boundary, then Visor owns provisioning and the hanzo-org\nownership tag.","tags":["k8s"],"requestBody":{"content":{"application/json":{"example":{"name":"prod","nodePool":{"count":2,"name":"gpu","size":"gpu-h100x8-640gb"},"region":"nyc3"},"schema":{"$ref":"#/components/schemas/createClusterReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"doClusterId":"cl-1","doksClusterId":"cl-1","kind":"managed","name":"prod","nodeCount":0,"nodePools":[],"region":"nyc3","status":"provisioning"},"schema":{"$ref":"#/components/schemas/clusterView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/k8s/clusters/{id}":{"delete":{"operationId":"deleteKubernetesCluster","summary":"Destroys a DOKS cluster by id and answers 204.","description":"Destroys a DOKS cluster by id and answers 204. ADMIN-GATED, like\ncreate. Visor scopes the delete to the org (refuses a foreign id), so this can\nonly ever remove the caller org's own cluster.","tags":["k8s"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the provider's DOKS cluster id. Visor scopes the lookup to the caller's\norg, so another tenant's id resolves to not-found rather than their cluster.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"visor"},"get":{"operationId":"getKubernetesCluster","summary":"Returns one cluster's detail: node pools + worker nodes.","description":"Returns one cluster's detail: node pools + worker nodes. Visor scopes\nthe lookup to the org (a foreign or missing id resolves to not-found), so a tenant\ncan never read another tenant's cluster by guessing an id.","tags":["k8s"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the provider's DOKS cluster id. Visor scopes the lookup to the caller's\norg, so another tenant's id resolves to not-found rather than their cluster.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"doksClusterId":"cl-1","kind":"managed","name":"prod","nodeCount":1,"nodePools":[{"count":1,"name":"gpu","poolId":"p-1","size":"gpu-h100x8-640gb"}],"nodeSize":"gpu-h100x8-640gb","nodes":[{"id":"node-1","name":"node-1","status":"active"}],"region":"nyc3","status":"running"},"schema":{"$ref":"#/components/schemas/clusterDetailView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/k8s/nodes":{"get":{"operationId":"listKubernetesNodes","summary":"Returns every DOKS worker node in the org's clusters as a machine — the SAME set the fleet folds in (managedMachines), exposed directly under the k8s noun.","description":"Returns every DOKS worker node in the org's clusters as a machine —\nthe SAME set the fleet folds in (managedMachines), exposed directly under the k8s\nnoun. House account (hanzo-org cluster tag) + BYOC, deduped by Visor.","tags":["k8s"],"responses":{"200":{"content":{"application/json":{"example":{"nodes":[{"id":"node-1","name":"node-1","region":"nyc3","status":"active","type":"s-4vcpu-8gb","vcpu":4}]},"schema":{"$ref":"#/components/schemas/nodeList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/kb/connectors":{"get":{"operationId":"get_v1_kb_connectors","summary":"Returns every supported knowledge connector with THIS org's connection state and the REAL number of documents each has ingested into the org's store.","description":"Returns every supported knowledge connector with THIS org's\nconnection state and the REAL number of documents each has ingested into the\norg's store. A provider that is configured for the deployment but not yet\nconnected appears as disconnected, so the console can offer a Connect button.\nNo secret is ever returned.","tags":["kb"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kbConnectorsOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/connectors/catalog":{"get":{"operationId":"get_v1_kb_connectors_catalog","summary":"Returns the ONE catalog of everything a caller can connect: every first-party connector and every long-tail one, in a single list sorted by provider.","description":"Returns the ONE catalog of everything a caller can\nconnect: every first-party connector and every long-tail one, in a single list\nsorted by provider. `configured` reports whether this deployment holds OAuth\ncredentials for a source, so the console can show Connect rather than a dead\nbutton, and `kind` is a badge only — the connect and sync lifecycle is\nidentical for both. The catalog itself is org-independent; a validated\nprincipal is still required. It is metadata only: no secret is ever returned.","tags":["kb"],"responses":{"200":{"content":{"application/json":{"example":{"connectors":[{"configured":true,"description":"Repositories, READMEs, and issues.","displayName":"GitHub","kind":"native","provider":"github"}]},"schema":{"$ref":"#/components/schemas/catalogOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/connectors/{provider}":{"delete":{"operationId":"delete_v1_kb_connectors_by_provider","summary":"Revokes a connection: it tombstones the stored credential so a later sync cannot reuse it, purges this provider's points from the org's vector namespace, and marks the connector disconnected.","description":"Revokes a connection: it tombstones the stored credential\nso a later sync cannot reuse it, purges this provider's points from the org's\nvector namespace, and marks the connector disconnected. The documents already\ningested stay in the org's store — they are the org's own data — but stop being\nretrievable by search; a caller deletes them through the document surface.","tags":["kb"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the connector to act on: github, slack, google or notion.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/connectionOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/connectors/{provider}/callback":{"get":{"operationId":"get_v1_kb_connectors_by_provider_callback","summary":"CompleteConnectorOAuth finishes an OAuth connection: it exchanges the provider's code for a token, seals that token in KMS, and records the connection.","description":"CompleteConnectorOAuth finishes an OAuth connection: it exchanges the\nprovider's code for a token, seals that token in KMS, and records the\nconnection. THE ORG COMES FROM THE SIGNED STATE, not from a header and not from\nthe provider, so an attacker cannot bind their own account to someone else's\norg — a tampered, expired or foreign-provider state is refused outright. The\ntoken itself is never returned, never written into the document, and never\nlogged; the document holds only its KMS path.","tags":["kb"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the connector completing its flow, from the path.","schema":{"type":"string"}},{"name":"code","in":"query","required":false,"description":"Code is the provider's authorization code, exchanged for a token.","schema":{"type":"string"}},{"name":"state","in":"query","required":false,"description":"State is the org-bound value this server signed at connect time.","schema":{"type":"string"}},{"name":"error","in":"query","required":false,"description":"Error is the provider's denial reason when the user refused consent.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/connectionOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/connectors/{provider}/connect":{"get":{"operationId":"get_v1_kb_connectors_by_provider_connect","summary":"StartConnectorOAuth returns the provider authorize URL the console opens to connect this org's account.","description":"StartConnectorOAuth returns the provider authorize URL the console opens to\nconnect this org's account. There is no server-side redirect — the console\nstays in control of the navigation. The URL carries a state this server SIGNED\nover the caller's validated org, so the connection the callback completes can\nonly ever land in that org.","tags":["kb"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the connector to act on: github, slack, google or notion.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kbAuthorizeOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/connectors/{provider}/sync":{"post":{"operationId":"post_v1_kb_connectors_by_provider_sync","summary":"Pulls the provider's documents for the caller's org and files them as knowledge sources, which the store's own hook then indexes — so a synced document is retrievable exactly like a hand-written page.","description":"Pulls the provider's documents for the caller's org and files\nthem as knowledge sources, which the store's own hook then indexes — so a\nsynced document is retrievable exactly like a hand-written page. The org is the\nvalidated tenant and the credential is read from KMS, so an org can only ever\nsync its own connection. A provider failure is reported honestly (502) and\nrecorded on the connector rather than silently swallowed.","tags":["kb"],"parameters":[{"name":"provider","in":"path","required":true,"description":"Provider is the connector to act on: github, slack, google or notion.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kbSyncOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/graph":{"get":{"operationId":"get_v1_kb_graph","summary":"Returns the caller org's knowledge as a node/edge graph shaped for a force-directed renderer: pages, memories and synced sources as nodes; the page parent tree, the wikilinks between pages, and each source's connector provenance as edges.","description":"Returns the caller org's knowledge as a node/edge graph\nshaped for a force-directed renderer: pages, memories and synced sources as\nnodes; the page parent tree, the wikilinks between pages, and each source's\nconnector provenance as edges. Wikilink targets are resolved HERE by title or\nslug, so a rename never needs an edge rewrite and a link that matches no page\nrenders as its own \"unresolved\" node instead of vanishing. ?project= narrows\nit. A store outage degrades to an honest empty graph, never a 5xx.","tags":["kb"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows the graph to one project scope. Empty reads the whole org.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/kb/import":{"post":{"operationId":"post_v1_kb_import","summary":"Import an Obsidian, Notion, Roam or Evernote export into the org's knowledge base","description":"Ingests an uploaded export as a tree of kb-page documents with its link structure intact. `?format=` picks the normalizer — obsidian, notion, roam or evernote — and the export arrives as a multipart `file` part, or as the raw request body when there is no multipart part: an Obsidian or Notion vault zip, a Roam JSON (raw or inside the zip Roam downloads), or an Evernote .enex.\n\nThe pages are filed through the SAME ingest path a connector sync uses, so the kb-page hook indexes each one for retrieval AND extracts its `[[wikilinks]]` into kb-link edges — the imported vault is searchable and its graph is navigable without a second pass. Parents are filed before their children, and each page takes a slug unique within the org (suffixed -2, -3, … on collision), so a re-import adds pages rather than overwriting the ones already there.\n\nScoped to the caller's validated org; `?project=` narrows every imported page to one project. No validated principal is 403, and an org that has not installed the kb module is refused with the install call to make first. The bounds are 64 MB per upload, 5000 pages and 8 MB per archive entry: pages past the five-thousandth are dropped and a larger entry is truncated at its bound, and a page the store rejects is skipped — so the answer's `imported` count is what was actually filed, not what was sent.","tags":["kb"],"x-app":"knowledge"}},"/v1/kb/search":{"post":{"operationId":"post_v1_kb_search","summary":"Runs a semantic search over the caller org's own knowledge — its wiki pages, its agent memories and everything its connectors have synced — and returns the matching passages.","description":"Runs a semantic search over the caller org's own knowledge —\nits wiki pages, its agent memories and everything its connectors have synced —\nand returns the matching passages. This is the RAG entry point: an agent asks\n\"what does this org know about X\" and the org's OWN vector namespace answers.\nThe org comes from the validated principal, and both the collection and the\npayload filter are pinned to it, so cross-tenant retrieval is impossible. An\nunreachable index returns an honest empty result set with degraded=true, never\na 5xx.","tags":["kb"],"requestBody":{"content":{"application/json":{"example":{"limit":5,"query":"how do we rotate the signing key"},"schema":{"$ref":"#/components/schemas/searchIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/searchOut"}}},"description":"ok"}},"x-app":"knowledge"}},"/v1/keys":{"delete":{"operationId":"delete_v1_keys","summary":"Revokes the caller's own API key of the requested class.","description":"Revokes the caller's own API key of the requested class. The class is\nthe same field mint takes — `?type=publishable`, defaulting to secret — so\nrevoking the key that ships in a browser bundle does not sign its holder out of\ntheir own API: the other key keeps working.\n\nRevoking is how a key is replaced when it does not need replacing; minting the\nsame class again rotates it in one step. IAM drops the credential immediately,\nbut the gateway caches keys for a few minutes, so a request that beat the cache\nexpiry may still be served.\n\nFor callers written against the older shape, the class is also accepted in a JSON\nrequest body, read only when `?type=` is absent.","tags":["keys"],"parameters":[{"name":"type","in":"query","required":false,"description":"Type is the key class to act on: \"secret\" (sk-, session-equivalent, belongs\non a server) or \"publishable\" (pk-, org-identifying, safe in a browser\nbundle). Omitted means secret, which is what every existing caller means.","schema":{"type":"string"},"example":"publishable"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/revokedKey"}}},"description":"ok"}},"x-app":"account"},"get":{"operationId":"get_v1_keys","summary":"Returns the caller's own API keys — every type they hold, read AUTHORITATIVELY from IAM rather than from the session claim, which lags a key minted moments ago.","description":"Returns the caller's own API keys — every type they hold, read\nAUTHORITATIVELY from IAM rather than from the session claim, which lags a key\nminted moments ago. No secret material comes back: a secret key is represented\nby its prefix, and only a publishable key (public by construction) carries its\nfull value.\n\nA transient IAM read failure reports an empty set rather than a 5xx, so the\npage shows the honest empty state and never a fabricated key.","tags":["keys"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/apiKeyList"}}},"description":"ok"}},"x-app":"account"},"post":{"operationId":"post_v1_keys","summary":"Creates — or rotates — the caller's API key of the requested type and returns it ONCE.","description":"Creates — or rotates — the caller's API key of the requested type and\nreturns it ONCE. A real IAM failure surfaces as 502, never a fabricated key.\n\nRotating is what creating means here: a user holds one key per type, so the\nendpoint is idempotent by (caller, type) and the superseded credential stops\nworking. Two live secrets for one user would make \"revoke my key\" a lie.","tags":["keys"],"requestBody":{"content":{"application/json":{"example":{"type":"publishable"},"schema":{"$ref":"#/components/schemas/keyTypeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mintedKey"}}},"description":"ok"}},"x-app":"account"}},"/v1/kms/auth/login":{"post":{"operationId":"post_v1_kms_auth_login","summary":"Exchange a machine credential for an IAM bearer token","description":"Takes a tenant's machine credential — a client id and client secret — and returns an owner-scoped IAM access token with its lifetime, which is the bearer the caller then carries on the org-scoped secret operations.\n\nIt is deliberately public and unauthenticated, because it IS the credential exchange and runs before any principal exists. That makes it the one route in this subsystem rate-limited PER SOURCE IP, keyed on the real TCP peer rather than on any caller-supplied header.\n\nThe submitted secret is never logged and never echoed, and failures collapse to one clean status with no upstream detail: 401 when the credential does not authenticate, 502 when the identity provider is unreachable, 503 when no issuer is configured. That is on purpose — a richer error would be a validity oracle for guessed credentials.","tags":["kms"],"x-app":"kms"}},"/v1/kms/config":{"get":{"operationId":"get_v1_kms_config","summary":"Runtime configuration for the KMS console","description":"Returns what the console needs before anyone has signed in: the brand, the OIDC issuer it authenticates against, the API base for this subsystem and the path of the login exchange.\n\nPublic on purpose, and it holds nothing sensitive — it is deliberately kept under this subsystem's own namespace rather than under an admin prefix, so a gateway that admin-gates the admin routes cannot break the console's legitimate pre-login fetch.","tags":["kms"],"x-app":"kms"}},"/v1/kms/health":{"get":{"operationId":"get_v1_kms_health","summary":"Whether this broker can actually serve secrets","description":"A real readiness probe, not a liveness stub: 200 only when the store is open AND a master key is configured, with `signing` reporting whether signing keys are set up too. Anything less answers 503 with `ready:false` and the reason — no in-process store, or no master key — which are exactly the two states in which the secret operations refuse.\n\nNot token-gated, because the platform must be able to probe it without a credential. It reports the broker's configuration state only; no secret, no key material and no tenant name appears in it.","tags":["kms"],"x-app":"kms"}},"/v1/kms/secrets":{"get":{"operationId":"get_v1_kms_secrets","summary":"List the secrets your org holds, without their values","description":"Returns the METADATA of the caller's own secrets: each one's name, path, environment and sealing scheme. No value and no ciphertext is included — this operation exists to enumerate what is held, and reading a value is a separate, per-secret call.\n\nScoped to the caller's own org and nothing else, structurally: there is no org in the path, the store root is derived from the validated org claim, and a caller therefore has no way to name another tenant's namespace. `path` narrows to a subpath and `env` selects the environment; both are also accepted under the operator's spellings, `secretPath` and `environment`.\n\nAdmission is fail-closed and in order: a validated member, an org that is a DNS-1123 label, and a store holding a master key — 403, 400 and 503 respectively, all decided before any record is touched.","tags":["kms"],"x-app":"kms"},"post":{"operationId":"post_v1_kms_secrets","summary":"Store or replace one secret in your org","description":"Upserts one secret under the caller's own org. The value is sealed before it is written — a fresh per-secret data key, itself wrapped by the master key — so plaintext never reaches disk. The receipt confirms the name and environment that were written and does not echo the value.\n\n`env` is REQUIRED on a write and has no default, which is the rule most easily got wrong here: reads and deletes still fall back to the default environment for older callers, but a write must not, because the environment is part of the storage key. A silently defaulted write lands in a bucket the readers that resolve project, environment and path never look in, and the stale value keeps being served — so the write fails loudly instead.\n\n`name` is required, `path` is an optional subpath beneath the org root, and the org is taken from the validated claim rather than the body. Same fail-closed admission as the rest of the secret surface: validated member, well-formed org, master key present.","tags":["kms"],"x-app":"kms"}},"/v1/kms/secrets/{wildcard1}":{"delete":{"operationId":"delete_v1_kms_secrets_by_wildcard1","summary":"Delete one secret from your org","description":"Removes one secret from the caller's own org and confirms the name and environment that were removed. Deleting a secret that is not there is a 404, not a silent success, so a caller can tell a real deletion from a typo.\n\nThe trailing path is the secret's subpath and name beneath the caller's org root, and `env` selects the environment, defaulting when omitted. Scoped to the caller's own org — the store root comes from the validated claim, never from the request — under the same fail-closed admission as the reads.","tags":["kms"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"kms"},"get":{"operationId":"get_v1_kms_secrets_by_wildcard1","summary":"Read one secret's value","description":"Opens one sealed secret belonging to the caller's own org and returns its value in the response body, with the name and environment it was resolved under. This is the broker's purpose, and the response body is the ONLY place the value appears — it is not logged, and it is never carried in an error.\n\nThe trailing path is the secret's subpath and name beneath the caller's org root; `env` selects the environment and falls back to the default when omitted. A secret that is not there is a plain 404 that names nothing about the store.\n\nScoped to the caller's own org and nothing else: there is no org in the path, so another tenant's secret is not merely refused, it is unnameable. Admission is fail-closed — validated member, well-formed org, master key present — and an unconfigured master key is 503 rather than an empty read.","tags":["kms"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"kms"}},"/v1/kv":{"get":{"operationId":"get_v1_kv","summary":"ListKV lists the caller org's Hanzo KV stores.","description":"ListKV lists the caller org's Hanzo KV stores. Each one is a DEDICATED Valkey\ninstance the org alone runs, so the host is that instance's own in-cluster\nService and the port is 6379.","tags":["kv"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_kv","summary":"Provision a key-value store for your org","description":"Launches your org's OWN key-value instance and answers with its `kv://` connection string. The instance is yours alone: a deployment in your own tenant namespace, so its admin credential is naturally scoped to you and no other tenant shares the process. Off-cluster, where there is no orchestrator to launch one, this fails closed with 503 rather than handing back a shared one.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["kv"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/kv/{name}":{"delete":{"operationId":"delete_v1_kv_by_name","summary":"DropKV deprovisions one Hanzo KV store.","description":"DropKV deprovisions one Hanzo KV store. It reverts any app instance bound to\nit back to Base BEFORE tearing down the org's dedicated Valkey instance, then\ndeletes the sealed credential and removes the metadata row. Answers 204 with\nno body; a second call is a 404.","tags":["kv"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"sessions"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_kv_by_name","summary":"GetKV returns one Hanzo KV store's metadata.","description":"GetKV returns one Hanzo KV store's metadata. It carries the store's status,\nits instance address and the Valkey user it authenticates as (\"default\", the\nonly user a requirepass instance has) — never the password. A still-booting\ninstance reads \"provisioning\", reconciled from the operator's live view.","tags":["kv"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"sessions"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/legal/documents":{"get":{"operationId":"get_v1_legal_documents","summary":"Returns the org's generated documents, newest first, WITHOUT their rendered content — fetch one document to read its body.","description":"Returns the org's generated documents, newest first, WITHOUT\ntheir rendered content — fetch one document to read its body.\n\nThe response is marked no-store: these records name the counterparties an org is\ncontracting with, and must not sit in a shared cache.","tags":["legal"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit bounds the page. Absent or unparseable means the store's own default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/documentPage"}}},"description":"ok"}},"x-app":"legal"},"post":{"operationId":"post_v1_legal_documents","summary":"Renders a document from a template and the caller's own merge data, seals it in the org's store, and returns it with its rendered body.","description":"Renders a document from a template and the caller's own\nmerge data, seals it in the org's store, and returns it with its rendered body.\n\nThe render is PURE and deterministic — no clock, no I/O — so the same template\nversion and the same data always produce identical bytes, which is what makes a\ngenerated contract reproducible. It fails CLOSED on a missing merge field: there\nis no blank-filled contract, only a 400 naming the fields that were absent. When\nthe template is counsel-review the rendered body opens with the counsel notice,\nwhich no caller can suppress.\n\nThe document is a DRAFT. Hanzo Legal manages documents; it does not give legal\nadvice and does not determine that a document is valid or sufficient.","tags":["legal"],"requestBody":{"content":{"application/json":{"example":{"data":{"counterparty":"Acme, Inc.","date":"2026-07-30"},"templateId":"nda"},"schema":{"$ref":"#/components/schemas/generateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/documentReply"}}},"description":"created"}},"x-app":"legal"}},"/v1/legal/documents/{id}":{"get":{"operationId":"get_v1_legal_documents_by_id","summary":"Returns one of the org's documents WITH its rendered body.","description":"Returns one of the org's documents WITH its rendered body. 404\nwhen the org has no document with that id — a document is never readable across\norgs.\n\nThe response is marked no-store: the body is contract text, sealed at rest and\nreturned only to the owning org, and must not sit in a shared cache.","tags":["legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document's server-minted handle, \"doc_\"-prefixed.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/documentReply"}}},"description":"ok"}},"x-app":"legal"}},"/v1/legal/documents/{id}/sign":{"post":{"operationId":"post_v1_legal_documents_by_id_sign","summary":"Opens an e-signature request over one document and moves it to out_for_signature, returning the provider's reference for the request.","description":"Opens an e-signature request over one document and moves it\nto out_for_signature, returning the provider's reference for the request.\n\nThe provider is whatever this deployment has wired. The honest default is\n\"manual\": the request is recorded and the org fulfils it out of band — nothing\nhere fabricates a signature, and the stub never reports itself complete.","tags":["legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to send for signature, from the path.","schema":{"type":"string"},"example":"doc_1f…"}],"requestBody":{"content":{"application/json":{"example":{"id":"doc_1f…","signers":[{"email":"ada@acme.com","name":"Ada"}]},"schema":{"$ref":"#/components/schemas/signRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/signReply"}}},"description":"ok"}},"x-app":"legal"}},"/v1/legal/documents/{id}/sign/complete":{"post":{"operationId":"post_v1_legal_documents_by_id_sign_complete","summary":"Record that a generated document's signature request completed","description":"Records completion of the signature request opened over a generated document and answers the document with a `signed` flag.\n\nThe e-sign provider's own status is consulted FIRST and is the default answer; an explicit `signed` field in the body overrides it. That override is the whole point: the default `manual` provider never self-completes, so a reviewer (or a real provider's webhook) is what moves the document. A completion flips the document to `signed`, stamps `signedAt`, and writes a `legal.document.signed` audit event; a provider still reporting incomplete answers 200 with the document unchanged, so the call is safe to repeat and never fabricates a signature.\n\nOrg-scoped and fails closed: a validated principal is required (403 without one), the document is read under the caller's OWN org so another tenant's id is a 404, a document with no open signature request is a 400, and a provider whose status call errors is a 502.","tags":["legal"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"legal"}},"/v1/legal/filings":{"get":{"operationId":"get_v1_legal_filings","summary":"Returns the org's filing records, newest first — which documents were filed where, through which provider, and what the filing's honest status is.","description":"Returns the org's filing records, newest first — which documents\nwere filed where, through which provider, and what the filing's honest status is.","tags":["legal"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit bounds the page. Absent or unparseable means the store's own default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/filingPage"}}},"description":"ok"}},"x-app":"legal"},"post":{"operationId":"post_v1_legal_filings","summary":"Records a filing of one or more of the org's documents with a state or agency, and returns the tracking record.","description":"Records a filing of one or more of the org's documents with a\nstate or agency, and returns the tracking record.\n\nIt is a TRACKING record, not an autonomous filing. With no filing partner wired\nthe honest status is \"manual\" and the note says so: the documents were generated\nfor signature, and the org files them through its registered agent. Nothing here\ninvents a filing id it does not have.\n\nEvery document id must belong to the caller's org; one that does not is a 404\nnaming it, so a filing can never reach across tenants.","tags":["legal"],"requestBody":{"content":{"application/json":{"example":{"documentIds":["doc_1f…"],"jurisdiction":"DE"},"schema":{"$ref":"#/components/schemas/filingRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/filingReply"}}},"description":"created"}},"x-app":"legal"}},"/v1/legal/health":{"get":{"operationId":"get_v1_legal_health","summary":"Reports that the legal subsystem is serving and how many built-in templates its catalog carries.","description":"Reports that the legal subsystem is serving and how many built-in\ntemplates its catalog carries. It reads no tenant, so a liveness prober that\nsends no principal is answered rather than refused.","tags":["legal"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/legalHealth"}}},"description":"ok"}},"x-app":"legal"}},"/v1/legal/templates":{"get":{"operationId":"get_v1_legal_templates","summary":"Returns the org's effective template catalog: every built-in template, with any the org has overridden replaced by its own latest version.","description":"Returns the org's effective template catalog: every built-in\ntemplate, with any the org has overridden replaced by its own latest version.\n\nThe listing carries each template's metadata and its declared MERGE FIELDS — the\nkeys a document generation must supply — but never the template bodies; fetch one\ntemplate to get its body. Templates in the formation and equity categories are\nmarked counselReview: every document rendered from them carries a counsel notice,\nand that posture cannot be dropped by an override.","tags":["legal"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/templateCatalog"}}},"description":"ok"}},"x-app":"legal"}},"/v1/legal/templates/{id}":{"get":{"operationId":"get_v1_legal_templates_by_id","summary":"Returns one template resolved for the caller's org — the org's own override if it has saved one, else the built-in — with its full text/template body and its declared merge fields.","description":"Returns one template resolved for the caller's org — the org's\nown override if it has saved one, else the built-in — with its full text/template\nbody and its declared merge fields. 404 when neither exists.","tags":["legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the template's stable id, e.g. \"nda\" or \"safe\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/templateReply"}}},"description":"ok"}},"x-app":"legal"},"put":{"operationId":"put_v1_legal_templates_by_id","summary":"Saves the org's own version of a template — a custom NDA, a house MSA — and returns it with its new version number.","description":"Saves the org's own version of a template — a custom\nNDA, a house MSA — and returns it with its new version number. It takes effect\nfor that org only; other orgs keep the built-in.\n\nTwo boundaries cannot be crossed here. Overriding a built-in INHERITS its\ncategory and its counsel-review posture, which can be raised but never dropped;\nand a formation or equity template is counsel-review whatever the caller sends,\nso no org can generate a securities-class document without the notice.\n\nThe body is validated on save, not at generation: a template that references an\nUNDECLARED merge field is refused with 400 rather than stored and rendered blank\ninto a contract months later.","tags":["legal"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the template to override, from the path. Overriding a built-in id\ninherits that built-in's category, title and counsel-review posture.","schema":{"type":"string"},"example":"nda"}],"requestBody":{"content":{"application/json":{"example":{"body":"…{{.counterparty}}…","fields":[{"key":"counterparty","label":"Counterparty"}],"id":"nda","title":"Acme Mutual NDA"},"schema":{"$ref":"#/components/schemas/templateOverride"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/templateReply"}}},"description":"ok"}},"x-app":"legal"}},"/v1/licensing/download/{release}":{"get":{"operationId":"get_v1_licensing_download_by_release","summary":"Download resolves a release to its artifact, gated on a valid license.","description":"Download resolves a release to its artifact, gated on a valid license.\n\nThe gate is the LICENSE token, not the IAM bearer: being signed in is not\npermission to download a paid binary — holding a good license for it is. The\ntoken must verify against this deployment's public key, be unrevoked, be\nscoped to the release's app, and carry every feature the release requires.\nPresent it as the `X-License-Token` header (preferred, since a header does not\nland in proxy logs) or as `?token=`.\n\nThe response pairs the artifact URL with its cosign signature so the client\nverifies the binary BEFORE trusting it: a signed URL alone proves where the\nbytes came from, not what they are. A yanked release is 410 Gone.","tags":["licensing"],"parameters":[{"name":"release","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Artifact"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/fingerprint":{"post":{"operationId":"post_v1_licensing_fingerprint","summary":"Fingerprint turns raw device signals into the opaque value that binds a license to one machine.","description":"Fingerprint turns raw device signals into the opaque value that binds a license\nto one machine.\n\nThis is the anti-copy step: the value returned here is folded into the signed\ntoken, so a token minted with it runs only on the device it was bound to. The\nderivation is one-way and salted — the signals are never stored and never\nechoed back — so the response is safe to persist client-side and pass to\nissue. Signals too weak to identify a machine (a hostname alone) are refused\nrather than turned into a binding that would collide with other machines.","tags":["licensing"],"requestBody":{"content":{"application/json":{"example":{"signals":{"arch":"arm64","cpuid":"…","machine_id":"…","os":"linux"}},"schema":{"$ref":"#/components/schemas/FingerprintRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FingerprintResponse"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/healthz":{"get":{"operationId":"get_v1_licensing_healthz","summary":"Health reports which signer this deployment mints with, and in which env.","description":"Health reports which signer this deployment mints with, and in which env.\n\nIt answers 200 whenever the process is up: there is nothing downstream to\nprobe, since the KMS is reached only when a token is actually minted. Its\nvalue is the `signer` field — `\"signer\":\"local\"` on a production host says\nthat deployment is signing licenses with a development key, which is a\nmisconfiguration worth paging on rather than a healthy 200.","tags":["licensing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthView"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/issue":{"post":{"operationId":"post_v1_licensing_issue","summary":"Issue mints a signed license token for a product the caller's org already pays for.","description":"Issue mints a signed license token for a product the caller's org already pays\nfor.\n\nThe order is the whole security argument: the caller is an IAM-validated\nprincipal, commerce is then asked whether that principal's ORG holds an ACTIVE\nentitlement for the product, and only then is a token signed — by the KMS,\nnever by key material in this process. A product the org does not own answers\n403 and no token. The signed features are the plan's features verbatim, so the\nengine enforces exactly what was bought, and the expiry is clamped to the\nentitlement's so a token cannot outlive the subscription that paid for it.\n\nThe token is the credential the engine runs on. Treat it as a secret.","tags":["licensing"],"requestBody":{"content":{"application/json":{"example":{"product":"engine","signals":{"machine_id":"…","os":"linux"}},"schema":{"$ref":"#/components/schemas/IssueRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueResponse"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/jwks":{"get":{"operationId":"get_v1_licensing_jwks","summary":"Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and /jwks.","description":"Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and\n/jwks.\n\nThis is the only public-safe surface here and the reason the whole scheme\nworks offline: the engine embeds or fetches this key once and then verifies\nevery license itself, with no call home per launch. The private half never\nenters this process — it lives in the KMS — so nothing served here is a\nsecret. `provider` names the KMS holding that half; `\"local\"` means a\ndevelopment key, and a token signed by one is not a production credential.","tags":["licensing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyView"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/pubkey":{"get":{"operationId":"get_v1_licensing_pubkey","summary":"Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and /jwks.","description":"Pubkey publishes the Ed25519 PUBLIC verification key, at both /pubkey and\n/jwks.\n\nThis is the only public-safe surface here and the reason the whole scheme\nworks offline: the engine embeds or fetches this key once and then verifies\nevery license itself, with no call home per launch. The private half never\nenters this process — it lives in the KMS — so nothing served here is a\nsecret. `provider` names the KMS holding that half; `\"local\"` means a\ndevelopment key, and a token signed by one is not a production credential.","tags":["licensing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyView"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/releases":{"get":{"operationId":"get_v1_licensing_releases","summary":"Lists the signed binary releases this deployment can serve.","description":"Lists the signed binary releases this deployment can serve.\n\nMetadata only, and no download URL: the artifact is behind GET\n/v1/licensing/download/{release}, which is gated on a valid license token.\nKnowing that a release exists is not permission to run it, which is why this\nlist needs no license of its own.","tags":["licensing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseList"}}},"description":"ok"}},"x-app":"licensing"},"post":{"operationId":"post_v1_licensing_releases","summary":"Publishes a signed binary release, answering 201 Created.","description":"Publishes a signed binary release, answering 201 Created.\n\nOutside dev a release MUST carry its cosign signature: this is how a binary\nbecomes downloadable, so accepting an unsigned one would let an unverifiable\nartifact into the distribution path. Org-admin only — publishing is an\noperator action, not something a licensee does.","tags":["licensing"],"requestBody":{"content":{"application/json":{"example":{"artifact_ref":"s3://…","cosign_signature":"MEUCIQ…","id":"engine-1.4.0-linux-arm64","platform":"linux/arm64","product":"engine","version":"1.4.0"},"schema":{"$ref":"#/components/schemas/Release"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}},"description":"created"}},"x-app":"licensing"}},"/v1/licensing/releases/{release}":{"get":{"operationId":"get_v1_licensing_releases_by_release","summary":"Reads one release's metadata: its product, version, platform and the cosign material a client verifies the binary against.","description":"Reads one release's metadata: its product, version, platform and the\ncosign material a client verifies the binary against.\n\nAn unknown id is 404. Like the list, this is metadata only — the bytes are\nbehind the license-gated download.","tags":["licensing"],"parameters":[{"name":"release","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/revoke":{"post":{"operationId":"post_v1_licensing_revoke","summary":"Revoke turns off tokens that have already been issued.","description":"Revoke turns off tokens that have already been issued.\n\nA signed token cannot be un-signed, so revocation is the only way to withdraw\none: this appends an entry that verify and the license-gated download both\nconsult. It is a POST rather than a DELETE because it APPENDS a durable,\nattributed record — the entry names the admin who recorded it and when —\nrather than removing one.\n\nOrg-admin only. Scope it as narrowly as the incident allows: \"nonce\" for one\nleaked token, \"holder\" for one compromised account, \"fingerprint\" for one\nstolen machine, \"release\" when a whole build is bad.","tags":["licensing"],"requestBody":{"content":{"application/json":{"example":{"reason":"leaked in a public gist","scope":"nonce","value":"9f2c…"},"schema":{"$ref":"#/components/schemas/RevokeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeResponse"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/licensing/verify":{"post":{"operationId":"post_v1_licensing_verify","summary":"Verify checks a license token online: signature, schema, expiry, app_id and the revocation list.","description":"Verify checks a license token online: signature, schema, expiry, app_id and\nthe revocation list.\n\nIt is UNAUTHENTICATED and always answers 200 — a bad token is `valid:false`\nwith a reason rather than an error status, because \"is this token good\" is a\nquestion anyone may ask about a credential they already hold and the answer is\nthe same either way. It is also OPTIONAL: the engine verifies OFFLINE against\nthe published public key (GET /v1/licensing/pubkey) and needs this endpoint\nonly to learn about revocation, so an outage here never stops a paid customer\nworking.","tags":["licensing"],"requestBody":{"content":{"application/json":{"example":{"token":"eyJ2Ijox….Ab3…"},"schema":{"$ref":"#/components/schemas/VerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResponse"}}},"description":"ok"}},"x-app":"licensing"}},"/v1/links":{"get":{"operationId":"get_v1_links","summary":"Lists your linked accounts and the devices they sit on.","description":"Lists your linked accounts and the devices they sit on.\n\nIt answers the caller's own links plus a devices projection of the same rows\nfolded per machine — the cross-machine \"AI Providers / Accounts\" view. A\ndevice is a projection, not a stored entity: its labels come from its\nmost-recently-seen account, so there is no device to create and none to\ngarbage-collect. Revoked links are INCLUDED rather than dropped, because a\nlogged-out account keeps its usage history and audit trail. Scoped to the\ncaller: a validated principal and a non-empty org, else 403.","tags":["links"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/linkList"}}},"description":"ok"}},"x-app":"link"},"post":{"operationId":"post_v1_links","summary":"Registers a signed-in AI provider account on a machine.","description":"Registers a signed-in AI provider account on a machine.\n\nIt records that a developer has signed into one provider account on one\nmachine — a Claude Max or ChatGPT Plus subscription, a Hanzo key, a raw\nprovider key — and answers 201 with the stored link. Re-reporting the same\n(machine, provider, account) UPDATES that link rather than creating a second,\nso a collector may call this on every heartbeat. machine and provider are\nrequired (400 otherwise), as is a valid kind, and every field is\nlength-bounded. Scoped to the caller: a validated principal and a non-empty\norg, else 403, so a caller writes only their OWN accounts within their own org.","tags":["links"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/enrollReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/linkView"}}},"description":"created"}},"x-app":"link"}},"/v1/links/devices/{machine}":{"get":{"operationId":"get_v1_links_devices_by_machine","summary":"Shows one machine: its accounts, usage and live sessions.","description":"Shows one machine: its accounts, usage and live sessions.\n\nIt answers one device — its host and OS labels, every account the caller has\nsigned in on that machine with its latest usage, and how many agent sessions\nthe caller currently has running on it. The device labels come from the\nmost-recently-seen account, since a device is a projection of its links rather\nthan a row of its own. A machine with none of the caller's accounts is 404,\nwhich is also the answer when the machine belongs to someone else — the scope\nmakes the two indistinguishable, deliberately. The session count reports 0\nwhere the agent plane is not mounted rather than failing the read.","tags":["links"],"parameters":[{"name":"machine","in":"path","required":true,"description":"Machine is the machine to act on, from the path. It is scoped to the\ncaller, so a machine with none of the caller's accounts is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deviceView"}}},"description":"ok"}},"x-app":"link"}},"/v1/links/devices/{machine}/revoke":{"post":{"operationId":"post_v1_links_devices_by_machine_revoke","summary":"Logs out every account on one machine and stops its sessions.","description":"Logs out every account on one machine and stops its sessions.\n\nIt revokes every one of the caller's accounts on one machine and stops the\nagent sessions they were running, answering with how many of each. This is the\n\"I lost that laptop\" button. Revoked links are RETAINED, not deleted, so usage\nhistory and the audit trail survive a log-out — the rows come back in the\nresponse with their new status. The session stop reaches only the REVOKING\nuser's own sessions, so a shared machine name can never be used to stop a\nco-tenant's work, and a stop that fails does not fail the revoke: the revoked\nrow is the durable truth and the count then honestly reports fewer. A machine\nwith nothing left to revoke is 404.","tags":["links"],"parameters":[{"name":"machine","in":"path","required":true,"description":"Machine is the machine to act on, from the path. It is scoped to the\ncaller, so a machine with none of the caller's accounts is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/revokeResp"}}},"description":"ok"}},"x-app":"link"}},"/v1/links/route":{"get":{"operationId":"get_v1_links_route","summary":"Gets the failover order across your linked accounts.","description":"Gets the failover order across your linked accounts.\n\nIt answers an ordered redundancy plan over the caller's LINKED (not revoked)\naccounts: each candidate with its remaining rate-limit headroom, whether it is\nroutable right now, how it BILLS (plan or commerce), and a reason when it is\nnot — plus the primary to try first. It is what lets a router fail over from\none subscription to another and fall back to the metered API as the\nalways-available backstop, knowing the cost consequence before it dials.\n\nIt is POLICY, not execution: the plan is computed purely from the usage\nsnapshots already in the registry, never by probing a provider, so it is a\ntotal function of the links and costs nothing to ask for. Actually dialing,\ndetecting a live 429 and advancing to the next candidate belongs to the\ncaller. A link with no snapshot counts as full headroom.","tags":["links"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutePlan"}}},"description":"ok"}},"x-app":"link"}},"/v1/links/usage":{"get":{"operationId":"get_v1_links_usage","summary":"Shows one provider account's own usage dashboard.","description":"Shows one provider account's own usage dashboard.\n\nIt answers the time series for a SINGLE provider account — the windows in\nrange plus the currently-open ones — as that provider's own meter reported it:\n\"my plan is 47% through its 6h window, resets at 14:20\". current is the newest\ninstance of each lane (the headline); windows is the history behind it, both\ncomputed from ONE deduped read. provider is required; an unknown window class\nor range is 400, never a quiet fallback to a different one. When no series is\navailable the response is a 200 with available:false and empty lists — an\nhonest \"we have no data\", which is a different claim from zero usage.","tags":["links"],"parameters":[{"name":"provider","in":"query","required":false,"description":"Provider is the provider whose meter to read. Required.","schema":{"type":"string"}},{"name":"account","in":"query","required":false,"description":"Account narrows to one account when a user has several with the provider.","schema":{"type":"string"}},{"name":"window","in":"query","required":false,"description":"Window selects a window class: 6h, day, week or month. Empty reads all.","schema":{"type":"string"}},{"name":"range","in":"query","required":false,"description":"Range is the period, one of 1h, 24h, 7d or 30d; empty means 24h, and an\nunknown label is 400, never a quiet fallback.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/boardResp"}}},"description":"ok"}},"x-app":"link"},"post":{"operationId":"post_v1_links_usage","summary":"Reports usage samples from the device collector.","description":"Reports usage samples from the device collector.\n\nIt ingests a batch of usage samples and answers with how many were accepted,\nwhether history was durably stored, and the links they refreshed. A report\nalso REFRESHES one link per distinct (machine, provider, account) it names, so\na running collector keeps the accounts overview current without a separate\nregistration call.\n\nA caller can only ever report for THEMSELVES: org and subject come from the\nvalidated bearer, never from the body, so no sample can be attributed to\nanother user or tenant. History is FAIL-SOFT and stored says which happened —\na warehouse outage still accepts the report and refreshes the links rather\nthan failing the device, and answers 202 either way. Send either one sample\ninline or up to 256 in samples; an empty batch or an over-long one is 400, as\nis a provider, window class or kind outside the closed vocabulary — an\nunrecognized window is refused rather than rewritten, because a silently\nreclassified sample would fill a dashboard with a class nobody reported.","tags":["links"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingestReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingestResp"}}},"description":"accepted"}},"x-app":"link"}},"/v1/links/usage/accounts":{"get":{"operationId":"get_v1_links_usage_accounts","summary":"Breaks down what the gateway routed through each of your accounts.","description":"Breaks down what the gateway routed through each of your accounts.\n\nIt answers one row per linked account the GATEWAY actually routed through,\nplus their total — requests, prompt and completion tokens, and cost. This is\nthe routed ledger, the read twin of the counter the router writes, and it is\ndistinct from both of its neighbours: not the device collector's plan\nsnapshots, and not the org money ledger. The source and scope fields on the\nresponse say so on every payload. The same shape answers in the billing\nnamespace, from one shaping function, so the two mounts cannot drift.","tags":["links"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountsUsage"}}},"description":"ok"}},"x-app":"link"}},"/v1/links/usage/summary":{"get":{"operationId":"get_v1_links_usage_summary","summary":"Shows plan consumption and Hanzo spend side by side.","description":"Shows plan consumption and Hanzo spend side by side.\n\nIt answers the global usage board over one window: the caller's own linked\naccounts, metered from each provider's own login, alongside their org's\nHanzo-routed inference. These come from different ledgers and mean different\nthings, so every row is LABELLED by source, by scope and by availability, and\nTHE TWO ARE NEVER SUMMED — a plan's percentage is not money, and a provider's\nown spend is not a Hanzo charge. The rows sit side by side and say what they\nare.\n\nOne resolver fixes the window for both halves, so the two sets always cover\nthe same period. range is one of 1h, 24h, 7d or 30d and defaults to 24h;\nanything else is 400 rather than a silent substitution. A ledger that cannot\nanswer reports available:false instead of a zero that would read as \"no usage\".","tags":["links"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the period, one of 1h, 24h, 7d or 30d; empty means 24h, and an\nunknown label is 400, never a silent substitution.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/summaryResp"}}},"description":"ok"}},"x-app":"link"}},"/v1/links/{id}":{"delete":{"operationId":"delete_v1_links_by_id","summary":"Logs out one account and stops the sessions it was running.","description":"Logs out one account and stops the sessions it was running.\n\nIt revokes a single linked account and stops the agent sessions that ran under\nit, answering with the revoked row and how many sessions stopped. The link is\nRETAINED with a revoked status rather than deleted, so its usage history and\nthe audit trail survive the log-out — which also means a revoked account still\nappears in the list, and is excluded from the route plan rather than absent\nfrom it. The session stop is narrowed to the revoking user's own sessions on\nthat device, provider and account, and a stop that fails does not fail the\nrevoke: the revoked row is the durable truth. An id that does not exist, or\nbelongs to another user or org, is the same 404.","tags":["links"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the link to act on, from the path. It is scoped to the caller, so\nanother user's or org's id is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/revokeResp"}}},"description":"ok"}},"x-app":"link"},"get":{"operationId":"get_v1_links_by_id","summary":"Reads one linked account.","description":"Reads one linked account.\n\nIt answers a single link — its device, provider, account, plan, how it bills,\nits status and its latest usage snapshot. An id that does not exist, or\nbelongs to another user or org, is the same 404: the scope is a bound\npredicate on the read, so a wrong id and a foreign id are indistinguishable\nand neither confirms the other's existence. The static paths on this\ncollection — route, usage, devices — register before this one and win\nfirst-match, so a link whose id collided with one of those words could not be\naddressed here.","tags":["links"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the link to act on, from the path. It is scoped to the caller, so\nanother user's or org's id is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/linkView"}}},"description":"ok"}},"x-app":"link"}},"/v1/logs/health":{"get":{"operationId":"get_v1_logs_health","summary":"How many log records this deployment holds for your org","description":"Reports the native log store's live state for the calling tenant: the subsystem version and `records`, the count actually held right now rather than a constant. Not a dependency probe — the store is in-process, so this answers 200 whenever the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`.","tags":["logs"],"x-app":"metrics"}},"/v1/logs/query":{"get":{"operationId":"get_v1_logs_query","summary":"Search your org's logs by label, time and substring","description":"Answers `{count, records}`, newest first. `match` is the same `k=v,k2=v2` superset label matcher the metrics query uses; `contains` is a case-insensitive substring test against the record body; `start` and `end` are nanosecond bounds.\n\nA bound that is absent, empty or unparseable becomes 0, which means UNBOUNDED — a malformed `start` widens the search rather than failing it. `limit` caps the page and defaults to 100 when absent or non-positive, so an unfiltered read is never the whole ring.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`, so a search can only reach the org the edge asserted.","tags":["logs"],"x-app":"metrics"}},"/v1/logs/write":{"post":{"operationId":"post_v1_logs_write","summary":"Append structured log records for your org","description":"Takes `{records:[{t, level, body, labels}]}`, appends each one, and answers `{written}`. Bodies are stored verbatim; `labels` are the indexed dimensions a query filters on, so what you do not label you can only find by substring.\n\n`t` is NANOSECONDS since the Unix epoch. A record sent without one is stored at 0 and then falls outside any query carrying a lower bound — the usual reason a successful write does not read back. Retention is a bounded ring, 1048576 records per org, oldest evicted first. No record is validated or rejected, so `written` is the number of records SENT; only a body that does not decode at all is 400.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`; each org's records live in its own WAL-durable store.","tags":["logs"],"x-app":"metrics"}},"/v1/machines":{"get":{"operationId":"listMachines","summary":"Returns every machine the caller's org has — Visor's registry, the live DigitalOcean droplets and the DOKS worker nodes (deduped into one union), plus the BYO machines that dialed in via `hanzo link` (provider \"byo\").","description":"Returns every machine the caller's org has — Visor's registry, the\nlive DigitalOcean droplets and the DOKS worker nodes (deduped into one union),\nplus the BYO machines that dialed in via `hanzo link` (provider \"byo\").\n\nA source Visor cannot answer for is logged and skipped, never an error: one\nwedged upstream must not hide the machines the other sources can see.","tags":["machines"],"responses":{"200":{"content":{"application/json":{"example":{"machines":[{"id":"web-1","mem":"4 GB","name":"Web 1","provider":"digitalocean","publicIp":"1.2.3.4","region":"sfo3","status":"running","type":"s-2vcpu-4gb","vcpu":2}]},"schema":{"$ref":"#/components/schemas/machineList"}}},"description":"ok"}},"x-app":"visor"},"post":{"operationId":"post_v1_machines","summary":"Launch a metered machine for your org, or price one first with dryRun","description":"Provisions a machine owned by the caller's org and answers 201 with the machine. Send `dryRun: true` to get a PRICE QUOTE instead: 200 with the upstream quote passed through verbatim, nothing launched and nothing spent. Two response shapes on one address is the rule to know, and it is why this is not a typed op.\n\nMetering is not this plane's: the launch fronts the compute provider's resell endpoint, which owns the balance gate and the per-hour meter, and cloud only forwards the tenant. Ownership is the validated principal's org and is never read from the body, so a launch always lands in the caller's OWN tenant and the machine it creates is only ever visible to that tenant. Fails closed: a validated principal is required (403 without one) and `size` (or its `instanceType` alias) is required (400).","tags":["machines"],"x-app":"visor"}},"/v1/machines/agents":{"get":{"operationId":"listMachineAgents","summary":"Returns every agent↔machine binding in the caller's org — which machines are running which cloud Agent, with vm's own reconciled status.","description":"Returns every agent↔machine binding in the caller's org — which\nmachines are running which cloud Agent, with vm's own reconciled status.","tags":["machines"],"responses":{"200":{"content":{"application/json":{"example":{"agentBindings":[{"agentName":"bot-a","machineId":"drop-a","publicIp":"1.2.3.4","status":"running"}]},"schema":{"$ref":"#/components/schemas/bindingList"}}},"description":"ok"}},"x-app":"visor"}},"/v1/machines/{id}":{"delete":{"operationId":"deleteMachine","summary":"Terminates one of the caller org's machines.","description":"Terminates one of the caller org's machines. Visor takes the\nmachine identity as owner+name, and the owner is the validated principal, so a\ncaller can only ever terminate its own tenant's machine. Answers 204.","tags":["machines"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine's org-scoped NAME — the stable key Visor addresses a\nmachine by (owner/name), not the ephemeral provider id.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"visor"},"get":{"operationId":"getMachine","summary":"Returns one of the caller org's machines by its org-scoped name.","description":"Returns one of the caller org's machines by its org-scoped name.\nVisor keys the lookup by owner/name, so an id belonging to another tenant\nresolves to not-found rather than another org's machine.","tags":["machines"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine's org-scoped NAME — the stable key Visor addresses a\nmachine by (owner/name), not the ephemeral provider id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"id":"web-1","name":"Web 1","publicIp":"1.2.3.4","region":"sfo3","status":"running","type":"s-2vcpu-4gb","vcpu":2},"schema":{"$ref":"#/components/schemas/machineView"}}},"description":"ok"}},"x-app":"visor"}},"/v1/machines/{id}/agent":{"delete":{"operationId":"unbindMachineAgent","summary":"Detaches the agent runtime from one of the caller org's machines.","description":"Detaches the agent runtime from one of the caller org's\nmachines. The machine stays — this halts the bot, it does not terminate the\ncompute. Answers 204.","tags":["machines"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine's org-scoped NAME — the stable key Visor addresses a\nmachine by (owner/name), not the ephemeral provider id.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"visor"},"get":{"operationId":"getMachineAgent","summary":"Returns the agent binding of one of the caller org's machines, or 404 when the machine runs no bot runtime.","description":"Returns the agent binding of one of the caller org's\nmachines, or 404 when the machine runs no bot runtime.","tags":["machines"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine's org-scoped NAME — the stable key Visor addresses a\nmachine by (owner/name), not the ephemeral provider id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"agentName":"bot-a","botVersion":"1.4.0","machineId":"drop-a","status":"running"},"schema":{"$ref":"#/components/schemas/agentBinding"}}},"description":"ok"}},"x-app":"visor"},"put":{"operationId":"bindMachineAgent","summary":"Binds a cloud Agent to one of the caller org's machines: the machine is recorded as running that Agent's @hanzo/bot runtime.","description":"Binds a cloud Agent to one of the caller org's machines: the\nmachine is recorded as running that Agent's @hanzo/bot runtime. The owning org is\nthe validated tenant, never a client field.","tags":["machines"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the machine to bind, from the URL path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"agentName":"bot-a","botVersion":"1.4.0"},"schema":{"$ref":"#/components/schemas/bindAgentReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"agentName":"bot-a","machineId":"drop-a","status":"binding"},"schema":{"$ref":"#/components/schemas/agentBinding"}}},"description":"ok"}},"x-app":"visor"}},"/v1/marketing/audiences":{"get":{"operationId":"get_v1_marketing_audiences","summary":"Returns the org's saved audiences, most recently updated first.","description":"Returns the org's saved audiences, most recently updated first.","tags":["marketing"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudienceList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_audiences","summary":"Saves a cohort filter for the caller's org.","description":"Saves a cohort filter for the caller's org. Name is required.\nOmitting event saves the WHOLE-ORG audience — every mailable customer — which\nneeds no analytics warehouse; naming one narrows that roster to the customers\nwho fired it within windowDays.","tags":["marketing"],"requestBody":{"content":{"application/json":{"example":{"event":"model.invoked","name":"Model users, last 30d","windowDays":30},"schema":{"$ref":"#/components/schemas/Audience"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audience"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/audiences/{id}":{"delete":{"operationId":"delete_v1_marketing_audiences_by_id","summary":"Removes one of the caller org's audiences and answers 204.","description":"Removes one of the caller org's audiences and answers 204. It\ndeletes the saved filter only — no customer, event or enrollment is touched.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the audience id from the path, as returned by create.","schema":{"type":"string"},"example":"aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a"}],"responses":{"204":{"description":"no content"}},"x-app":"marketing"},"get":{"operationId":"get_v1_marketing_audiences_by_id","summary":"Returns one of the caller org's saved audiences.","description":"Returns one of the caller org's saved audiences. An audience\nbelonging to another org reads as not found.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the audience id from the path, as returned by create.","schema":{"type":"string"},"example":"aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audience"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/audiences/{id}/preview":{"get":{"operationId":"get_v1_marketing_audiences_by_id_preview","summary":"Evaluates the cohort LIVE — the same resolution an enrollment would run — and reports how big it is and how many real mailboxes it reaches.","description":"Evaluates the cohort LIVE — the same resolution an enrollment\nwould run — and reports how big it is and how many real mailboxes it reaches.\nIt is the honest answer to \"is this send worth making\": a cohort of 500 that\nmails 3 says so, in deliverable and unmatched. Nothing is sent.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the audience id from the path, as returned by create.","schema":{"type":"string"},"example":"aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a"}],"responses":{"200":{"content":{"application/json":{"example":{"available":true,"count":500,"deliverable":3,"sample":["u_1","u_2"],"source":"event.fact","unmatched":497},"schema":{"$ref":"#/components/schemas/AudiencePreview"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/calendar":{"get":{"operationId":"get_v1_marketing_calendar","summary":"Returns the org's calendar, soonest scheduled first, optionally narrowed to one status.","description":"Returns the org's calendar, soonest scheduled first,\noptionally narrowed to one status.","tags":["marketing"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status keeps only posts in that state (draft, scheduled, published,\nfailed, canceled). Empty means every post.","schema":{"type":"string"},"example":"scheduled"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_calendar","summary":"Adds a post to the content calendar.","description":"Adds a post to the content calendar. Channel and body are\nrequired. A scheduledAt in the future makes the post \"scheduled\" and the\ndurable sweep publishes it when it comes due — claimed once, so a post\npublishes at most once; without one it stays a draft.","tags":["marketing"],"requestBody":{"content":{"application/json":{"example":{"body":"Hanzo Cloud is live.","channel":"x","scheduledAt":1780000000,"title":"Launch day"},"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/calendar/{id}":{"delete":{"operationId":"delete_v1_marketing_calendar_by_id","summary":"Removes one of the caller org's posts and answers 204.","description":"Removes one of the caller org's posts and answers 204. A\npost already published is deleted from the calendar only — nothing is\nretracted from the network it went out on.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the post id from the path, as returned by create.","schema":{"type":"string"},"example":"cal_1d7f3b9e5a2c8046f1b3d5a7c9e02468"}],"responses":{"204":{"description":"no content"}},"x-app":"marketing"},"get":{"operationId":"get_v1_marketing_calendar_by_id","summary":"Returns one of the caller org's posts, including the exact error behind a failed publish.","description":"Returns one of the caller org's posts, including the exact\nerror behind a failed publish. A post belonging to another org reads as not\nfound.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the post id from the path, as returned by create.","schema":{"type":"string"},"example":"cal_1d7f3b9e5a2c8046f1b3d5a7c9e02468"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"description":"ok"}},"x-app":"marketing"},"put":{"operationId":"put_v1_marketing_calendar_by_id","summary":"Replaces a post's editable fields.","description":"Replaces a post's editable fields. It is a full write, not\na patch, and it RESETS the lifecycle from the schedule: a scheduledAt makes\nthe post \"scheduled\" again and none makes it a draft — so editing a failed\npost requeues it rather than leaving it stuck.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the server-assigned post id (\"cal_\" + 128 random bits).","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"body":"Hanzo Cloud is live — try it free.","channel":"x","scheduledAt":1780003600,"title":"Launch day"},"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/calendar/{id}/publish":{"post":{"operationId":"post_v1_marketing_calendar_by_id_publish","summary":"Publishes a post NOW, synchronously, whatever its schedule.","description":"Publishes a post NOW, synchronously, whatever its\nschedule. No social connector is wired today, so every channel answers an\nhonest 501 naming the seam a real one would plug into, and the post is\nrecorded failed with that exact reason — never a faked \"published\".","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the post id from the path, as returned by create.","schema":{"type":"string"},"example":"cal_1d7f3b9e5a2c8046f1b3d5a7c9e02468"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarPost"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/campaigns":{"get":{"operationId":"get_v1_marketing_campaigns","summary":"Returns the org's campaigns, most recently updated first, optionally narrowed to one lifecycle status.","description":"Returns the org's campaigns, most recently updated first,\noptionally narrowed to one lifecycle status.","tags":["marketing"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status keeps only campaigns in that lifecycle state (draft, scheduled,\nactive, paused, completed). Empty means every campaign.","schema":{"type":"string"},"example":"active"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":25}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_campaigns","summary":"Registers a campaign in the caller's org.","description":"Registers a campaign in the caller's org. Name is required;\nchannel defaults to email and status to draft, and a future scheduledAt with\nno explicit status makes the campaign \"scheduled\". Budget and spend are cents\nand are clamped to \u003e= 0. The id, createdAt and updatedAt of the input are\nignored — the server assigns them.","tags":["marketing"],"requestBody":{"content":{"application/json":{"example":{"budget":50000,"channel":"meta","name":"Spring Launch","objective":"signups","scheduledAt":1780000000},"schema":{"$ref":"#/components/schemas/Campaign"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/campaigns/{id}":{"delete":{"operationId":"delete_v1_marketing_campaigns_by_id","summary":"Removes one of the caller org's campaigns and answers 204.","description":"Removes one of the caller org's campaigns and answers 204. A\ncampaign belonging to another org reads as not found and is left untouched.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign id from the path, as returned by create.","schema":{"type":"string"},"example":"camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60"}],"responses":{"204":{"description":"no content"}},"x-app":"marketing"},"get":{"operationId":"get_v1_marketing_campaigns_by_id","summary":"Returns one of the caller org's campaigns.","description":"Returns one of the caller org's campaigns. A campaign belonging to\nanother org reads as not found.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign id from the path, as returned by create.","schema":{"type":"string"},"example":"camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}},"description":"ok"}},"x-app":"marketing"},"put":{"operationId":"put_v1_marketing_campaigns_by_id","summary":"Replaces a campaign's editable fields.","description":"Replaces a campaign's editable fields. It is a full write, not\na patch: every field takes the value in the body, and an omitted one is\ncleared. The id comes from the path — the body cannot retarget another\ncampaign — and createdAt is never rewritten.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the server-assigned campaign id (\"camp_\" + 128 random bits).","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"budget":50000,"channel":"meta","name":"Spring Launch","objective":"signups","spend":12500,"status":"active"},"schema":{"$ref":"#/components/schemas/Campaign"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/campaigns/{id}/schedule":{"post":{"operationId":"post_v1_marketing_campaigns_by_id_schedule","summary":"Sets a campaign's send time and moves it to \"scheduled\".","description":"Sets a campaign's send time and moves it to \"scheduled\". A\nscheduledAt of 0 clears the schedule and returns it to \"draft\".","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the campaign id from the path.","schema":{"type":"string"},"example":"camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60"}],"requestBody":{"content":{"application/json":{"example":{"id":"camp_9f2a1c7d4e8b0a6f3d2c5b1e7a9f4c60","scheduledAt":1780000000},"schema":{"$ref":"#/components/schemas/ScheduleInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/promos":{"get":{"operationId":"get_v1_marketing_promos","summary":"Returns every promo the deployment offers with its live counters: how many orgs have redeemed it and how many redemptions remain under the cap.","description":"Returns every promo the deployment offers with its live counters:\nhow many orgs have redeemed it and how many redemptions remain under the cap.\nThe promos are fleet-wide, not per-org — only the counters move.","tags":["marketing"],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"promo":{"active":true,"code":"first1000","maxRedemptions":1000,"percentOff":90},"redeemed":137,"remaining":863}]},"schema":{"$ref":"#/components/schemas/PromoList"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/promos/{code}/eligibility":{"get":{"operationId":"get_v1_marketing_promos_by_code_eligibility","summary":"Prices a promo against a plan and seat count.","description":"Prices a promo against a plan and seat count. It is PURE: nothing\nis redeemed, credited or counted, so it is safe to call from a pricing page on\nevery keystroke. An inactive promo or an exhausted cap quotes ineligible with\nthe reason rather than erroring.","tags":["marketing"],"parameters":[{"name":"code","in":"path","required":true,"description":"Code is the promo code from the path.","schema":{"type":"string"},"example":"first1000"},{"name":"plan","in":"query","required":false,"description":"Plan is the plan being priced: pro, max or team. Anything else (including\nthe free Developer plan) has no list price and so nothing to discount.","schema":{"type":"string"},"example":"team"},{"name":"seats","in":"query","required":false,"description":"Seats is the Team seat count; 0 means 1, and it is ignored for the\nsingle-seat plans.","schema":{"type":"integer"},"example":12}],"responses":{"200":{"content":{"application/json":{"example":{"chargeCents":418900,"code":"first1000","discountCents":179100,"eligible":true,"listCents":19900,"plan":"team","remaining":863,"seats":12},"schema":{"$ref":"#/components/schemas/Quote"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/promos/{code}/redeem":{"post":{"operationId":"post_v1_marketing_promos_by_code_redeem","summary":"Records the caller org's claim on a promo.","description":"Records the caller org's claim on a promo. NOTHING IS CREDITED:\nthe redemption is a row, and credit into an org is an admin decision made on\nthe admin surface against an auditable ledger.\n\nThe plan is DERIVED from the org's live ACTIVE/TRIALING paid subscription and\ncan never be named by the caller — an org with no qualifying subscription is\nrefused, and so is one whose subscription cannot be read. The seat count is\nthe single-seat floor (claimSeats), so the recorded figure has no input that\ncan inflate it.\n\nGuards run under one lock so the cap cannot be raced past: the fleet-wide\nredemption cap, one redemption per org, one per payment instrument (REQUIRED),\nand the per-redemption ceiling.\n\nIt is IDEMPOTENT: an org that already redeemed gets its original redemption\nback with alreadyRedeemed true.","tags":["marketing"],"parameters":[{"name":"code","in":"path","required":true,"description":"Code is the promo code from the path.","schema":{"type":"string"},"example":"first1000"}],"requestBody":{"content":{"application/json":{"example":{"code":"first1000","instrument":"pm_1QxYz2AbCdEf"},"schema":{"$ref":"#/components/schemas/RedeemInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemResult"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/promos/{code}/redemption":{"get":{"operationId":"get_v1_marketing_promos_by_code_redemption","summary":"Returns the caller org's OWN redemption of a promo — an org-scoped read, so it can never surface another tenant's.","description":"Returns the caller org's OWN redemption of a promo — an\norg-scoped read, so it can never surface another tenant's. Not found when this\norg has not redeemed it.","tags":["marketing"],"parameters":[{"name":"code","in":"path","required":true,"description":"Code is the promo code from the path, e.g. \"first1000\".","schema":{"type":"string"},"example":"first1000"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Redemption"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences":{"get":{"operationId":"get_v1_marketing_sequences","summary":"Returns the org's drip sequences, most recently updated first.","description":"Returns the org's drip sequences, most recently updated first.","tags":["marketing"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SequenceList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_sequences","summary":"Registers a drip sequence in the caller's org.","description":"Registers a drip sequence in the caller's org. Name is\nrequired; status defaults to draft, and a sequence must be ACTIVE before it\nwill accept enrollments. The id, createdAt and updatedAt of the input are\nignored — the server assigns them.","tags":["marketing"],"requestBody":{"content":{"application/json":{"example":{"name":"Trial onboarding","status":"draft"},"schema":{"$ref":"#/components/schemas/Sequence"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sequence"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}":{"get":{"operationId":"get_v1_marketing_sequences_by_id","summary":"Returns one of the caller org's sequences together with its steps in send order.","description":"Returns one of the caller org's sequences together with its steps\nin send order. A sequence belonging to another org reads as not found.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path, as returned by create.","schema":{"type":"string"},"example":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SequenceView"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}/enroll":{"post":{"operationId":"post_v1_marketing_sequences_by_id_enroll","summary":"Adds one contact or a whole audience to a sequence and schedules the first step for each.","description":"Adds one contact or a whole audience to a sequence and schedules the\nfirst step for each. The sequence must be ACTIVE (a draft sends nothing), and\nthe request must name exactly one of address or audienceId.\n\nEnrolling is ALL this does: the message itself is sent later by the drip\nengine, through the suppression gate, so an opted-out customer can be enrolled\nhere and still never be mailed. Re-posting is safe — an address this sequence\nalready took is counted in alreadyEnrolled and never double-dripped — which is\nwhat makes retrying a partially-applied announcement a resume rather than a\nsecond send.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"audienceId":"aud_4c1e9b7a2d6f0538e4a7c9b1d3f5027a","channel":"email"},"schema":{"$ref":"#/components/schemas/EnrollInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"alreadyEnrolled":3,"enrolled":409,"resolved":412},"schema":{"$ref":"#/components/schemas/EnrollResult"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}/enrollments":{"get":{"operationId":"get_v1_marketing_sequences_by_id_enrollments","summary":"Returns who is walking one sequence, most recently enrolled first, with each walk's current step and next due time.","description":"Returns who is walking one sequence, most recently enrolled\nfirst, with each walk's current step and next due time.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path.","schema":{"type":"string"},"example":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentList"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}/enrollments/{eid}/cancel":{"post":{"operationId":"post_v1_marketing_sequences_by_id_enrollments_by_eid_cancel","summary":"Stops one walk mid-sequence and answers 204: no further step is sent, and steps already delivered are not recalled.","description":"Stops one walk mid-sequence and answers 204: no further step\nis sent, and steps already delivered are not recalled. Only an ACTIVE\nenrollment can be canceled — one already completed or canceled reads as not\nfound.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path.","schema":{"type":"string"},"example":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248"},{"name":"eid","in":"path","required":true,"description":"EID is the enrollment id from the path, as returned by a single-address\nenroll.","schema":{"type":"string"},"example":"enr_2a8d6f0b4c1e9375a0d2f6b8c4e19f73"}],"responses":{"204":{"description":"no content"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}/status":{"post":{"operationId":"post_v1_marketing_sequences_by_id_status","summary":"Flips draft/active/archived — the activation gate for sending, since only an active sequence accepts enrollments.","description":"Flips draft/active/archived — the activation gate for\nsending, since only an active sequence accepts enrollments. It does not touch\nenrollments already walking: archiving stops new ones, not in-flight ones.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path.","schema":{"type":"string"},"example":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248"}],"requestBody":{"content":{"application/json":{"example":{"id":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248","status":"active"},"schema":{"$ref":"#/components/schemas/SequenceStatus"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SequenceStatus"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/sequences/{id}/steps":{"get":{"operationId":"get_v1_marketing_sequences_by_id_steps","summary":"Returns one sequence's steps in send order.","description":"Returns one sequence's steps in send order.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sequence id from the path, as returned by create.","schema":{"type":"string"},"example":"seq_7b3e5a1c9d024f68b0a3e7c5d9f1a248"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_sequences_by_id_steps","summary":"Appends a message to the END of a sequence: the new step's idx is one past the last, so steps arrive in the order they are added.","description":"Appends a message to the END of a sequence: the new step's idx is one\npast the last, so steps arrive in the order they are added. Body is required\nand delaySeconds must be \u003e= 0. Adding a step does not disturb enrollments\nalready walking — one that has passed this index simply never sees it.","tags":["marketing"],"parameters":[{"name":"id","in":"path","required":true,"description":"SequenceID is the sequence id from the path (the route's :id).","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"body":"Here is how to make your first request…","delaySeconds":86400,"subject":"Day 1: your first model call"},"schema":{"$ref":"#/components/schemas/StepInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Step"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/summary":{"get":{"operationId":"get_v1_marketing_summary","summary":"Rolls up the caller org's campaigns: how many there are, how many are active, and the summed budget and spend in cents.","description":"Rolls up the caller org's campaigns: how many there are, how many are\nactive, and the summed budget and spend in cents.","tags":["marketing"],"responses":{"200":{"content":{"application/json":{"example":{"active":3,"budget":500000,"campaigns":12,"spend":128400},"schema":{"$ref":"#/components/schemas/Summary"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/suppressions":{"delete":{"operationId":"delete_v1_marketing_suppressions","summary":"Re-subscribes an address on one channel and answers 204.","description":"Re-subscribes an address on one channel and answers 204. An\naddress that is not on the list reads as not found.","tags":["marketing"],"parameters":[{"name":"channel","in":"query","required":false,"description":"Channel is the surface opted out of: email, sms, social, meta, google or\ntiktok. Empty means email. Opting out of one leaves the others reachable.","schema":{"type":"string"},"example":"email"},{"name":"address","in":"query","required":false,"description":"Address is the recipient, normalized (lower-cased, trimmed) so an opt-out\ncannot be slipped past on a case or whitespace difference. Required.","schema":{"type":"string"},"example":"person@example.com"},{"name":"reason","in":"query","required":false,"description":"Reason is a free-text note, capped at 1024 bytes. The public one-click\nendpoint records \"one-click unsubscribe\".","schema":{"type":"string"}},{"name":"createdAt","in":"query","required":false,"description":"CreatedAt is unix seconds, server-assigned.","schema":{"type":"integer"}}],"responses":{"204":{"description":"no content"}},"x-app":"marketing"},"get":{"operationId":"get_v1_marketing_suppressions","summary":"Returns the org's opt-out list, newest first — everyone the send gate will refuse to deliver to.","description":"Returns the org's opt-out list, newest first — everyone the\nsend gate will refuse to deliver to.","tags":["marketing"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned; 0 means 200 and nothing above 1000 is honoured.","schema":{"type":"integer"},"example":100}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuppressionList"}}},"description":"ok"}},"x-app":"marketing"},"post":{"operationId":"post_v1_marketing_suppressions","summary":"Records an opt-out for the org (admin / self-service management).","description":"Records an opt-out for the org (admin / self-service\nmanagement). Address is required; channel defaults to email. It is idempotent:\nre-suppressing the same tuple keeps the original record rather than erroring.\nFrom here on the ONE send gate refuses that recipient on that channel.","tags":["marketing"],"requestBody":{"content":{"application/json":{"example":{"address":"person@example.com","channel":"email","reason":"asked support to stop"},"schema":{"$ref":"#/components/schemas/Suppression"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Suppression"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketing/unsubscribe":{"get":{"operationId":"get_v1_marketing_unsubscribe","summary":"Is the PUBLIC one-click endpoint (no principal): a recipient clicks the signed link in an email footer.","description":"Is the PUBLIC one-click endpoint (no principal): a recipient\nclicks the signed link in an email footer. The token binds (org, channel,\naddress), so a caller can only opt OUT exactly the tuple it was minted for —\nnever another address and never another org. An invalid token is refused, and\na deployment with no KMS-sealed key refuses rather than accepting anything.","tags":["marketing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org is the org the link was minted for.","schema":{"type":"string"},"example":"acme"},{"name":"channel","in":"query","required":false,"description":"Channel is the surface to opt out of.","schema":{"type":"string"},"example":"email"},{"name":"address","in":"query","required":false,"description":"Address is the recipient to opt out.","schema":{"type":"string"},"example":"person@example.com"},{"name":"token","in":"query","required":false,"description":"Token is the HMAC over (org, channel, address). It is the ONLY authority\nhere — there is no principal — so it binds the request to one tuple and\nnothing else.","schema":{"type":"string"},"example":"9f2a…"}],"responses":{"200":{"content":{"application/json":{"example":{"address":"person@example.com","channel":"email","unsubscribed":true},"schema":{"$ref":"#/components/schemas/Unsubscribed"}}},"description":"ok"}},"x-app":"marketing"}},"/v1/marketplace":{"get":{"operationId":"get_v1_marketplace","summary":"Discover lists every tool and agent the caller can reach in their own org and project, enriched with any public listing's title, category and price, and with installed=true on the ones already activated for that scope.","description":"Discover lists every tool and agent the caller can reach in their own org and\nproject, enriched with any public listing's title, category and price, and with\ninstalled=true on the ones already activated for that scope. It is the shop\nwindow: one read that answers what exists, what it costs and what is already on.","tags":["marketplace"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/marketCatalog"}}},"description":"ok"}},"x-app":"marketplace"}},"/v1/marketplace/install":{"post":{"operationId":"post_v1_marketplace_install","summary":"Install activates one tool for the caller's own org and project.","description":"Install activates one tool for the caller's own org and project. A marketplace\ninstall IS the tool plane's activation write — one store, one truth — so an\ninstalled capability is immediately dispatchable and a monetized one is priced\nfrom its listing at every call. The tool must resolve in the caller's scope, so\ninstalling something that does not exist is refused rather than recorded.","tags":["marketplace"],"requestBody":{"content":{"application/json":{"example":{"tool":"summarize"},"schema":{"$ref":"#/components/schemas/installReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/installState"}}},"description":"ok"}},"x-app":"marketplace"}},"/v1/marketplace/listings":{"get":{"operationId":"get_v1_marketplace_listings","summary":"Returns the listings the caller's own org has published — what this org is offering, not what it can buy.","description":"Returns the listings the caller's own org has published — what this\norg is offering, not what it can buy. A publisher only ever sees its own rows.","tags":["marketplace"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/listingPage"}}},"description":"ok"}},"x-app":"marketplace"},"post":{"operationId":"post_v1_marketplace_listings","summary":"Publish offers one tool on the marketplace, optionally monetized.","description":"Publish offers one tool on the marketplace, optionally monetized. The tool must\nalready resolve in the publisher's own scope, so a listing can never advertise a\ncapability that does not exist; a listing with a price must name the payout wallet\nthe x402 seam settles to, so a monetized offer is never unpayable. The price is\nexact to 18 decimal places, so a per-call price below a cent is a real price and\nnot a rounded-away zero. The listing is owned by the publishing org, paid into a\nwallet of that same org, and answers 201 with the created row.","tags":["marketplace"],"requestBody":{"content":{"application/json":{"example":{"price":"0.0025","public":true,"recipient":"wal_9f2","title":"Summarize","tool":"summarize"},"schema":{"$ref":"#/components/schemas/publishReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Listing"}}},"description":"created"}},"x-app":"marketplace"}},"/v1/marketplace/listings/{id}":{"delete":{"operationId":"delete_v1_marketplace_listings_by_id","summary":"Unpublish withdraws one of the caller org's listings from the marketplace and answers 204.","description":"Unpublish withdraws one of the caller org's listings from the marketplace and\nanswers 204. Only the publishing org can remove its own listing; an id that is\nunknown, or belongs to another org, is the same 404, so a probe learns nothing\nabout what exists. Removing a listing removes its price from per-call enforcement;\nit does not uninstall the tool for anyone who already installed it.","tags":["marketplace"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the listing to unpublish, from the path.","schema":{"type":"string"},"example":"lst_1"}],"responses":{"204":{"description":"no content"}},"x-app":"marketplace"}},"/v1/marketplace/uninstall":{"post":{"operationId":"post_v1_marketplace_uninstall","summary":"Uninstall deactivates one tool for the caller's own org and project, so it stops being dispatchable there.","description":"Uninstall deactivates one tool for the caller's own org and project, so it stops\nbeing dispatchable there. It is the exact inverse of install and touches the same\nactivation record; deactivating something that was never active is not an error.\nThe listing itself is untouched — this withdraws the caller's use of a capability,\nnot anyone's offer of it.","tags":["marketplace"],"requestBody":{"content":{"application/json":{"example":{"tool":"summarize"},"schema":{"$ref":"#/components/schemas/installReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/installState"}}},"description":"ok"}},"x-app":"marketplace"}},"/v1/mcp/servers":{"get":{"operationId":"get_v1_mcp_servers","summary":"Lists the external MCP servers the caller's org has registered.","description":"Lists the external MCP servers the caller's org has registered.\nEach record carries the URL and the name of the header its credential is\ninjected into; the credential VALUE lives only in KMS and is never returned,\nso hasSecret is the whole of what this surface says about it.","tags":["mcp"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcpServerList"}}},"description":"ok"}},"x-app":"tools"},"post":{"operationId":"post_v1_mcp_servers","summary":"Gives the caller's org one more external MCP server, so its tools join the org's tool plane and the fleet's MCP door.","description":"Gives the caller's org one more external MCP server, so its tools\njoin the org's tool plane and the fleet's MCP door. It is the ONE way an org\ngains a server, whether it typed the URL in or enabled a catalog listing: both\nwrite the SAME record, and `source` says which it was. A second registration\npath would be a second place for a server to exist, and then a second place to\nforget to check the credential.\n\nThe credential VALUE is sealed in KMS under a per-org ref; the row keeps only\nthe URL, the header name to inject it into, and a has-secret flag — so a secret\nwith no KMS configured is refused 503 rather than stored in the clear. The URL\nis SSRF-validated here and re-checked by the dialer at connect time, which is\nthe DNS-rebinding defense.\n\nEnabling a listing the org already enabled REVISES that server rather than\nadding a near-duplicate beside it, so a retried enable is the same one server.\nAnswers 201 with the stored record.","tags":["mcp"],"requestBody":{"content":{"application/json":{"example":{"authHeader":"Authorization","listing":"com.stripe_mcp","secret":"Bearer …"},"schema":{"$ref":"#/components/schemas/createServerReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPServer"}}},"description":"created"}},"x-app":"tools"}},"/v1/mcp/servers/{id}":{"delete":{"operationId":"delete_v1_mcp_servers_by_id","summary":"Deregisters one of the caller org's external MCP servers, so its tools leave the registry.","description":"Deregisters one of the caller org's external MCP servers, so its\ntools leave the registry. Scoped to the caller's org, so an id belonging to\nanother tenant is a 404 and not a delete. Answers 204 with no body; a server\nthis org does not have is 404.","tags":["mcp"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the server to deregister, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"tools"}},"/v1/meet/getToken":{"post":{"operationId":"post_v1_meet_gettoken","summary":"Mint a join token for one video room","description":"Answers with a LiveKit join token for exactly the room named in the body. The body is the RAW token as text/plain — one opaque string, not JSON and not wrapped in an envelope, which is what the office client reads.\n\nThe caller presents its workspace session as a Bearer. Every clause is a refusal: the session must verify, its SIGNED workspace claim must equal the room's leading name segment — rooms are named `\u003cworkspace\u003e_\u003croom\u003e_\u003cid\u003e`, and that prefix is the only thing binding a room to a tenant — and the session must carry a privileged workspace role, so a guest is refused rather than seated.\n\nThe participant identity is the SESSION'S, never the body's. `_id` is accepted for compatibility with the published client bundle and deliberately ignored: LiveKit treats the identity as unique and ejects a duplicate, so honouring a caller-chosen one would let anyone in a workspace kick out a colleague and impersonate them. `participantName` is a display name only.\n\nAn unconfigured deployment answers 503 under its own name rather than 404, and the refusal states only that the office is unconfigured — the reason names key material and stays in the boot log.","tags":["meet"],"x-app":"meet"}},"/v1/meet/health":{"get":{"operationId":"get_v1_meet_health","summary":"Health reports whether the office can mint join tokens.","description":"Health reports whether the office can mint join tokens.\n\nIt reports whether this deployment holds the LiveKit key pair it needs:\nready:true with 200 when tokens can be minted, the SAME body with ready:false,\nstatus \"degraded\" and 503 when they cannot — so a probe and a dashboard both\nread the degraded state instead of someone grepping a boot log.\n\nIt takes no credential and is reachable on every public host, so it withholds\nboth the reason and the signing key's name on purpose: ready is the whole\ndashboard fact, and the reason — which names the key file and the Secret — is\nwritten to the boot log where an operator already is.","tags":["meet"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/meetHealth"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/meetHealth"}}},"description":"service unavailable"}},"x-app":"meet"}},"/v1/meet/session":{"get":{"operationId":"get_v1_meet_session","summary":"What this caller may open a room in","description":"Answers the three facts the native lobby cannot know on its own: the identity a seat would be taken under, the LiveKit address the browser dials, and the workspaces this caller may open a room in.\n\nIt is the SAME decision getToken makes, asked before the room exists rather than after it is named. A room is bound to its tenant by its name's leading workspace segment, and only a workspace this answer lists will be admitted — so the lobby offers exactly what the mint would grant, and a person is never shown a room they would then be refused. Workspaces the caller holds only a guest role in are omitted for that reason.\n\nAn empty list is a real answer, not a fault: an IAM identity with no workspace has no room to open, and the lobby says so instead of failing.\n\n`ws` is empty when this deployment has not been told where its media plane is (LIVEKIT_WS). Token minting is unaffected — the published office client supplies its own address — so this is a degraded native UI, not a degraded service, and the lobby refuses to dial rather than guessing a host.","tags":["meet"],"x-app":"meet"}},"/v1/memory/delete":{"post":{"operationId":"post_v1_memory_delete","summary":"Delete one of the authenticated user's memories","description":"Delete one of the authenticated user's memories","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/facts":{"get":{"operationId":"get_v1_memory_facts","summary":"List the authenticated user's stored facts","description":"List the authenticated user's stored facts","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/list":{"get":{"operationId":"get_v1_memory_list","summary":"List the authenticated user's memories, newest first","description":"List the authenticated user's memories, newest first","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/recall":{"get":{"operationId":"get_v1_memory_recall","summary":"Recall recent/relevant memories for context injection; with q it","description":"Recall recent/relevant memories for context injection; with q it\nranks semantically, without q it returns the most recent","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/remember":{"post":{"operationId":"post_v1_memory_remember","summary":"Store a memory for the authenticated user","description":"Store a memory for the authenticated user","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/search":{"get":{"operationId":"get_v1_memory_search","summary":"Search the authenticated user's memories (semantic, text fallback)","description":"Search the authenticated user's memories (semantic, text fallback)","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/memory/update":{"post":{"operationId":"post_v1_memory_update","summary":"Update one of the authenticated user's memories","description":"Update one of the authenticated user's memories","tags":["memory"],"x-app":"github.com/hanzoai/ai"}},"/v1/mesh/services":{"get":{"operationId":"get_v1_mesh_services","summary":"Returns the Zero Trust edge services the caller's org owns.","description":"Returns the Zero Trust edge services the caller's org owns.\n\nOne row per real ZT edge service tagged with the org's \"org-\u003corg\u003e\" role\nattribute: mtls is \"required\" when the service mandates end-to-end encryption and\n\"enabled\" otherwise (the fabric always mutually authenticates every link), and\nstatus is \"active\" because a listed service is a configured, dialable entry. A\nservice tagged for another org, or tagged for none, is invisible here.\n\nUnlike the network and router reads this does NOT degrade: an unconfigured\ndeployment answers 503 and an unreachable controller surfaces the upstream's\nstatus, so a mesh page never renders \"no services\" for a fabric it simply could\nnot read.","tags":["mesh"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/meshServiceList"}}},"description":"ok"}},"x-app":"zt"}},"/v1/messages":{"post":{"operationId":"post_v1_messages","summary":"Implements the Anthropic Messages API.","description":"Implements the Anthropic Messages API.","tags":["messages"],"x-app":"github.com/hanzoai/ai"}},"/v1/messages/count_tokens":{"post":{"operationId":"post_v1_messages_count_tokens","summary":"Implements POST /v1/messages/count_tokens.","description":"Implements POST /v1/messages/count_tokens. Claude Code\ncalls it before a request; it returns {\"input_tokens\": N} for the given\nmodel + messages + tools.","tags":["messages"],"x-app":"github.com/hanzoai/ai"}},"/v1/metrics/batch":{"post":{"operationId":"post_v1_metrics_batch","summary":"Ingest a MetricBatch — the same payload the ZAP transport carries","description":"Writes every sample in a luxfi/metric `MetricBatch` into the calling org's store and answers `{written}`: the number of SAMPLES stored, not families and not metrics. This is the exact wire shape the ZAP `MsgMetricBatch` transport carries, so the HTTP door and the optional ZAP push receiver share one code path and one meaning — the transport is an optimisation, never a different contract.\n\nA counter or gauge lands as one sample. A histogram or summary contributes DERIVED `\u003cname\u003e_sum` and `\u003cname\u003e_count` series, so one metric can write more than one sample and `written` can exceed the number of metrics you sent. The batch's own `TimestampNs` stamps every sample it carries.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`; each org gets its own store, WAL-durable under the deployment's data dir. A body that does not decode is 400.","tags":["metrics"],"x-app":"metrics"}},"/v1/metrics/health":{"get":{"operationId":"get_v1_metrics_health","summary":"How many metric series this deployment holds for your org","description":"Reports the native metrics store's live state for the calling tenant: the subsystem version, the resolved `org`, and `series` — the number of distinct series actually held right now, read out of the store rather than a constant. It is not a dependency probe and has nothing downstream to fail on: the store is in-process, so this answers 200 whenever the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`. This surface trusts the edge rather than re-deriving the org from a validated claim of its own, so it belongs behind the gateway and nowhere else.","tags":["metrics"],"x-app":"metrics"}},"/v1/metrics/query":{"get":{"operationId":"get_v1_metrics_query","summary":"Read your org's series back over a time range","description":"Answers `{count, series}`, where `count` is the number of matching SERIES and each series carries the samples that fall inside the window. `name` selects one series name, and an absent or empty `name` returns every series the org holds. `match` is a `k=v,k2=v2` label matcher applied as a SUPERSET test: a series matches when it carries all the named labels with those values, extra labels and all.\n\n`start` and `end` are nanoseconds since the Unix epoch, and here is the rule worth knowing: a bound that is absent, empty or unparseable becomes 0, which this store reads as UNBOUNDED. A malformed `start` therefore silently widens the query instead of failing it. There is no limit parameter — the window and the matcher are the whole of what bounds the answer.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`, so a query can only ever read the org the edge asserted.","tags":["metrics"],"x-app":"metrics"}},"/v1/metrics/write":{"post":{"operationId":"post_v1_metrics_write","summary":"Append samples to your org's named, labelled series","description":"Takes `{series:[{name, labels, samples:[{t, v}]}]}`, appends every sample, creating each series on first write, and answers `{written}` — again counting SAMPLES, so three series of ten samples is 30.\n\nA series is identified by its name PLUS its whole label set, so adding one label makes a different series rather than annotating an existing one. Timestamps `t` are NANOSECONDS since the Unix epoch; a sample sent without one is stored at 0 and is then excluded by any query that sets a lower bound, which is the usual reason a write that reported success does not read back. Retention is per series and bounded — past 65536 samples the oldest are evicted.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`. A body that does not decode is 400; nothing else is validated or rejected.","tags":["metrics"],"x-app":"metrics"}},"/v1/ml/health":{"get":{"operationId":"get_v1_ml_health","summary":"Whether model serving can actually work right now","description":"Reports whether the model-serving plane is genuinely usable: that the Kubernetes API answers, that the InferenceService CRD is actually served by this cluster, and that the cluster holds at least one serving runtime to run a model ON. It is a REAL probe, not status theatre — it makes a live call rather than reporting a flag set at boot.\n\n200 only when everything checks out. Otherwise 503 CARRYING THE REPORT — which component failed, and the real error — and that body is the reason this is not a typed op: a typed op reaches a non-2xx by returning an error, and the envelope that produces would drop exactly the detail the probe exists to deliver.\n\nThe runtime count is reported as its own field and is a SEPARATE fact from the CRD being served: a cluster with the CRD but no runtime accepts a deploy and then never schedules it, so reporting only the CRD would answer 200 while every model hangs. A runtime list this service cannot read reports the read error instead of a count, because a missing grant is a broken probe and not an empty cluster.\n\nIt answers about the cluster, not about a tenant, so it takes no org and reveals no tenant data. A cluster with no kserve CRD reports degraded honestly rather than failing later at the first deploy.","tags":["ml"],"x-app":"ml"}},"/v1/ml/models":{"get":{"operationId":"get_v1_ml_models","summary":"Lists the inference models deployed in the caller's org.","description":"Lists the inference models deployed in the caller's org. Each entry\ncarries the model's name, when Kubernetes admitted it, and kserve's live status\n— the spec is on the single-model read. An org that has deployed nothing gets\nan empty list.","tags":["ml"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mlResourceList"}}},"description":"ok"}},"x-app":"ml"},"post":{"operationId":"post_v1_ml_models","summary":"Deploy an inference model","description":"Deploys a model into the caller's own tenant namespace and answers the created resource, 201. The spec is the kserve InferenceService spec, relayed as given, so anything kserve serves is deployable here without this layer knowing what it is.\n\nTHE BALANCE GATE RUNS FIRST, before a namespace or a resource exists, so an unfunded org cannot start GPU compute and then be billed for it. It fails CLOSED: a commerce that cannot be reached refuses rather than admits. The refusal carries the fleet's nested error body — the 402 shape a funded-balance client already parses — which is precisely why this route is not a typed op. On success the submission fee is debited from the caller org's own ledger, asynchronously and best-effort; ongoing GPU-hour cost is metered elsewhere.\n\nThe tenant namespace is derived from the VALIDATED org and project — never from a field — and the mapping is injective in both, so two tenants can never land in one namespace. An unvalidated caller is refused before any of that. The name must be a DNS-1123 label; a name already taken in the tenant's namespace is a 409.","tags":["ml"],"x-app":"ml"}},"/v1/ml/models/{name}":{"delete":{"operationId":"delete_v1_ml_models_by_name","summary":"Deletes a deployed inference model.","description":"Deletes a deployed inference model. kserve owns the teardown: the\nInferenceService goes away and the serving deployment behind it follows, so the\nmodel stops answering predict calls. Answers 204, or 404 for a name the\ncaller's org does not own.","tags":["ml"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource to act on, taken from the path. Lower-cased and\ntrimmed to the DNS-1123 label a CustomResource's metadata.name must be.","schema":{"type":"string"},"example":"sentiment"}],"responses":{"204":{"description":"no content"}},"x-app":"ml"},"get":{"operationId":"get_v1_ml_models_by_name","summary":"Returns one deployed inference model.","description":"Returns one deployed inference model. Its spec comes with it, and\nkserve's live status, which is where readiness and the serving address appear.\nA name the caller's org does not own answers 404, exactly as an unknown name\ndoes, so a probe learns nothing about another tenant's models.","tags":["ml"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource to act on, taken from the path. Lower-cased and\ntrimmed to the DNS-1123 label a CustomResource's metadata.name must be.","schema":{"type":"string"},"example":"sentiment"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mlResource"}}},"description":"ok"}},"x-app":"ml"},"patch":{"operationId":"patch_v1_ml_models_by_name","summary":"Change a deployed model in place","description":"Applies a JSON merge patch to one of the caller org's deployed models and answers the updated resource — the way to change a model's image, replica count or resource requests without tearing the deployment down.\n\nThe body is relayed to Kubernetes VERBATIM. That is deliberate and it is why this route is not a typed op: re-encoding a merge patch changes what it means, because an integer that round-trips through a generic decoder comes back a float. Merge-patch semantics apply as written — a null removes a field, and a list is replaced whole rather than merged.\n\nScoped to the caller's own tenant namespace, resolved from the validated org and project; a name the caller's tenant does not hold is a 404, never another tenant's resource. An empty body is refused, and a patch Kubernetes rejects comes back 422 with its reason rather than being silently dropped.","tags":["ml"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ml"}},"/v1/ml/models/{name}/predict":{"post":{"operationId":"post_v1_ml_models_by_name_predict","summary":"Run inference against one of your deployed models","description":"Sends the request body to the named model's predictor and answers the predictor's reply — its status code, its body bytes and its Content-Type, all unchanged. This is the inference call itself, not a description of one.\n\nVERBATIM IS THE CONTRACT, and it is why this route is not a typed op: a model-side error has to surface as the model's own error, not as this layer's paraphrase of it. The body shape is the kserve v2 inference protocol's, which means the runtime decides it, not this API. The v2 model name defaults to the resource name — kserve's single-model convention — and a multi-model runtime selects one with the `model` query parameter.\n\nA model that exists but has no serving address yet answers 503 'not ready' rather than a confusing connection error: deployed is not the same as serving. Scoped to the caller's own tenant namespace from the validated org and project, so a name another tenant owns is simply a 404. The predictor's response body is read up to a fixed ceiling.","tags":["ml"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ml"}},"/v1/models":{"get":{"operationId":"get_v1_models","summary":"Returns the list of available models from the routing table.","description":"Returns the list of available models from the routing table.\n\nPUBLIC BY DESIGN, AND IT DOES NOT AUTHENTICATE — that is the whole contract, so it\nis stated here rather than left to be inferred. The catalogue is the same for\neveryone (listAvailableModels takes no principal), docs.hanzo.ai fetches it from\nthe browser, and every policy layer around it already says so out loud: the authz\nfilter lists \"models\" as public, filter_balance refuses to gate it (a 402 here was\na console-wide outage), the rate limiter excludes it, and cloud's spend.Reachable\ncarries /v1/models/ as \"the model catalog the shell reads for discovery\".\n\nSO THE Authorization HEADER IS NOT AN ADMISSION CHECK HERE. It is read for ONE\nthing — annotating gated SKUs with the caller's own access standing — and\nannotation degrades to nothing when there is no verified principal.\n\nIt used to hold a \"require authentication\" gate that authenticated nobody: it\nrejected an ABSENT credential and a MALFORMED one, then accepted any string that\nmerely looked like a key. `Bearer sk-` followed by 36 zeroes returned 200 in\nproduction; so did a JWT three days expired. It was a shape check wearing an auth\ncheck's clothes, and its cost was diagnostic: /v1/models is the natural \"is my auth\nworking?\" probe, and answering 200 to a dead credential sent people debugging the\nwrong system. A public endpoint must not appear to validate. Either check the\ncredential or ignore it — this one ignores it, deliberately and visibly.\n\nRemoving that gate discloses nothing new: the catalogue was already reachable by\nanyone willing to type three characters, so there is no confidentiality delta, only\nan honesty one.","tags":["models"],"x-app":"ai"}},"/v1/models/providers":{"get":{"operationId":"get_v1_models_providers","summary":"Public, secret-free list of the providers serving the models that GET /v1/models lists — the same source, projected.","description":"Public, secret-free list of the providers serving the models that\nGET /v1/models lists — the same source, projected. Safe unauthenticated: no\nkeys, URLs, or config are returned, and it reports a SET of names, never\nwhich provider serves which model.","tags":["models"],"x-app":"ai"}},"/v1/models/{model}/access":{"get":{"operationId":"get_v1_models_by_model_access","summary":"Returns the caller's own standing for a gated model: \"granted\", \"requested\", or empty when they have never asked.","description":"Returns the caller's own standing for a gated model:\n\"granted\", \"requested\", or empty when they have never asked.","tags":["models"],"parameters":[{"name":"model","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai"},"post":{"operationId":"post_v1_models_by_model_access","summary":"Records the caller's waitlist request for a gated model and answers their new standing.","description":"Records the caller's waitlist request for a gated model and\nanswers their new standing. Authed, idempotent, and self-scoped: the row is keyed\nto the caller's own org and identity, never to a body-supplied owner.","tags":["models"],"parameters":[{"name":"model","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai"}},"/v1/mq/health":{"get":{"operationId":"get_v1_mq_health","summary":"Reports whether the message plane behind this surface answers.","description":"Reports whether the message plane behind this surface answers.","tags":["mq"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/info":{"get":{"operationId":"get_v1_mq_info","summary":"Returns the broker's identity and the org's stream count.","description":"Returns the broker's identity and the org's stream count.","tags":["mq"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/infoOut"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/streams":{"get":{"operationId":"get_v1_mq_streams","summary":"Returns the org's streams, name-ordered, with their live state.","description":"Returns the org's streams, name-ordered, with their live state.","tags":["mq"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the streams returned (1–1000, default 100).","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Offset skips that many streams, name-ordered.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Streams"}}},"description":"ok"}},"x-app":"mq"},"post":{"operationId":"post_v1_mq_streams","summary":"Creates a durable stream in the org's namespace and returns it.","description":"Creates a durable stream in the org's namespace and returns it.","tags":["mq"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Config"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stream"}}},"description":"created"}},"x-app":"mq"}},"/v1/mq/streams/{name}":{"delete":{"operationId":"delete_v1_mq_streams_by_name","summary":"Removes a stream with all its messages and consumers.","description":"Removes a stream with all its messages and consumers. Irreversible.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"mq"},"get":{"operationId":"get_v1_mq_streams_by_name","summary":"Returns one stream's configuration and live state.","description":"Returns one stream's configuration and live state.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stream"}}},"description":"ok"}},"x-app":"mq"},"put":{"operationId":"put_v1_mq_streams_by_name","summary":"Reconfigures an existing stream; the path names the stream, and the immutable fields (storage, retention) must restate what they are.","description":"Reconfigures an existing stream; the path names the stream, and the\nimmutable fields (storage, retention) must restate what they are.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, unique within the org (alphanumeric, hyphens, underscores).","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Config"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stream"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/streams/{name}/messages":{"get":{"operationId":"get_v1_mq_streams_by_name_messages","summary":"Reads stored messages without a consumer: by sequence, by newest on a subject, or walking a subject forward from a sequence.","description":"Reads stored messages without a consumer: by sequence, by newest on a\nsubject, or walking a subject forward from a sequence.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, from the path.","schema":{"type":"string"}},{"name":"seq","in":"query","required":false,"description":"Seq reads the message at this sequence (with next_by_subject: the walk's start).","schema":{"type":"integer"}},{"name":"last_by_subject","in":"query","required":false,"description":"LastBySubject reads the newest message on this org-relative subject.","schema":{"type":"string"}},{"name":"next_by_subject","in":"query","required":false,"description":"NextBySubject walks forward from seq collecting messages on this org-relative subject (wildcards supported).","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps a next_by_subject walk (1–1000, default 100).","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/readOut"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/streams/{name}/messages/{seq}":{"delete":{"operationId":"delete_v1_mq_streams_by_name_messages_by_seq","summary":"Erases one message by sequence; the sequence gap remains.","description":"Erases one message by sequence; the sequence gap remains.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, from the path.","schema":{"type":"string"}},{"name":"seq","in":"path","required":true,"description":"Seq is the message's stream sequence, from the path.","schema":{"type":"integer"}}],"responses":{"204":{"description":"no content"}},"x-app":"mq"}},"/v1/mq/streams/{name}/purge":{"post":{"operationId":"post_v1_mq_streams_by_name_purge","summary":"Removes messages from a stream, leaving its consumers in place.","description":"Removes messages from a stream, leaving its consumers in place.","tags":["mq"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the stream name, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purge"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/purgeOut"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/streams/{stream}/consumers":{"get":{"operationId":"get_v1_mq_streams_by_stream_consumers","summary":"Returns a stream's consumers, name-ordered, with delivery state.","description":"Returns a stream's consumers, name-ordered, with delivery state.","tags":["mq"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream name, from the path.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the consumers returned (1–1000, default 100).","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Offset skips that many consumers, name-ordered.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pickOut"}}},"description":"ok"}},"x-app":"mq"},"post":{"operationId":"post_v1_mq_streams_by_stream_consumers","summary":"Creates a durable pull consumer on a stream and returns it.","description":"Creates a durable pull consumer on a stream and returns it.","tags":["mq"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream name, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/makeIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Consumer"}}},"description":"created"}},"x-app":"mq"}},"/v1/mq/streams/{stream}/consumers/{name}":{"delete":{"operationId":"delete_v1_mq_streams_by_stream_consumers_by_name","summary":"Removes a consumer and its delivery state; unacknowledged messages stay in the stream.","description":"Removes a consumer and its delivery state; unacknowledged messages\nstay in the stream.","tags":["mq"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream name, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer name, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"mq"},"get":{"operationId":"get_v1_mq_streams_by_stream_consumers_by_name","summary":"Returns one consumer's configuration and delivery state.","description":"Returns one consumer's configuration and delivery state.","tags":["mq"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream name, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Consumer"}}},"description":"ok"}},"x-app":"mq"}},"/v1/mq/streams/{stream}/consumers/{name}/next":{"post":{"operationId":"post_v1_mq_streams_by_stream_consumers_by_name_next","summary":"Pulls the consumer's next batch.","description":"Pulls the consumer's next batch. Delivered messages are acknowledged on\ndelivery — the broker will not redeliver what this call returns; an empty\nwait answers 408.","tags":["mq"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream name, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer name, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/nextIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/readOut"}}},"description":"ok"}},"x-app":"mq"}},"/v1/networks":{"get":{"operationId":"get_v1_networks","summary":"Returns the caller's org overlay network on the Zero Trust fabric.","description":"Returns the caller's org overlay network on the Zero Trust fabric.\n\nThe org has at most ONE overlay, projected from the edge-routers tagged with its\n\"org-\u003corg\u003e\" role attribute: nodes is the real router count and status is\n\"connected\" once at least one router has dialed home, \"provisioning\" while none\nhas. An org with no routers gets an empty list, never a fabricated network.\n\nThe read degrades rather than erroring: a deployment with no ZT credential, and a\ncontroller that cannot be reached, both answer 200 with an empty list so the\nconsole's Networks page renders a clean empty state instead of an error.","tags":["networks"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/networkList"}}},"description":"ok"}},"x-app":"zt"}},"/v1/networks/routers":{"get":{"operationId":"get_v1_networks_routers","summary":"Returns the Zero Trust routers the caller's org owns.","description":"Returns the Zero Trust routers the caller's org owns.\n\nOne row per real ZT edge-router tagged with the org's \"org-\u003corg\u003e\" role attribute,\ncarrying the controller's own health signal: \"online\" when connected, \"disabled\"\nwhen administratively disabled, \"offline\" otherwise. region is filled only from a\n\"region-\u003cslug\u003e\" role attribute and omitted when the router carries none, so the\ncolumn renders \"—\" rather than a guess.\n\nThe read degrades rather than erroring: a deployment with no ZT credential, and a\ncontroller that cannot be reached, both answer 200 with an empty list.","tags":["networks"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/routerList"}}},"description":"ok"}},"x-app":"zt"}},"/v1/networks/{id}":{"get":{"operationId":"get_v1_networks_by_id","summary":"Returns one overlay network by id, scoped to the caller's org.","description":"Returns one overlay network by id, scoped to the caller's org.\n\nThe org has exactly one overlay network and its id is derived from the org, so\nany other id — another tenant's, or one that does not exist — is 404 rather than\na peek across the tenant boundary. An org whose network exists but has no\nedge-routers is 404 too, for the same reason the list is empty: there is no\noverlay until something is on it.","tags":["networks"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the network id from the path. The URL is the addressing authority, so\nit binds from there whatever else the request carries.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/networkView"}}},"description":"ok"}},"x-app":"zt"}},"/v1/notify/health":{"get":{"operationId":"get_v1_notify_health","summary":"Reports that the notify send surface is mounted.","description":"Reports that the notify send surface is mounted.\n\nIt is a pure liveness probe: it answers 200 whenever this subsystem is mounted\nand checks nothing downstream, so an \"ok\" here says the routes are reachable, not\nthat any provider credential is configured. The body is notifyd's verbatim, so\nprobes and clients that keyed on the standalone service keep working unchanged.","tags":["notify"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/notifyHealth"}}},"description":"ok"}},"x-app":"notify"}},"/v1/notify/send":{"post":{"operationId":"post_v1_notify_send","summary":"Delivers one transactional message by email or SMS through the caller org's own provider credential.","description":"Delivers one transactional message by email or SMS through the caller\norg's own provider credential.\n\nThe channel comes from the body — sms or email — and the provider credential is\nread from KMS at orgs/\u003corg\u003e/notify/\u003cservice\u003e/\u003ckey\u003e, never from the environment.\nThe org is the validated principal's, never a client-supplied value, so a caller\ncan only ever send as their own tenant; an unauthenticated caller gets 401.\nNaming no provider picks the one whose credentials are actually configured\n(Twilio, then Plivo for SMS; Twilio Email, then SMTP for email) and fails closed\nwhen none is. Delivery is synchronous and per recipient: one recipient answers\nthe bare {message_id,status} outcome, several answer the {items:[…]} envelope. A\nterminal provider failure is a 200 whose status is failed with the reason in\nerror, never a transport error. sync=true is REQUIRED — an async dispatch\nanswers 503, because the queue plane that would run it is owned elsewhere. The\nmessage body wins verbatim when present; otherwise template_id (or the event\nname) selects a built-in template rendered against template_vars.","tags":["notify"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/notifySend"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"notify"}},"/v1/notify/send/email":{"post":{"operationId":"post_v1_notify_send_email","summary":"Delivers one transactional email through the caller org's own provider credential.","description":"Delivers one transactional email through the caller org's own\nprovider credential.\n\nIt is the channel-pinned form of the generic send: identical in every respect\nexcept that the channel is fixed to email, OVERRIDING whatever the body names —\nso a body that says sms still goes out as mail. The provider is the org's own\nemail credential from KMS (Twilio Email, then SMTP), resolved for the validated\nprincipal's org; an unauthenticated caller gets 401. Subject is carried on the\nemail channel only.","tags":["notify"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/notifySend"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"notify"}},"/v1/notify/send/sms":{"post":{"operationId":"post_v1_notify_send_sms","summary":"Delivers one transactional SMS through the caller org's own provider credential.","description":"Delivers one transactional SMS through the caller org's own provider\ncredential.\n\nIt is the channel-pinned form of the generic send: identical in every respect\nexcept that the channel is fixed to sms, OVERRIDING whatever the body names —\nso a body that says email still goes out as a text message. The provider is the\norg's own SMS credential from KMS (Twilio, then Plivo), resolved for the\nvalidated principal's org; an unauthenticated caller gets 401.","tags":["notify"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/notifySend"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"notify"}},"/v1/o11y/alerts":{"get":{"operationId":"GetAlerts","summary":"Returns the org's current alerts.","description":"Returns the org's current alerts. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAlertsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/alerts/last":{"get":{"operationId":"get_v1_o11y_alerts_last","summary":"Replay the alert records this process took","description":"Answers the most recent Alertmanager deliveries THIS process received, as plain text — one greppable `ALERT-RECEIVED` line per alert, followed by the `ALERT-DELIVERED` / `ALERT-UNDELIVERED` outcome of carrying it out of the process, newest last, so piping to `tail` reads in arrival order. `(none)` when nothing has landed.\n\nArrival and delivery are separate lines because they are separate facts that fail independently. Alertmanager can tell you it dispatched a notification, never that anything received it; this process taking the call says nothing about whether a human was reached. Reading only the first as if it were the second is how a pager stays silent for months behind a log where everything looks fine.\n\nThe ring is PROCESS-LOCAL and bounded to the last 200 lines. Both are the point: a record that outlived the process that took the call would be a claim about something nobody observed, and an unbounded log is a memory leak with a nice name. A restart empties it.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/alerts/{receiver}":{"post":{"operationId":"post_v1_o11y_alerts_by_receiver","summary":"Take an Alertmanager notification and page a human","description":"Records one Alertmanager webhook delivery and pages the on-call. Each alert prints an `ALERT-RECEIVED` line and joins the replay ring, then the batch is carried out of the process by the egress chain: the org's KMS-custodied Slack bot token first (the ONE product Slack egress, not a second webhook credential), falling back to a plain POST to `CLOUD_ALERTS_WEBHOOK_URL` — which needs no Slack connection and so works in exactly the state that silences the first. Resolved notifications page too: \"it recovered\" is the half of an incident people are actually waiting for.\n\nTHE STATUS CODE REPORTS DELIVERY, NOT ARRIVAL. 200 `ok` means an egress accepted the batch. If none did — including when none is configured at all — it answers **503** naming the failure, so Alertmanager retries and counts it in `alertmanager_notifications_failed_total`. An alert nobody could be told about must never answer the same way as one that was delivered.\n\nA body that will not parse is still recorded (with empty fields) rather than rejected: the delivery happened, which is the fact being recorded, and a 400 would make Alertmanager retry a malformed payload forever.\n\nThe receiver segment is Alertmanager's own receiver name, a parameter rather than a hand-listed route because the receiver set is config, not code.","tags":["o11y"],"parameters":[{"name":"receiver","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/o11y/api/{project_id}/envelope/":{"post":{"operationId":"post_v1_o11y_api_by_project_id_envelope","summary":"Receive a Sentry envelope on the SDK's own DSN path","description":"Accepts an application/x-sentry-envelope frame from a Sentry SDK — the batched wire format carrying events, sessions and attachments — and ingests it against the project named in the path.\n\nTHE /api/ SEGMENT IS NOT OURS TO NAME. An SDK appends its own fixed /api/\u003cproject\u003e/envelope/ suffix to whatever DSN it is given, so this address is the SDK's, received verbatim. We receive this shape; we do not publish it. The clean spelling of the same wire is /v1/sentry/{project}/envelope/.\n\nAUTHENTICATED BY THE DSN PUBLIC KEY, never a Hanzo session, and therefore exempt from the principal gate: the ingest verifier checks the key in constant time, fails closed, and derives the org from it. A keyless submission is a 401 from that verifier — not a 403 from the gate, and not a 404 — which is how you tell the hops apart. The exemption is matched by method plus prefix plus suffix, never a bare prefix, so no read is reachable through it.","tags":["o11y"],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/o11y/api/{project_id}/store/":{"post":{"operationId":"post_v1_o11y_api_by_project_id_store","summary":"Receive a single Sentry event on the SDK's own DSN path","description":"The legacy single-event form of the envelope ingest: one JSON event rather than a framed batch, kept because SDKs in the field still send it.\n\nSame address ownership and same authentication as the envelope route — the /api/ segment is the SDK's, the DSN public key is the credential, the principal gate does not apply, and a keyless submission is a 401 from the ingest verifier.","tags":["o11y"],"parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/o11y/authz/check":{"post":{"operationId":"AuthzCheck","summary":"Evaluates a batch of transactions — relation plus object — for the authenticated caller and answers each with its authorization verdict, in the order they were asked.","description":"Evaluates a batch of transactions — relation plus object — for\nthe authenticated caller and answers each with its authorization verdict, in\nthe order they were asked. It is the read a UI uses to decide which controls\nto show.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yTransaction"},"type":"array"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCheckOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/auto_complete/attribute_values":{"post":{"operationId":"post_v1_o11y_auto_complete_attribute_values","summary":"Reads the attribute-value request from the body rather than off the query string — the spelling the newer builder uses to send its filters alongside the request.","description":"Reads the attribute-value request from the body rather\nthan off the query string — the spelling the newer builder uses to send its\nfilters alongside the request.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.FilterAttributeValueRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/autocomplete/aggregate_attributes":{"get":{"operationId":"get_v1_o11y_autocomplete_aggregate_attributes","summary":"Lists the attributes usable as an aggregate target for the given telemetry and operator — what a filter builder offers after the aggregation is chosen.","description":"Lists the attributes usable as an aggregate target for\nthe given telemetry and operator — what a filter builder offers after the\naggregation is chosen.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the attributes come from — traces, logs,\nmetrics or meter. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the attribute will be used under, e.g.\ncount, avg, sum. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the attributes to those containing it.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many attributes come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAggregateAttributesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/autocomplete/attribute_keys":{"get":{"operationId":"get_v1_o11y_autocomplete_attribute_keys","summary":"Lists the attribute keys available for filtering the given telemetry, each with its data type and whether it is a materialized column.","description":"Lists the attribute keys available for filtering the given\ntelemetry, each with its data type and whether it is a materialized column.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — traces, logs, metrics or\nmeter. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under. The\nruntime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/autocomplete/attribute_values":{"get":{"operationId":"get_v1_o11y_autocomplete_attribute_values","summary":"Lists the values one attribute key has taken — string, number and bool values in their own lists — for completing a filter.","description":"Lists the values one attribute key has taken — string,\nnumber and bool values in their own lists — for completing a filter.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — traces, logs or\nmetrics. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under. The\nruntime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64, float64\nor bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/availability":{"get":{"operationId":"get_v1_o11y_availability","summary":"Reports how much of the Hanzo fleet is up — the current per-service inventory plus an up-versus-reporting trend across the window.","description":"Reports how much of the Hanzo fleet is up — the current\nper-service inventory plus an up-versus-reporting trend across the window.\nBoth come from the fleet prober's own measurements: every service is asked its\nhealth URL every 30 seconds, so a service is listed as down because it did not\nanswer, never because something failed to collect it. PLATFORM SUDO ONLY —\nthis is the whole fleet's inventory, not tenant data, so every customer is\n403. An unreachable telemetry store answers 503 rather than an empty trend,\nbecause a board of zeroes and a fleet that is down look identical.","tags":["o11y"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the trend window in seconds. Default 3600, capped at 604800 (7d).","schema":{"type":"integer"},"example":3600},{"name":"stepSec","in":"query","required":false,"description":"StepSec is the bucket width in seconds, clamped to [30, 3600]. Absent\npicks ~60 buckets across the range.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.availabilityResponse"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/channels":{"get":{"operationId":"ListChannels","summary":"Lists the org's notification channels.","description":"Lists the org's notification channels. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yChannelsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateChannel","summary":"Creates a notification channel, answering with the stored channel.","description":"Creates a notification channel, answering with the stored\nchannel. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableChannel"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yChannelOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/channels/test":{"post":{"operationId":"TestChannel","summary":"Sends a test notification to the posted receiver.","description":"Sends a test notification to the posted receiver. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.alertmanagertypes.Receiver"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/channels/{id}":{"delete":{"operationId":"DeleteChannelByID","summary":"Removes a notification channel, by id.","description":"Removes a notification channel, by id. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetChannelByID","summary":"Returns one notification channel, by id.","description":"Returns one notification channel, by id. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yChannelOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateChannelByID","summary":"Replaces a notification channel's receiver, by id.","description":"Replaces a notification channel's receiver, by id. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yChannelUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/cloud-integrations/{cloud_provider}/agent-check-in":{"post":{"operationId":"AgentCheckInDeprecated","summary":"Is the deployed agent's check-in on its original hyphenated path, kept for backward compatibility with agents already running.","description":"Is the deployed agent's check-in on its original\nhyphenated path, kept for backward compatibility with agents already\nrunning. Viewer gate — the agent's role is viewer.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAgentCheckInIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAgentCheckInOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/accounts":{"get":{"operationId":"ListAccounts","summary":"Lists the cloud-integration accounts connected for the given provider.","description":"Lists the cloud-integration accounts connected for the given\nprovider. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAccountsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateAccount","summary":"Connects a new cloud-integration account for the given provider from its posted config and credentials, answering with the account and the artifact the agent deploys to complete the connection.","description":"Connects a new cloud-integration account for the given\nprovider from its posted config and credentials, answering with the account\nand the artifact the agent deploys to complete the connection. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreateAccountIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreateAccountOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/accounts/check_in":{"post":{"operationId":"AgentCheckIn","summary":"Is the deployed agent's check-in — the path consistent with the account surface, reporting the agent's account and telemetry state so the connection can be tracked.","description":"Is the deployed agent's check-in — the path consistent with the\naccount surface, reporting the agent's account and telemetry state so the\nconnection can be tracked. Viewer gate — the agent's role is viewer.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAgentCheckInIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAgentCheckInOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}":{"delete":{"operationId":"DisconnectAccount","summary":"Tears down a connected account for the given provider, by id.","description":"Tears down a connected account for the given provider, by\nid. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetAccount","summary":"Returns one connected account for the given provider, by id.","description":"Returns one connected account for the given provider, by id. Admin\ngate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAccountOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateAccount","summary":"Changes a connected account's configuration for the given provider, by id.","description":"Changes a connected account's configuration for the given\nprovider, by id. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdateAccountIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}/services":{"get":{"operationId":"ListAccountServicesMetadata","summary":"Lists the services metadata for one connected account of the given provider, by account id.","description":"Lists the services metadata for one connected\naccount of the given provider, by account id. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServicesMetadataOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/accounts/{id}/services/{service_id}":{"get":{"operationId":"GetAccountService","summary":"Returns one service and its configuration for a connected account of the given provider, by account id and service id.","description":"Returns one service and its configuration for a connected\naccount of the given provider, by account id and service id. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"service_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateService","summary":"Changes a service's configuration for one connected account of the given provider, by account id and service id.","description":"Changes a service's configuration for one connected account of\nthe given provider, by account id and service id. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"service_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdateServiceIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/credentials":{"get":{"operationId":"GetConnectionCredentials","summary":"Returns the credentials the connecting agent needs to establish the cloud integration, for the given cloud provider.","description":"Returns the credentials the connecting agent needs\nto establish the cloud integration, for the given cloud provider. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCredentialsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/services":{"get":{"operationId":"ListServicesMetadata","summary":"Lists the services the given provider can collect from, optionally scoped to one cloud integration.","description":"Lists the services the given provider can collect from,\noptionally scoped to one cloud integration. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"cloud_integration_id","in":"query","required":false,"description":"CloudIntegrationID, when set, scopes the listing to one cloud integration.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServicesMetadataOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/cloud_integrations/{cloud_provider}/services/{service_id}":{"get":{"operationId":"GetService","summary":"Returns one service the given provider can collect from, by service id, optionally scoped to one cloud integration.","description":"Returns one service the given provider can collect from, by\nservice id, optionally scoped to one cloud integration. Admin gate.","tags":["o11y"],"parameters":[{"name":"cloud_provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"service_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"cloud_integration_id","in":"query","required":false,"description":"CloudIntegrationID, when set, scopes the service to one cloud integration.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/clusters/attribute_keys":{"get":{"operationId":"get_v1_o11y_clusters_attribute_keys","summary":"Lists the metric attribute keys Kubernetes clusters report, for building cluster filters.","description":"Lists the metric attribute keys Kubernetes clusters\nreport, for building cluster filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/clusters/attribute_values":{"get":{"operationId":"get_v1_o11y_clusters_attribute_values","summary":"Lists the values one cluster attribute key has taken, for building cluster filters.","description":"Lists the values one cluster attribute key has taken,\nfor building cluster filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/clusters/list":{"post":{"operationId":"post_v1_o11y_clusters_list","summary":"Lists Kubernetes clusters over a time range, each with its CPU and memory usage against allocatable capacity and its attributes; filterable, groupable and paginated.","description":"Lists Kubernetes clusters over a time range, each with its CPU\nand memory usage against allocatable capacity and its attributes;\nfilterable, groupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.ClusterListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yClusterListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/complete/google":{"get":{"operationId":"get_v1_o11y_complete_google","summary":"Complete a Google sign-in","description":"The callback Google redirects a user back to after they approve the sign-in. It exchanges the authorization code, establishes the session and answers 303 to the console.\n\nThe answer is a Location header and no body, which is why it is not a typed operation — declaring a JSON response for a redirect would publish a shape that does not exist and hide the header that is the entire point.\n\nUNAUTHENTICATED by necessity: it is how a caller GETS a principal, so requiring one would be circular. It is not an open door — the code it carries is single-use and verified against the provider.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/complete/oidc":{"get":{"operationId":"get_v1_o11y_complete_oidc","summary":"Complete a generic OIDC sign-in","description":"The callback any configured OIDC provider redirects back to. Same shape and same reasoning as the Google callback: the code is exchanged, the session is established, and the answer is a 303 to the console rather than a body.\n\nUNAUTHENTICATED by necessity — this is the act of obtaining a principal, and the provider's own code is what authenticates it.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/complete/saml":{"post":{"operationId":"post_v1_o11y_complete_saml","summary":"Complete a SAML sign-in","description":"The assertion consumer service: the identity provider POSTs its signed assertion here, and a valid one establishes the session and answers 303 to the console.\n\nA redirect, not a value, so it is not a typed operation. UNAUTHENTICATED by necessity and authenticated in fact by the assertion's signature, which is checked against the configured provider before any session exists.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/countErrors":{"post":{"operationId":"post_v1_o11y_counterrors","summary":"Counts the grouped exceptions in the query window for the caller's org.","description":"Counts the grouped exceptions in the query window for the caller's\norg.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorsCountIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"integer"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/daemonsets/attribute_keys":{"get":{"operationId":"get_v1_o11y_daemonsets_attribute_keys","summary":"Lists the metric attribute keys Kubernetes daemonsets report, for building daemonset filters.","description":"Lists the metric attribute keys Kubernetes daemonsets\nreport, for building daemonset filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/daemonsets/attribute_values":{"get":{"operationId":"get_v1_o11y_daemonsets_attribute_values","summary":"Lists the values one daemonset attribute key has taken, for building daemonset filters.","description":"Lists the values one daemonset attribute key has\ntaken, for building daemonset filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/daemonsets/list":{"post":{"operationId":"post_v1_o11y_daemonsets_list","summary":"Lists Kubernetes daemonsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available node counts, restarts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes daemonsets over a time range, each with the\nCPU and memory its pods used against request and limit, desired and\navailable node counts, restarts and attributes; filterable, groupable and\npaginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.DaemonSetListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDaemonSetListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/dashboard_views":{"get":{"operationId":"ListDashboardViews","summary":"Returns every saved view in the calling user's org.","description":"Returns every saved view in the calling user's org. Saved\nviews are shared org-wide.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardViewListOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateDashboardView","summary":"Persists the calling user's dashboard-listing state (query, sort, order) as a named, reusable view shared across the org.","description":"Persists the calling user's dashboard-listing state (query,\nsort, order) as a named, reusable view shared across the org.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardViewPostable"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardViewOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/dashboard_views/{id}":{"delete":{"operationId":"DeleteDashboardView","summary":"Removes a saved view.","description":"Removes a saved view. Saved views are shared org-wide.\nDeleting a non-existent view refuses with the runtime's not-found.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"put":{"operationId":"UpdateDashboardView","summary":"Replaces a saved view's name and data.","description":"Replaces a saved view's name and data. Saved views are shared\norg-wide.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the saved view id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardViewUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardViewOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/dashboards":{"get":{"operationId":"ListDashboardsV2","summary":"Returns a page of v2-shape dashboards for the org.","description":"Returns a page of v2-shape dashboards for the org. This is the\npure, user-independent list — it carries no pin state; use\ndashboardListForUserV2 for the personalized, pin-aware list. Supports a filter\nDSL (query), sort (updated_at/created_at/name), order (asc/desc), and\noffset-based pagination (limit/offset).\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"query","in":"query","required":false,"description":"Query is the filter DSL over dashboard columns and tags, e.g.\n`name:cpu source:user`. Empty lists everything.","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Sort is the sort field: updated_at, created_at or name. Empty sorts by\nupdated_at.","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order is the sort direction: asc or desc. Empty orders desc.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many dashboards come back. Zero means the default of 20;\nthe runtime caps it at 200.","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many dashboards to skip for pagination.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardListOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateDashboardV2","summary":"Creates a dashboard in the v2 format that follows the Perses spec and answers with the stored dashboard.","description":"Creates a dashboard in the v2 format that follows the Perses\nspec and answers with the stored dashboard.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardPostable"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/dashboards/{id}":{"delete":{"operationId":"DeleteDashboardV2","summary":"Deletes a v2-shape dashboard along with its tag relations.","description":"Deletes a v2-shape dashboard along with its tag relations.\nLocked dashboards are rejected.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetDashboardV2","summary":"Returns a v2-shape dashboard.","description":"Returns a v2-shape dashboard.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardOut"}}},"description":"ok"}},"x-app":"o11y"},"patch":{"operationId":"PatchDashboardV2","summary":"Applies an RFC 6902 JSON Patch to a v2-shape dashboard.","description":"Applies an RFC 6902 JSON Patch to a v2-shape dashboard. The\npatch is applied against the postable view (metadata, spec, tags), so individual\npanels, queries, variables, layouts or tags can be updated without re-sending the\nrest. Apply is lenient — remove on a missing path is a no-op and add creates any\nmissing parent objects — and the result is still validated. Locked dashboards are\nrejected. The request body is the bare JSON Patch operations array.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the dashboard id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardPatchIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateDashboardV2","summary":"Updates a v2-shape dashboard's metadata, spec and tag set.","description":"Updates a v2-shape dashboard's metadata, spec and tag set.\nThe name is immutable and locked dashboards are rejected.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the dashboard id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/dashboards/{id}/clone":{"post":{"operationId":"CloneDashboardV2","summary":"Clones an existing v2-shape dashboard.","description":"Clones an existing v2-shape dashboard. User and integration\ndashboards can be cloned; system dashboards are rejected. The clone keeps the\nsource's display name, panels and tags, but gets a freshly generated unique\ninternal name and is always created as an unlocked user dashboard owned by the\ncaller.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/dashboards/{id}/lock":{"delete":{"operationId":"UnlockDashboardV2","summary":"Unlocks a v2-shape dashboard.","description":"Unlocks a v2-shape dashboard. Only the dashboard's creator or\nan org admin may lock or unlock.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"put":{"operationId":"LockDashboardV2","summary":"Locks a v2-shape dashboard.","description":"Locks a v2-shape dashboard. Only the dashboard's creator or an\norg admin may lock or unlock.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/dashboards/{id}/public":{"delete":{"operationId":"DeletePublicDashboard","summary":"Deletes the public-sharing config and disables public sharing of a dashboard.","description":"Deletes the public-sharing config and disables public\nsharing of a dashboard.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetPublicDashboard","summary":"Returns the public-sharing config for a dashboard.","description":"Returns the public-sharing config for a dashboard.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreatePublicDashboard","summary":"Creates the public-sharing config for a dashboard and enables public sharing, answering with the new share's id.","description":"Creates the public-sharing config for a dashboard and\nenables public sharing, answering with the new share's id.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the dashboard id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardWriteIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIdentifiableOut"}}},"description":"created"}},"x-app":"o11y"},"put":{"operationId":"UpdatePublicDashboard","summary":"Updates the public-sharing config for a dashboard.","description":"Updates the public-sharing config for a dashboard.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the dashboard id from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardWriteIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/dependency_graph":{"post":{"operationId":"post_v1_o11y_dependency_graph","summary":"Returns the service dependency graph over the requested window: every parent→child edge observed, with call and error rates and latency percentiles per edge.","description":"Returns the service dependency graph over the requested\nwindow: every parent→child edge observed, with call and error rates and\nlatency percentiles per edge.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDependencyGraphIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yDependency"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/deployments/attribute_keys":{"get":{"operationId":"get_v1_o11y_deployments_attribute_keys","summary":"Lists the metric attribute keys Kubernetes deployments report, for building deployment filters.","description":"Lists the metric attribute keys Kubernetes\ndeployments report, for building deployment filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/deployments/attribute_values":{"get":{"operationId":"get_v1_o11y_deployments_attribute_values","summary":"Lists the values one deployment attribute key has taken, for building deployment filters.","description":"Lists the values one deployment attribute key has\ntaken, for building deployment filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/deployments/list":{"post":{"operationId":"post_v1_o11y_deployments_list","summary":"Lists Kubernetes deployments over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes deployments over a time range, each with the\nCPU and memory its pods used against request and limit, desired and\navailable replica counts, restarts and attributes; filterable, groupable and\npaginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.DeploymentListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeploymentListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/disks":{"get":{"operationId":"get_v1_o11y_disks","summary":"Lists the storage disks the datastore reports, with their names and types.","description":"Lists the storage disks the datastore reports, with their names and\ntypes.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yDisk"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/domains":{"get":{"operationId":"ListAuthDomains","summary":"Lists the org's auth domains — the email domains whose SSO configuration this org owns.","description":"Lists the org's auth domains — the email domains whose SSO\nconfiguration this org owns. Admin gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAuthDomainsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateAuthDomain","summary":"Claims an email domain for the org and configures how its users sign in; the answer is the new domain's id.","description":"Claims an email domain for the org and configures how its\nusers sign in; the answer is the new domain's id. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPostableAuthDomain"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreatedOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/domains/{id}":{"delete":{"operationId":"DeleteAuthDomain","summary":"Releases an email domain and discards its SSO configuration, by id.","description":"Releases an email domain and discards its SSO\nconfiguration, by id. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetAuthDomain","summary":"Returns one auth domain with its SSO configuration, by id.","description":"Returns one auth domain with its SSO configuration, by id.\nAdmin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAuthDomainOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateAuthDomain","summary":"Replaces one auth domain's SSO configuration, by id.","description":"Replaces one auth domain's SSO configuration, by id. Admin\ngate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdatableAuthDomain"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/downtime_schedules":{"get":{"operationId":"ListDowntimeSchedules","summary":"Lists all planned maintenance windows, optionally narrowed to the active ones or the recurring ones.","description":"Lists all planned maintenance windows, optionally\nnarrowed to the active ones or the recurring ones. Viewer gate.","tags":["o11y"],"parameters":[{"name":"active","in":"query","required":false,"description":"Active, when \"true\" or \"false\", keeps only the active or inactive windows.\nAbsent lists all.","schema":{"type":"string"}},{"name":"recurring","in":"query","required":false,"description":"Recurring, when \"true\" or \"false\", keeps only the recurring or one-off\nwindows. Absent lists all.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDowntimeSchedulesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateDowntimeSchedule","summary":"Creates a planned maintenance window, answering with the stored schedule.","description":"Creates a planned maintenance window, answering with\nthe stored schedule. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostablePlannedMaintenance"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDowntimeScheduleOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/downtime_schedules/{id}":{"delete":{"operationId":"DeleteDowntimeScheduleByID","summary":"Removes a planned maintenance window, by id.","description":"Removes a planned maintenance window, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetDowntimeScheduleByID","summary":"Returns one planned maintenance window, by id.","description":"Returns one planned maintenance window, by id. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDowntimeScheduleOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateDowntimeScheduleByID","summary":"Replaces a planned maintenance window, by id.","description":"Replaces a planned maintenance window, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDowntimeUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/errorFromErrorID":{"get":{"operationId":"get_v1_o11y_errorfromerrorid","summary":"Returns one exception instance and the span it happened on, by its error id within a group at a timestamp.","description":"Returns one exception instance and the span it happened on,\nby its error id within a group at a timestamp.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"timestamp","in":"query","required":true,"description":"Timestamp is the instance's time as a nanosecond epoch spelled as a\nstring. Required.","schema":{"type":"string"}},{"name":"groupID","in":"query","required":true,"description":"GroupID is the exception group the instance belongs to. Required.","schema":{"type":"string"}},{"name":"errorID","in":"query","required":false,"description":"ErrorID is the exception instance id. Required by errorFromErrorID and\nnextPrevErrorIDs; unused by errorFromGroupID.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorWithSpan"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/errorFromGroupID":{"get":{"operationId":"get_v1_o11y_errorfromgroupid","summary":"Returns the representative exception instance of a group at a timestamp, and the span it happened on.","description":"Returns the representative exception instance of a group at a\ntimestamp, and the span it happened on.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"timestamp","in":"query","required":true,"description":"Timestamp is the instance's time as a nanosecond epoch spelled as a\nstring. Required.","schema":{"type":"string"}},{"name":"groupID","in":"query","required":true,"description":"GroupID is the exception group the instance belongs to. Required.","schema":{"type":"string"}},{"name":"errorID","in":"query","required":false,"description":"ErrorID is the exception instance id. Required by errorFromErrorID and\nnextPrevErrorIDs; unused by errorFromGroupID.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorWithSpan"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/errortracking/issues":{"get":{"operationId":"get_v1_o11y_errortracking_issues","summary":"Lists the caller's org's grouped error issues (by fingerprint) with status, level, counts and first/last-seen.","description":"Lists the caller's org's grouped error issues (by\nfingerprint) with status, level, counts and first/last-seen.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status narrows to one lifecycle state: unresolved, resolved or ignored.","schema":{"type":"string"}},{"name":"level","in":"query","required":false,"description":"Level narrows to one severity, e.g. error, warning, info.","schema":{"type":"string"}},{"name":"environment","in":"query","required":false,"description":"Environment narrows to one deployment environment.","schema":{"type":"string"}},{"name":"serviceName","in":"query","required":false,"description":"ServiceName narrows to one reporting service.","schema":{"type":"string"}},{"name":"query","in":"query","required":false,"description":"Query narrows to issues whose text contains it.","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Sort orders the page, e.g. lastSeen, firstSeen, count.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many issues to skip. Zero starts at the first.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many issues come back. Zero means the default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorIssuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/errortracking/issues/{id}":{"get":{"operationId":"get_v1_o11y_errortracking_issues_by_id","summary":"Returns one grouped issue with its latest occurrence sample.","description":"Returns one grouped issue with its latest occurrence sample.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the issue id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorGettableIssueOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_errortracking_issues_by_id","summary":"Changes an issue's lifecycle — resolve, ignore, reopen or assign — and returns the updated issue.","description":"Changes an issue's lifecycle — resolve, ignore, reopen or\nassign — and returns the updated issue. Fields left unset are left unchanged.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the issue id.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorUpdateIssueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorIssueOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/event":{"post":{"operationId":"post_v1_o11y_event","summary":"Records one product-analytics event for the signed-in user — a track event with a name and free-form attributes.","description":"Records one product-analytics event for the signed-in user — a track\nevent with a name and free-form attributes.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yEventIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMessage"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/explorer/views":{"get":{"operationId":"get_v1_o11y_explorer_views","summary":"Lists the caller's org's saved explorer views, optionally narrowed to one source page, name or category.","description":"Lists the caller's org's saved explorer views, optionally\nnarrowed to one source page, name or category.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"sourcePage","in":"query","required":false,"description":"SourcePage narrows the views to one source page, e.g. traces, logs.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows the views to one name.","schema":{"type":"string"}},{"name":"category","in":"query","required":false,"description":"Category narrows the views to one category.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewListOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_explorer_views","summary":"Saves a new explorer view for the caller's org and returns its id.","description":"Saves a new explorer view for the caller's org and returns its\nid.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.SavedView"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewCreateOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/explorer/views/{viewId}":{"delete":{"operationId":"delete_v1_o11y_explorer_views_by_viewid","summary":"Deletes one saved explorer view by id.","description":"Deletes one saved explorer view by id.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"viewId","in":"path","required":true,"description":"ViewID is the view's id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewDeleteOut"}}},"description":"ok"}},"x-app":"o11y"},"get":{"operationId":"get_v1_o11y_explorer_views_by_viewid","summary":"Returns one saved explorer view by id.","description":"Returns one saved explorer view by id.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"viewId","in":"path","required":true,"description":"ViewID is the view's id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"put_v1_o11y_explorer_views_by_viewid","summary":"Replaces one saved explorer view by id with the given view and echoes it back.","description":"Replaces one saved explorer view by id with the given view and\nechoes it back.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"viewId","in":"path","required":true,"description":"ViewID is the id of the view to replace, taken from the URL.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySavedViewOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/export_raw_data":{"post":{"operationId":"post_v1_o11y_export_raw_data","summary":"Export raw telemetry rows as a file","description":"Runs a query and returns its rows as a downloadable CSV or JSONL attachment, chunked, with a trailer that says whether the export completed — so a truncated download is detectable rather than silently short.\n\nThe answer is a file, not a value, which is why it is not a typed operation: the body is neither JSON nor bounded. Use the query operations when you want rows in a response.\n\nA validated, org-scoped principal is required and the export carries that principal's own tenant only.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/factor_password/forgot":{"post":{"operationId":"ForgotPassword","summary":"Starts the forgotten-password flow: the named user is mailed a reset link.","description":"Starts the forgotten-password flow: the named user is mailed\na reset link. Unauthenticated by design, and deliberately quiet about\nwhether the address exists.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yForgotPasswordIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/features":{"get":{"operationId":"get_v1_o11y_features","summary":"Returns the supported feature flags and their resolved values for the caller's org.","description":"Returns the supported feature flags and their resolved values for\nthe caller's org.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFeaturesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/fields/keys":{"get":{"operationId":"get_v1_o11y_fields_keys","summary":"Returns the telemetry field keys matching the selector — the signal's fields grouped by name, and whether the catalog is complete.","description":"Returns the telemetry field keys matching the selector — the\nsignal's fields grouped by name, and whether the catalog is complete.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"signal","in":"query","required":false,"description":"Signal is the telemetry to read the fields of — traces, logs or metrics.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source narrows the fields to one source within the signal.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back.","schema":{"type":"integer"}},{"name":"startUnixMilli","in":"query","required":false,"description":"StartUnixMilli is the window start as a unix millisecond epoch. Zero reads\nas unset.","schema":{"type":"integer"}},{"name":"endUnixMilli","in":"query","required":false,"description":"EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as\nunset.","schema":{"type":"integer"}},{"name":"fieldContext","in":"query","required":false,"description":"FieldContext narrows the keys to one context — resource, scope, attribute,\nspan, log or metric.","schema":{"type":"string"}},{"name":"fieldDataType","in":"query","required":false,"description":"FieldDataType narrows the keys to one data type.","schema":{"type":"string"}},{"name":"metricName","in":"query","required":false,"description":"MetricName narrows the keys to those on one metric.","schema":{"type":"string"}},{"name":"metricNamespace","in":"query","required":false,"description":"MetricNamespace narrows the keys to one metric namespace.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/fields/values":{"get":{"operationId":"get_v1_o11y_fields_values","summary":"Returns the values one telemetry field has taken — string, bool, number and related values — and whether the value list is complete.","description":"Returns the values one telemetry field has taken — string, bool,\nnumber and related values — and whether the value list is complete.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"signal","in":"query","required":false,"description":"Signal is the telemetry to read the field of — traces, logs or metrics.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source narrows the field to one source within the signal.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back.","schema":{"type":"integer"}},{"name":"startUnixMilli","in":"query","required":false,"description":"StartUnixMilli is the window start as a unix millisecond epoch. Zero reads\nas unset.","schema":{"type":"integer"}},{"name":"endUnixMilli","in":"query","required":false,"description":"EndUnixMilli is the window end as a unix millisecond epoch. Zero reads as\nunset.","schema":{"type":"integer"}},{"name":"fieldContext","in":"query","required":false,"description":"FieldContext narrows the field to one context.","schema":{"type":"string"}},{"name":"fieldDataType","in":"query","required":false,"description":"FieldDataType narrows the field to one data type.","schema":{"type":"string"}},{"name":"metricName","in":"query","required":false,"description":"MetricName narrows the field to one metric.","schema":{"type":"string"}},{"name":"metricNamespace","in":"query","required":false,"description":"MetricNamespace narrows the field to one metric namespace.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name is the field whose values to read.","schema":{"type":"string"}},{"name":"existingQuery","in":"query","required":false,"description":"ExistingQuery is the query the field appears in, so related values can be\nsuggested for it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/filter_suggestions":{"get":{"operationId":"get_v1_o11y_filter_suggestions","summary":"Suggests attribute keys and example filter queries for the query builder, seeded by what the org's own telemetry carries.","description":"Suggests attribute keys and example filter queries for the\nquery builder, seeded by what the org's own telemetry carries. Only the logs\ndata source is supported today.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":true,"description":"DataSource is the signal suggestions are drawn from; only logs is\nsupported today. Required.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows attribute suggestions to keys containing it.","schema":{"type":"string"}},{"name":"existingFilter","in":"query","required":false,"description":"ExistingFilter is the current filter set, JSON base64url-encoded, so\nexample queries build on it rather than repeat it.","schema":{"type":"string"}},{"name":"attributesLimit","in":"query","required":false,"description":"AttributesLimit caps how many attribute keys come back.","schema":{"type":"integer"}},{"name":"examplesLimit","in":"query","required":false,"description":"ExamplesLimit caps how many example queries come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFilterSuggestionsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/gateway/ingestion_keys":{"get":{"operationId":"GetIngestionKeys","summary":"Lists the workspace's ingestion keys, paginated.","description":"Lists the workspace's ingestion keys, paginated. Editor\ngate.","tags":["o11y"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page number.","schema":{"type":"integer"}},{"name":"per_page","in":"query","required":false,"description":"PerPage is the page size.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIngestionKeysOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateIngestionKey","summary":"Mints an ingestion key for the workspace, answering with the created key.","description":"Mints an ingestion key for the workspace, answering with\nthe created key. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableIngestionKey"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreatedIngestionKeyOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/gateway/ingestion_keys/limits/{limitId}":{"delete":{"operationId":"DeleteIngestionKeyLimit","summary":"Removes an ingestion key limit, by limit id.","description":"Removes an ingestion key limit, by limit id. Editor\ngate.","tags":["o11y"],"parameters":[{"name":"limitId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"patch":{"operationId":"UpdateIngestionKeyLimit","summary":"Changes an ingestion key limit, by limit id.","description":"Changes an ingestion key limit, by limit id. Editor\ngate.","tags":["o11y"],"parameters":[{"name":"limitId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdateLimitIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/gateway/ingestion_keys/search":{"get":{"operationId":"SearchIngestionKeys","summary":"Lists the workspace's ingestion keys whose name matches the search, paginated.","description":"Lists the workspace's ingestion keys whose name matches\nthe search, paginated. Editor gate.","tags":["o11y"],"parameters":[{"name":"name","in":"query","required":false,"description":"Name is the substring to match ingestion-key names against.","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page is the 1-based page number.","schema":{"type":"integer"}},{"name":"per_page","in":"query","required":false,"description":"PerPage is the page size.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIngestionKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/gateway/ingestion_keys/{keyId}":{"delete":{"operationId":"DeleteIngestionKey","summary":"Removes an ingestion key, by id.","description":"Removes an ingestion key, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"patch":{"operationId":"UpdateIngestionKey","summary":"Changes an ingestion key, by id.","description":"Changes an ingestion key, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdateIngestionKeyIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/gateway/ingestion_keys/{keyId}/limits":{"post":{"operationId":"CreateIngestionKeyLimit","summary":"Sets a signal limit on an ingestion key, by key id, answering with the created limit.","description":"Sets a signal limit on an ingestion key, by key id,\nanswering with the created limit. Editor gate.","tags":["o11y"],"parameters":[{"name":"keyId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreateLimitIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreatedLimitOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/getResetPasswordToken/{id}":{"get":{"operationId":"GetResetPasswordTokenDeprecated","summary":"Returns a user's password-reset token, creating one if none is live.","description":"Returns a user's password-reset token, creating one\nif none is live. Deprecated in favor of the reset_password_tokens pair,\nwhich separates reading from minting. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yResetTokenOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/global/config":{"get":{"operationId":"get_v1_o11y_global_config","summary":"Returns the deployment's global configuration: its public endpoints and which identity providers are enabled.","description":"Returns the deployment's global configuration: its public\nendpoints and which identity providers are enabled. Open by design — the\nsign-in page reads it before anyone is signed in.\n\nOpen by design; the runtime's own gate is OpenAccess.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yGlobalConfigOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/health":{"get":{"operationId":"get_v1_o11y_health","summary":"Reports service health.","description":"Reports service health. With live set, the datastore connection is\nchecked too and an unhealthy store refuses with 503.\n\nOpen by design; the runtime's own gate is OpenAccess.","tags":["o11y"],"parameters":[{"name":"live","in":"query","required":false,"description":"Live also checks the datastore connection; an unreachable store refuses\nwith 503.","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yHealthOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/healthz":{"get":{"operationId":"get_v1_o11y_healthz","summary":"Health of the observability runtime's services","description":"Reports whether every service in the runtime's registry is healthy, and names them grouped by state — so a failure says WHICH component is down, not merely that something is. An unhealthy registry answers 503, not a 200 with a false flag inside, so a plain status check cannot read a sick runtime as well.\n\nUNAUTHENTICATED by design, like the other two probes: it carries no tenant data and is reached by k8s and by external checks that hold no principal.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/hosts/attribute_keys":{"get":{"operationId":"get_v1_o11y_hosts_attribute_keys","summary":"Lists the metric attribute keys hosts report, for building host filters — each with its data type and whether it is a materialized column.","description":"Lists the metric attribute keys hosts report, for building\nhost filters — each with its data type and whether it is a materialized\ncolumn.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/hosts/attribute_values":{"get":{"operationId":"get_v1_o11y_hosts_attribute_values","summary":"Lists the values one host attribute key has taken, for building host filters — string, number and bool values in their own lists.","description":"Lists the values one host attribute key has taken, for\nbuilding host filters — string, number and bool values in their own lists.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/hosts/list":{"post":{"operationId":"post_v1_o11y_hosts_list","summary":"Lists monitored hosts over a time range, each with its CPU, memory, I/O wait and 15-minute load, whether it is actively reporting, its OS and its attributes; filterable, groupable and paginated.","description":"Lists monitored hosts over a time range, each with its CPU,\nmemory, I/O wait and 15-minute load, whether it is actively reporting, its\nOS and its attributes; filterable, groupable and paginated. The answer also\nsays whether any host metrics were received at all and which clusters and\nnodes sent them, so an empty page is distinguishable from a fleet that never\nreported.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.HostListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yHostListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/checks":{"get":{"operationId":"get_v1_o11y_infra_monitoring_checks","summary":"Reports whether the metrics and attributes an infra-monitoring section needs are being received — for each collector receiver or processor involved, what is present and what is missing, with a user-facing message and a docs link per missing piece.","description":"Reports whether the metrics and attributes an infra-monitoring\nsection needs are being received — for each collector receiver or processor\ninvolved, what is present and what is missing, with a user-facing message\nand a docs link per missing piece. Ready is true only when nothing is\nmissing.","tags":["o11y"],"parameters":[{"name":"type","in":"query","required":true,"description":"Type is the section to check — hosts, processes, pods, nodes,\ndeployments, daemonsets, statefulsets, jobs, namespaces, clusters or\nvolumes. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraChecksOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/clusters":{"post":{"operationId":"post_v1_o11y_infra_monitoring_clusters","summary":"Lists Kubernetes clusters with CPU and memory usage against allocatable capacity summed over their nodes, plus per-group node readiness and pod phase counts.","description":"Lists Kubernetes clusters with CPU and memory usage against\nallocatable capacity summed over their nodes, plus per-group node readiness\nand pod phase counts. Rows answer as 'list' under the default\nk8s.cluster.name grouping or 'grouped_list' under a custom groupBy; a metric\nwith no data in the window answers -1. Filterable by expression, orderable\nby usage or allocatable, paginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableClusters"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraClustersOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/daemonsets":{"post":{"operationId":"post_v1_o11y_infra_monitoring_daemonsets","summary":"Lists Kubernetes daemonsets with the CPU and memory their pods used against request and limit, the latest desired and current scheduled NODE counts (node counts, not pod counts), and per-group pod phase counts.","description":"Lists Kubernetes daemonsets with the CPU and memory their\npods used against request and limit, the latest desired and current\nscheduled NODE counts (node counts, not pod counts), and per-group pod phase\ncounts. Rows answer as 'list' under the default k8s.daemonset.name grouping\nor 'grouped_list' under a custom groupBy; a metric with no data in the\nwindow answers -1. Filterable by expression, orderable by the pod metrics or\nthe node counts, paginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableDaemonSets"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraDaemonSetsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/deployments":{"post":{"operationId":"post_v1_o11y_infra_monitoring_deployments","summary":"Lists Kubernetes deployments with the CPU and memory their pods used against request and limit, the latest desired and available replica counts, and per-group pod phase counts.","description":"Lists Kubernetes deployments with the CPU and memory their\npods used against request and limit, the latest desired and available\nreplica counts, and per-group pod phase counts. Rows answer as 'list' under\nthe default k8s.deployment.name grouping or 'grouped_list' under a custom\ngroupBy; a metric with no data in the window answers -1. Filterable by\nexpression, orderable by the pod metrics or the replica counts, paginated by\noffset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableDeployments"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraDeploymentsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/hosts":{"post":{"operationId":"post_v1_o11y_infra_monitoring_hosts","summary":"Lists hosts with key infrastructure metrics — CPU, memory, I/O wait and disk usage percentages and 15-minute load — plus an active/inactive status from whether the host reported in the last ten minutes.","description":"Lists hosts with key infrastructure metrics — CPU, memory, I/O\nwait and disk usage percentages and 15-minute load — plus an\nactive/inactive status from whether the host reported in the last ten\nminutes. Rows answer as 'list' under the default host.name grouping or\n'grouped_list' under a custom groupBy; a metric with no data in the window\nanswers -1. Filterable by expression and by status, orderable by any of the\nfive metrics, paginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableHosts"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraHostsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/jobs":{"post":{"operationId":"post_v1_o11y_infra_monitoring_jobs","summary":"Lists Kubernetes jobs with the CPU and memory their pods used against request and limit, the latest desired-successful, active, failed and successful pod counters, and per-group pod phase counts — the phase counts are current state while the counters are cumulative over the job's life.","description":"Lists Kubernetes jobs with the CPU and memory their pods used\nagainst request and limit, the latest desired-successful, active, failed and\nsuccessful pod counters, and per-group pod phase counts — the phase counts\nare current state while the counters are cumulative over the job's life.\nRows answer as 'list' under the default k8s.job.name grouping or\n'grouped_list' under a custom groupBy; a metric with no data in the window\nanswers -1. Filterable by expression, orderable by the pod metrics or the\njob counters, paginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableJobs"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraJobsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/namespaces":{"post":{"operationId":"post_v1_o11y_infra_monitoring_namespaces","summary":"Lists Kubernetes namespaces with the CPU and memory their pods used and per-group pod phase counts.","description":"Lists Kubernetes namespaces with the CPU and memory their\npods used and per-group pod phase counts. Rows answer as 'list' under the\ndefault k8s.namespace.name grouping or 'grouped_list' under a custom\ngroupBy, aggregating pods either way; a metric with no data in the window\nanswers -1. Filterable by expression, orderable by cpu or memory, paginated\nby offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableNamespaces"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraNamespacesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/nodes":{"post":{"operationId":"post_v1_o11y_infra_monitoring_nodes","summary":"Lists Kubernetes nodes with CPU and memory usage against allocatable capacity, per-group readiness counts and per-group phase counts for the pods scheduled on them.","description":"Lists Kubernetes nodes with CPU and memory usage against\nallocatable capacity, per-group readiness counts and per-group phase counts\nfor the pods scheduled on them. Rows answer as 'list' under the default\nk8s.node.name grouping (each row one node with its readiness condition) or\n'grouped_list' under a custom groupBy; a metric with no data in the window\nanswers -1. Filterable by expression, orderable by usage or allocatable,\npaginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableNodes"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraNodesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/pods":{"post":{"operationId":"post_v1_o11y_infra_monitoring_pods","summary":"Lists Kubernetes pods with CPU and memory usage against request and limit, the pod's phase and its age, plus its namespace, node, owning workload and cluster attributes.","description":"Lists Kubernetes pods with CPU and memory usage against request\nand limit, the pod's phase and its age, plus its namespace, node, owning\nworkload and cluster attributes. Rows answer as 'list' under the default\nk8s.pod.uid grouping (each row one pod) or 'grouped_list' under a custom\ngroupBy (each row aggregating its pods with per-phase counts); a metric with\nno data in the window answers -1. Filterable by expression, orderable by the\nsix pod metrics, paginated by offset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostablePods"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraPodsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/pvcs":{"post":{"operationId":"post_v1_o11y_infra_monitoring_pvcs","summary":"Lists Kubernetes persistent volume claims with available, capacity and used bytes and inode counts, plus the claim's pod, namespace, node, statefulset and cluster attributes.","description":"Lists Kubernetes persistent volume claims with available,\ncapacity and used bytes and inode counts, plus the claim's pod, namespace,\nnode, statefulset and cluster attributes. Rows answer as 'list' under the\ndefault k8s.persistentvolumeclaim.name grouping or 'grouped_list' under a\ncustom groupBy; a metric with no data in the window answers -1. Filterable\nby expression, orderable by the six volume metrics, paginated by offset and\nlimit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableVolumes"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraVolumesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_monitoring/statefulsets":{"post":{"operationId":"post_v1_o11y_infra_monitoring_statefulsets","summary":"Lists Kubernetes statefulsets with the CPU and memory their pods used against request and limit, the latest desired and current replica counts, and per-group pod phase counts.","description":"Lists Kubernetes statefulsets with the CPU and memory\ntheir pods used against request and limit, the latest desired and current\nreplica counts, and per-group pod phase counts. Rows answer as 'list' under\nthe default k8s.statefulset.name grouping or 'grouped_list' under a custom\ngroupBy; a metric with no data in the window answers -1. Filterable by\nexpression, orderable by the pod metrics or the replica counts, paginated by\noffset and limit.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableStatefulSets"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraStatefulSetsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/infra_onboarding/k8s/status":{"get":{"operationId":"get_v1_o11y_infra_onboarding_k8s_status","summary":"Reports how far Kubernetes infra onboarding has progressed: which metric families have arrived and, per pod, which required metadata labels are present.","description":"Reports how far Kubernetes infra onboarding has progressed:\nwhich metric families have arrived and, per pod, which required metadata\nlabels are present.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOnboardingOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/integrations":{"get":{"operationId":"ListIntegrations","summary":"Lists the available integrations and whether each is installed in the caller's org, optionally narrowed to installed or not-installed.","description":"Lists the available integrations and whether each is\ninstalled in the caller's org, optionally narrowed to installed or\nnot-installed. Viewer gate.","tags":["o11y"],"parameters":[{"name":"is_installed","in":"query","required":false,"description":"IsInstalled, when \"true\" or \"false\", keeps only integrations in that\ninstalled state; empty lists them all.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIntegrationsListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/integrations/install":{"post":{"operationId":"InstallIntegration","summary":"Installs an integration into the caller's org from its id and configuration, answering with the installed catalog item.","description":"Installs an integration into the caller's org from its id\nand configuration, answering with the installed catalog item. Viewer gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.InstallIntegrationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInstallOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/integrations/uninstall":{"post":{"operationId":"UninstallIntegration","summary":"Removes an integration from the caller's org by id.","description":"Removes an integration from the caller's org by id.\nViewer gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.UninstallIntegrationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIntegrationAck"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/integrations/{integrationId}":{"get":{"operationId":"GetIntegration","summary":"Returns one integration's full detail — its overview, configuration steps, collected data and assets — together with its installation record when the org has installed it.","description":"Returns one integration's full detail — its overview,\nconfiguration steps, collected data and assets — together with its\ninstallation record when the org has installed it. Viewer gate.","tags":["o11y"],"parameters":[{"name":"integrationId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yIntegrationOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/integrations/{integrationId}/connection_status":{"get":{"operationId":"GetIntegrationConnectionStatus","summary":"Reports whether the integration's logs and metrics have been received over the lookback window, so the console can show a live connection state.","description":"Reports whether the integration's logs and\nmetrics have been received over the lookback window, so the console can show\na live connection state. An integration that is not installed answers with an\nempty status rather than an error. Viewer gate.","tags":["o11y"],"parameters":[{"name":"integrationId","in":"path","required":true,"schema":{"type":"string"}},{"name":"lookback_seconds","in":"query","required":false,"description":"LookbackSeconds is how far back to look for received telemetry, in\nseconds.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yConnectionStatusOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/invite":{"post":{"operationId":"CreateInvite","summary":"Invites one person to the caller's org by email, with the role they will hold when they accept.","description":"Invites one person to the caller's org by email, with the role\nthey will hold when they accept. Deprecated in favor of creating users\ndirectly; kept because callers still hold it. Admin gate, enforced by the\nruntime this op relays to.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInviteIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInviteOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/invite/bulk":{"post":{"operationId":"CreateBulkInvite","summary":"Invites several people to the caller's org in one call, refusing the whole batch when any email repeats.","description":"Invites several people to the caller's org in one call,\nrefusing the whole batch when any email repeats. Deprecated alongside\ncreateInvite. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yBulkInviteIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAck"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/jobs/attribute_keys":{"get":{"operationId":"get_v1_o11y_jobs_attribute_keys","summary":"Lists the metric attribute keys Kubernetes jobs report, for building job filters.","description":"Lists the metric attribute keys Kubernetes jobs report, for\nbuilding job filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/jobs/attribute_values":{"get":{"operationId":"get_v1_o11y_jobs_attribute_values","summary":"Lists the values one job attribute key has taken, for building job filters.","description":"Lists the values one job attribute key has taken, for\nbuilding job filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/jobs/list":{"post":{"operationId":"post_v1_o11y_jobs_list","summary":"Lists Kubernetes jobs over a time range, each with the CPU and memory its pods used against request and limit, desired-successful, active, failed and successful pod counts, restarts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes jobs over a time range, each with the CPU and\nmemory its pods used against request and limit, desired-successful, active,\nfailed and successful pod counts, restarts and attributes; filterable,\ngroupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.JobListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yJobListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/licenses":{"get":{"operationId":"get_v1_o11y_licenses","summary":"Lists the org's licenses.","description":"Lists the org's licenses. This build has no enterprise edition, so\nthe list is intentionally empty.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLicensesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/licenses/active":{"get":{"operationId":"get_v1_o11y_licenses_active","summary":"Activates the enterprise license.","description":"Activates the enterprise license. This build has no\nenterprise edition, so the licensing provider refuses it as unsupported.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLicenseActiveOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/listErrors":{"post":{"operationId":"post_v1_o11y_listerrors","summary":"Lists the grouped exceptions in the query window — each an exception type with its message, count, service and first/last-seen — for the caller's org.","description":"Lists the grouped exceptions in the query window — each an\nexception type with its message, count, service and first/last-seen — for the\ncaller's org.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorsListIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yListError"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/livez":{"get":{"operationId":"get_v1_o11y_livez","summary":"Liveness of the observability process","description":"Answers 200 unconditionally while the process is running, and asserts NOTHING about the telemetry stores behind it. That is what makes it a liveness probe: a container that answers this is worth leaving alive, and restarting on a store outage would only remove the thing reporting the outage.\n\nUNAUTHENTICATED by design, and one of exactly three /v1/o11y paths that are. It carries no tenant data, and gating it would break the k8s probes and the external health checks without protecting anything. Use the health probe, not this one, to ask whether the runtime can actually serve.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/llm/annotation":{"get":{"operationId":"ListLLMAnnotations","summary":"Lists human annotations on traces and observations, optionally scoped to one review queue.","description":"Lists human annotations on traces and observations,\noptionally scoped to one review queue.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"query","required":false,"description":"TraceID narrows to annotations on one trace.","schema":{"type":"string"}},{"name":"queue","in":"query","required":false,"description":"Queue narrows to one review queue.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Status narrows to one review status, e.g. PENDING.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMAnnotationsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateLLMAnnotation","summary":"Adds a human annotation to a trace or observation, optionally in a review queue.","description":"Adds a human annotation to a trace or observation,\noptionally in a review queue.\n\nCallers need the editor role; the runtime's own gate enforces it, and it\nvalidates the payload and stamps the annotation's author and org.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMIngestAnnotation"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMAnnotationOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/llm/observations":{"get":{"operationId":"ListLLMObservations","summary":"Lists gen_ai spans as LLM observations — each an LLM call with its model, token counts, cost and latency projected from gen_ai.* attributes, newest first, over the query window.","description":"Lists gen_ai spans as LLM observations — each an LLM call with\nits model, token counts, cost and latency projected from gen_ai.* attributes,\nnewest first, over the query window.\n\nCallers need the viewer role; the runtime's own gate enforces it, and scopes\nthe read to the caller's validated tenant.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a unix-millisecond epoch. Zero means\n24h before the end.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a unix-millisecond epoch. Zero means now.","schema":{"type":"integer"}},{"name":"traceId","in":"query","required":false,"description":"TraceID narrows the view to one trace.","schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"description":"SessionID narrows the view to one conversation.","schema":{"type":"string"}},{"name":"userId","in":"query","required":false,"description":"UserID narrows the view to one end user.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows the view to observations of one name.","schema":{"type":"string"}},{"name":"model","in":"query","required":false,"description":"Model narrows the view to one model.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMObservationsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/llm/score/{id}":{"delete":{"operationId":"DeleteLLMScore","summary":"Hard-deletes a score by id.","description":"Hard-deletes a score by id.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetLLMScore","summary":"Returns a single score by id.","description":"Returns a single score by id.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMScoreOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/llm/scores":{"get":{"operationId":"ListLLMScores","summary":"Lists eval scores and human-feedback signals attached to traces and observations, newest first.","description":"Lists eval scores and human-feedback signals attached to traces\nand observations, newest first.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"query","required":false,"description":"TraceID narrows to scores on one trace.","schema":{"type":"string"}},{"name":"observationId","in":"query","required":false,"description":"ObservationID narrows to scores on one observation.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows to scores of one name.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source narrows to scores from one source, e.g. API, EVAL.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMScoresOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateLLMScore","summary":"Attaches an eval score or human-feedback signal to a trace or a single observation.","description":"Attaches an eval score or human-feedback signal to a trace or a\nsingle observation.\n\nCallers need the editor role; the runtime's own gate enforces it, and it\nvalidates the payload and stamps the score's author and org.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMIngestScore"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMScoreOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/llm/sessions":{"get":{"operationId":"ListLLMSessions","summary":"Lists conversations — gen_ai spans grouped by session.id, with their trace and observation counts, tokens and cost.","description":"Lists conversations — gen_ai spans grouped by session.id, with\ntheir trace and observation counts, tokens and cost.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a unix-millisecond epoch. Zero means\n24h before the end.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a unix-millisecond epoch. Zero means now.","schema":{"type":"integer"}},{"name":"traceId","in":"query","required":false,"description":"TraceID narrows the view to one trace.","schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"description":"SessionID narrows the view to one conversation.","schema":{"type":"string"}},{"name":"userId","in":"query","required":false,"description":"UserID narrows the view to one end user.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows the view to observations of one name.","schema":{"type":"string"}},{"name":"model","in":"query","required":false,"description":"Model narrows the view to one model.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMSessionsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/llm/traces":{"get":{"operationId":"ListLLMTraces","summary":"Lists LLM traces — gen_ai spans grouped by trace_id, with cost, tokens and latency rolled up across each trace.","description":"Lists LLM traces — gen_ai spans grouped by trace_id, with cost,\ntokens and latency rolled up across each trace.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a unix-millisecond epoch. Zero means\n24h before the end.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a unix-millisecond epoch. Zero means now.","schema":{"type":"integer"}},{"name":"traceId","in":"query","required":false,"description":"TraceID narrows the view to one trace.","schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"description":"SessionID narrows the view to one conversation.","schema":{"type":"string"}},{"name":"userId","in":"query","required":false,"description":"UserID narrows the view to one end user.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows the view to observations of one name.","schema":{"type":"string"}},{"name":"model","in":"query","required":false,"description":"Model narrows the view to one model.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMTracesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/llm/users":{"get":{"operationId":"ListLLMUsers","summary":"Lists end users — gen_ai spans grouped by user.id, with their session, trace and observation counts, tokens and cost.","description":"Lists end users — gen_ai spans grouped by user.id, with their\nsession, trace and observation counts, tokens and cost.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a unix-millisecond epoch. Zero means\n24h before the end.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a unix-millisecond epoch. Zero means now.","schema":{"type":"integer"}},{"name":"traceId","in":"query","required":false,"description":"TraceID narrows the view to one trace.","schema":{"type":"string"}},{"name":"sessionId","in":"query","required":false,"description":"SessionID narrows the view to one conversation.","schema":{"type":"string"}},{"name":"userId","in":"query","required":false,"description":"UserID narrows the view to one end user.","schema":{"type":"string"}},{"name":"name","in":"query","required":false,"description":"Name narrows the view to observations of one name.","schema":{"type":"string"}},{"name":"model","in":"query","required":false,"description":"Model narrows the view to one model.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMUsersOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/llm_pricing_rules":{"get":{"operationId":"ListLLMPricingRules","summary":"Returns the LLM pricing rules for the caller's org, with pagination and an optional search and override filter.","description":"Returns the LLM pricing rules for the caller's org, with\npagination and an optional search and override filter.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"q","in":"query","required":false,"description":"Search matches rules by model or provider.","schema":{"type":"string"}},{"name":"isOverride","in":"query","required":false,"description":"IsOverride, when \"true\" or \"false\", narrows to user-pinned rules or to\nsynced ones; empty returns both. It is a string because a query param is a\nstring on the wire, and the runtime reads absent as \"no filter\".","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rows to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rows come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMPricingRulesOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"CreateOrUpdateLLMPricingRules","summary":"Writes the pricing-rule batch — the single write endpoint used by both the user and the Zeus sync job.","description":"Writes the pricing-rule batch — the single write\nendpoint used by both the user and the Zeus sync job. Per-rule match is by id,\nthen sourceId, then insert; an override row is fully preserved when the\nrequest omits isOverride, only its synced_at stamped.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMUpdatablePricingRules"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/llm_pricing_rules/{id}":{"delete":{"operationId":"DeleteLLMPricingRule","summary":"Hard-deletes a pricing rule by id.","description":"Hard-deletes a pricing rule by id. If the rule was\nauto-synced, the next sync cycle recreates it.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetLLMPricingRule","summary":"Returns a single LLM pricing rule by id.","description":"Returns a single LLM pricing rule by id.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLLMPricingRuleOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs":{"get":{"operationId":"get_v1_o11y_logs","summary":"Returns the most recent log records in the query window, newest first — each record an open object carrying its nanosecond timestamp and whatever fields the record was ingested with.","description":"Returns the most recent log records in the query window, newest\nfirst — each record an open object carrying its nanosecond timestamp and\nwhatever fields the record was ingested with.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps how many records come back. Zero means the default of 100.","schema":{"type":"integer"}},{"name":"timestampStart","in":"query","required":false,"description":"TimestampStart is the start of the window as a nanosecond epoch. Zero\nmeans fifteen minutes before the end.","schema":{"type":"integer"}},{"name":"timestampEnd","in":"query","required":false,"description":"TimestampEnd is the end of the window as a nanosecond epoch. Zero means\nnow.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogRecordsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/aggregate":{"get":{"operationId":"get_v1_o11y_logs_aggregate","summary":"Returns the logs aggregate buckets for the query window.","description":"Returns the logs aggregate buckets for the query window. The\nruntime currently answers the empty set; the shape is the contract.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogAggregateOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/fields":{"get":{"operationId":"get_v1_o11y_logs_fields","summary":"Returns the log field catalog: the fields already selected as indexed columns, and the interesting ones seen in the data that could be.","description":"Returns the log field catalog: the fields already selected as\nindexed columns, and the interesting ones seen in the data that could be.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldCatalogOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_logs_fields","summary":"Changes how one log field is stored — selects or deselects it as a materialized column and tunes its index — and echoes the setting back.","description":"Changes how one log field is stored — selects or deselects it\nas a materialized column and tunes its index — and echoes the setting back.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldSetting"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldSetting"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/livetail":{"get":{"operationId":"get_v1_o11y_logs_livetail","summary":"Follow log records as they arrive","description":"Streams matching log records continuously instead of answering once, so a console tail shows lines as they land rather than at the end of a window.\n\nIt is a STREAM, which is why it is not a typed operation: there is no single complete value to name, and a generated client that waited for one would hang on the first tail. Read the bounded window with the log read instead when you want an answer rather than a feed.\n\nA validated, org-scoped principal is required and the feed carries that principal's own tenant only.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/logs/pipelines":{"post":{"operationId":"post_v1_o11y_logs_pipelines","summary":"Saves the given log parsing pipelines as the new config version for the caller's org and starts deploying it.","description":"Saves the given log parsing pipelines as the new config\nversion for the caller's org and starts deploying it. The set REPLACES the\ncurrent one: a pipeline left out of the request is dropped from the new\nversion, and an empty set drops them all.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPipelineCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPipelinesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/pipelines/preview":{"post":{"operationId":"post_v1_o11y_logs_pipelines_preview","summary":"Runs the given log parsing pipelines over the given sample records without saving anything, and returns the transformed records plus whatever the collector logged while simulating them.","description":"Runs the given log parsing pipelines over the given\nsample records without saving anything, and returns the transformed records\nplus whatever the collector logged while simulating them.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPipelinePreviewIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPipelinePreviewOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/pipelines/{version}":{"get":{"operationId":"get_v1_o11y_logs_pipelines_by_version","summary":"Returns the caller's org's log parsing pipelines at one config version — \"latest\" for the newest — along with that version's deployment record and the recent version history.","description":"Returns the caller's org's log parsing pipelines at one config\nversion — \"latest\" for the newest — along with that version's deployment\nrecord and the recent version history.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"version","in":"path","required":true,"description":"Version is the config version to read — a positive number, or \"latest\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPipelinesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/logs/promote_paths":{"get":{"operationId":"get_v1_o11y_logs_promote_paths","summary":"Lists the log body paths already promoted or indexed, with the indexes each carries.","description":"Lists the log body paths already promoted or indexed, with the\nindexes each carries.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPromotedOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_logs_promote_paths","summary":"Promotes and indexes log body paths: each named path is lifted out of the JSON body into its own column, with the indexes the caller asked for.","description":"Promotes and indexes log body paths: each named path is lifted\nout of the JSON body into its own column, with the indexes the caller asked\nfor. Paths must start with \"body.\".\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yLogPromotePath"},"type":"array"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogPromoteOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/consumer-lag/consumer-details":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_consumer-lag_consumer-details","summary":"Returns the consumer side of a consumer-lag view: the consumer groups reading the topic/partition named in variables, with their throughput and latency over the window.","description":"Returns the consumer side of a consumer-lag view: the\nconsumer groups reading the topic/partition named in variables, with their\nthroughput and latency over the window.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/consumer-lag/network-latency":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_consumer-lag_network-latency","summary":"Returns consumer network latency correlated per client: a throughput pass over the window finds the consumer clients, then their fetch latency joins in as a latency column per client/instance/service.","description":"Returns consumer network latency correlated per client:\na throughput pass over the window finds the consumer clients, then their\nfetch latency joins in as a latency column per client/instance/service.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/consumer-lag/producer-details":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_consumer-lag_producer-details","summary":"Returns the producer side of a consumer-lag view: the producers writing to the topic/partition named in variables, with their throughput and latency over the window.","description":"Returns the producer side of a consumer-lag view: the\nproducers writing to the topic/partition named in variables, with their\nthroughput and latency over the window.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/onboarding/consumers":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_onboarding_consumers","summary":"Checks whether the spans the Kafka consumer views need are arriving, row for row like producersOnboarding.","description":"Checks whether the spans the Kafka consumer views need\nare arriving, row for row like producersOnboarding.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueChecksOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/onboarding/kafka":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_onboarding_kafka","summary":"Checks whether Kafka's own metrics — consumer lag and partition telemetry — are arriving, so the lag views can be lit up.","description":"Checks whether Kafka's own metrics — consumer lag and\npartition telemetry — are arriving, so the lag views can be lit up.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueChecksOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/onboarding/producers":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_onboarding_producers","summary":"Checks whether the spans the Kafka producer views need are arriving — one row per required span attribute, with a pass/fail status and, on failure, what is missing from the instrumentation.","description":"Checks whether the spans the Kafka producer views need\nare arriving — one row per required span attribute, with a pass/fail status\nand, on failure, what is missing from the instrumentation.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueChecksOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/partition-latency/consumer":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_partition-latency_consumer","summary":"Returns the consumer-group latency detail for the topic and partition named in the request's variables.","description":"Returns the consumer-group latency detail for the\ntopic and partition named in the request's variables.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/partition-latency/overview":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_partition-latency_overview","summary":"Returns the per-partition latency overview for the window — each topic/partition with its throughput and latency profile.","description":"Returns the per-partition latency overview for the window —\neach topic/partition with its throughput and latency profile.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/span/evaluation":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_span_evaluation","summary":"Correlates producer and consumer spans over the evaluation window (eval_time bounds the scan) and returns the pairings with their end-to-end delay — the check that messages produced are being consumed.","description":"Correlates producer and consumer spans over the evaluation\nwindow (eval_time bounds the scan) and returns the pairings with their\nend-to-end delay — the check that messages produced are being consumed.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/topic-throughput/consumer":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_topic-throughput_consumer","summary":"Returns the consumer topic-throughput overview for the window — what each consumer group read, per topic.","description":"Returns the consumer topic-throughput overview for the\nwindow — what each consumer group read, per topic.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/topic-throughput/consumer-details":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_topic-throughput_consumer-details","summary":"Breaks one consumer topic's throughput down using the topic and service named in variables.","description":"Breaks one consumer topic's throughput down using\nthe topic and service named in variables.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/topic-throughput/producer":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_topic-throughput_producer","summary":"Returns the producer topic-throughput overview for the window — what each producer service wrote, per topic.","description":"Returns the producer topic-throughput overview for the\nwindow — what each producer service wrote, per topic.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/kafka/topic-throughput/producer-details":{"post":{"operationId":"post_v1_o11y_messaging-queues_kafka_topic-throughput_producer-details","summary":"Breaks one producer topic's throughput down using the topic and service named in variables.","description":"Breaks one producer topic's throughput down using\nthe topic and service named in variables.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/messaging-queues/queue-overview":{"post":{"operationId":"post_v1_o11y_messaging-queues_queue-overview","summary":"Lists the messaging destinations observed in the window — one row per queue/destination/service combination with its throughput and latency columns.","description":"Lists the messaging destinations observed in the window — one\nrow per queue/destination/service combination with its throughput and\nlatency columns. Filters narrow by queue system, destination, service or any\nspan attribute.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueListIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueueRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metric/metric_metadata":{"get":{"operationId":"get_v1_o11y_metric_metric_metadata","summary":"Serves the OLDER /metric/metric_metadata route.","description":"Serves the OLDER /metric/metric_metadata route. It is\nNOT the same op as metrics.go's metricMetadata (/metrics/metadata): different\npath, different input (this one also scopes by service). Two slices named one\nGo function for two routes; the route is the identity, so the name follows it.\nRenamed rather than merged — collapsing them would silently drop the service\nscope this one accepts.\nIt returns one metric's metadata — its type, unit, description,\ntemporality, monotonicity and histogram buckets — optionally scoped to the\nmetric as one service reports it.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":false,"description":"MetricName is the metric to read.","schema":{"type":"string"}},{"name":"serviceName","in":"query","required":false,"description":"ServiceName scopes the metadata to the metric as one service reports it.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricMetadataOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metric_reduction_rules":{"get":{"operationId":"ListMetricReductionRules","summary":"Lists the org's metric volume-control (label reduction) rules, pageable and sortable by name, volume or recency.","description":"Lists the org's metric volume-control (label reduction)\nrules, pageable and sortable by name, volume or recency.","tags":["o11y"],"parameters":[{"name":"orderBy","in":"query","required":false,"description":"OrderBy sorts the page: metric, ingested_volume, reduced_volume or\nlast_updated. Unset means ingested_volume.","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order is asc or desc. Unset means desc.","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Search narrows the page to rules whose metric name contains it.","schema":{"type":"string"}},{"name":"metricName","in":"query","required":false,"description":"MetricName narrows the page to one metric's rule.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many rules to skip, for paging.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many rules come back, at most 1000. Unset means 10.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleListOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateMetricReductionRule","summary":"Creates a volume-control rule for a metric and returns it with its id; a metric that already has a rule is refused.","description":"Creates a volume-control rule for a metric and returns\nit with its id; a metric that already has a rule is refused.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleCreateIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/metric_reduction_rules/preview":{"post":{"operationId":"PreviewMetricReductionRule","summary":"Estimates the series reduction and the dashboards and alerts a candidate volume-control rule would touch, without persisting it.","description":"Estimates the series reduction and the dashboards and\nalerts a candidate volume-control rule would touch, without persisting it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRulePreviewIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRulePreviewOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metric_reduction_rules/stats":{"get":{"operationId":"GetMetricReductionRuleStats","summary":"Returns total ingested vs retained series and samples and the estimated monthly savings across all volume-control rules.","description":"Returns total ingested vs retained series and samples and\nthe estimated monthly savings across all volume-control rules.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionStatsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metric_reduction_rules/timeseries":{"get":{"operationId":"GetMetricReductionRuleTimeseries","summary":"Returns ingested vs retained series over time across all volume-control rules, in hourly buckets, in the query-range time-series response shape.","description":"Returns ingested vs retained series over time across\nall volume-control rules, in hourly buckets, in the query-range time-series\nresponse shape.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionSeriesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metric_reduction_rules/{id}":{"delete":{"operationId":"DeleteMetricReductionRuleByID","summary":"Deletes a volume-control rule by its id.","description":"Deletes a volume-control rule by its id.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the rule's id.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetMetricReductionRuleByID","summary":"Returns one volume-control rule by its id.","description":"Returns one volume-control rule by its id.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the rule's id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateMetricReductionRuleByID","summary":"Updates the match type and labels of a volume-control rule by its id; the metric name is immutable.","description":"Updates the match type and labels of a volume-control rule\nby its id; the metric name is immutable.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the rule's id.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleSaveIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yReductionRuleOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics":{"get":{"operationId":"ListMetrics","summary":"Lists the distinct metric names seen in a time range, each with its description, type, unit, temporality and monotonicity.","description":"Lists the distinct metric names seen in a time range, each with\nits description, type, unit, temporality and monotonicity.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a Unix timestamp in milliseconds.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a Unix timestamp in milliseconds.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many metrics come back; unset means 100, at most 5000.","schema":{"type":"integer"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the page to metric names containing it.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source narrows the page by ingestion source.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/alerts":{"get":{"operationId":"GetMetricAlerts","summary":"Lists the alert rules that reference a metric.","description":"Lists the alert rules that reference a metric.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":true,"description":"MetricName is the metric's name; it may contain slashes, e.g.\nrun.googleapis.com/request_latencies. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricAlertsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/attributes":{"get":{"operationId":"GetMetricAttributes","summary":"Returns one metric's attribute keys, each with its unique values and their count.","description":"Returns one metric's attribute keys, each with its unique\nvalues and their count.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":true,"description":"MetricName is the metric's name; it may contain slashes. Required.","schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the start of the window as a Unix timestamp in milliseconds.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the end of the window as a Unix timestamp in milliseconds.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricAttributesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/dashboards":{"get":{"operationId":"GetMetricDashboardsV2","summary":"Lists the dashboard panels that reference a metric.","description":"Lists the dashboard panels that reference a metric.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":true,"description":"MetricName is the metric's name; it may contain slashes, e.g.\nrun.googleapis.com/request_latencies. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricDashboardsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/highlights":{"get":{"operationId":"GetMetricHighlights","summary":"Returns one metric's headline numbers: data points, total and active time series, and when it was last received.","description":"Returns one metric's headline numbers: data points, total\nand active time series, and when it was last received.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":true,"description":"MetricName is the metric's name; it may contain slashes, e.g.\nrun.googleapis.com/request_latencies. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricHighlightsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/inspect":{"post":{"operationId":"InspectMetrics","summary":"Returns one metric's raw time series over a window of at most thirty minutes — each series with its labels and timestamp/value pairs.","description":"Returns one metric's raw time series over a window of at most\nthirty minutes — each series with its labels and timestamp/value pairs.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricInspectIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricInspectOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/metadata":{"get":{"operationId":"GetMetricMetadata","summary":"Returns one metric's metadata: description, type, unit, temporality and monotonicity.","description":"Returns one metric's metadata: description, type, unit,\ntemporality and monotonicity.","tags":["o11y"],"parameters":[{"name":"metricName","in":"query","required":true,"description":"MetricName is the metric's name; it may contain slashes, e.g.\nrun.googleapis.com/request_latencies. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricMetadataOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"UpdateMetricMetadata","summary":"Updates one metric's metadata — description, type, unit, temporality, monotonicity — and answers with the bare success envelope.","description":"Updates one metric's metadata — description, type, unit,\ntemporality, monotonicity — and answers with the bare success envelope.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricMetadataSaveIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricAckOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/onboarding":{"get":{"operationId":"GetMetricsOnboardingStatus","summary":"Reports whether any non-O11y metrics have been ingested — the lightweight check onboarding polls.","description":"Reports whether any non-O11y metrics have been ingested —\nthe lightweight check onboarding polls.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricOnboardingOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/stats":{"post":{"operationId":"GetMetricsStats","summary":"Lists metrics with their sample and time-series counts for a time range — the volume view of the metrics explorer, pageable and sortable.","description":"Lists metrics with their sample and time-series counts for a\ntime range — the volume view of the metrics explorer, pageable and sortable.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricStatsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricStatsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/metrics/treemap":{"post":{"operationId":"GetMetricsTreemap","summary":"Returns the proportional distribution of metrics by sample count or time-series count, as the entries of a treemap.","description":"Returns the proportional distribution of metrics by sample\ncount or time-series count, as the entries of a treemap.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricTreemapIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricTreemapOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/namespaces/attribute_keys":{"get":{"operationId":"get_v1_o11y_namespaces_attribute_keys","summary":"Lists the metric attribute keys Kubernetes namespaces report, for building namespace filters.","description":"Lists the metric attribute keys Kubernetes namespaces\nreport, for building namespace filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/namespaces/attribute_values":{"get":{"operationId":"get_v1_o11y_namespaces_attribute_values","summary":"Lists the values one namespace attribute key has taken, for building namespace filters.","description":"Lists the values one namespace attribute key has\ntaken, for building namespace filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/namespaces/list":{"post":{"operationId":"post_v1_o11y_namespaces_list","summary":"Lists Kubernetes namespaces over a time range, each with the CPU and memory its pods used, their phase counts and its attributes; filterable, groupable and paginated.","description":"Lists Kubernetes namespaces over a time range, each with the\nCPU and memory its pods used, their phase counts and its attributes;\nfilterable, groupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.NamespaceListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yNamespaceListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/nextPrevErrorIDs":{"get":{"operationId":"get_v1_o11y_nextpreverrorids","summary":"Returns the ids of the exception instances immediately after and before a given one within its group — the paging cursor the error detail view walks.","description":"Returns the ids of the exception instances immediately after\nand before a given one within its group — the paging cursor the error detail\nview walks.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"timestamp","in":"query","required":true,"description":"Timestamp is the instance's time as a nanosecond epoch spelled as a\nstring. Required.","schema":{"type":"string"}},{"name":"groupID","in":"query","required":true,"description":"GroupID is the exception group the instance belongs to. Required.","schema":{"type":"string"}},{"name":"errorID","in":"query","required":false,"description":"ErrorID is the exception instance id. Required by errorFromErrorID and\nnextPrevErrorIDs; unused by errorFromGroupID.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yNextPrevErrorIDs"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/nodes/attribute_keys":{"get":{"operationId":"get_v1_o11y_nodes_attribute_keys","summary":"Lists the metric attribute keys Kubernetes nodes report, for building node filters.","description":"Lists the metric attribute keys Kubernetes nodes report,\nfor building node filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/nodes/attribute_values":{"get":{"operationId":"get_v1_o11y_nodes_attribute_values","summary":"Lists the values one node attribute key has taken, for building node filters.","description":"Lists the values one node attribute key has taken, for\nbuilding node filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/nodes/list":{"post":{"operationId":"post_v1_o11y_nodes_list","summary":"Lists Kubernetes nodes over a time range, each with its CPU and memory usage against allocatable capacity, readiness condition counts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes nodes over a time range, each with its CPU and\nmemory usage against allocatable capacity, readiness condition counts and\nattributes; filterable, groupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.NodeListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yNodeListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/org/preferences":{"get":{"operationId":"ListOrgPreferences","summary":"Lists every org-scoped preference, each with its current and default value.","description":"Lists every org-scoped preference, each with its current\nand default value. Admin gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPreferencesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/org/preferences/{name}":{"get":{"operationId":"GetOrgPreference","summary":"Returns one org-scoped preference, by name.","description":"Returns one org-scoped preference, by name. Admin gate.","tags":["o11y"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPreferenceOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateOrgPreference","summary":"Sets one org-scoped preference, by name.","description":"Sets one org-scoped preference, by name. Admin gate.","tags":["o11y"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdatablePreference"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/orgs/me":{"get":{"operationId":"GetMyOrganization","summary":"Returns the caller's own organization.","description":"Returns the caller's own organization. Admin gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOrganizationOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateMyOrganization","summary":"Rewrites the caller's own organization record — display name, name, alias — always addressed as \"me\", never by id.","description":"Rewrites the caller's own organization record — display name,\nname, alias — always addressed as \"me\", never by id. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOrganization"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/orgs/me/filters":{"get":{"operationId":"GetQuickFilters","summary":"Returns the org's quick filters for every signal — the attribute shortlists its explorers offer as one-click filters.","description":"Returns the org's quick filters for every signal — the\nattribute shortlists its explorers offer as one-click filters. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQuickFiltersOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateQuickFilters","summary":"Replaces the org's quick filters for one signal with the attribute list given.","description":"Replaces the org's quick filters for one signal with the\nattribute list given. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdatableQuickFilters"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/orgs/me/filters/{signal}":{"get":{"operationId":"GetSignalFilters","summary":"Returns the org's quick filters for one signal — traces, logs, metrics, exceptions or api_monitoring.","description":"Returns the org's quick filters for one signal — traces,\nlogs, metrics, exceptions or api_monitoring. Viewer gate.","tags":["o11y"],"parameters":[{"name":"signal","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySignalFiltersOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pods/attribute_keys":{"get":{"operationId":"get_v1_o11y_pods_attribute_keys","summary":"Lists the metric attribute keys Kubernetes pods report, for building pod filters.","description":"Lists the metric attribute keys Kubernetes pods report, for\nbuilding pod filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pods/attribute_values":{"get":{"operationId":"get_v1_o11y_pods_attribute_values","summary":"Lists the values one pod attribute key has taken, for building pod filters.","description":"Lists the values one pod attribute key has taken, for\nbuilding pod filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pods/list":{"post":{"operationId":"post_v1_o11y_pods_list","summary":"Lists Kubernetes pods over a time range, each with its CPU and memory usage against request and limit, restart count, phase counts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes pods over a time range, each with its CPU and\nmemory usage against request and limit, restart count, phase counts and\nattributes; filterable, groupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PodListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPodListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/processes/attribute_keys":{"get":{"operationId":"get_v1_o11y_processes_attribute_keys","summary":"Lists the metric attribute keys processes report, for building process filters.","description":"Lists the metric attribute keys processes report, for\nbuilding process filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/processes/attribute_values":{"get":{"operationId":"get_v1_o11y_processes_attribute_values","summary":"Lists the values one process attribute key has taken, for building process filters.","description":"Lists the values one process attribute key has taken,\nfor building process filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/processes/list":{"post":{"operationId":"post_v1_o11y_processes_list","summary":"Lists monitored processes over a time range, each with its name, PID, command line and CPU and memory usage; filterable, groupable and paginated.","description":"Lists monitored processes over a time range, each with its name,\nPID, command line and CPU and memory usage; filterable, groupable and\npaginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.ProcessListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yProcessListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/product/metrics":{"get":{"operationId":"get_v1_o11y_product_metrics","summary":"Returns one product's RED series — request rate, errors, p50 and p95 latency — for the caller's org, plus that org's LLM usage rollup over the same window.","description":"Returns one product's RED series — request rate, errors, p50\nand p95 latency — for the caller's org, plus that org's LLM usage rollup over\nthe same window. The series come from org-tagged request spans, so a tenant\nonly ever aggregates its own traffic; a validated platform SuperAdmin sees the\nwhole product's RED, while usage stays the caller's own org either way. A\nwell-formed product with no backing workload answers empty series; a malformed\nslug is a 400.","tags":["o11y"],"parameters":[{"name":"product","in":"query","required":false,"description":"Product is the console product slug to read, e.g. \"kms\". Required.","schema":{"type":"string"},"example":"kms"},{"name":"range","in":"query","required":false,"description":"Range is the window in seconds. Default 3600, capped at 604800 (7d).","schema":{"type":"integer"},"example":3600},{"name":"stepSec","in":"query","required":false,"description":"StepSec is the bucket width in seconds, clamped to [30, 3600]. Absent\npicks ~60 buckets across the range.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.metricsResponse"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/public/dashboards/{id}":{"get":{"operationId":"GetPublicDashboardData","summary":"Returns the sanitized dashboard data for public access — the read a shared dashboard's public page makes.","description":"Returns the sanitized dashboard data for public access —\nthe read a shared dashboard's public page makes.\n\nAnonymous, scoped to the public dashboard's read scope; the runtime's own gate\nenforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardDataOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/public/dashboards/{id}/widgets/{idx}/query_range":{"get":{"operationId":"GetPublicDashboardWidgetQueryRange","summary":"Returns the query-range result for one widget of a public dashboard.","description":"Returns the query-range result for one widget\nof a public dashboard. When the share fixes its own time range the caller's\nstartTime/endTime are ignored; otherwise they bound the window as millisecond\nepochs.\n\nAnonymous, scoped to the public dashboard's read scope; the runtime's own gate\nenforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the public dashboard id from the path.","schema":{"type":"string"}},{"name":"idx","in":"path","required":true,"description":"Idx is the widget's index from the path.","schema":{"type":"string"}},{"name":"startTime","in":"query","required":false,"description":"StartTime is the window start as a millisecond epoch. Used only when the\nshare enables a caller-chosen time range.","schema":{"type":"string"}},{"name":"endTime","in":"query","required":false,"description":"EndTime is the window end as a millisecond epoch. Used only when the share\nenables a caller-chosen time range.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yWidgetQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pvcs/attribute_keys":{"get":{"operationId":"get_v1_o11y_pvcs_attribute_keys","summary":"Lists the metric attribute keys persistent volume claims report, for building volume filters.","description":"Lists the metric attribute keys persistent volume claims\nreport, for building volume filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pvcs/attribute_values":{"get":{"operationId":"get_v1_o11y_pvcs_attribute_values","summary":"Lists the values one persistent-volume-claim attribute key has taken, for building volume filters.","description":"Lists the values one persistent-volume-claim attribute\nkey has taken, for building volume filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/pvcs/list":{"post":{"operationId":"post_v1_o11y_pvcs_list","summary":"Lists Kubernetes persistent volume claims over a time range, each with its available, capacity and used bytes, inode counts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes persistent volume claims over a time range, each\nwith its available, capacity and used bytes, inode counts and attributes;\nfilterable, groupable and paginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.VolumeListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPvcListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/query":{"get":{"operationId":"get_v1_o11y_query","summary":"Evaluates one instant PromQL query against the org's metrics and returns the result at a single point in time.","description":"Evaluates one instant PromQL query against the org's metrics and\nreturns the result at a single point in time.\n\nThe result is polymorphic by PromQL's own contract — a matrix, vector,\nscalar or string, discriminated by resultType — so it is carried verbatim\nrather than forced into one of its shapes.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"query","in":"query","required":true,"description":"Query is the PromQL expression to evaluate. Required.","schema":{"type":"string"}},{"name":"time","in":"query","required":false,"description":"Time is the evaluation timestamp — epoch seconds or RFC3339. Empty\nevaluates at now.","schema":{"type":"string"}},{"name":"stats","in":"query","required":false,"description":"Stats set to any non-empty value includes query statistics in the answer.","schema":{"type":"string"}},{"name":"timeout","in":"query","required":false,"description":"Timeout caps evaluation time, as a duration in seconds.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPromQueryOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/query_filter/analyze":{"post":{"operationId":"post_v1_o11y_query_filter_analyze","summary":"Analyzes a query and extracts the metric names it reads and the columns it groups by.","description":"Analyzes a query and extracts the metric names it reads\nand the columns it groups by.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAnalyzeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAnalyzeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/query_progress":{"get":{"operationId":"get_v1_o11y_query_progress","summary":"Watch one running query's progress","description":"Reports how far a submitted query has got — rows scanned, bytes read, elapsed — and HOLDS the connection until the next update rather than answering immediately.\n\nThe long poll is the whole point, and the reason this cannot be a typed operation: an answer that arrived only when the query finished would report progress on nothing. The websocket form of the same read is /ws/query_progress.\n\nA validated, org-scoped principal is required; a query id belonging to another tenant is simply not found.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/query_range":{"get":{"operationId":"get_v1_o11y_query_range","summary":"Runs a Prometheus-style range query over metrics — the legacy read that predates the v5 querier — and returns the matrix, vector or scalar the query resolved to.","description":"Runs a Prometheus-style range query over metrics — the\nlegacy read that predates the v5 querier — and returns the matrix, vector or\nscalar the query resolved to.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":true,"description":"Start is the window start — a unix timestamp (seconds, with optional\nfraction) or an RFC 3339 time. Required.","schema":{"type":"string"}},{"name":"end","in":"query","required":true,"description":"End is the window end, in the same form as Start, and not before it.\nRequired.","schema":{"type":"string"}},{"name":"step","in":"query","required":true,"description":"Step is the query resolution, e.g. 60s, 1m, 1h — a positive duration.\nRequired.","schema":{"type":"string"}},{"name":"query","in":"query","required":true,"description":"Query is the PromQL expression to evaluate. Required.","schema":{"type":"string"}},{"name":"stats","in":"query","required":false,"description":"Stats, when \"all\", asks for query statistics alongside the result.","schema":{"type":"string"}},{"name":"timeout","in":"query","required":false,"description":"Timeout caps how long the query may run, e.g. 30s, 1m — a positive\nduration. Absent means the server default.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMetricsQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_query_range","summary":"Executes a composite query over a time range: builder queries over traces, logs and metrics, formulas, trace operators, PromQL and Datastore SQL, answering time series, scalars or raw records as the request type asks.","description":"Executes a composite query over a time range: builder\nqueries over traces, logs and metrics, formulas, trace operators, PromQL and\nDatastore SQL, answering time series, scalars or raw records as the request\ntype asks.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.QueryRangeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/query_range/format":{"post":{"operationId":"post_v1_o11y_query_range_format","summary":"Parses a builder query and echoes it back normalized to the v3 shape — the endpoint the UI uses to canonicalize a query without running it.","description":"Parses a builder query and echoes it back normalized to the\nv3 shape — the endpoint the UI uses to canonicalize a query without running\nit.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.QueryRangeParamsV3"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangeFormatOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/query_range/preview":{"post":{"operationId":"post_v1_o11y_query_range_preview","summary":"Validates a composite query and renders the Datastore statements it would run WITHOUT executing it — a dry run for agentic and tooling use.","description":"Validates a composite query and renders the Datastore\nstatements it would run WITHOUT executing it — a dry run for agentic and\ntooling use. verbose=false trades the rendered SQL and EXPLAIN for a\nlightweight per-query verdict.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangePreviewIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yQueryRangePreviewOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/readyz":{"get":{"operationId":"get_v1_o11y_readyz","summary":"Readiness of the observability runtime to serve","description":"Reports whether the runtime's registered services are healthy enough to take traffic, and answers 503 when they are not — which is what takes a booting or degraded replica out of the load balancer instead of letting it serve errors.\n\nUNAUTHENTICATED by design, like the other two probes. It reads the same service registry the health probe reads, so the two agree by construction; readiness is the question a router asks and health is the question an operator asks.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/register":{"post":{"operationId":"post_v1_o11y_register","summary":"Creates the FIRST organization and its admin user.","description":"Creates the FIRST organization and its admin user. It is open by\ndesign — there is nobody to be signed in as yet — and refuses once setup has\ncompleted, after which new users arrive by invitation only.\n\nOpen by design; the runtime's own gate is OpenAccess.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRegisterIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRegisterOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/resetPassword":{"post":{"operationId":"ResetPassword","summary":"Sets a new password for whoever the reset token was minted for, consuming the token.","description":"Sets a new password for whoever the reset token was minted\nfor, consuming the token. Unauthenticated: the token is the proof.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yResetPasswordIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/reset_password_tokens/verify":{"post":{"operationId":"VerifyResetPasswordToken","summary":"Checks that a reset-password token exists and has not expired, without consuming it.","description":"Checks that a reset-password token exists and has not\nexpired, without consuming it. Unauthenticated: the token is the proof.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yResetTokenRef"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/reviews":{"get":{"operationId":"get_v1_o11y_reviews","summary":"Returns a page of the caller org's human-review queues, newest first, narrowed to the caller's project.","description":"Returns a page of the caller org's human-review queues,\nnewest first, narrowed to the caller's project. Another org's queues are never\nvisible.","tags":["o11y"],"parameters":[{"name":"page","in":"query","required":false,"description":"Page is the 1-based page to read. Default 1.","schema":{"type":"integer"},"example":1},{"name":"limit","in":"query","required":false,"description":"Limit is how many rows to return. Default 20, capped at 100.","schema":{"type":"integer"},"example":20}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annQueueList"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_reviews","summary":"Creates a human-review queue in the caller's org and project.","description":"Creates a human-review queue in the caller's org and\nproject. A name already used by another queue in the same project is a 409.","tags":["o11y"],"requestBody":{"content":{"application/json":{"example":{"name":"hallucination review","scoreConfigIds":["quality"]},"schema":{"$ref":"#/components/schemas/o11y.createQueueReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annQueueView"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/reviews/{id}":{"delete":{"operationId":"delete_v1_o11y_reviews_by_id","summary":"Removes one review queue and every item in it.","description":"Removes one review queue and every item in it. A queue\nid belonging to another org answers the same 404 an unknown id does, so a\nprobe learns nothing about what exists.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue to act on, from the path.","schema":{"type":"string"},"example":"annq_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annQueueDeleted"}}},"description":"ok"}},"x-app":"o11y"},"get":{"operationId":"get_v1_o11y_reviews_by_id","summary":"Returns one review queue with its pending and completed counts and its first page of items.","description":"Returns one review queue with its pending and completed\ncounts and its first page of items. A queue id belonging to another org is a\n404, never a cross-tenant read.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue to act on, from the path.","schema":{"type":"string"},"example":"annq_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annQueueDetailView"}}},"description":"ok"}},"x-app":"o11y"},"patch":{"operationId":"patch_v1_o11y_reviews_by_id","summary":"Changes a review queue's name, description or score-config set.","description":"Changes a review queue's name, description or\nscore-config set. A field the request omits is left alone. A name another\nqueue in the same project already uses is a 409; a queue id belonging to\nanother org is a 404.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue to update, from the path.","schema":{"type":"string"},"example":"annq_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"annq_1","name":"hallucination review v2"},"schema":{"$ref":"#/components/schemas/o11y.updateQueueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annQueueView"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/reviews/{id}/items":{"get":{"operationId":"get_v1_o11y_reviews_by_id_items","summary":"Returns a page of one review queue's items, newest first, optionally filtered to PENDING or COMPLETED.","description":"Returns a page of one review queue's items, newest\nfirst, optionally filtered to PENDING or COMPLETED. A queue id belonging to\nanother org is a 404, never a cross-tenant list.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue whose items to list, from the path.","schema":{"type":"string"},"example":"annq_1"},{"name":"status","in":"query","required":false,"description":"Status filters to PENDING or COMPLETED items. Absent returns both.","schema":{"type":"string"},"example":"PENDING"},{"name":"page","in":"query","required":false,"description":"Page is the 1-based page to read. Default 1.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit is how many rows to return. Default 20, capped at 100.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annItemList"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_reviews_by_id_items","summary":"Enqueues traces, observations or sessions on a review queue.","description":"Enqueues traces, observations or sessions on a review\nqueue. Each item names exactly one object, either by traceId / observationId /\nsessionId or by objectType plus objectId; every item enters PENDING. A queue\nid belonging to another org is a 404.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue to add to, from the path.","schema":{"type":"string"},"example":"annq_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"annq_1","items":[{"traceId":"tr_1"}]},"schema":{"$ref":"#/components/schemas/o11y.addItemsIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annItemsCreated"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/reviews/{id}/items/{itemId}":{"patch":{"operationId":"patch_v1_o11y_reviews_by_id_items_by_itemid","summary":"Moves one queue item between PENDING and COMPLETED and sets its assignee.","description":"Moves one queue item between PENDING and COMPLETED\nand sets its assignee. Completing an item stamps its completedAt. An item that\nexists under a different queue answers the same 404 an unknown item does, and\nso does a queue belonging to another org.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the annotation queue the item belongs to, from the path.","schema":{"type":"string"},"example":"annq_1"},{"name":"itemId","in":"path","required":true,"description":"ItemID is the item to update, from the path.","schema":{"type":"string"},"example":"annqi_1"}],"requestBody":{"content":{"application/json":{"example":{"id":"annq_1","itemId":"annqi_1","status":"COMPLETED"},"schema":{"$ref":"#/components/schemas/o11y.updateItemIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.annItemView"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/roles":{"get":{"operationId":"ListRoles","summary":"Lists every role in the caller's org — the managed ones the platform seeds and the custom ones its admins created.","description":"Lists every role in the caller's org — the managed ones the\nplatform seeds and the custom ones its admins created.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRolesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateRole","summary":"Creates a custom role in the caller's org from a name, an optional description and the transaction groups it grants, answering the new role's id.","description":"Creates a custom role in the caller's org from a name, an optional\ndescription and the transaction groups it grants, answering the new role's id.\n\nNames are lowercase letters and hyphens only, and may not start with the\nreserved managed-role prefix; the runtime refuses anything else.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoleCreateIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoleCreateOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/roles/{id}":{"delete":{"operationId":"DeleteRole","summary":"Deletes a custom role.","description":"Deletes a custom role. A role that still has user or\nservice-account assignees, or an auth-domain mapping, is refused; managed\nroles cannot be deleted.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetRole","summary":"Returns one role with the transaction groups it grants.","description":"Returns one role with the transaction groups it grants.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoleOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateRole","summary":"Replaces a custom role's description and transaction groups.","description":"Replaces a custom role's description and transaction groups.\nBoth fields are mandatory — send an empty string or an empty array to clear\none — and managed roles cannot be edited.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoleUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/roles/{id}/users":{"get":{"operationId":"GetUsersByRoleID","summary":"Returns every org member holding a role, by role id.","description":"Returns every org member holding a role, by role id. Admin\ngate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUsersOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/route_policies":{"get":{"operationId":"GetAllRoutePolicies","summary":"Lists the org's route policies.","description":"Lists the org's route policies. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoutePoliciesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateRoutePolicy","summary":"Creates a route policy, answering with the stored policy.","description":"Creates a route policy, answering with the stored policy. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableRoutePolicy"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoutePolicyOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/route_policies/{id}":{"delete":{"operationId":"DeleteRoutePolicyByID","summary":"Removes a route policy, by id.","description":"Removes a route policy, by id. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetRoutePolicyByID","summary":"Returns one route policy, by id.","description":"Returns one route policy, by id. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoutePolicyOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateRoutePolicy","summary":"Replaces a route policy, by id, answering with the stored policy.","description":"Replaces a route policy, by id, answering with the stored\npolicy. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoutePolicyUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRoutePolicyOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules":{"get":{"operationId":"ListRules","summary":"Lists all alert rules with their current evaluation state.","description":"Lists all alert rules with their current evaluation state. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRulesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateRule","summary":"Creates a new alert rule and answers with the stored rule.","description":"Creates a new alert rule and answers with the stored rule. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/rules/test":{"post":{"operationId":"TestRule","summary":"Fires a test notification for a rule definition without saving it, answering with how many series would alert.","description":"Fires a test notification for a rule definition without saving it,\nanswering with how many series would alert. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTestRuleOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}":{"delete":{"operationId":"DeleteRuleByID","summary":"Removes an alert rule, by id.","description":"Removes an alert rule, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetRuleByID","summary":"Returns one alert rule with its evaluation state, by id.","description":"Returns one alert rule with its evaluation state, by id. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleOut"}}},"description":"ok"}},"x-app":"o11y"},"patch":{"operationId":"PatchRuleByID","summary":"Applies a partial update to an alert rule, by id, answering with the stored rule — the common toggle for enabling or muting a rule.","description":"Applies a partial update to an alert rule, by id, answering\nwith the stored rule — the common toggle for enabling or muting a rule.\nEditor gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateRuleByID","summary":"Replaces an alert rule's definition, by id.","description":"Replaces an alert rule's definition, by id. Editor gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/filter_keys":{"get":{"operationId":"GetRuleHistoryFilterKeys","summary":"Returns the distinct label keys present in a rule's history entries over the selected range, for building history filters.","description":"Returns the distinct label keys present in a rule's\nhistory entries over the selected range, for building history filters. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"startUnixMilli","in":"query","required":false,"description":"StartUnixMilli is the window start, unix milliseconds.","schema":{"type":"integer"}},{"name":"endUnixMilli","in":"query","required":false,"description":"EndUnixMilli is the window end, unix milliseconds.","schema":{"type":"integer"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50, capped at 200.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryFilterKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/filter_values":{"get":{"operationId":"GetRuleHistoryFilterValues","summary":"Returns the distinct values a given label key has taken across a rule's history entries.","description":"Returns the distinct values a given label key has\ntaken across a rule's history entries. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"startUnixMilli","in":"query","required":false,"schema":{"type":"integer"}},{"name":"endUnixMilli","in":"query","required":false,"schema":{"type":"integer"}},{"name":"searchText","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"name","in":"query","required":true,"description":"Name is the label key whose values to list. Required.","schema":{"type":"string"}},{"name":"existingQuery","in":"query","required":false,"description":"ExistingQuery is a filter expression scoping which values appear.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryFilterValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/overall_status":{"get":{"operationId":"GetRuleHistoryOverallStatus","summary":"Returns the overall firing/inactive intervals for a rule over the selected range.","description":"Returns the overall firing/inactive intervals for\na rule over the selected range. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the window start, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the window end, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryOverallStatusOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"GetOverallStateTransitions","summary":"Returns the overall firing/inactive windows for a rule, for the posted query range.","description":"Returns the overall firing/inactive windows for a\nrule, for the posted query range. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryQueryIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOverallStateTransitionsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/stats":{"get":{"operationId":"GetRuleHistoryStats","summary":"Returns trigger and resolution statistics for a rule over the selected time range, current window against the prior one.","description":"Returns trigger and resolution statistics for a rule over\nthe selected time range, current window against the prior one. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the window start, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the window end, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryStatsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"GetRuleStats","summary":"Returns trigger and resolution statistics for a rule, current window against the prior one, for the posted query range.","description":"Returns trigger and resolution statistics for a rule, current\nwindow against the prior one, for the posted query range. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryQueryIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleStatsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/timeline":{"get":{"operationId":"GetRuleHistoryTimeline","summary":"Returns paginated timeline entries for a rule's state transitions, filterable by state and a label expression, cursor-paginated.","description":"Returns paginated timeline entries for a rule's state\ntransitions, filterable by state and a label expression, cursor-paginated. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the window start, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the window end, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}},{"name":"state","in":"query","required":false,"description":"State keeps only entries in one alert state, e.g. firing or normal.","schema":{"type":"string"}},{"name":"filterExpression","in":"query","required":false,"description":"FilterExpression narrows entries to those whose labels match it.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many entries come back. Absent means 50.","schema":{"type":"integer"}},{"name":"order","in":"query","required":false,"description":"Order sorts by time, asc or desc.","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"Cursor resumes a previous page; opaque, returned as nextCursor.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryTimelineOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"GetRuleStateHistory","summary":"Returns a rule's state-transition timeline for the posted query range, each entry carrying its related-logs or related-traces link.","description":"Returns a rule's state-transition timeline for the posted\nquery range, each entry carrying its related-logs or related-traces link. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryQueryIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleStateTimelineOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/rules/{id}/history/top_contributors":{"get":{"operationId":"GetRuleHistoryTopContributors","summary":"Returns the label combinations that contributed most to a rule firing over the selected range.","description":"Returns the label combinations that contributed\nmost to a rule firing over the selected range. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the window start, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}},{"name":"end","in":"query","required":false,"description":"End is the window end, unix milliseconds. Required by the runtime.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryContributorsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"GetRuleStateHistoryTopContributors","summary":"Returns the label combinations that contributed most to a rule firing, for the posted query range.","description":"Returns the label combinations that\ncontributed most to a rule firing, for the posted query range. Viewer gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleHistoryQueryIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRuleStateContributorsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/service/entry_point_operations":{"post":{"operationId":"post_v1_o11y_service_entry_point_operations","summary":"Returns one service's entry-point operations with the same latency and error profile topOperations reports.","description":"Returns one service's entry-point operations with the\nsame latency and error profile topOperations reports.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOperationsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOperationsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/service/top_level_operations":{"post":{"operationId":"post_v1_o11y_service_top_level_operations","summary":"Maps each service to its entry-point span names — for the one service named in the request, or for every service when none is.","description":"Maps each service to its entry-point span names — for the\none service named in the request, or for every service when none is.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTopLevelOpsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/service/top_operations":{"post":{"operationId":"post_v1_o11y_service_top_operations","summary":"Returns one service's heaviest operations in the window, each with p50/p95/p99 latency, how often it ran and how often it errored.","description":"Returns one service's heaviest operations in the window, each\nwith p50/p95/p99 latency, how often it ran and how often it errored.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOperationsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOperationsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/service_accounts":{"get":{"operationId":"ListServiceAccounts","summary":"Lists the caller's org's service accounts.","description":"Lists the caller's org's service accounts.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateServiceAccount","summary":"Creates a service account in the caller's org, answering its id.","description":"Creates a service account in the caller's org,\nanswering its id. The name — a lowercase letter followed by lowercase\nletters, digits or hyphens — becomes the account's email local part.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountCreateIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountCreateOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/me":{"get":{"operationId":"GetMyServiceAccount","summary":"Returns the calling service account itself, with the roles it holds — the self-inspection read for a key-authenticated caller.","description":"Returns the calling service account itself, with the\nroles it holds — the self-inspection read for a key-authenticated caller.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateMyServiceAccount","summary":"Renames the calling service account.","description":"Renames the calling service account.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yMyServiceAccountUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/{id}":{"delete":{"operationId":"DeleteServiceAccount","summary":"Deletes a service account and revokes every key it holds.","description":"Deletes a service account and revokes every key it\nholds.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetServiceAccount","summary":"Returns one service account with the roles it holds.","description":"Returns one service account with the roles it holds.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateServiceAccount","summary":"Renames a service account.","description":"Renames a service account.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/{id}/keys":{"get":{"operationId":"ListServiceAccountKeys","summary":"Lists a service account's API keys — metadata only, never the secrets.","description":"Lists a service account's API keys — metadata only,\nnever the secrets.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAPIKeysOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateServiceAccountKey","summary":"Mints an API key for a service account and answers the key's id and its secret — the one time the secret is ever shown.","description":"Mints an API key for a service account and answers\nthe key's id and its secret — the one time the secret is ever shown.\n\nExpiresAt is a unix timestamp in seconds; zero means the key never expires,\nand a timestamp in the past is refused.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAPIKeyCreateIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAPIKeyCreateOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/{id}/keys/{fid}":{"delete":{"operationId":"RevokeServiceAccountKey","summary":"Revokes an API key.","description":"Revokes an API key. Revocation is immediate and\npermanent.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"fid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"put":{"operationId":"UpdateServiceAccountKey","summary":"Renames an API key or moves its expiry.","description":"Renames an API key or moves its expiry.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"fid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAPIKeyUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/{id}/roles":{"get":{"operationId":"GetServiceAccountRoles","summary":"Lists the roles a service account holds.","description":"Lists the roles a service account holds.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountRolesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateServiceAccountRole","summary":"Assigns a role, named by its id, to a service account.","description":"Assigns a role, named by its id, to a service\naccount.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"RoleID is the id of the role to assign. Required.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServiceAccountRoleGrantIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/service_accounts/{id}/roles/{rid}":{"delete":{"operationId":"DeleteServiceAccountRole","summary":"Removes a role from a service account.","description":"Removes a role from a service account.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"rid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/services":{"post":{"operationId":"post_v1_o11y_services","summary":"Lists the instrumented services seen in the window, each with the request profile of its entry-point spans: p99 and average latency, call and error rates, and the entry-point operations the numbers were computed over.","description":"Lists the instrumented services seen in the window, each with the\nrequest profile of its entry-point spans: p99 and average latency, call and\nerror rates, and the entry-point operations the numbers were computed over.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServicesIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yServicesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/services/list":{"get":{"operationId":"get_v1_o11y_services_list","summary":"Lists the name of every service the trace store holds, with no window applied — the complete catalog, for pickers and autocomplete.","description":"Lists the name of every service the trace store holds, with no\nwindow applied — the complete catalog, for pickers and autocomplete.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/sessions":{"delete":{"operationId":"DeleteSession","summary":"Signs the calling session out, invalidating its tokens.","description":"Signs the calling session out, invalidating its tokens. The\naccess token on the call names the session to end.","tags":["o11y"],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"get_v1_o11y_sessions","summary":"List the caller org's LLM sessions","description":"Answers the caller org's LLM-observability sessions — traces grouped by session id on the gen_ai span plane — paged by limit and offset, in the runtime's own envelope, passed through unchanged.\n\nAn org-less caller is refused HERE, at the cloud boundary, before the request reaches the runtime, and the org the runtime then scopes on is that SAME validated tenant. The two cannot disagree: the tenant is minted from the principal's own claim at ingress and a client copy never survives it.\n\nThere is deliberately no session-detail route to pair with this. The runtime serves the list only; detail is composed client-side from this list plus the traces filtered by session, so a caller looking for one is looking for something that was never served rather than something that broke.","tags":["o11y"],"x-app":"o11y"}},"/v1/o11y/sessions/context":{"get":{"operationId":"GetSessionContext","summary":"Tells a sign-in page what an email address can do: which orgs the address belongs to and, per org, which password and SSO routes are open to it.","description":"Tells a sign-in page what an email address can do: which\norgs the address belongs to and, per org, which password and SSO routes are\nopen to it. Unauthenticated: it runs before any session exists.","tags":["o11y"],"parameters":[{"name":"email","in":"query","required":false,"description":"Email is the address about to sign in. Required.","schema":{"type":"string"}},{"name":"ref","in":"query","required":false,"description":"Ref is the page the sign-in started from, carried into SSO redirects.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySessionContextOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/sessions/email_password":{"post":{"operationId":"CreateSessionByEmailPassword","summary":"Signs a user in with email and password and answers with the session's token pair.","description":"Signs a user in with email and password and\nanswers with the session's token pair. Unauthenticated: this call is how\nauthentication begins.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yEmailPasswordSessionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTokenOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/sessions/rotate":{"post":{"operationId":"RotateSession","summary":"Exchanges a refresh token for a fresh token pair, retiring the old pair.","description":"Exchanges a refresh token for a fresh token pair, retiring the\nold pair. The access token being rotated identifies the session.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRotateSessionIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTokenOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/settings/apdex":{"get":{"operationId":"get_v1_o11y_settings_apdex","summary":"Returns apdex settings for the named services.","description":"Returns apdex settings for the named services.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"services","in":"query","required":false,"description":"Services are the service names, comma separated.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yApdexOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_settings_apdex","summary":"Sets one service's apdex threshold and the status codes excluded from its score.","description":"Sets one service's apdex threshold and the status codes excluded\nfrom its score.\n\nAdmin only, as the mux tree has always gated it (AdminAccess); the runtime's\nown gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yApdexSetIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yApdexSetOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/settings/ttl":{"get":{"operationId":"get_v1_o11y_settings_ttl","summary":"Returns the org's current retention policy: default TTL, custom per-label rules, and cold-storage settings where configured.","description":"Returns the org's current retention policy: default TTL, custom\nper-label rules, and cold-storage settings where configured.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRetentionOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_o11y_settings_ttl","summary":"Sets the org's retention policy for one signal: the default TTL in days, ordered per-label retention rules, and optional cold-storage settings.","description":"Sets the org's retention policy for one signal: the default TTL\nin days, ordered per-label retention rules, and optional cold-storage\nsettings.\n\nAdmin only, as the mux tree has always gated it (AdminAccess); the runtime's\nown gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRetentionSetIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRetentionSetOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/span_mapper_groups":{"get":{"operationId":"ListSpanMapperGroups","summary":"Lists the caller's org's mapping groups, optionally only the enabled ones.","description":"Lists the caller's org's mapping groups, optionally only the\nenabled ones.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"enabled","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperGroupsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateSpanMapperGroup","summary":"Creates a mapping group: the name it is known by, the span and resource attributes whose presence selects a span into it, and whether it is on.","description":"Creates a mapping group: the name it is known by, the\nspan and resource attributes whose presence selects a span into it, and\nwhether it is on.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableSpanMapperGroup"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperGroupOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/span_mapper_groups/{groupId}":{"delete":{"operationId":"DeleteSpanMapperGroup","summary":"Deletes a mapping group and every mapper under it.","description":"Deletes a mapping group and every mapper under it.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"patch":{"operationId":"UpdateSpanMapperGroup","summary":"Changes a group's name, condition or enabled state.","description":"Changes a group's name, condition or enabled state.\nEvery field is optional and only the ones sent are applied.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperGroupUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/span_mapper_groups/{groupId}/span_mappers":{"get":{"operationId":"ListSpanMappers","summary":"Lists the mappers belonging to one group, in the order they are applied.","description":"Lists the mappers belonging to one group, in the order they are\napplied.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMappersOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateSpanMapper","summary":"Adds a mapper to a group: which field context it reads, the move or copy it performs, and whether it is on.","description":"Adds a mapper to a group: which field context it reads, the\nmove or copy it performs, and whether it is on.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperCreateIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/span_mapper_groups/{groupId}/span_mappers/{mapperId}":{"delete":{"operationId":"DeleteSpanMapper","summary":"Deletes one mapper from a group.","description":"Deletes one mapper from a group.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}},{"name":"mapperId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"patch":{"operationId":"UpdateSpanMapper","summary":"Changes a mapper's field context, config or enabled state.","description":"Changes a mapper's field context, config or enabled state.\nEvery field is optional and only the ones sent are applied.\n\nCallers need the admin role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"groupId","in":"path","required":true,"schema":{"type":"string"}},{"name":"mapperId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanMapperUpdateIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/span_percentile":{"post":{"operationId":"post_v1_o11y_span_percentile","summary":"Places one span's duration among its peers: the p50/p90/p99 durations of like spans, and the percentile the given duration lands at.","description":"Places one span's duration among its peers: the p50/p90/p99\ndurations of like spans, and the percentile the given duration lands at.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanPercentileIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySpanPercentileOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/statefulsets/attribute_keys":{"get":{"operationId":"get_v1_o11y_statefulsets_attribute_keys","summary":"Lists the metric attribute keys Kubernetes statefulsets report, for building statefulset filters.","description":"Lists the metric attribute keys Kubernetes\nstatefulsets report, for building statefulset filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the keys come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the keys will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the keys must appear on.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the keys to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the keys to one kind — tag or resource. Empty means all;\nan invalid value reads as empty.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many keys come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeKeysOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/statefulsets/attribute_values":{"get":{"operationId":"get_v1_o11y_statefulsets_attribute_values","summary":"Lists the values one statefulset attribute key has taken, for building statefulset filters.","description":"Lists the values one statefulset attribute key\nhas taken, for building statefulset filters.","tags":["o11y"],"parameters":[{"name":"dataSource","in":"query","required":false,"description":"DataSource is the telemetry the values come from — metrics for the infra\nfaces. The runtime requires it.","schema":{"type":"string"}},{"name":"aggregateOperator","in":"query","required":false,"description":"AggregateOperator is the aggregation the values will be used under, e.g.\nnoop, count, avg. The runtime requires it for non-metrics sources.","schema":{"type":"string"}},{"name":"aggregateAttribute","in":"query","required":false,"description":"AggregateAttribute is the metric the values must appear on.","schema":{"type":"string"}},{"name":"attributeKey","in":"query","required":false,"description":"AttributeKey is the key whose values to list.","schema":{"type":"string"}},{"name":"filterAttributeKeyDataType","in":"query","required":false,"description":"FilterAttributeKeyDataType is the key's data type — string, int64,\nfloat64 or bool. Empty means unspecified.","schema":{"type":"string"}},{"name":"searchText","in":"query","required":false,"description":"SearchText narrows the values to those containing it.","schema":{"type":"string"}},{"name":"tagType","in":"query","required":false,"description":"TagType narrows the search to one kind of key — tag or resource.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many values come back. Absent means 50.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yInfraAttributeValuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/statefulsets/list":{"post":{"operationId":"post_v1_o11y_statefulsets_list","summary":"Lists Kubernetes statefulsets over a time range, each with the CPU and memory its pods used against request and limit, desired and available replica counts, restarts and attributes; filterable, groupable and paginated.","description":"Lists Kubernetes statefulsets over a time range, each with\nthe CPU and memory its pods used against request and limit, desired and\navailable replica counts, restarts and attributes; filterable, groupable and\npaginated.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.StatefulSetListRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yStatefulSetListOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/stats":{"get":{"operationId":"get_v1_o11y_stats","summary":"Returns the collected usage statistics for the caller's org, as the stats reporter aggregates them — a map whose keys are the reporter's own counter names.","description":"Returns the collected usage statistics for the caller's org, as the\nstats reporter aggregates them — a map whose keys are the reporter's own\ncounter names.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yOrgStatsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/status":{"get":{"operationId":"get_v1_o11y_status","summary":"Reports whether a product's service is live: an in-cluster health probe with its measured latency, fused with the per-replica up inventory.","description":"Reports whether a product's service is live: an in-cluster\nhealth probe with its measured latency, fused with the per-replica up\ninventory. Infra health is not tenant-partitioned — a service is up or down\nfor everyone — so any validated caller is served, but an unvalidated one is\nrefused. A product with no backing workload answers down/unknown-service\nwithout probing anything; a malformed slug is a 400.","tags":["o11y"],"parameters":[{"name":"product","in":"query","required":false,"description":"Product is the console product slug to probe, e.g. \"kms\". Required.","schema":{"type":"string"},"example":"kms"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.statusResult"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/substitute_vars":{"post":{"operationId":"post_v1_o11y_substitute_vars","summary":"Substitutes a query's variables and returns the resolved request, without running it — what a dashboard does before it queries.","description":"Substitutes a query's variables and returns the\nresolved request, without running it — what a dashboard does before it\nqueries.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.QueryRangeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySubstituteVarsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/testChannel":{"post":{"operationId":"TestChannelDeprecated","summary":"Sends a test notification to the posted receiver.","description":"Sends a test notification to the posted receiver. The\nlegacy path; prefer /channels/test. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.alertmanagertypes.Receiver"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/testRule":{"post":{"operationId":"TestRuleNotification","summary":"Fires a test notification for the posted rule definition and answers with how many series alerted and a status message.","description":"Fires a test notification for the posted rule definition\nand answers with how many series alerted and a status message. The legacy\npath; prefer /rules/test. Editor gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTestNotificationOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/third-party-apis/overview/domain":{"post":{"operationId":"post_v1_o11y_third-party-apis_overview_domain","summary":"Returns one external domain's endpoint-level breakdown — each endpoint with its rate, error and latency columns over the window.","description":"Returns one external domain's endpoint-level breakdown — each\nendpoint with its rate, error and latency columns over the window.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDomainsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDomainsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/third-party-apis/overview/list":{"post":{"operationId":"post_v1_o11y_third-party-apis_overview_list","summary":"Lists the external domains the instrumented services call, with request rate, error percentage and latency per domain.","description":"Lists the external domains the instrumented services call, with\nrequest rate, error percentage and latency per domain. Rows whose domain is\na bare IP address are dropped unless show_ip asks for them.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDomainsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDomainsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/error-traces":{"post":{"operationId":"GetDraftFunnelErrorTraces","summary":"Returns the errored traces through a step transition of a funnel described inline.","description":"Returns the errored traces through a step transition of\na funnel described inline.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/overview":{"post":{"operationId":"GetDraftFunnelOverview","summary":"Returns the conversion overview of a funnel described inline.","description":"Returns the conversion overview of a funnel described\ninline.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/slow-traces":{"post":{"operationId":"GetDraftFunnelSlowTraces","summary":"Returns the slowest traces through a step transition of a funnel described inline.","description":"Returns the slowest traces through a step transition of\na funnel described inline.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/steps":{"post":{"operationId":"GetDraftFunnelStepMetrics","summary":"Returns the per-step metrics of a funnel described inline.","description":"Returns the per-step metrics of a funnel described inline.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/steps/overview":{"post":{"operationId":"GetDraftFunnelStepOverview","summary":"Returns the conversion between two steps of a funnel described inline.","description":"Returns the conversion between two steps of a funnel\ndescribed inline.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/analytics/validate":{"post":{"operationId":"ValidateDraftFunnelTraces","summary":"Lists the traces that match a funnel described inline — the builder's \"try this\" before anything is saved.","description":"Lists the traces that match a funnel described inline —\nthe builder's \"try this\" before anything is saved.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDraftFunnelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/list":{"get":{"operationId":"ListTraceFunnels","summary":"Lists the caller's org's funnels, each with its steps and who last touched it.","description":"Lists the caller's org's funnels, each with its steps and who last\ntouched it.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/new":{"post":{"operationId":"CreateTraceFunnel","summary":"Creates an empty funnel with a name, answering the funnel it created.","description":"Creates an empty funnel with a name, answering the funnel it\ncreated. Steps are added afterwards with the steps update.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelCreateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/steps/update":{"put":{"operationId":"UpdateTraceFunnelSteps","summary":"Replaces a funnel's steps — the funnel is named in the body rather than the path — and answers the funnel as it now stands.","description":"Replaces a funnel's steps — the funnel is named in the body\nrather than the path — and answers the funnel as it now stands. A name or\ndescription sent alongside is applied too; an empty one leaves it as it was.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelStepsUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}":{"delete":{"operationId":"DeleteTraceFunnel","summary":"Deletes a funnel.","description":"Deletes a funnel. The answer carries no data — the runtime\nacknowledges with the success envelope alone, which is what this Out says.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelDeleteOut"}}},"description":"ok"}},"x-app":"o11y"},"get":{"operationId":"GetTraceFunnel","summary":"Returns one funnel with its steps.","description":"Returns one funnel with its steps.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateTraceFunnel","summary":"Renames a funnel or rewrites its description, answering the funnel as it now stands.","description":"Renames a funnel or rewrites its description, answering the\nfunnel as it now stands.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelUpdateIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/error-traces":{"post":{"operationId":"GetTraceFunnelErrorTraces","summary":"Returns the errored traces through a step transition of a saved funnel — the entry point for \"why is this step failing\".","description":"Returns the errored traces through a step transition of a\nsaved funnel — the entry point for \"why is this step failing\".","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelStepWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/overview":{"post":{"operationId":"GetTraceFunnelOverview","summary":"Returns a saved funnel's conversion overview over a window: how many entered, how many converted, the rate and the latency.","description":"Returns a saved funnel's conversion overview over a window:\nhow many entered, how many converted, the rate and the latency.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelStepWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/slow-traces":{"post":{"operationId":"GetTraceFunnelSlowTraces","summary":"Returns the slowest traces through a step transition of a saved funnel — the entry point for \"why is this step slow\".","description":"Returns the slowest traces through a step transition of a\nsaved funnel — the entry point for \"why is this step slow\".","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelStepWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/steps":{"post":{"operationId":"GetTraceFunnelStepMetrics","summary":"Returns a saved funnel's per-step metrics over a window — the counts and latencies at each step, in step order.","description":"Returns a saved funnel's per-step metrics over a window — the\ncounts and latencies at each step, in step order.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/steps/overview":{"post":{"operationId":"GetTraceFunnelStepOverview","summary":"Returns the conversion between two named steps of a saved funnel — the step-to-step drill-down behind the overview.","description":"Returns the conversion between two named steps of a saved\nfunnel — the step-to-step drill-down behind the overview.","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelStepWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/trace-funnels/{funnel_id}/analytics/validate":{"post":{"operationId":"ValidateTraceFunnelTraces","summary":"Lists the traces that match a saved funnel over a window — the read that answers \"is this funnel finding anything at all\".","description":"Lists the traces that match a saved funnel over a window — the\nread that answers \"is this funnel finding anything at all\".","tags":["o11y"],"parameters":[{"name":"funnel_id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelWindowIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFunnelRowsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces":{"get":{"operationId":"get_v1_o11y_traces","summary":"Lists the caller org's recent traces — one row per trace with its span count and wall-clock duration, most recently active first.","description":"Lists the caller org's recent traces — one row per trace with\nits span count and wall-clock duration, most recently active first. This is\nthe trace SEARCH: it is where a trace id comes from, and the spans behind any\nrow are then read from GET /v1/o11y/traces/{traceId}. Every row belongs to the\ncaller's own org — the tenant is the validated principal, never an input, and\nthere is no administrator widening, because a trace list is a tenant's records\nrather than a rollup over them. An unreachable telemetry store answers 503\nrather than an empty page, because \"no traces\" and \"cannot see the traces\" are\ndifferent facts and only one of them is about the caller's system.","tags":["o11y"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the window in seconds, counted back from now over each trace's\nlast activity. Default 3600, capped at 604800 (7d).","schema":{"type":"integer"},"example":3600},{"name":"limit","in":"query","required":false,"description":"Limit is how many traces to return. Default 50, capped at 500.","schema":{"type":"integer"},"example":50},{"name":"minDurationMs","in":"query","required":false,"description":"MinDurationMs keeps only traces that lasted at least this many\nmilliseconds. Zero or absent keeps every trace in the window.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.tracesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces/fields":{"get":{"operationId":"GetTraceFields","summary":"Returns the trace field catalog: the span fields already selected as indexed columns, and the interesting ones seen in the data that could be.","description":"Returns the trace field catalog: the span fields already selected\nas indexed columns, and the interesting ones seen in the data that could be.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldCatalogOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"UpdateTraceField","summary":"Changes how one span field is stored — selects or deselects it as a materialized column and tunes its index — and echoes the setting back.","description":"Changes how one span field is stored — selects or deselects\nit as a materialized column and tunes its index — and echoes the setting back.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldSetting"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yFieldSetting"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces/{traceId}":{"get":{"operationId":"SearchTraces","summary":"Returns one trace's spans as a column/row table, optionally centred on a span and walked a fixed number of levels up and down from it — the read the trace explorer opens a trace with.","description":"Returns one trace's spans as a column/row table, optionally\ncentred on a span and walked a fixed number of levels up and down from it —\nthe read the trace explorer opens a trace with.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"path","required":true,"schema":{"type":"string"}},{"name":"spanId","in":"query","required":false,"schema":{"type":"string"}},{"name":"levelUp","in":"query","required":false,"schema":{"type":"integer"}},{"name":"levelDown","in":"query","required":false,"schema":{"type":"integer"}},{"name":"spanRenderLimit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yTraceSpanWindow"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces/{traceId}/aggregations":{"post":{"operationId":"GetTraceAggregations","summary":"Computes span aggregations over one trace — span count, duration or share of execution time — grouped by the resource field each aggregation names.","description":"Computes span aggregations over one trace — span count,\nduration or share of execution time — grouped by the resource field each\naggregation names.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceAggregationsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceAggregationsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces/{traceId}/flamegraph":{"post":{"operationId":"GetFlamegraph","summary":"Returns a trace's flamegraph: spans bucketed by depth level, each level ordered as it is drawn, around the selected span.","description":"Returns a trace's flamegraph: spans bucketed by depth level,\neach level ordered as it is drawn, around the selected span.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceFlamegraphIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceFlamegraphOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/traces/{traceId}/waterfall":{"post":{"operationId":"GetWaterfallV4","summary":"Returns a trace's waterfall: every span when the trace is small enough, a capped window around the selected span when it is not, with the uncollapsed subtrees the caller asked to keep open.","description":"Returns a trace's waterfall: every span when the trace is\nsmall enough, a capped window around the selected span when it is not, with\nthe uncollapsed subtrees the caller asked to keep open.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"traceId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceWaterfallIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceWaterfallOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/usage":{"get":{"operationId":"get_v1_o11y_usage","summary":"Returns ingestion usage counts bucketed over the requested window, optionally narrowed to one service.","description":"Returns ingestion usage counts bucketed over the requested window,\noptionally narrowed to one service.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"start","in":"query","required":true,"description":"Start is the window start, as epoch nanoseconds. Required.","schema":{"type":"string"}},{"name":"end","in":"query","required":true,"description":"End is the window end, as epoch nanoseconds. Required.","schema":{"type":"string"}},{"name":"step","in":"query","required":false,"description":"Step is the bucket width in seconds. The runtime requires it.","schema":{"type":"integer"}},{"name":"service","in":"query","required":false,"description":"Service narrows usage to one service. Empty covers all.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/o11y.O11yUsageItem"},"type":"array"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/user":{"get":{"operationId":"ListUsersDeprecated","summary":"Lists the org's members with their single legacy role.","description":"Lists the org's members with their single legacy role.\nDeprecated in favor of listUsers, which answers without the role. Admin gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUsersOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/user/me":{"get":{"operationId":"GetMyUserDeprecated","summary":"Returns the calling user with their single legacy role.","description":"Returns the calling user with their single legacy role.\nDeprecated in favor of getMyUser. Open to any authenticated caller.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUserOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/user/preferences":{"get":{"operationId":"ListUserPreferences","summary":"Lists every preference of the calling user, each with its current and default value.","description":"Lists every preference of the calling user, each with\nits current and default value. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPreferencesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/user/preferences/{name}":{"get":{"operationId":"GetUserPreference","summary":"Returns one preference of the calling user, by name.","description":"Returns one preference of the calling user, by name.\nViewer gate.","tags":["o11y"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPreferenceOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateUserPreference","summary":"Sets one preference of the calling user, by name.","description":"Sets one preference of the calling user, by name.\nViewer gate.","tags":["o11y"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdatablePreference"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/user/{id}":{"delete":{"operationId":"DeleteUserDeprecated","summary":"Removes one org member, by user id.","description":"Removes one org member, by user id. The same operation\nas deleteUser on the legacy singular path. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetUserDeprecated","summary":"Returns one org member with their single legacy role, by user id.","description":"Returns one org member with their single legacy role, by\nuser id. Admins may read anyone; a non-admin only themselves (the runtime's\nself-access gate).","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUserOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateUserDeprecated","summary":"Renames one org member and may move their legacy role, answering with the updated record.","description":"Renames one org member and may move their legacy role,\nanswering with the updated record. Admins may update anyone; a non-admin\nonly themselves (the runtime's self-access gate).","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUserUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUserOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/users":{"get":{"operationId":"ListUsers","summary":"Lists the caller's org members.","description":"Lists the caller's org members. Admin gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUsersOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"CreateUser","summary":"Creates a member of the caller's org in the pending-invite state and mails them their invitation; the answer is the new user's id.","description":"Creates a member of the caller's org in the pending-invite state\nand mails them their invitation; the answer is the new user's id. Admin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yPostableUser"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yCreatedOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/users/me":{"get":{"operationId":"GetMyUser","summary":"Returns the calling user together with every role they hold.","description":"Returns the calling user together with every role they hold. Open\nto any authenticated caller.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUserWithRolesOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateMyUserV2","summary":"Renames the calling user.","description":"Renames the calling user. Open to any authenticated caller.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUpdatableUser"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/users/me/dashboards":{"get":{"operationId":"ListDashboardsForUserV2","summary":"Is dashboardListV2 personalized for the calling user: each dashboard carries the caller's pinned state, and pinned dashboards float to the top of the requested ordering.","description":"Is dashboardListV2 personalized for the calling user:\neach dashboard carries the caller's pinned state, and pinned dashboards float to\nthe top of the requested ordering. Supports the same filter DSL, sort, order and\npagination.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"query","in":"query","required":false,"description":"Query is the filter DSL over dashboard columns and tags, e.g.\n`name:cpu source:user`. Empty lists everything.","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Sort is the sort field: updated_at, created_at or name. Empty sorts by\nupdated_at.","schema":{"type":"string"}},{"name":"order","in":"query","required":false,"description":"Order is the sort direction: asc or desc. Empty orders desc.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many dashboards come back. Zero means the default of 20;\nthe runtime caps it at 200.","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many dashboards to skip for pagination.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardListForUserOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/users/me/dashboards/{id}/pins":{"delete":{"operationId":"UnpinDashboardV2","summary":"Removes the caller's pin for a dashboard.","description":"Removes the caller's pin for a dashboard. Idempotent —\nunpinning a dashboard that was not pinned still succeeds.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"put":{"operationId":"PinDashboardV2","summary":"Pins a dashboard for the calling user.","description":"Pins a dashboard for the calling user. A user can pin at most ten\ndashboards; pinning at the limit refuses with the runtime's conflict. Re-pinning\nan already-pinned dashboard is a no-op success. Pinning mutates only the caller's\npin list, not the dashboard, so a viewer may pin what a viewer may read.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the resource id from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/users/me/factor_password":{"put":{"operationId":"UpdateMyPassword","summary":"Replaces the calling user's password, refusing when the old one does not match.","description":"Replaces the calling user's password, refusing when the old\none does not match. Open to any authenticated caller.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yChangePasswordIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/users/{id}":{"delete":{"operationId":"DeleteUser","summary":"Removes one org member, by user id.","description":"Removes one org member, by user id. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"GetUser","summary":"Returns one org member together with every role they hold, by user id.","description":"Returns one org member together with every role they hold, by user\nid. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUserWithRolesOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"UpdateUser","summary":"Renames one org member, by user id — someone else, never the caller, who renames themselves through updateMyUser.","description":"Renames one org member, by user id — someone else, never the\ncaller, who renames themselves through updateMyUser. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yUserUpdate"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/users/{id}/reset_password_tokens":{"get":{"operationId":"GetResetPasswordToken","summary":"Returns the reset-password token a user already has; absent one, the answer is a not-found rather than a fresh token.","description":"Returns the reset-password token a user already has; absent\none, the answer is a not-found rather than a fresh token. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yResetTokenOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"CreateResetPasswordToken","summary":"Creates or regenerates a user's reset-password token: a live token is returned as it is, an expired one is replaced.","description":"Creates or regenerates a user's reset-password token: a\nlive token is returned as it is, an expired one is replaced. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yResetTokenOut"}}},"description":"created"}},"x-app":"o11y"}},"/v1/o11y/users/{id}/roles":{"get":{"operationId":"GetRolesByUserID","summary":"Returns every role one org member holds, by user id.","description":"Returns every role one org member holds, by user id. Admin\ngate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yRolesOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"SetRoleByUserID","summary":"Assigns a role, by role name, to one org member — someone else, never the caller.","description":"Assigns a role, by role name, to one org member — someone else,\nnever the caller. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySetRoleIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yAck"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/users/{id}/roles/{roleId}":{"delete":{"operationId":"RemoveUserRoleByUserIDAndRoleID","summary":"Takes a role away from one org member, by user id and role id — someone else, never the caller.","description":"Takes a role away from one org member, by user id and role\nid — someone else, never the caller. Admin gate.","tags":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/variables/query":{"post":{"operationId":"post_v1_o11y_variables_query","summary":"Evaluates a dashboard variable query and returns the values the variable may take.","description":"Evaluates a dashboard variable query and returns the values the\nvariable may take.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardVarsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDashboardVarsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/version":{"get":{"operationId":"get_v1_o11y_version","summary":"Reports the running build: its version, whether an enterprise edition is present (\"N\" in this build), and whether first-user setup has completed.","description":"Reports the running build: its version, whether an enterprise\nedition is present (\"N\" in this build), and whether first-user setup has\ncompleted.\n\nOpen by design; the runtime's own gate is OpenAccess.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yVersionOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/o11y/zeus/hosts":{"get":{"operationId":"GetHosts","summary":"Returns the deployment's host info from Zeus.","description":"Returns the deployment's host info from Zeus. Viewer gate.","tags":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yGettableHostOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"PutHost","summary":"Records the deployment's host in Zeus, overwriting any prior one.","description":"Records the deployment's host in Zeus, overwriting any prior one.\nAdmin gate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableHost"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/o11y/zeus/profiles":{"put":{"operationId":"PutProfile","summary":"Records the deployment's profile in Zeus — how the team uses observability today and what they plan — overwriting any prior one.","description":"Records the deployment's profile in Zeus — how the team uses\nobservability today and what they plan — overwriting any prior one. Admin\ngate.","tags":["o11y"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.PostableProfile"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"o11y"}},"/v1/openapi.json":{"get":{"operationId":"get_v1_openapi.json","summary":"The API description this SDK was generated from","description":"Serves the OpenAPI document for the routes this process actually answers — generated from the live router at request time, not from a checked-in file that can disagree with it.\n\nOn an app it is that app's own surface; on the fleet's front door it is the woven document for every mounted app. Unauthenticated by design: a client has to be able to read the contract before it holds a credential, and the document grants nothing.\n\nRendered once and served as bytes thereafter, so the route table's immutability is what makes a repeat request a memcpy rather than a re-encode of a megabyte document.","x-app":"openapi"}},"/v1/oracles":{"get":{"operationId":"get_v1_oracles","summary":"Reports the on-chain price/data oracles from the graph's O-Chain PriceFeed registry.","description":"Reports the on-chain price/data oracles from the graph's O-Chain\nPriceFeed registry. A reachable graph with no feeds answers an honest empty list;\nan unreachable or erroring graph likewise degrades to an empty list at 200 rather\nthan a 502, so the console never error-toasts. No feed is ever fabricated.","tags":["oracles"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/oraclesOut"}}},"description":"ok"}},"x-app":"explorer"}},"/v1/org/settings":{"delete":{"operationId":"delete_v1_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"}},"/v1/org/settings/list":{"delete":{"operationId":"delete_v1_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["org"],"x-app":"github.com/hanzoai/ai"}},"/v1/orgs":{"post":{"operationId":"post_v1_orgs","summary":"Onboard creates the caller's organization.","description":"Onboard creates the caller's organization. Two flows, keyed on whether the caller\nalready has a home org (mirrors app/onboard/route.ts):\n\n  - FIRST-RUN (no home org): create + MOVE the user in as admin, so their next\n    JWT carries the new owner and the cloud scopes everything to it. This is the\n    path a fresh OAuth sign-up takes, from the sign-up application's org.\n  - ADDITIONAL (owner set): create the org but do NOT move the user — a move\n    changes their IAM owner (stripping a SuperAdmin's status + orphaning their\n    current org). They reach the new org via the OrgSwitcher, which re-scopes\n    X-Org-Id without touching IAM membership. A personal-org request from someone\n    who already has an org is meaningless → 409.","tags":["orgs"],"requestBody":{"content":{"application/json":{"example":{"name":"Acme"},"schema":{"$ref":"#/components/schemas/onboardReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/onboardResp"}}},"description":"ok"}},"x-app":"account"}},"/v1/orgs/{org}/entitlements":{"get":{"operationId":"get_v1_orgs_by_org_entitlements","summary":"Get lists the products an org has ENABLED — its own intent, which the console's paid-product sidebar reads to decide what to show.","description":"Get lists the products an org has ENABLED — its own intent, which the console's\npaid-product sidebar reads to decide what to show. It is distinct from what the\norg's plan ENTITLES it to (that is GET /v1/entitlements, resolved from commerce).\n\nA caller may only read its OWN org's row; a platform super admin may read any.","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/entitlementsView"}}},"description":"ok"}},"x-app":"entitlements"},"post":{"operationId":"post_v1_orgs_by_org_entitlements","summary":"Post turns products on or off for an org and returns the enabled set afterwards.","description":"Post turns products on or off for an org and returns the enabled set afterwards.\n\nA product may only be ENABLED if the org's plan already ENTITLES it, so enabling\nnever spends new money — a product the plan does not grant answers 402 and the\nconsole routes that to an upgrade prompt. DISABLING is never gated. A platform\nsuper admin bypasses the plan check (operator comp/grant) and may target any org;\neveryone else may only change their own. Commerce unreachable is a 503, never an\nimplicit yes.","tags":["orgs"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"add":["chat"],"remove":["engine"]},"schema":{"$ref":"#/components/schemas/mutateReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/entitlementsView"}}},"description":"ok"}},"x-app":"entitlements"}},"/v1/payments":{"post":{"operationId":"takePayment","summary":"Take a card payment and credit the org's balance","description":"Takes a payment: charges a single-use card token and credits the caller's org\nbalance, exactly once.\n\nThis is the operation behind \"collect money from a customer\". It runs the SAME\ncore the console's card top-up runs (commerce billing.TakePayment), so the\nserver-side amount bounds, the idempotency guard and the ledger credit are\nshared rather than reimplemented — a second charge path would eventually\ndouble-charge somebody.\n\nThe ORG is the caller's, taken from the validated principal and never from the\ninput, so a payment can only ever credit the account of whoever made the call.\n\nA payment is RISK-SCREENED before the card is charged, so this can be refused\nwithout any money moving: 403 means the screen did not authorise it, and 503 means\nthe screen could not reach a decision — that one is worth retrying, and no charge\nwas attempted either way.\n\nSend an idempotencyKey. An agent retries by construction, and the key is what\nturns a retry into a replay of the first receipt instead of a second charge.\n\nThe answer states whether it settled in SANDBOX or live mode (`test`), and\ncarries the processor's own reference (`processorRef`) so the charge can be\nreconciled against the processor rather than taken on trust.\n\nA named builder, not a closure, so zipdoc can lift this prose into the registry.\n\nIt BUILDS the handler rather than being it, because the screen has to sit inside\nthe value every projection of this op dispatches to — see exposePayments. `charge`\nis the money move, `take` is the screened door onto it, and the only registrable\none is the second.","tags":["payments"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentOut"}}},"description":"created"}},"x-app":"commerce"}},"/v1/payments/{id}":{"get":{"operationId":"getPayment","summary":"Read one settled payment by its id","description":"Reads one settled payment out of the caller's org ledger.\n\nThe org scopes the read by construction — the ledger is namespaced to it — so\nan id belonging to another tenant is simply not found rather than found and\nthen filtered. A ledger row that is not a payment is likewise not found, so\nthis cannot be used to walk the org's usage debits.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["payments"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the ledger transaction id a payment returned.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRecord"}}},"description":"ok"}},"x-app":"commerce"}},"/v1/pipelines":{"get":{"operationId":"get_v1_pipelines","summary":"Returns one build-and-deploy pipeline per app, with its latest run.","description":"Returns one build-and-deploy pipeline per app, with its latest run.\n\nIt returns one pipeline per application in the caller's org — its repo or image\nsource, its current status, and when its most recent deployment ran and how long\nit took. A pipeline is a PROJECTION of an app plus its newest deployment, not a\nseparate record: it comes into existence with the app and is triggered only\nthrough /deploy, never here. Requires a validated principal; 403 without one.","tags":["pipelines"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pipelineBoard"}}},"description":"ok"}},"x-app":"platform"}},"/v1/plans":{"get":{"operationId":"get_v1_plans","summary":"Returns the Hanzo cloud plan catalog: every cloud tier with its price, included capacity, limits and feature list, scoped to the caller's catalog.","description":"Returns the Hanzo cloud plan catalog: every cloud tier with its\nprice, included capacity, limits and feature list, scoped to the caller's\ncatalog. A reseller org sees its own overrides in place of the canonical\nrecords it has replaced, and the canonical record for every tier it has not.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/blockchain":{"get":{"operationId":"get_v1_plans_blockchain","summary":"Returns the blockchain RPC plan catalog: the tiers metered in monthly compute units, with their prices, limits and overage terms.","description":"Returns the blockchain RPC plan catalog: the tiers metered\nin monthly compute units, with their prices, limits and overage terms. It is\nthe canonical catalog for every caller — these plans carry no reseller\noverrides.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/cloud":{"get":{"operationId":"get_v1_plans_cloud","summary":"Returns the cloud plan catalog.","description":"Returns the cloud plan catalog. It is the same section\nListCloudPlans answers and a separate operation because it is a separate\naddress, and an address is what every projection keys on.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/dns":{"get":{"operationId":"get_v1_plans_dns","summary":"ListDNSPlans returns the DNS plan catalog: the tiers priced on zones, records per zone and queries per day.","description":"ListDNSPlans returns the DNS plan catalog: the tiers priced on zones, records\nper zone and queries per day. It is the canonical catalog for every caller —\nthese plans carry no reseller overrides.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/entitlements/{id}":{"get":{"operationId":"get_v1_plans_entitlements_by_id","summary":"Returns what one plan GRANTS and not what it costs: the canonical namespaced entitlement block and the flat license-feature list derived from it.","description":"Returns what one plan GRANTS and not what it costs: the\ncanonical namespaced entitlement block and the flat license-feature list\nderived from it. It is the entitlement half of ResolvePlan, over the same\ncatalog and the same 404 for an id no catalog holds — the read a licensing or\nquota gate makes.","tags":["plans"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the plan's catalog id or slug — \"pro\", \"team\", \"world-enterprise\",\n\"rpc-growth\". Both are matched, so a slug resolves the plan it names.","schema":{"type":"string"},"example":"team"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planEntitlements"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/entries":{"get":{"operationId":"get_v1_plans_entries","summary":"The raw plan authority rows","description":"Returns every plan row as stored — the administrative view behind the public plan catalog. The plan authority is cross-tenant pricing data, so the gate is a PLATFORM admin enforced by the handler itself: an org-level admin is refused 403 no matter what they may do inside their own org.","tags":["plans"],"x-app":"commerce"},"post":{"operationId":"post_v1_plans_entries","summary":"Add a subscription plan","description":"Creates a plan from the body and answers it at 201. The slug is required and globally unique — a duplicate is 409 — and the row is marked authoritative on creation, so the corrective seed will leave it alone. Price, annual price and the contact-sales flag are stored exactly as sent, never coerced, so the difference between a free plan and a quote-only plan survives. PLATFORM admin only.","tags":["plans"],"x-app":"commerce"}},"/v1/plans/entries/{slug}":{"delete":{"operationId":"delete_v1_plans_entries_by_slug","summary":"Remove a plan from the authority","description":"Deletes the addressed plan and answers 204. It removes the plan from the catalog buyers choose from; it does not touch subscriptions already sold against it, which keep their stored plan id. PLATFORM admin only — an org-level admin is refused 403 — and an unknown slug is 404.","tags":["plans"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_plans_entries_by_slug","summary":"Edit a plan, leaving the fields you omit alone","description":"Loads the addressed plan, applies the body over it and answers the stored result, so a partial edit never silently zeroes a price or the contact-sales flag. The slug is IMMUTABLE: a body naming a different slug is rejected outright before anything is written, because a rename would orphan every subscription that stored the old id — deprecate and create instead. An admin edit marks the row authoritative so the seed stops correcting it. PLATFORM admin only; an unknown slug is 404.","tags":["plans"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/plans/gpu":{"get":{"operationId":"get_v1_plans_gpu","summary":"ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPUs, host memory and hourly price.","description":"ListGPUTiers returns the rentable GPU configurations, each with its accelerator\ncount and model, VRAM, vCPUs, host memory and hourly price.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planTierList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/health":{"get":{"operationId":"get_v1_plans_health","summary":"Health reports that the plans subsystem is mounted and serving.","description":"Health reports that the plans subsystem is mounted and serving. It answers from\nthe process itself and consults neither the catalog bundle nor the goja host,\nso it stays \"ok\" while either is degraded.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"example":{"service":"plans","status":"ok"},"schema":{"$ref":"#/components/schemas/planHealth"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/policy":{"get":{"operationId":"get_v1_plans_policy","summary":"Returns the published pricing policy: whether pricing is transparent, the revenue-sharing terms (idle compute resale and the open-source share) and the principles the catalog is priced by.","description":"Returns the published pricing policy: whether pricing is\ntransparent, the revenue-sharing terms (idle compute resale and the open-source\nshare) and the principles the catalog is priced by.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/regions":{"get":{"operationId":"get_v1_plans_regions","summary":"Returns the regions cloud capacity is offered in, each with its display name and physical location.","description":"Returns the regions cloud capacity is offered in, each with its\ndisplay name and physical location.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planRegionList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/resolve/{id}":{"get":{"operationId":"get_v1_plans_resolve_by_id","summary":"Resolves one plan to everything a consumer of the catalog needs at once: its canonical entitlement block, the flat license-feature list a signed license carries, its billing reference, and the catalog it came from.","description":"Resolves one plan to everything a consumer of the catalog needs at\nonce: its canonical entitlement block, the flat license-feature list a signed\nlicense carries, its billing reference, and the catalog it came from. The id\nmay be the plan's id or its slug, and it is resolved against the caller's\ncatalog, so a reseller's override wins over the canonical record. An id no\ncatalog holds answers 404.","tags":["plans"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the plan's catalog id or slug — \"pro\", \"team\", \"world-enterprise\",\n\"rpc-growth\". Both are matched, so a slug resolves the plan it names.","schema":{"type":"string"},"example":"pro"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planResolution"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/schema":{"get":{"operationId":"get_v1_plans_schema","summary":"Returns the two JSON Schema documents this surface speaks: entitlements.schema.json, which declares every entitlement key with its type, unit and enum, and plan.schema.json, which a catalog plan record conforms to.","description":"Returns the two JSON Schema documents this surface speaks:\nentitlements.schema.json, which declares every entitlement key with its type,\nunit and enum, and plan.schema.json, which a catalog plan record conforms to.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planSchemas"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/seed":{"post":{"operationId":"post_v1_plans_seed","summary":"Seed the embedded plan catalog, without overwriting administrative edits","description":"Upserts the shipped plan rows and answers how many were created and how many corrected. It is idempotent and non-destructive — a row an administrator authored or edited is left as it stands — so it is safe against a live authority and fills only what is missing or has drifted. PLATFORM admin only, and a deployment with no seed source wired answers 500 rather than quietly seeding nothing.","tags":["plans"],"x-app":"commerce"}},"/v1/plans/storage":{"get":{"operationId":"get_v1_plans_storage","summary":"Returns the block-storage price block: the price per GB per month and the volume size bounds a cloud plan may attach.","description":"Returns the block-storage price block: the price per GB per\nmonth and the volume size bounds a cloud plan may attach.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/subscriptions":{"get":{"operationId":"get_v1_plans_subscriptions","summary":"Returns the subscription ladder — the personal and team tiers a customer buys to use the cloud, each with its monthly and annual price, seat rules, limits and billing reference.","description":"Returns the subscription ladder — the personal and team\ntiers a customer buys to use the cloud, each with its monthly and annual price,\nseat rules, limits and billing reference. Scoped to the caller's catalog.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/tools":{"get":{"operationId":"get_v1_plans_tools","summary":"Returns the per-use price of every metered tool — web search, code interpreter, image generation, speech — each with the unit it is billed in.","description":"Returns the per-use price of every metered tool — web search,\ncode interpreter, image generation, speech — each with the unit it is billed\nin.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planToolList"}}},"description":"ok"}},"x-app":"plan"}},"/v1/plans/vocab":{"get":{"operationId":"get_v1_plans_vocab","summary":"Returns the entitlement key vocabulary: every key with its namespace, JSON type, nullability, unit, enum and title, the list of namespaces, and the engine features a license can grant.","description":"Returns the entitlement key vocabulary: every key with\nits namespace, JSON type, nullability, unit, enum and title, the list of\nnamespaces, and the engine features a license can grant. It is derived from\nentitlements.schema.json on every call, so it cannot fall behind the schema.","tags":["plans"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planVocab"}}},"description":"ok"}},"x-app":"plan"}},"/v1/platform/apps":{"get":{"operationId":"get_v1_platform_apps","summary":"What this organization has declared, and what CD did with it","description":"Returns the declarations in the caller's own org directory, each joined with the Hanzo CD Application reconciling it — sync verdict, health, the universe commit last applied. `cd` is null for a declaration the delivery plane has no Application for, which is the normal state of one that exists only on a branch.\n\nIf the delivery plane cannot be read, the declarations are still returned and `cdUnavailable` says why. An unreadable plane never renders as \"nothing has been reconciled\".","tags":["platform"],"x-app":"platform"},"post":{"operationId":"post_v1_platform_apps","summary":"Deploy an app through cd.hanzo.ai","description":"Builds a git repository into an image and writes the declaration that names it — a values file in `hanzoai/universe` under `charts/app/values/\u003cnamespace\u003e/\u003cname\u003e.yaml`, which the `fleet` ApplicationSet renders as one Application. That file IS the deployment: nothing else has to be applied.\n\n`mode` decides whether anything can go live. The default, `branch`, pushes to `deploy/\u003cnamespace\u003e/\u003cname\u003e/\u003ctag\u003e` and returns a review URL; the generator reads main, so a branch declaration deploys NOTHING and merging the review is the deliberate act. `commit` writes main, and proves the image is pullable first — a declaration naming an image the registry cannot serve is an ImagePullBackOff with no rollback path.\n\nOmit `tag` to build; give it to declare an image an earlier call already built, which is how a green build is released without rebuilding it.\n\nAn org is its name: the values DIRECTORY, the destination NAMESPACE and the AppProject FENCE are all `\u003corg\u003e`, and the image is `\u003cregistry\u003e/\u003corg\u003e/\u003capp\u003e`. None of them is a request field — the directory decides what CD admits the sync under and the repository decides what the cluster pulls, so a caller who could name either could reach outside its own org.\n\n`org` is an ACT-AS, not a placement field: it defaults to the caller's own, and naming another requires SuperAdmin. So does naming a RESERVED org — the platform's own namespace family (the brands and their environments, the control and delivery planes, `admin`) — even when it is the caller's own, because an IAM org named `kube-system` does not own Kubernetes. Both refuse rather than downgrade, so an escape attempt is never indistinguishable from a normal request.\n\nA host outside the caller's org subtree is refused: claim and verify a custom domain first.","tags":["platform"],"x-app":"platform"}},"/v1/platform/apps/{app}":{"get":{"operationId":"get_v1_platform_apps_by_app","summary":"One declaration","description":"The values file for one app as git declares it: image repository and tag, hosts, replicas, and whether CD is automated on it. 404 when this organization declares no such app.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"platform"}},"/v1/platform/apps/{app}/cd":{"get":{"operationId":"get_v1_platform_apps_by_app_cd","summary":"One app's reconciliation","description":"The Hanzo CD Application for one declaration, on its own — the poll a deploy view makes while it waits, without re-reading the whole inventory. 404 while the declaration exists only on a branch, because the generator reads main.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"platform"}},"/v1/platform/cd":{"get":{"operationId":"get_v1_platform_cd","summary":"The delivery plane","description":"Every Hanzo CD Application this caller may observe, with its sync verdict, health, the universe revision last applied, and whether automation and self-heal are on. A SuperAdmin sees the fleet; an org admin sees only Applications whose destination namespace IS its own organization, and never a reserved one.\n\nA cluster with no CD installed answers an empty plane. A plane that cannot be READ answers 503 and says why — the two are opposite facts and never share a shape.","tags":["platform"],"x-app":"platform"}},"/v1/platform/ci":{"get":{"operationId":"get_v1_platform_ci","summary":"Continuous integration (not wired)","description":"Answers 501. The forge's Actions runs need a Forgejo API client and this deployment has none; an empty run list would be indistinguishable from a forge with no runs.","tags":["platform"],"x-app":"platform"}},"/v1/platform/fleet":{"get":{"operationId":"get_v1_platform_fleet","summary":"Returns the platform's own service tier, and where it has drifted.","description":"Returns the platform's own service tier, and where it has drifted.\n\nIt returns the board for the services the PLATFORM itself runs — iam, kms,\ngateway and the rest — as `{apps, summary}`: per service its environment, health,\nphase, the image tag its CR DECLARES, the tag actually running, and the drift\nbetween them, plus a summary counting the board green, yellow and red.\n\nThis is not a customer surface. `/v1/platform/projects/:project/apps` is a\ntenant's apps; this is the tier those tenants run ON, which is why the two are\nnamed differently rather than sharing a prefix.\n\nAdmission is scoped at the SCAN, before any CR is read: a platform SuperAdmin\nobserves the whole fleet, an org admin observes only their own org's namespaces,\nand an org that owns none gets an empty board — a non-super caller never even\nlists another org's services. Narrow further with `env`, `health`, `org`, or\n`drift=1` for only what has drifted.\n\nIt degrades honestly rather than failing whole: a namespace that does not exist\nis skipped, and a running-state read the caller cannot make leaves the running\ntag empty — an unknown, never a guess — while the declared, health and phase\ncolumns still render.","tags":["platform"],"parameters":[{"name":"env","in":"query","required":false,"description":"Env narrows to one lifecycle env: main, test or dev.","schema":{"type":"string"}},{"name":"health","in":"query","required":false,"description":"Health narrows to one health colour: green, yellow or red.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org narrows to one image namespace.","schema":{"type":"string"}},{"name":"drift","in":"query","required":false,"description":"Drift is `1` or `true` to show only rows that have actually drifted. It is\na STRING and not a bool because those two spellings are exactly what the\nboard has always accepted, and a bool would silently widen that to `?drift`\nalone and to `TRUE` — a behaviour change wearing a type change's clothes.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/driftBoard"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/fleet/{app}":{"get":{"operationId":"get_v1_platform_fleet_by_app","summary":"Returns one platform service, resolved to production by default.","description":"Returns one platform service, resolved to production by default.\n\nIt returns a single platform service by its CR name, with the same\ndeclared-versus-running and drift facts the board carries. The name must be a\nDNS-1123 label; anything else is 400.\n\nNamespaces are scanned in lifecycle order — main, then test, then dev — and the\nfirst match wins, so a bare name resolves to PRODUCTION. The scan covers only the\nnamespaces the caller is authorized for, so an org admin can never read a service\noutside their own org, and a name found in none of them is 404 rather than a leak.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"description":"App is the service's CR name, from the path. It must be a DNS-1123 label.","schema":{"type":"string"}},{"name":"env","in":"query","required":false,"description":"Env narrows the scan to one lifecycle env: main, test or dev. Omitted, the\nnamespaces are scanned in lifecycle order and the first match wins, so a\nbare name resolves to PRODUCTION.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/fleet/{app}/deploy":{"post":{"operationId":"post_v1_platform_fleet_by_app_deploy","summary":"Rolls a platform service's pods, in a named environment.","description":"Rolls a platform service's pods, in a named environment.\n\nIt triggers a rolling restart of one platform service's Deployment by stamping a\nfresh restart annotation, and answers 202 with the app, the namespace, the\nenvironment and the timestamp. It restarts pods; it does NOT change the image — a\nversion change is the release path, not this.\n\nSuperAdmin ONLY, and deliberately narrower than the read gate beside it. The only\nnamespaces this board touches are the platform's own tier, so a restart here\nrecycles a SHARED service every tenant depends on. A brand-org admin is a\ncustomer-org admin, not a platform operator: observing the board is bounded and\naudited, and restarting production identity is not.\n\n`?env=main|test|dev` is REQUIRED — a bare call does not default to production,\nwhich is what closes the fat-finger and confused-deputy hazard — and any other\nvalue is 400. A service with no Deployment to restart in that environment is 404.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"description":"App is the service's CR name, from the path. It must be a DNS-1123 label.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/restartRef"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/restarted"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/platform/health":{"get":{"operationId":"get_v1_platform_health","summary":"Reports whether this control plane can actually deploy anything.","description":"Reports whether this control plane can actually deploy anything.\n\nA real probe, not a status page. It answers 200 only when the metadata store is\nopen AND the cluster is genuinely reachable — proved by LISTING the operator App\nCRD, which settles reachability and CRD presence in one bounded call, and which\nis the exact question every deploy depends on. Anything else is 503 carrying the\nreal reason and whether the CRD was found.\n\nA constructed cluster client proves nothing — it is built from a kubeconfig, not\nfrom a reachable apiserver — so this deliberately spends a round trip rather than\nreporting `ok` while every deploy fails. Not admin-gated: liveness has to be\nprobe-able without a credential.","tags":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/readiness"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/readiness"}}},"description":"service unavailable"}},"x-app":"platform"}},"/v1/platform/projects":{"get":{"operationId":"get_v1_platform_projects","summary":"Returns your org's projects, each with how many apps live under it.","description":"Returns your org's projects, each with how many apps live under it.\n\nIt lists the caller org's projects with the number of platform applications in\neach. A project is IAM's resource — it is created and deleted at\n/v1/iam/projects, never here — so this is the ONE projection IAM cannot serve:\nthe project plus what the platform has put under it.\n\nRequires a validated principal; 403 without one, and the org comes from that\nvalidated identity rather than a request header. This is the console's first\nauthenticated read, so a project store that is not yet initialised degrades to\nan EMPTY list rather than a 500 — a new org genuinely has zero projects — and\nthe real cause is surfaced to operators instead of to the caller.","tags":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectView"},"type":"array"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}":{"get":{"operationId":"get_v1_platform_projects_by_project","summary":"Returns one project and its app count.","description":"Returns one project and its app count.\n\nIt returns a single project of the caller's org with the number of platform\napplications under it. A project this org does not have is 404, which is also\nwhat another tenant's project looks like from here. Requires a validated\nprincipal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project's name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps":{"get":{"operationId":"get_v1_platform_projects_by_project_apps","summary":"Returns the applications in one project, with what the cluster says about them.","description":"Returns the applications in one project, with what the cluster says\nabout them.\n\nIt lists the caller org's applications under one project. Each row carries the\nstored record and, for an app that is live or deploying, the LIVE phase and\nhealth read from its operator Service CR; an app with sealed env also carries\nits secret-sync state. Those cluster reads are best-effort — an unreachable\ncluster leaves those fields empty and never blocks the listing.\n\nThe project must exist in IAM for this org, or the answer is 404; the `default`\nproject is implicit and always accepted, because it is part of what an org IS.\nRequires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project's name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/appView"},"type":"array"}}},"description":"ok"}},"x-app":"platform"},"post":{"operationId":"post_v1_platform_projects_by_project_apps","summary":"Creates an application from a git repo or a container image.","description":"Creates an application from a git repo or a container image.\n\nIt registers a new application under one of the caller org's projects and\nanswers 201 with it. Creating does NOT deploy: the app lands in `draft` and\nnothing reaches the cluster until /deploy.\n\n`source` is `git` — which requires `repo.url` — or `image`, which requires\n`image.repository`; anything else is 400. A git app builds with zero-config\n`pack` by default and may opt into `dockerfile`; an image app never builds. The\nrepo URL and Dockerfile path are validated here against the SAME allowlist the\nprivileged build enforces, so an unsafe source is refused before it is ever\npersisted.\n\nThe `slug` is the app's identity in the cluster: given or derived from `name`,\nit must match `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`, and a slug already used in\nthis project is 409. `replicas` and `storageGb` are clamped to the deployment's\nlimits rather than refused.\n\nEnv keys must match `^[A-Za-z_][A-Za-z0-9_]*$`. A variable marked `secret: true`\nis SEALED into KMS and its plaintext is never written to the database — and if\nKMS is unavailable the create fails 503 rather than falling back to storing a\nsecret in the clear.\n\nThe app is seeded with its canonical default host, so it has a working HTTPS URL\nthe moment it deploys. A bare custom domain cannot be attached here — it has to\ngo through add-domain and DNS verification first. Requires a validated\nprincipal; 403 without one, and every cluster object it will later create lands\nin that org's own `tenant-\u003corg\u003e` namespace.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project to create the application under, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/createAppReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appView"}}},"description":"created"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}":{"delete":{"operationId":"delete_v1_platform_projects_by_project_apps_by_app","summary":"Deletes an application and tears down what it runs.","description":"Deletes an application and tears down what it runs.\n\nIt removes the application record and tears down what it owns in the org's\ntenant namespace — its operator Service CR and its KMSSecret — then answers 204.\nAn app this org and project do not have is 404, never a silent success.\n\nTeardown is best-effort by design: a cluster that refuses or is unreachable does\nnot block the delete, so the record cannot be left orphaned behind a broken\ncluster; the failure is logged for operators and the orphan reaper reconciles\nit. Requires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"platform"},"get":{"operationId":"get_v1_platform_projects_by_project_apps_by_app","summary":"Returns one application, with its live phase, health and secret sync.","description":"Returns one application, with its live phase, health and secret sync.\n\nIt returns a single application of the caller's org together with what the\ncluster currently reports for it: the operator Service CR's phase and health,\nand whether its sealed env has synced. An app this org and project do not have\nis 404. Requires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/deploy":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_deploy","summary":"Deploys the app — building it first if it comes from git.","description":"Deploys the app — building it first if it comes from git.\n\nIt starts a new, monotonically versioned deployment of the app and answers 202\nwith the deployment record. A 202 is an ACCEPTED deployment, not a live one.\n\nAn IMAGE app deploys the tag you name (falling back to the app's tag, then\n`latest`) by writing its operator Service CR; the operator reconciles it to\nrunning. A GIT app launches an in-cluster BuildKit Job at `commit` — or the app's\nbranch — and comes back in `building`; the Service CR is applied later, by the\nreconciler, once the Job succeeds. The reconciler is restart-safe, so a build in\nflight survives a cloud restart.\n\nDeploys are bounded per org: over the concurrent-deploy cap is 429 and NOTHING is\nrecorded, so a rejected deploy leaves no phantom in the history. An unreachable\ncluster is 503 but still records an honest `error` deployment, because a deploy\nthat was attempted and failed must not be indistinguishable from one never made.\nEvery other failure is likewise recorded in its real terminal state.\n\nThis is metered work: a git build is billed to the org's ledger in wall-clock\nbuild minutes once the Job finishes, and the running deployment is billed for its\ncompute per tick for as long as it stays live. Requires a validated principal; 403\nwithout one, and everything is written into that org's own `tenant-\u003corg\u003e`\nnamespace.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deployReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deploymentView"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/deployments":{"get":{"operationId":"get_v1_platform_projects_by_project_apps_by_app_deployments","summary":"Returns an app's deployment history.","description":"Returns an app's deployment history.\n\nIt lists every deployment recorded for one of the caller org's applications,\nnewest version first, each with its version, status, source, commit and image.\nFailed and superseded attempts are included — that is the point of a history.\nRequires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/deploymentView"},"type":"array"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/deployments/{id}":{"get":{"operationId":"get_v1_platform_projects_by_project_apps_by_app_deployments_by_id","summary":"Returns one deployment of one app.","description":"Returns one deployment of one app.\n\nIt returns a single deployment by id, scoped to the named application of the\ncaller's org — so an id belonging to another app or another tenant is 404, not a\nread. Requires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"description":"ID is the deployment's id, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deploymentView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/deployments/{id}/logs":{"get":{"operationId":"get_v1_platform_projects_by_project_apps_by_app_deployments_by_id_logs","summary":"Returns real logs for a deployment — the build's, then the app's.","description":"Returns real logs for a deployment — the build's, then the app's.\n\nIt returns the deployment's recorded status timeline together with LIVE pod logs\npulled from the cluster: the build pod's output while a git build is running, and\nthe running app's output once it is deployed. The `source` field says which of\nthe two the body is — `build`, `app` or `none` — so a console can label the pane\nhonestly.\n\nIt never fabricates log content. When no pod exists yet, or the cluster is\nunreachable, it degrades to the recorded timeline and says so. Every cluster read\nis confined to the caller org's own namespaces and time-boxed. Requires a\nvalidated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"description":"ID is the deployment's id, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deployLogs"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/domains":{"get":{"operationId":"get_v1_platform_projects_by_project_apps_by_app_domains","summary":"Returns every hostname this app answers on.","description":"Returns every hostname this app answers on.\n\nIt lists the app's hosts: the permanent default host it was born with, any\norg-subtree hosts attached to it, and every custom host claimed for it with its\nverification state and, while pending, the DNS challenge records to publish. Live\nendpoint status for each host is observed from the cluster. Requires a validated\nprincipal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/domainView"},"type":"array"}}},"description":"ok"}},"x-app":"platform"},"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_domains","summary":"Attaches a hostname — instantly if you already own it, otherwise with a DNS challenge.","description":"Attaches a hostname — instantly if you already own it, otherwise with a\nDNS challenge.\n\nIt attaches `host` to the app, and which of two things happens depends on who\nowns the name. A host inside the caller org's own subtree is structurally owned,\nso it goes ACTIVE immediately and answers 201. A bring-your-own host is claimed\nas PENDING and answers the DNS challenge records to publish; it is NOT rendered\ninto the app's ingress until /verify passes.\n\nClaims are globally unique. A host already claimed by another organization is\n409, and so is one claimed by a different app in your own; re-adding this app's\nOWN claim is idempotent and answers its current state at 200. The default host is\nalways attached and re-adding it is 409. A host under the platform's shared apex\nthat is not the caller's own subtree is 403 — it belongs to whoever owns that\nsubtree and can never be grabbed through the custom path.\n\n`host` must be a valid DNS hostname; anything else is 400. Requires a validated\nprincipal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/addDomainReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/domainView"}}},"description":"ok"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/domainView"}}},"description":"created"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/domains/{host}":{"delete":{"operationId":"delete_v1_platform_projects_by_project_apps_by_app_domains_by_host","summary":"Detaches a hostname and releases the claim.","description":"Detaches a hostname and releases the claim.\n\nIt drops the host from the app's ingress and releases any custom claim on it, so\nthe name becomes claimable again — by this org or any other. Answers 204.\n\nThe default host is permanent and cannot be removed: that is 400, not 404. A host\nthat is neither attached nor claimed here is 404. Requires a validated principal;\n403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the hostname, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/domains/{host}/verify":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_domains_by_host_verify","summary":"Checks a custom domain's DNS and turns it on if it passes.","description":"Checks a custom domain's DNS and turns it on if it passes.\n\nIt runs the DNS challenge check for a pending custom host and, when it passes,\nmarks the host verified and renders it into the app's ingress so it starts\nserving.\n\nA check that RAN and did not pass is not an error: it answers 200 with the host\nstill pending and the reason in `detail`, so a console can show the operator what\nDNS is actually returning. An already-verified host answers as-is without\nre-checking. A host not claimed by this app is 404. Requires a validated\nprincipal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the hostname, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/domainView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/env":{"put":{"operationId":"put_v1_platform_projects_by_project_apps_by_app_env","summary":"Replaces an app's environment variables.","description":"Replaces an app's environment variables.\n\nIt writes the app's whole environment set and answers the updated application.\nThis is the one post-create write path for env, and it REPLACES rather than\nmerges: a variable absent from the body is gone, and a secret dropped from the\nset leaves the app's Secret on its next deploy.\n\nKeys must match `^[A-Za-z_][A-Za-z0-9_]*$`. A value marked `secret: true` is\nsealed into KMS and blanked in the database, so plaintext is never persisted —\nand the write fails 503 if KMS is unavailable rather than storing one in the\nclear.\n\nThe rule worth knowing: this does not restart anything. Once the app has been\ndeployed the secret sync is re-declared immediately so the operator\nre-materialises the Secret, but RUNNING pods keep the environment they started\nwith until their next deploy or restart. Requires a validated principal; 403\nwithout one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/setEnvReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/preview":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_preview","summary":"Puts a branch on its own URL.","description":"Puts a branch on its own URL.\n\nIt deploys an already-built `image` to a per-branch preview and answers its URL,\nthe branch, the preview's slug and the deployment. The preview is a FIRST-CLASS\napplication named `\u003capp\u003e-\u003cbranch\u003e` in the same project and tenant namespace, with\nits own default host — so it is completely isolated from production while reusing\nthe same deploy mechanic. Re-previewing a branch converges that same target in\nplace rather than stacking another one.\n\nIt carries NO environment variables, deliberately: a preview never inherits\nproduction's secrets. It also does not build — `image` is required and must\nalready exist, and `branch` defaults to the parent app's. A branch that does not\nresolve to a valid slug distinct from the parent's is 400. Requires a validated\nprincipal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the parent application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the parent application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/previewReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/previewView"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/promote":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_promote","summary":"Promotes an already-built release to the app.","description":"Promotes an already-built release to the app.\n\nIt redeploys an image that already exists — named either by `deploymentId`, which\npromotes that deployment's exact built image, or by `tag`, resolved the same way\na deploy resolves one. One of the two is required; neither is 400.\n\nPromotion never builds. A deployment that carries no built image cannot be\npromoted and is 400, and a deployment id outside this app is 404. It runs through\nthe same deploy core as everything else, so it takes a NEW version number and is\nsubject to the same per-org concurrency cap. Requires a validated principal; 403\nwithout one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/promoteReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deploymentView"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/rollback":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_rollback","summary":"Goes back to the previous release.","description":"Goes back to the previous release.\n\nIt redeploys a prior image: the one named by `deploymentId`, or — with no body —\nthe newest earlier deployment that carries a real built image and did not error,\nskipping the release currently live. An app with nothing earlier to return to is\n400.\n\nA rollback is a deploy of an old image, not a rewind: it takes a NEW version\nnumber and appends to the history rather than erasing what came after. Both\nlookups are scoped to this app and org, so another tenant's image can never be\nrolled in. Requires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/rollbackReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deploymentView"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/start":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_start","summary":"Starts a stopped app back up.","description":"Starts a stopped app back up.\n\nIt scales the app's Service back to its configured replica count and marks it\nlive, answering the updated application. It does not redeploy: the image already\non the Service CR is what comes back.\n\nThe billing watermark is reset to now as part of starting, so the org is charged\nfor THIS live span and never for the gap the app spent stopped. An app with no\nService CR is 404, an unreachable cluster is 503, and a cluster that refuses the\nscale is 502. Requires a validated principal; 403 without one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/projects/{project}/apps/{app}/stop":{"post":{"operationId":"post_v1_platform_projects_by_project_apps_by_app_stop","summary":"Stops an app without deleting it.","description":"Stops an app without deleting it.\n\nIt scales the app's Service to zero replicas and marks it stopped, answering the\nupdated application. Nothing else is removed — the record, its env, its domains\nand its deployment history all survive, and /start brings it back at the same\nreplica count.\n\nAn app that is not deployed has no Service CR to scale and is 404. An\nunreachable cluster is 503 and a cluster that refuses the scale is 502. Because\nthe pods stop, so does the compute metering. Requires a validated principal; 403\nwithout one.","tags":["platform"],"parameters":[{"name":"project","in":"path","required":true,"description":"Project is the project the application lives under, from the path.","schema":{"type":"string"}},{"name":"app","in":"path","required":true,"description":"App is the application's slug, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appView"}}},"description":"ok"}},"x-app":"platform"}},"/v1/platform/sites":{"get":{"operationId":"get_v1_platform_sites","summary":"Returns every project your org owns.","description":"Returns every project your org owns.\n\nEach row carries the slug, name, framework, visibility, status and live URL —\nthe same rows console and the builder render, because there is only one store\nbehind both. It requires a validated principal (403 without one) and is keyed\nby that principal's org, so it never contains another tenant's project.","tags":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsProject"},"type":"array"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_platform_sites","summary":"Creates a project — the handle a site is deployed and served under — and answers 201 with it in `draft`.","description":"Creates a project — the handle a site is deployed and served\nunder — and answers 201 with it in `draft`.\n\n`name` is required; `slug` is derived from the name when omitted and is the\nidentifier that matters — it becomes the S3 key segment, the public host\n`\u003cslug\u003e.hanzo.app`, and the handle every later call addresses, so it must\nmatch `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$` and may not be a reserved label\nsuch as `api` or `admin`. `framework` is a build hint from a closed set,\ndefaulting to `static`; it never gates a deploy, it only tells CI how to build\na linked repo.\n\nTwo defaults are worth knowing: the analytics beacon is ON unless `analytics`\nis explicitly false, and `visibility` is `public` unless asked otherwise.\nPublishing publicly is free; PRIVATE is the paid feature, and an unfunded org\nasking for it is refused rather than quietly published as public. Creation\nalso provisions the project's data space and a canonical git repo, both\nbest-effort — neither can fail the create.\n\nScope: a validated principal is required (403 without one) and the project is\ncreated in THAT principal's org. The slug is unique per org, so a slug already\nused in the caller's own org is a 409 while the same slug in another org is\nirrelevant.","tags":["platform"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"created"}},"x-app":"projects"}},"/v1/platform/sites/{slug}":{"delete":{"operationId":"delete_v1_platform_sites_by_slug","summary":"Deletes a project and takes its site off the internet.","description":"Deletes a project and takes its site off the internet.\n\nThe metadata delete is authoritative and everything after it is best-effort,\nin this order: the public `\u003cslug\u003e` subdomain binding is released so the slug is\nfree to reclaim, the release rows are dropped so a reclaimed slug never\ninherits the previous owner's rollback menu, the S3 origin is purged under\nBOTH `\u003corg\u003e/\u003cslug\u003e/` and the site's sibling release space, and the edge\ncache-tag is flushed. A failure in any of those is logged and the delete still\nanswers 204 — resurrecting a project because a purge missed would be worse\nthan a leaked prefix.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404 and\nnothing of theirs is touched.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"projects"},"get":{"operationId":"get_v1_platform_sites_by_slug","summary":"Returns one project of yours by slug — its settings, its live URL and the deployment currently serving it.","description":"Returns one project of yours by slug — its settings, its live URL\nand the deployment currently serving it.\n\nScope: a validated principal is required (403 without one) and the lookup is\nkeyed by (org, slug), so another tenant's slug is a 404 exactly like a\nnonexistent one.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"},"patch":{"operationId":"patch_v1_platform_sites_by_slug","summary":"Changes a project's settings, and only the settings you send.","description":"Changes a project's settings, and only the settings you send.\n\nEvery field is optional and absent means \"leave it\": `name` may not be blanked,\n`framework` must stay a known build hint, and `cacheControl` is capped at 256\ncharacters with no newlines (it becomes a response header). `visibility` flips\npublic/private under the same rule as create — public is free, private needs a\nfunded org. `upstream` and `license` are free-text credit for third-party work,\nand sending \"\" clears one. Changing anything reconciles the project's canonical\ngit repo, so a visibility change reaches the source and not just the listing.\n\n`hidden`/`hiddenReason` are platform MODERATION and are ignored unless the\ncaller is a platform admin; they remove a project from the public catalogue\nwithout touching the publisher's own visibility choice, so un-hiding restores\nexactly what they asked for.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to update, from the path. The URL is the addressing\nauthority — a `slug` in the body cannot move the write to another project.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/deploy":{"post":{"operationId":"post_v1_platform_sites_by_slug_deploy","summary":"Upload a built site — this is where a zip goes live","description":"Takes a built site live at `https://\u003cslug\u003e.hanzo.app`. The content type decides the shape: a `zip` or `tar.gz` — raw in the body or as a multipart file part, which is what the platform's upload UI posts — is stored and served immediately, answering 200 with the finished deployment; a JSON body instead queues a build from the site's linked repo and answers 202 with a queued deployment plus, where one could be minted, a scoped upload grant for CI. The git path requires a linked repo (400 without one).\n\nThe hosting gate is fail-closed and runs first, before anything is parsed or uploaded: 402 for an unfunded org, 503 for unreachable commerce, nothing written. The debit lands only on success — a failed upload is never billed and never flips the live site — and a redeploy answers the SAME URL, because slug and apex are stable.\n\nScope: a validated principal is required (403 without one) and the site is resolved within that principal's org, so another tenant's slug is a 404. Object storage must be configured (503); an archive that does not walk is a 400 and one over the size cap is a 413.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"projects"}},"/v1/platform/sites/{slug}/deployments":{"get":{"operationId":"get_v1_platform_sites_by_slug_deployments","summary":"Returns a project's deploy history, newest version first.","description":"Returns a project's deploy history, newest version first.\n\nEvery deploy of the project is a row — uploads, generated sites, and git/CI\nbuilds alike — carrying its version, status, source, commit, live URL, file\ncount and byte count. The short-lived upload grant a queued git deployment was\nhanded is NOT replayed here: it exists only on the 202 that minted it, so a\ngrant cannot outlive its build by being fetched again.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsDeployment"},"type":"array"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/deployments/{id}":{"get":{"operationId":"get_v1_platform_sites_by_slug_deployments_by_id","summary":"Returns one deployment of a project by id.","description":"Returns one deployment of a project by id.\n\nIt is how a console follows a build: the status (`queued`, `uploading`,\n`live`, `error`), the message a failure left, and the URL and prefix it went\nlive at. Like the history, it never replays the upload grant.\n\nScope: a validated principal is required (403 without one). Both the project\nand the deployment are resolved within that principal's org, so a deployment\nof another project — or of another tenant — is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the deployment belongs to, from the path.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"description":"ID is the deployment id, from the path. A deployment of another project —\nor of another tenant's project — is not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployment"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/domains":{"get":{"operationId":"get_v1_platform_sites_by_slug_domains","summary":"Returns every custom hostname this site holds: the live ones, plus any pending claim with the DNS records it still owes.","description":"Returns every custom hostname this site holds: the live ones, plus\nany pending claim with the DNS records it still owes.\n\n`domains` is the routing answer — the hosts that are verified right now —\nwhile `claims` is the full panel, one row per host, each saying whether it is\nlive or pending and, if pending, exactly what to publish.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomains"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_platform_sites_by_slug_domains","summary":"Attaches one or more CUSTOM public hostnames to this org's site.","description":"Attaches one or more CUSTOM public hostnames to this org's site.\n\nBinding a host you do not own would let you shadow it at the edge, so which\noutcome you get depends on whether ownership is already established: a SuperAdmin\nvouches (the operator manages the customer's DNS, so its bind IS the proof) and\nbinds VERIFIED immediately; every other caller, INCLUDING an admin of the\ndeployment's own brand org, has the host CLAIMED as pending and gets the DNS\nchallenge back in `bound[].records`. A pending claim HOLDS the name so nobody\nelse can take it, but it does not route until POST .../domains/{host}/verify\nproves control.\n\nA hostname we operate is refused to a non-vouched caller (those are assigned\nby the platform, never claimed), a host another site already holds is a 409,\nand a name the platform holds is a 400 for EVERY caller — a vouch skips the\nownership proof, never the host table's own invariant. Claims and binds are\nidempotent for the same\n(org, slug), and re-claiming returns the SAME token rather than invalidating a\nrecord the customer has already published. The edge cache-tag is flushed\nafterwards so a newly-verified host serves the current build immediately.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site the hosts attach to, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomainsBind"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsBoundDomains"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/domains/{host}":{"delete":{"operationId":"delete_v1_platform_sites_by_slug_domains_by_host","summary":"Gives a custom hostname back, so the name is free to reuse.","description":"Gives a custom hostname back, so the name is free to reuse.\n\nA claim is FIRST-COME and global, so an add-only surface was not ownership but\na leak: a customer who mistyped a domain, or claimed one they later moved\nelsewhere, could neither reuse it nor let anyone else. This is the third\nwriter that closes it. The release is scoped to (host, org, slug), so it can\nonly ever drop THIS tenant's own claim, and it is IDEMPOTENT: releasing a host\nwe do not hold is a clean 204, never a 404 that would let a caller probe which\nhosts other tenants hold. The edge cache-tag is flushed, since the host stops\nrouting here.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the host is attached to, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the custom hostname, from the path. It is cleaned to its canonical\nform (lowercased, trailing dot dropped) before anything is looked up.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/domains/{host}/verify":{"post":{"operationId":"post_v1_platform_sites_by_slug_domains_by_host_verify","summary":"Checks the DNS challenge for a pending custom hostname and, when it passes, promotes the host so it begins routing at the edge.","description":"Checks the DNS challenge for a pending custom hostname and, when\nit passes, promotes the host so it begins routing at the edge.\n\nIt answers 200 either way, with the host's honest current state: verified once\nthe TXT record is found, still pending — with the records to publish and the\nresolver's own explanation in `detail` — when it is not. A not-yet is not an\nerror: the check ran, DNS simply has not propagated, and the customer retries.\nAn already-verified host is returned unchanged without re-resolving. On a\nsuccessful promotion the edge cache-tag is flushed, since the host routes as\nof that moment.\n\nScope: a validated principal is required (403 without one). Both the site and\nthe claim are resolved within that principal's org, so a host claimed by\nanother tenant is \"not claimed by this site\".","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the host is attached to, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the custom hostname, from the path. It is cleaned to its canonical\nform (lowercased, trailing dot dropped) before anything is looked up.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomain"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/publish":{"post":{"operationId":"post_v1_platform_sites_by_slug_publish","summary":"Promotes a build output into a new release AND goes live with it — create+activate in one call, which is the 99% path.","description":"Promotes a build output into a new release AND goes live with it —\ncreate+activate in one call, which is the 99% path.\n\nIt is exactly the two halves in sequence with no extra semantics, so the\nstaged flow and the one-shot flow can never drift apart: `source` is promoted\nunder the same org-relative rule and the same guards CreateRelease applies,\nthen the site's pointer is flipped to it, the public host is claimed and the\nedge is purged. Idempotent on unchanged bytes — same manifest, same release id,\nno copy — and billed once, after the release exists.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site to publish, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsPublish"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/purge":{"post":{"operationId":"post_v1_platform_sites_by_slug_purge","summary":"Flushes the site's edge cache without redeploying anything.","description":"Flushes the site's edge cache without redeploying anything.\n\nIt invalidates the edge cache-tag `site-\u003corg\u003e-\u003cslug\u003e` and stamps `lastPurgeAt`\n(unix seconds), and it NEVER writes or deletes the S3 origin — the live build\nkeeps serving; only stale copies held at the edge drop, so the next request\nre-fetches the current artifact from origin. Idempotent, and an edge that is\nunconfigured or failing is not fatal: `lastPurgeAt` is still stamped and the\nanswer is still the updated project.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/releases":{"get":{"operationId":"get_v1_platform_sites_by_slug_releases","summary":"Returns a site's releases newest-first, marking the active one — the rollback menu.","description":"Returns a site's releases newest-first, marking the active one —\nthe rollback menu.\n\nEach row carries the release id to activate, the source it was promoted from,\nits object and byte counts, and the URL if it is the one serving. Retention\nbounds the list, so it is the set that can actually still be rolled back to,\nnot a full history.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsRelease"},"type":"array"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_platform_sites_by_slug_releases","summary":"Promotes a build output into a new immutable release WITHOUT serving it — the staged half of publishing, for when you want to check a release before it goes live.","description":"Promotes a build output into a new immutable release WITHOUT\nserving it — the staged half of publishing, for when you want to check a\nrelease before it goes live. Answers 201.\n\n`source` is a path RELATIVE to your org's own storage space: the org segment\nis prepended server-side from the validated principal and the bucket is never\nin the request at all, so a server-side copy can only ever reach bytes your\norg already owns. The prefix is listed, content-addressed (SHA-256 over the\nsorted manifest of key/size/etag), and copied into an immutable\n`\u003corg\u003e/.releases/\u003cslug\u003e/\u003cid\u003e/` prefix; the row is written LAST, so a partial\ncopy is unreachable rather than merely unlikely. Re-publishing an unchanged\nsource is idempotent BY CONSTRUCTION — same bytes, same id, no copy at all.\n\nThe source must contain index.html at its root and stay under the same file\nand byte caps an artifact deploy does (413 past them); a source that changes\nmid-copy is a 409 and the release is abandoned. Each publish also reclaims\nreleases past the retention depth, so a site's release space stays bounded.\nThis is the billable half — the hosting gate runs before any copy, and the\ndebit lands once the release exists.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site to publish, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsPublish"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"created"}},"x-app":"projects"}},"/v1/platform/sites/{slug}/releases/{release}/activate":{"post":{"operationId":"post_v1_platform_sites_by_slug_releases_by_release_activate","summary":"Points the site at an existing release — the go-live, and equally the ROLLBACK.","description":"Points the site at an existing release — the go-live, and\nequally the ROLLBACK.\n\nAim it at an older release and the site serves that one again: releases are\nimmutable and retained to the retention depth, so nothing is rebuilt or\nre-copied and the flip is one atomic statement. Before the flip, two\nconditions run in the order that gives each its own honest answer — the ROW\nsays whether this release exists for this tenant at all (404, with no signal\nabout a foreign id), and only then do the BYTES say whether it can still serve\n(410 GONE when retention has reclaimed them; that rollback target is not\ncoming back, so publish again). Going live also claims the public host and\npurges the edge, so the release is reachable and no cached predecessor is\nserved. NOT billed: no new content is produced, only a pointer moved.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["platform"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site the release belongs to, from the path.","schema":{"type":"string"}},{"name":"release","in":"path","required":true,"description":"Release is the content-addressed release id (\"rel_\" + 32 hex chars), from\nthe path. Anything that is not that shape is not found, rather than being\ninterpolated into a storage prefix.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"ok"}},"x-app":"projects"}},"/v1/plugins":{"get":{"operationId":"get_v1_plugins","summary":"Reports what this deployment actually mounted: every subsystem the composition root declared and whether it is switched on.","description":"Reports what this deployment actually mounted: every subsystem the\ncomposition root declared and whether it is switched on. A plugin here is\nMOUNTED CODE that extends the deployment's own surface — not a tool an agent\ncalls — so this is an inventory and not a tool source. It is read off the same\nboot snapshot every traced request resolves its subsystem label against, so it\ncannot drift from what is serving. Enabled-only by default, because a caller\nasking what this deployment can do wants what is running; ?all=true adds the\nconfigured-but-off ones.","tags":["plugins"],"parameters":[{"name":"all","in":"query","required":false,"description":"All includes the configured-but-disabled subsystems too, but only when it is\nexactly the string \"true\". Otherwise only the running ones are reported.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pluginMountList"}}},"description":"ok"}},"x-app":"tools"}},"/v1/plugins/authored":{"get":{"operationId":"get_v1_plugins_authored","summary":"Lists the plugins the caller's org BUILT, newest first, each with the TypeScript as authored.","description":"Lists the plugins the caller's org BUILT, newest first,\neach with the TypeScript as authored. That is a different set with a different\nlifecycle from GET /v1/plugins, which reports the subsystems this deployment\nmounted. The bundled CommonJS the runtime executes is never included, and\nneither is any credential — a plugin names the connectors provider it needs and\nreads the credential from ctx.auth at run time.","tags":["plugins"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authoredPluginList"}}},"description":"ok"}},"x-app":"tools"}},"/v1/plugins/authored/{id}":{"delete":{"operationId":"delete_v1_plugins_authored_by_id","summary":"Removes one of the caller org's built plugins, so the runtime can no longer load it.","description":"Removes one of the caller org's built plugins, so the\nruntime can no longer load it. Scoped to the caller's org, so an id belonging\nto another tenant answers 404 and is not deleted.","tags":["plugins"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the plugin to remove, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pluginDeleted"}}},"description":"ok"}},"x-app":"tools"}},"/v1/plugins/build":{"post":{"operationId":"post_v1_plugins_build","summary":"Build a plugin for your org from TypeScript, or from an API spec a model writes it from","description":"Builds one plugin for the caller's org and answers 201 with the bundle's size, whether a model wrote the source, and the plugin as stored. Post `source` to build TypeScript as-is, or `spec` — an OpenAPI document or plain prose describing the endpoints — to have one generated; the generated source comes back in the answer, so a caller reads what will run before it runs. Exactly one of the two, and `name` must be one lowercase path segment; both or neither is 400.\n\nCOMPILING IS THE GATE. The source goes through the same pipeline the committed connectors do — esbuild to one CommonJS program, then compiled in the goja runtime that will actually execute it — and anything that fails is rejected and NEVER stored. So a plugin in the store is one this deployment has already loaded once, not one a model claimed was fine. A failed build answers 422 carrying the diagnostics a caller needs to fix it: the bundler's error, the source that failed, and whether the model wrote it — a body outside the declared success shape.\n\nCREDENTIALS ARE NOT PART OF A PLUGIN. A plugin names the connectors `provider` it needs and reads that credential from `ctx.auth` at run time, under KMS custody. Source that contains something shaped like a key is REFUSED rather than silently scrubbed, so a caller who pasted one finds out instead of shipping it — register it as a connector instead.\n\nRequires a validated principal; 403 without one. The plugin is stored under that principal's org and is what `/v1/plugins/authored` lists — never `/v1/plugins`, which is this deployment's mounted-subsystem inventory. Source over 512 KiB or a spec over 256 KiB is refused. Posting a `spec` to a deployment with no AI client configured is 503, and a generation that fails upstream is 502.","tags":["plugins"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildOut"}}},"description":"Success"}},"x-app":"tools"}},"/v1/prefs":{"get":{"operationId":"get_v1_prefs","summary":"Returns the signed-in caller's OWN preference document — the theme, density and pinned nav that follow them across every Hanzo surface.","description":"Returns the signed-in caller's OWN preference document — the theme,\ndensity and pinned nav that follow them across every Hanzo surface. There is no\npath to another user's preferences: not for an org admin, not for a platform\nSuperAdmin, because the subject is built from the validated credential and is the\nmandatory predicate on the read. A caller who has never saved anything gets an\nempty document at 200, never a 404, so the user menu always renders.","tags":["prefs"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/prefsView"}}},"description":"ok"}},"x-app":"prefs"},"patch":{"operationId":"patch_v1_prefs","summary":"Save the preference keys your surface owns, leaving every other key alone","description":"Merges a JSON object key-wise into the signed-in caller's OWN preference document and answers with the whole document after the merge, so a surface saves `theme` without having to send back the `density` another surface owns. The merge is SHALLOW and the key space is open: an unnamed key is left untouched, a named key is replaced whole, and a key sent with a `null` value is DELETED. The subject is the `\u003cowner\u003e/\u003cname\u003e` identity built from the validated credential and is the mandatory predicate on the write, so there is no path to another user's preferences — not for an org admin, not for a platform SuperAdmin. Fails closed: no validated principal is 403; an empty body or a literal `null` is 400; and a patch or a resulting document over 16 KiB or 128 keys is 413.","tags":["prefs"],"x-app":"prefs"}},"/v1/pricing":{"get":{"operationId":"get_v1_pricing","summary":"Returns the whole pricing catalog in one document: Zen and third-party models, providers, model families, the free-model list, plan and infrastructure pricing.","description":"Returns the whole pricing catalog in one document: Zen and\nthird-party models, providers, model families, the free-model list, plan and\ninfrastructure pricing. Every model and provider it names is filtered to what\nthe caller's org may see — the same gate the leaf routes apply, so this can\nnever be an un-gated second source for what they hide.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/base":{"get":{"operationId":"get_v1_pricing_base","summary":"Returns the Hanzo Base plans — the managed-instance tiers, each with its monthly and annual price, storage and request allowances and feature list.","description":"Returns the Hanzo Base plans — the managed-instance tiers,\neach with its monthly and annual price, storage and request allowances and\nfeature list.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/blockchain":{"get":{"operationId":"get_v1_pricing_blockchain","summary":"Returns the blockchain access plans — the RPC and node tiers, each with its monthly price, compute-unit allowance and feature list.","description":"Returns the blockchain access plans — the RPC and node\ntiers, each with its monthly price, compute-unit allowance and feature list.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/cloud":{"get":{"operationId":"get_v1_pricing_cloud","summary":"Returns the public cloud section of the catalog in one document: its instance plans, its regions and its block-storage prices.","description":"Returns the public cloud section of the catalog in one\ndocument: its instance plans, its regions and its block-storage prices. The\nsection's internal half — the provider costs Hanzo pays and the plan-to-\nprovider routing table — is stripped before it is served, so this is what a\ncustomer may see and nothing more.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/cloud/plans":{"get":{"operationId":"get_v1_pricing_cloud_plans","summary":"Returns just the cloud instance plans — each with its vCPU, memory, disk, CPU type, VM allowance, feature list and monthly and hourly price.","description":"Returns just the cloud instance plans — each with its vCPU,\nmemory, disk, CPU type, VM allowance, feature list and monthly and hourly\nprice. It is the plans of the cloud section on their own.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/cloud/regions":{"get":{"operationId":"get_v1_pricing_cloud_regions","summary":"Returns the regions a cloud instance can be placed in, each with its id, display name and physical location.","description":"Returns the regions a cloud instance can be placed in, each\nwith its id, display name and physical location. It is the regions of the\ncloud section on their own.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingRegionList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/cloud/storage":{"get":{"operationId":"get_v1_pricing_cloud_storage","summary":"Returns the block-storage prices of the cloud section: the per-GB monthly rate and the volume size bounds a caller may ask for.","description":"Returns the block-storage prices of the cloud\nsection: the per-GB monthly rate and the volume size bounds a caller may ask\nfor.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/compute":{"get":{"operationId":"get_v1_pricing_compute","summary":"Returns the compute section of the catalog: the cloud provider and region the prices are quoted for, the monthly markup applied to them, the full instance-size tier list and the named presets.","description":"Returns the compute section of the catalog: the cloud\nprovider and region the prices are quoted for, the monthly markup applied to\nthem, the full instance-size tier list and the named presets. It is the\nwhole section as the pricing source records it, un-gated — no model or\nprovider identity appears in it.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/compute/presets":{"get":{"operationId":"get_v1_pricing_compute_presets","summary":"Returns just the named compute sizes — the short, human-labelled list (\"Starter\", \"Pro\") a size picker renders, each carrying its provider slug, vCPU, memory, disk and price.","description":"Returns just the named compute sizes — the short,\nhuman-labelled list (\"Starter\", \"Pro\") a size picker renders, each carrying\nits provider slug, vCPU, memory, disk and price. It is the presets of the\ncompute section on their own, for a caller that does not need the full tier\ntable.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPresetList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/datastore":{"get":{"operationId":"get_v1_pricing_datastore","summary":"Returns the Hanzo Datastore rate card: the tier list, the per-GB storage and egress usage rates, the annual discount and the trial.","description":"Returns the Hanzo Datastore rate card: the tier list, the\nper-GB storage and egress usage rates, the annual discount and the trial. It is\nthe section as authored, un-gated — no provider identity appears in it.\n\nThe route was missing while the data existed, so this 404d and every visitor to\nhanzo.ai's Infrastructure tab was told pricing was \"temporarily unavailable\".","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/featured":{"get":{"operationId":"get_v1_pricing_featured","summary":"Returns the models the catalog highlights, filtered to what the caller's org may see.","description":"Returns the models the catalog highlights, filtered to what\nthe caller's org may see. It is the same catalog as ListModels narrowed to\nentries the pricing source marks featured.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingModelList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/free":{"get":{"operationId":"get_v1_pricing_free","summary":"Returns the models that cost nothing to call, filtered to what the caller's org may see.","description":"Returns the models that cost nothing to call, filtered to what\nthe caller's org may see. It is the same catalog as ListModels narrowed to\nentries the pricing source marks free.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingModelList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/gpu":{"get":{"operationId":"get_v1_pricing_gpu","summary":"ListGPUTiers returns the rentable GPU configurations, each with its accelerator count and model, VRAM, vCPU, host memory and hourly price.","description":"ListGPUTiers returns the rentable GPU configurations, each with its\naccelerator count and model, VRAM, vCPU, host memory and hourly price.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingTierList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/health":{"get":{"operationId":"get_v1_pricing_health","summary":"Health reports that the pricing subsystem is mounted and serving.","description":"Health reports that the pricing subsystem is mounted and serving. It answers\nfrom the process itself and consults neither the catalog bundle nor the\nenablement store, so it stays \"ok\" while either is degraded.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"example":{"service":"pricing","status":"ok"},"schema":{"$ref":"#/components/schemas/pricingHealth"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/iam":{"get":{"operationId":"get_v1_pricing_iam","summary":"ListIAMPlans returns the identity plans — the Hanzo IAM tiers, each with its monthly and annual price, monthly-active-user allowance and feature list.","description":"ListIAMPlans returns the identity plans — the Hanzo IAM tiers, each with its\nmonthly and annual price, monthly-active-user allowance and feature list.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/model/{name}":{"get":{"operationId":"get_v1_pricing_model_by_name","summary":"Returns one model's catalog entry — its pricing, context window and capabilities as the pricing source records them.","description":"Returns one model's catalog entry — its pricing, context window and\ncapabilities as the pricing source records them. A model hidden for the\ncaller's org answers the same 404 an unknown name does, so a disabled model\ngets no existence oracle.","tags":["pricing"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the model's name or its slugged id (\"zen4\",\n\"acme/some-model-1\"), matched case-insensitively. It comes from\nthe path: the URL is the addressing authority.","schema":{"type":"string"},"example":"zen4"}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/models":{"get":{"operationId":"get_v1_pricing_models","summary":"Returns the whole model catalog — Hanzo's own Zen models and every third-party model — filtered to what the caller's org may see.","description":"Returns the whole model catalog — Hanzo's own Zen models and every\nthird-party model — filtered to what the caller's org may see. A model an\nadmin has disabled is absent; one in beta appears only for an org granted it.\nA SuperAdmin sees every model, each annotated with its enablement state.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingModelList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/paas":{"get":{"operationId":"get_v1_pricing_paas","summary":"ListPaaSPlans returns the application-hosting plans — the deploy-and-host tiers, each with its monthly and annual price, app and memory allowances and feature list.","description":"ListPaaSPlans returns the application-hosting plans — the deploy-and-host\ntiers, each with its monthly and annual price, app and memory allowances and\nfeature list.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/policy":{"get":{"operationId":"get_v1_pricing_policy","summary":"Returns the pricing policy document: the revenue-sharing terms (the idle-resale share and the open-source share, each with its percentage and who is eligible) and the commitments Hanzo makes about how it bills — no hidden fees, no egress charges, no surprise bills.","description":"Returns the pricing policy document: the revenue-sharing\nterms (the idle-resale share and the open-source share, each with its\npercentage and who is eligible) and the commitments Hanzo makes about how it\nbills — no hidden fees, no egress charges, no surprise bills.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/providers":{"get":{"operationId":"get_v1_pricing_providers","summary":"Returns the model providers the catalog knows, each with its info object, filtered to what the caller's org may see.","description":"Returns the model providers the catalog knows, each with its\ninfo object, filtered to what the caller's org may see. A provider an admin\nhas disabled is absent — and so are its models everywhere else on this\nsurface, because a provider's state cascades to what it serves.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingProviderList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/services":{"get":{"operationId":"get_v1_pricing_services","summary":"Returns the managed-service rate cards — Search, Crawl, Vector, Console and Managed Services — each with its own tiers, and some with usage rates or a comparison table.","description":"Returns the managed-service rate cards — Search, Crawl,\nVector, Console and Managed Services — each with its own tiers, and some with\nusage rates or a comparison table. It is the section as authored, un-gated.\n\nThese are DISPLAY rate cards: what a product costs, not what a plan grants. No\nentitlement or limit fields ride here, so nothing can bill off them.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/subscriptions":{"get":{"operationId":"get_v1_pricing_subscriptions","summary":"Returns the API subscription plans — the account-level tiers a customer subscribes to, each with its monthly and annual price, included credit, rate limits and feature list.","description":"Returns the API subscription plans — the account-level\ntiers a customer subscribes to, each with its monthly and annual price,\nincluded credit, rate limits and feature list.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingPlanList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/summary":{"get":{"operationId":"get_v1_pricing_summary","summary":"Returns the catalog's headline statistics — model counts by family and the provider directory.","description":"Returns the catalog's headline statistics — model counts by\nfamily and the provider directory. The provider sub-object is filtered to what\nthe caller's org may see, so a disabled provider's name never leaks; the\naggregate counts are the catalog's own, over everything it holds.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/sync":{"post":{"operationId":"post_v1_pricing_sync","summary":"Refreshes the third-party section of the catalog from its upstream listings and returns the time the refreshed catalog was stamped with.","description":"Refreshes the third-party section of the catalog from its upstream\nlistings and returns the time the refreshed catalog was stamped with. The\nfetch runs in Go and the markup transform in the pricing bundle. SuperAdmin\nonly; every other caller is refused.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingSyncOut"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/pricing/tools":{"get":{"operationId":"get_v1_pricing_tools","summary":"Returns the per-use tool prices — web search, code interpreter, file storage, image generation, speech-to-text and text-to-speech — each with the unit it is billed by and its price in that unit.","description":"Returns the per-use tool prices — web search, code\ninterpreter, file storage, image generation, speech-to-text and\ntext-to-speech — each with the unit it is billed by and its price in that\nunit.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingToolList"}}},"description":"ok"}},"x-app":"pricing"}},"/v1/process-speech-to-text":{"post":{"operationId":"post_v1_process-speech-to-text","summary":"Convert speech to text","description":"Convert speech to text","tags":["process-speech-to-text"],"x-app":"github.com/hanzoai/ai"}},"/v1/projects":{"get":{"operationId":"get_v1_projects","summary":"Returns every project your org owns.","description":"Returns every project your org owns.\n\nEach row carries the slug, name, framework, visibility, status and live URL —\nthe same rows console and the builder render, because there is only one store\nbehind both. It requires a validated principal (403 without one) and is keyed\nby that principal's org, so it never contains another tenant's project.","tags":["projects"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsProject"},"type":"array"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_projects","summary":"Creates a project — the handle a site is deployed and served under — and answers 201 with it in `draft`.","description":"Creates a project — the handle a site is deployed and served\nunder — and answers 201 with it in `draft`.\n\n`name` is required; `slug` is derived from the name when omitted and is the\nidentifier that matters — it becomes the S3 key segment, the public host\n`\u003cslug\u003e.hanzo.app`, and the handle every later call addresses, so it must\nmatch `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$` and may not be a reserved label\nsuch as `api` or `admin`. `framework` is a build hint from a closed set,\ndefaulting to `static`; it never gates a deploy, it only tells CI how to build\na linked repo.\n\nTwo defaults are worth knowing: the analytics beacon is ON unless `analytics`\nis explicitly false, and `visibility` is `public` unless asked otherwise.\nPublishing publicly is free; PRIVATE is the paid feature, and an unfunded org\nasking for it is refused rather than quietly published as public. Creation\nalso provisions the project's data space and a canonical git repo, both\nbest-effort — neither can fail the create.\n\nScope: a validated principal is required (403 without one) and the project is\ncreated in THAT principal's org. The slug is unique per org, so a slug already\nused in the caller's own org is a 409 while the same slug in another org is\nirrelevant.","tags":["projects"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"created"}},"x-app":"projects"}},"/v1/projects/fork":{"post":{"operationId":"post_v1_projects_fork","summary":"Creates a project seeded from a PUBLISHED EXAMPLE — either a starter-kit template from the ONE embedded gallery catalog, or any live project on the platform (an example a seeded creator published, or another org's app serving at \u003cslug\u003e.hanzo.app).","description":"Creates a project seeded from a PUBLISHED EXAMPLE — either a\nstarter-kit template from the ONE embedded gallery catalog, or any live\nproject on the platform (an example a seeded creator published, or another\norg's app serving at \u003cslug\u003e.hanzo.app). Answers 201 with the new project.\n\n`slug` names the PARENT to fork and is required. Templates resolve first, and\nthe caller org's own private templates ahead of the public gallery, so a\ncurated template slug keeps meaning the same thing even if someone later\npublishes a live project under it; `variant` picks that template's\nformat/page/theme. If no template matches, the slug resolves to the UNIQUE\nlive project that owns it across all orgs — the same resolution the site edge\nuses to serve \u003cslug\u003e.hanzo.app, so what you can browse is what you can fork.\n\n`name` and `target` override the derived project name and slug; everything\nelse is inherited from the parent. A live parent contributes its REPO, so the\nchild builds from the same source — the parent's deployed bytes are never\ncopied, because releases are per-tenant by design and the fork publishes its\nown. The parent it actually resolved is stamped on the child as `forkedFrom`,\nso attribution is a fact recorded at fork time rather than a claim\nreconstructed later.\n\nIt funnels through the SAME create path POST /v1/projects uses, so slug\nvalidation, org scoping, ID minting and the 409 on a slug the caller's own org\nalready uses are identical.\n\nScope: a validated principal is required (403 without one) and the child is\ncreated in THAT principal's org.","tags":["projects"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsFork"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"created"}},"x-app":"projects"}},"/v1/projects/{slug}":{"delete":{"operationId":"delete_v1_projects_by_slug","summary":"Deletes a project and takes its site off the internet.","description":"Deletes a project and takes its site off the internet.\n\nThe metadata delete is authoritative and everything after it is best-effort,\nin this order: the public `\u003cslug\u003e` subdomain binding is released so the slug is\nfree to reclaim, the release rows are dropped so a reclaimed slug never\ninherits the previous owner's rollback menu, the S3 origin is purged under\nBOTH `\u003corg\u003e/\u003cslug\u003e/` and the site's sibling release space, and the edge\ncache-tag is flushed. A failure in any of those is logged and the delete still\nanswers 204 — resurrecting a project because a purge missed would be worse\nthan a leaked prefix.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404 and\nnothing of theirs is touched.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"projects"},"get":{"operationId":"get_v1_projects_by_slug","summary":"Returns one project of yours by slug — its settings, its live URL and the deployment currently serving it.","description":"Returns one project of yours by slug — its settings, its live URL\nand the deployment currently serving it.\n\nScope: a validated principal is required (403 without one) and the lookup is\nkeyed by (org, slug), so another tenant's slug is a 404 exactly like a\nnonexistent one.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"},"patch":{"operationId":"patch_v1_projects_by_slug","summary":"Changes a project's settings, and only the settings you send.","description":"Changes a project's settings, and only the settings you send.\n\nEvery field is optional and absent means \"leave it\": `name` may not be blanked,\n`framework` must stay a known build hint, and `cacheControl` is capped at 256\ncharacters with no newlines (it becomes a response header). `visibility` flips\npublic/private under the same rule as create — public is free, private needs a\nfunded org. `upstream` and `license` are free-text credit for third-party work,\nand sending \"\" clears one. Changing anything reconciles the project's canonical\ngit repo, so a visibility change reaches the source and not just the listing.\n\n`hidden`/`hiddenReason` are platform MODERATION and are ignored unless the\ncaller is a platform admin; they remove a project from the public catalogue\nwithout touching the publisher's own visibility choice, so un-hiding restores\nexactly what they asked for.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to update, from the path. The URL is the addressing\nauthority — a `slug` in the body cannot move the write to another project.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/deploy":{"post":{"operationId":"post_v1_projects_by_slug_deploy","summary":"Deploy a build — upload an archive, or trigger a build from the linked repo","description":"Takes a built site live at `https://\u003cslug\u003e.hanzo.app`. It accepts BOTH shapes on one address and the content type decides which: a `zip` or `tar.gz` archive — raw in the body or as a multipart file part — is uploaded and served immediately, answering 200 with the finished deployment; a JSON body instead queues a build from the project's linked repo and answers 202 with a queued deployment and, where one could be minted, a scoped upload grant for CI to write with. The git path needs a linked repo (400 without one) and is finished later by the completion hook.\n\nBilling is fail-closed and fails FIRST: the hosting gate runs before anything is parsed or uploaded, so an unfunded org is 402 and an unreachable commerce is 503 with nothing written. The debit lands only on success — a failed upload is never billed and never flips the live site, and a queued build is billed at completion rather than at queue time. A redeploy returns the SAME URL, because slug and apex are stable.\n\nScope: a validated principal is required (403 without one) and the project is resolved within that principal's org, so another tenant's slug is a 404. Object storage must be configured, else 503; an archive that does not walk is a 400 and one over the size cap is a 413.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"projects"}},"/v1/projects/{slug}/deployments":{"get":{"operationId":"get_v1_projects_by_slug_deployments","summary":"Returns a project's deploy history, newest version first.","description":"Returns a project's deploy history, newest version first.\n\nEvery deploy of the project is a row — uploads, generated sites, and git/CI\nbuilds alike — carrying its version, status, source, commit, live URL, file\ncount and byte count. The short-lived upload grant a queued git deployment was\nhanded is NOT replayed here: it exists only on the 202 that minted it, so a\ngrant cannot outlive its build by being fetched again.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsDeployment"},"type":"array"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/deployments/{id}":{"get":{"operationId":"get_v1_projects_by_slug_deployments_by_id","summary":"Returns one deployment of a project by id.","description":"Returns one deployment of a project by id.\n\nIt is how a console follows a build: the status (`queued`, `uploading`,\n`live`, `error`), the message a failure left, and the URL and prefix it went\nlive at. Like the history, it never replays the upload grant.\n\nScope: a validated principal is required (403 without one). Both the project\nand the deployment are resolved within that principal's org, so a deployment\nof another project — or of another tenant — is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the deployment belongs to, from the path.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"description":"ID is the deployment id, from the path. A deployment of another project —\nor of another tenant's project — is not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployment"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/deployments/{id}/complete":{"post":{"operationId":"post_v1_projects_by_slug_deployments_by_id_complete","summary":"CompleteDeployment is the CI completion hook that flips a queued git deployment to live (or error) once CI has synced the built site to S3.","description":"CompleteDeployment is the CI completion hook that flips a queued git\ndeployment to live (or error) once CI has synced the built site to S3.\n\n`status` must be `live` or `error`. On a LIVE completion the public host is\nclaimed FIRST, so the deployment reports the URL it actually OWNS — a\nCI-supplied `liveUrl` is a hint that can refine that URL but can never assert\na subdomain another tenant holds. `keys` is the manifest CI just uploaded,\nrelative to the deployment prefix: cloud reconciles the prefix against it so a\npage deleted from the build actually stops serving. Omit `keys` and nothing is\ndeleted — the prefix only grows. Reconciliation runs only on a live completion\n(pruning against a failed build's manifest would delete the site the last good\nbuild is still serving) and is best-effort, so a stale leftover never turns a\nsuccessful deploy into a 500. A live completion is also the one billable\nmoment on the git path; an error completion bills nothing.\n\nScope: a validated principal is required (403 without one). CI authenticates\nwith an org-scoped token through the gateway, so the deployment is resolved\nwithin that principal's org and another tenant's slug or deployment id is a\n404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the deployment belongs to, from the path.","schema":{"type":"string"}},{"name":"id","in":"path","required":true,"description":"ID is the queued deployment to complete, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsComplete"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployment"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/domains":{"get":{"operationId":"get_v1_projects_by_slug_domains","summary":"Returns every custom hostname this site holds: the live ones, plus any pending claim with the DNS records it still owes.","description":"Returns every custom hostname this site holds: the live ones, plus\nany pending claim with the DNS records it still owes.\n\n`domains` is the routing answer — the hosts that are verified right now —\nwhile `claims` is the full panel, one row per host, each saying whether it is\nlive or pending and, if pending, exactly what to publish.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomains"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_projects_by_slug_domains","summary":"Attaches one or more CUSTOM public hostnames to this org's site.","description":"Attaches one or more CUSTOM public hostnames to this org's site.\n\nBinding a host you do not own would let you shadow it at the edge, so which\noutcome you get depends on whether ownership is already established: a SuperAdmin\nvouches (the operator manages the customer's DNS, so its bind IS the proof) and\nbinds VERIFIED immediately; every other caller, INCLUDING an admin of the\ndeployment's own brand org, has the host CLAIMED as pending and gets the DNS\nchallenge back in `bound[].records`. A pending claim HOLDS the name so nobody\nelse can take it, but it does not route until POST .../domains/{host}/verify\nproves control.\n\nA hostname we operate is refused to a non-vouched caller (those are assigned\nby the platform, never claimed), a host another site already holds is a 409,\nand a name the platform holds is a 400 for EVERY caller — a vouch skips the\nownership proof, never the host table's own invariant. Claims and binds are\nidempotent for the same\n(org, slug), and re-claiming returns the SAME token rather than invalidating a\nrecord the customer has already published. The edge cache-tag is flushed\nafterwards so a newly-verified host serves the current build immediately.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site the hosts attach to, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomainsBind"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsBoundDomains"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/domains/{host}":{"delete":{"operationId":"delete_v1_projects_by_slug_domains_by_host","summary":"Gives a custom hostname back, so the name is free to reuse.","description":"Gives a custom hostname back, so the name is free to reuse.\n\nA claim is FIRST-COME and global, so an add-only surface was not ownership but\na leak: a customer who mistyped a domain, or claimed one they later moved\nelsewhere, could neither reuse it nor let anyone else. This is the third\nwriter that closes it. The release is scoped to (host, org, slug), so it can\nonly ever drop THIS tenant's own claim, and it is IDEMPOTENT: releasing a host\nwe do not hold is a clean 204, never a 404 that would let a caller probe which\nhosts other tenants hold. The edge cache-tag is flushed, since the host stops\nrouting here.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the host is attached to, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the custom hostname, from the path. It is cleaned to its canonical\nform (lowercased, trailing dot dropped) before anything is looked up.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"projects"}},"/v1/projects/{slug}/domains/{host}/verify":{"post":{"operationId":"post_v1_projects_by_slug_domains_by_host_verify","summary":"Checks the DNS challenge for a pending custom hostname and, when it passes, promotes the host so it begins routing at the edge.","description":"Checks the DNS challenge for a pending custom hostname and, when\nit passes, promotes the host so it begins routing at the edge.\n\nIt answers 200 either way, with the host's honest current state: verified once\nthe TXT record is found, still pending — with the records to publish and the\nresolver's own explanation in `detail` — when it is not. A not-yet is not an\nerror: the check ran, DNS simply has not propagated, and the customer retries.\nAn already-verified host is returned unchanged without re-resolving. On a\nsuccessful promotion the edge cache-tag is flushed, since the host routes as\nof that moment.\n\nScope: a validated principal is required (403 without one). Both the site and\nthe claim are resolved within that principal's org, so a host claimed by\nanother tenant is \"not claimed by this site\".","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project the host is attached to, from the path.","schema":{"type":"string"}},{"name":"host","in":"path","required":true,"description":"Host is the custom hostname, from the path. It is cleaned to its canonical\nform (lowercased, trailing dot dropped) before anything is looked up.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDomain"}}},"description":"ok"}},"x-app":"projects"}},"/v1/projects/{slug}/purge":{"post":{"operationId":"post_v1_projects_by_slug_purge","summary":"Flushes the site's edge cache without redeploying anything.","description":"Flushes the site's edge cache without redeploying anything.\n\nIt invalidates the edge cache-tag `site-\u003corg\u003e-\u003cslug\u003e` and stamps `lastPurgeAt`\n(unix seconds), and it NEVER writes or deletes the S3 origin — the live build\nkeeps serving; only stale copies held at the edge drop, so the next request\nre-fetches the current artifact from origin. Idempotent, and an edge that is\nunconfigured or failing is not fatal: `lastPurgeAt` is still stamped and the\nanswer is still the updated project.\n\nScope: a validated principal is required (403 without one) and the project is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsProject"}}},"description":"ok"}},"x-app":"projects"}},"/v1/prompts":{"get":{"operationId":"get_v1_prompts","summary":"List returns the caller org's prompt library as one row per prompt: its name, type, every version number it has, its taxonomy and when it last changed.","description":"List returns the caller org's prompt library as one row per prompt: its name,\ntype, every version number it has, its taxonomy and when it last changed. The\ntemplate bodies are deliberately absent — fetch one prompt to read its text.","tags":["prompts"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/promptList"}}},"description":"ok"}},"x-app":"prompts"},"post":{"operationId":"post_v1_prompts","summary":"Create records a prompt for the caller's org and answers 201 with it.","description":"Create records a prompt for the caller's org and answers 201 with it. A name the\norg already uses is NOT an error and NOT an overwrite: it appends a new version,\nso the library keeps real, inspectable history and the response carries the whole\nversion list. The name is also the URL segment the prompt is fetched by, which is\nwhy its shape is constrained and a handful of names are reserved.","tags":["prompts"],"requestBody":{"content":{"application/json":{"example":{"name":"greeting","prompt":"You are a helpful assistant.","tags":["support"]},"schema":{"$ref":"#/components/schemas/promptReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/promptDetail"}}},"description":"created"}},"x-app":"prompts"}},"/v1/prompts/catalog":{"get":{"operationId":"get_v1_prompts_catalog","summary":"Catalog returns the read-only starter prompt library shipped with the binary — reference content every tenant sees the same, NOT the caller's own prompts and never mixed into them.","description":"Catalog returns the read-only starter prompt library shipped with the binary —\nreference content every tenant sees the same, NOT the caller's own prompts and\nnever mixed into them. An org's library stays honestly empty until someone\nexplicitly imports a starter, which is an ordinary POST /v1/prompts. Entries that\nwould fail the create guards are dropped, so everything offered here can actually\nbe imported.","tags":["prompts"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/catalogList"}}},"description":"ok"}},"x-app":"prompts"}},"/v1/prompts/metrics":{"get":{"operationId":"get_v1_prompts_metrics","summary":"Metrics returns real per-prompt statistics for the caller's org: how many versions each prompt has, which one is current, and when it was created and last changed.","description":"Metrics returns real per-prompt statistics for the caller's org: how many versions\neach prompt has, which one is current, and when it was created and last changed.\nEvery number is counted from the store — nothing here is estimated or fabricated.","tags":["prompts"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/metricList"}}},"description":"ok"}},"x-app":"prompts"}},"/v1/prompts/{name}":{"delete":{"operationId":"delete_v1_prompts_by_name","summary":"Delete removes one of the caller org's prompts and every version of it, answering 204.","description":"Delete removes one of the caller org's prompts and every version of it, answering\n204. It is scoped to the caller's org, so a name another tenant owns is the same\n404 an unknown name gives. There is no undo: the version history goes with it.","tags":["prompts"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the prompt to act on, from the path.","schema":{"type":"string"},"example":"greeting"}],"responses":{"204":{"description":"no content"}},"x-app":"prompts"},"get":{"operationId":"get_v1_prompts_by_name","summary":"Get returns one of the caller org's prompts: its CURRENT template text plus the metadata of every version it has had.","description":"Get returns one of the caller org's prompts: its CURRENT template text plus the\nmetadata of every version it has had. The history carries version numbers, types\nand timestamps only — not each version's body — so a long history cannot inflate\nthis response. A name the caller's org does not own is 404, whoever owns it.","tags":["prompts"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the prompt to act on, from the path.","schema":{"type":"string"},"example":"greeting"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/promptDetail"}}},"description":"ok"}},"x-app":"prompts"}},"/v1/pubsub/jetstream/streams":{"get":{"operationId":"get_v1_pubsub_jetstream_streams","summary":"Returns the org's streams, sorted by name.","description":"Returns the org's streams, sorted by name.\n\nA stream is the durable log: it captures every message published to its\nsubjects and retains them by its own limits, independent of any consumer.\nThe listing is org-scoped server-side — one org can never see another's\nstreams, and the platform's own planes never appear.","tags":["pubsub"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/streamPage"}}},"description":"ok"}},"x-app":"pubsub"},"post":{"operationId":"post_v1_pubsub_jetstream_streams","summary":"Creates a durable stream capturing the given subjects and returns it.","description":"Creates a durable stream capturing the given subjects and\nreturns it. 409 when the org already has a stream of that name; the subjects\nare the org's own and cannot collide with another org's.","tags":["pubsub"],"requestBody":{"content":{"application/json":{"example":{"maxAge":86400,"name":"ORDERS","subjects":["orders.\u003e"]},"schema":{"$ref":"#/components/schemas/streamWrite"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/streamRecord"}}},"description":"created"}},"x-app":"pubsub"}},"/v1/pubsub/jetstream/streams/{stream}":{"delete":{"operationId":"delete_v1_pubsub_jetstream_streams_by_stream","summary":"Removes one stream of the caller's org — its retained messages and its consumers with it — and answers 204 with no body.","description":"Removes one stream of the caller's org — its retained messages\nand its consumers with it — and answers 204 with no body. 404 when the org\nhas no stream of that name.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream's name, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"pubsub"},"get":{"operationId":"get_v1_pubsub_jetstream_streams_by_stream","summary":"Returns one stream of the caller's org — its configuration and its live state (messages, bytes, sequence range, consumer count).","description":"Returns one stream of the caller's org — its configuration and its\nlive state (messages, bytes, sequence range, consumer count). 404 when the\norg has no stream of that name.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream's name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/streamRecord"}}},"description":"ok"}},"x-app":"pubsub"},"put":{"operationId":"put_v1_pubsub_jetstream_streams_by_stream","summary":"Rewrites a stream's configuration — subjects, limits, discard — and returns the updated stream.","description":"Rewrites a stream's configuration — subjects, limits, discard —\nand returns the updated stream. It is a PUT: the spec sent replaces the spec\nheld, with one reading for the enums a caller omits — an empty storage,\nretention or discard keeps the stream's current one, because JetStream holds\nstorage and retention immutable and refuses a change with a 400 rather than\nthis door pretending it took.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream to update, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"maxMsgs":100000,"subjects":["orders.\u003e","refunds.\u003e"]},"schema":{"$ref":"#/components/schemas/streamUpdate"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/streamRecord"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/jetstream/streams/{stream}/consumers":{"get":{"operationId":"get_v1_pubsub_jetstream_streams_by_stream_consumers","summary":"Returns one stream's consumers, sorted by name.","description":"Returns one stream's consumers, sorted by name. 404 when the\norg has no stream of that name.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream's name, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/consumerPage"}}},"description":"ok"}},"x-app":"pubsub"},"post":{"operationId":"post_v1_pubsub_jetstream_streams_by_stream_consumers","summary":"Creates a durable consumer on one stream and returns it.","description":"Creates a durable consumer on one stream and returns it. A\nconsumer is a named cursor: it tracks what has been delivered and what is\nacknowledged, so many workers can share it and none sees a message twice\noutside redelivery. 409 when the stream already has a consumer of that name\nwith a different configuration.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream to consume, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"ack":"explicit","filter":"orders.created","name":"worker"},"schema":{"$ref":"#/components/schemas/consumerWrite"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/consumerRecord"}}},"description":"created"}},"x-app":"pubsub"}},"/v1/pubsub/jetstream/streams/{stream}/consumers/{name}":{"delete":{"operationId":"delete_v1_pubsub_jetstream_streams_by_stream_consumers_by_name","summary":"Removes one consumer — its cursor, not the stream's messages — and answers 204 with no body.","description":"Removes one consumer — its cursor, not the stream's messages —\nand answers 204 with no body. 404 when the stream or the consumer does not\nexist.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"pubsub"},"get":{"operationId":"get_v1_pubsub_jetstream_streams_by_stream_consumers_by_name","summary":"Returns one consumer of one org stream — its configuration and its cursor: delivered and acknowledged sequences, pending and redelivered counts.","description":"Returns one consumer of one org stream — its configuration and\nits cursor: delivered and acknowledged sequences, pending and redelivered\ncounts. 404 when the stream or the consumer does not exist.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/consumerRecord"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/jetstream/streams/{stream}/consumers/{name}/next":{"post":{"operationId":"post_v1_pubsub_jetstream_streams_by_stream_consumers_by_name_next","summary":"Fetch pulls the next batch from a consumer and acknowledges it — the request/response way to consume a stream.","description":"Fetch pulls the next batch from a consumer and acknowledges it — the\nrequest/response way to consume a stream. The hand-off is at-most-once: a\nmessage returned here is acked here, so a caller that loses the response does\nnot see it again. Workers needing at-least-once delivery consume the same\nconsumer over the NATS port, where acks are theirs to send. An empty batch\nafter the wait is an empty page, not an error.","tags":["pubsub"],"parameters":[{"name":"stream","in":"path","required":true,"description":"Stream is the stream, from the path.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"description":"Name is the consumer, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"batch":10,"waitMs":2000},"schema":{"$ref":"#/components/schemas/fetchQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/messagePage"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/kv/{bucket}":{"delete":{"operationId":"delete_v1_pubsub_kv_by_bucket","summary":"Removes one bucket of the caller's org — every key and every revision with it — and answers 204 with no body.","description":"Removes one bucket of the caller's org — every key and every\nrevision with it — and answers 204 with no body. 404 when the org has no\nbucket of that name.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket's name, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"pubsub"},"post":{"operationId":"post_v1_pubsub_kv_by_bucket","summary":"Creates a KV bucket and returns it.","description":"Creates a KV bucket and returns it. A bucket is keyed state on\nthe same durable plane as the streams: each key holds up to History\nrevisions, entries can expire by TTL, and watchers on the NATS port see every\nwrite. 409 when the org already has a bucket of that name.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket's name within the org, from the path: 1–64 of\n[A-Za-z0-9_], no dash.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"history":5,"ttl":3600},"schema":{"$ref":"#/components/schemas/bucketWrite"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bucketRecord"}}},"description":"created"}},"x-app":"pubsub"}},"/v1/pubsub/kv/{bucket}/{key}":{"delete":{"operationId":"delete_v1_pubsub_kv_by_bucket_by_key","summary":"Delete removes one key — a delete marker in the key's history, so watchers see it and Get answers 404 — and answers 204 with no body.","description":"Delete removes one key — a delete marker in the key's history, so watchers\nsee it and Get answers 404 — and answers 204 with no body. 404 when the\nbucket does not exist.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket, from the path.","schema":{"type":"string"}},{"name":"key","in":"path","required":true,"description":"Key is the key, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"pubsub"},"get":{"operationId":"get_v1_pubsub_kv_by_bucket_by_key","summary":"Get returns one key's current value and revision.","description":"Get returns one key's current value and revision. 404 when the bucket does\nnot exist, the key was never written, or its latest revision is a delete.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket, from the path.","schema":{"type":"string"}},{"name":"key","in":"path","required":true,"description":"Key is the key, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kvEntry"}}},"description":"ok"}},"x-app":"pubsub"},"put":{"operationId":"put_v1_pubsub_kv_by_bucket_by_key","summary":"Put sets one key to one value and returns the revision the write created.","description":"Put sets one key to one value and returns the revision the write created.\nWrites are versioned: each put is a new revision and the bucket retains up to\nits History of them per key.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket, from the path.","schema":{"type":"string"}},{"name":"key","in":"path","required":true,"description":"Key is the key, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"value":"{\"theme\":\"dark\"}"},"schema":{"$ref":"#/components/schemas/kvWrite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kvAck"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/kv/{bucket}/{key}/history":{"get":{"operationId":"get_v1_pubsub_kv_by_bucket_by_key_history","summary":"History returns one key's retained revisions, oldest first — every put and every delete marker up to the bucket's History depth.","description":"History returns one key's retained revisions, oldest first — every put and\nevery delete marker up to the bucket's History depth. 404 when the bucket\ndoes not exist or the key was never written.","tags":["pubsub"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket, from the path.","schema":{"type":"string"}},{"name":"key","in":"path","required":true,"description":"Key is the key, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kvPage"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/publish":{"post":{"operationId":"post_v1_pubsub_publish","summary":"Publish puts one message on the org's bus.","description":"Publish puts one message on the org's bus. When a stream captures the subject\nthe write is DURABLE — the receipt names the stream and sequence only after\nJetStream has it on storage, and a repeated Nats-Msg-Id header within the\ndedup window answers duplicate instead of storing twice. When nothing\ncaptures it, the message goes out core NATS: delivered to current\nsubscribers, receipt {ok}, nothing retained.","tags":["pubsub"],"requestBody":{"content":{"application/json":{"example":{"data":"{\"id\":\"o_1\"}","headers":{"Nats-Msg-Id":"o_1"},"subject":"orders.created"},"schema":{"$ref":"#/components/schemas/busPublish"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/busAck"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/pubsub/request":{"post":{"operationId":"post_v1_pubsub_request","summary":"Request sends one request on the org's bus and waits for one reply — the synchronous half of pub/sub, for callers speaking to a responder subscribed on the NATS port.","description":"Request sends one request on the org's bus and waits for one reply — the\nsynchronous half of pub/sub, for callers speaking to a responder subscribed\non the NATS port. 404 when nobody is listening on the subject; 408 when a\nresponder exists but no reply arrived within the timeout.","tags":["pubsub"],"requestBody":{"content":{"application/json":{"example":{"data":"{\"sku\":\"gpu_1\"}","subject":"billing.quote","timeoutMs":2000},"schema":{"$ref":"#/components/schemas/busRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/busMessage"}}},"description":"ok"}},"x-app":"pubsub"}},"/v1/query":{"post":{"operationId":"post_v1_query","summary":"Handles POST /v1/query — {file_id,query,k}.","description":"Handles POST /v1/query — {file_id,query,k}. Returns LangChain\n(document,score) tuples.","tags":["query"],"x-app":"github.com/hanzoai/ai"}},"/v1/query_multiple":{"post":{"operationId":"post_v1_query_multiple","summary":"Handles POST /v1/query_multiple — {file_ids,query,k}.","description":"Handles POST /v1/query_multiple — {file_ids,query,k}.","tags":["query_multiple"],"x-app":"github.com/hanzoai/ai"}},"/v1/rag/context":{"get":{"operationId":"get_v1_rag_context","summary":"Return every stored chunk of one file_id (full document context).","description":"Return every stored chunk of one file_id (full document context).\nConsolidates the retired chat-rag-api GET /documents/{id}/context.","tags":["rag"],"x-app":"github.com/hanzoai/ai"}},"/v1/rag/delete":{"post":{"operationId":"post_v1_rag_delete","summary":"Delete all chunks of one or more uploaded files (by file_id) from the owner's Search+Vector index.","description":"Delete all chunks of one or more uploaded files (by file_id) from\nthe owner's Search+Vector index. Consolidates the retired chat-rag-api\nDELETE /documents.","tags":["rag"],"x-app":"github.com/hanzoai/ai"}},"/v1/rag/embed":{"post":{"operationId":"post_v1_rag_embed","summary":"Parse, chunk, and embed one uploaded file under its file_id into the unified Search+Vector index, scoped to the authenticated owner.","description":"Parse, chunk, and embed one uploaded file under its file_id into\nthe unified Search+Vector index, scoped to the authenticated owner. Provide\ninline `content` or a `url` to fetch+parse (PDF/CSV/XLSX/PPTX/…). Re-embedding\nthe same file_id replaces its chunks. Consolidates the retired chat-rag-api\nPOST /embed and /local/embed.","tags":["rag"],"x-app":"github.com/hanzoai/ai"}},"/v1/rag/query":{"post":{"operationId":"post_v1_rag_query","summary":"Retrieve the top-K chunks relevant to a query, scoped to a single uploaded file (`file_id`).","description":"Retrieve the top-K chunks relevant to a query, scoped to a single\nuploaded file (`file_id`). Hybrid keyword+vector retrieval over the same\nindex. Consolidates the retired chat-rag-api POST /query.","tags":["rag"],"x-app":"github.com/hanzoai/ai"}},"/v1/rag/query-multiple":{"post":{"operationId":"post_v1_rag_query-multiple","summary":"Retrieve the top-K chunks relevant to a query, scoped to a SET of uploaded files (`file_ids`).","description":"Retrieve the top-K chunks relevant to a query, scoped to a SET of\nuploaded files (`file_ids`). Consolidates the retired chat-rag-api POST\n/query_multiple. Shares one retrieval path with /rag/query.","tags":["rag"],"x-app":"github.com/hanzoai/ai"}},"/v1/referrals":{"get":{"operationId":"get_v1_referrals","summary":"Returns the caller's referral code, share link and the referrals they have made.","description":"Returns the caller's referral code, share link and the referrals they have made.\n\nThe code is a stable, deterministic function of the org, so the link in this\nresponse is the same one every time. Each row carries the referee and the status\nof that attribution.\n\nIT IS A PURE READ. It advances no referral, grants nothing and deposits nothing\n— a GET reports state, it never changes it. Qualification is the admin sweep's\njob (POST /v1/admin/referrals/sweep). The one row this handler can write is the\ncaller's OWN code-directory entry (EnsureCode), which materialises a value\nderiveCode already computes deterministically from the org id so the code has an\nO(1) reverse lookup; it carries no money, no referral state and no other tenant.","tags":["referrals"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/myReferrals"}}},"description":"ok"}},"x-app":"referrals"}},"/v1/referrals/claim":{"post":{"operationId":"post_v1_referrals_claim","summary":"Records that the caller's org signed up through a referral code.","description":"Records that the caller's org signed up through a referral code.\n\nThe REFEREE is the validated caller, never a client field, and the referrer is\nresolved from the code — so a caller can only ever attach THEMSELVES to someone\nelse's code. Referring yourself is 400 and an unknown code is 404.\n\nIt is idempotent and first-touch: an org can be referred once, ever. A repeat\ncall returns the referral already on file with created=false and 200, where the\nfirst call answers 201.\n\nRecording a referral grants nothing, and neither does anything downstream of it:\nthe edge later advances to qualified when the referee makes metered spend\n(POST /v1/admin/referrals/sweep), and that is the end of it. No credit is ever\nissued from this package.","tags":["referrals"],"requestBody":{"content":{"application/json":{"example":{"code":"H4NZ0ABC"},"schema":{"$ref":"#/components/schemas/claimRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/claimView"}}},"description":"ok"}},"x-app":"referrals"}},"/v1/registry/images":{"get":{"operationId":"get_v1_registry_images","summary":"Images lists the org's container repositories, read live from the OCI catalog and filtered server-side to the org's namespace — the page can only ever hold the caller's own images.","description":"Images lists the org's container repositories, read live from the OCI\ncatalog and filtered server-side to the org's namespace — the page can only\never hold the caller's own images.","tags":["registry"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryImageList"}}},"description":"ok"}},"x-app":"registry"}},"/v1/registry/packages":{"get":{"operationId":"get_v1_registry_packages","summary":"Packages lists the org's npm packages — `\u003corg\u003e` and `@\u003corg\u003e/…` — from the npm registry's search index, optionally narrowed by a query within that scope.","description":"Packages lists the org's npm packages — `\u003corg\u003e` and `@\u003corg\u003e/…` — from the\nnpm registry's search index, optionally narrowed by a query within that\nscope. The org boundary is applied server-side after the search, so a query\ncan never widen it.","tags":["registry"],"parameters":[{"name":"query","in":"query","required":false,"description":"Query narrows the listing within the org's scope when present; the org\nboundary itself is never widened by it. It rides the query string.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryPackageList"}}},"description":"ok"}},"x-app":"registry"}},"/v1/registry/projects":{"get":{"operationId":"get_v1_registry_projects","summary":"Projects lists the namespaces the caller can see with what each holds: the org's slug, its repository count on the OCI catalog, and its package count on the npm registry.","description":"Projects lists the namespaces the caller can see with what each holds: the\norg's slug, its repository count on the OCI catalog, and its package count\non the npm registry. Today that is exactly one row — the caller's org.","tags":["registry"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryProjectList"}}},"description":"ok"}},"x-app":"registry"}},"/v1/registry/status":{"get":{"operationId":"get_v1_registry_status","summary":"Status reports whether the OCI and npm registries are reachable and, when the OCI half is auth-gated, which token realm its challenge advertises — an honest lens for \"is the registry plane up\", never a fabricated ok.","description":"Status reports whether the OCI and npm registries are reachable and, when\nthe OCI half is auth-gated, which token realm its challenge advertises — an\nhonest lens for \"is the registry plane up\", never a fabricated ok.","tags":["registry"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryStatus"}}},"description":"ok"}},"x-app":"registry"}},"/v1/registry/tags":{"get":{"operationId":"get_v1_registry_tags","summary":"Tags lists one org-owned repository's tags, read live from the OCI registry.","description":"Tags lists one org-owned repository's tags, read live from the OCI registry.\nThe repository is addressed inside the org's namespace — a name outside it\ncannot be expressed, and an unknown one answers 404.","tags":["registry"],"parameters":[{"name":"image","in":"query","required":false,"description":"Image is the repository name inside the org's namespace, as returned by\nthe images op. It rides the query string.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryTagList"}}},"description":"ok"}},"x-app":"registry"}},"/v1/registry/token":{"post":{"operationId":"post_v1_registry_token","summary":"Token mints a short-lived, pull-only registry token for exactly one of the org's images, through the same IAM realm the docker CLI authenticates against.","description":"Token mints a short-lived, pull-only registry token for exactly one of the\norg's images, through the same IAM realm the docker CLI authenticates\nagainst. The scope is pinned server-side to `\u003corg\u003e/\u003cimage\u003e` with the `pull`\naction — no field exists to name another org's image or ask for push. Use it\nas `Authorization: Bearer …` on the OCI wire; it expires in minutes.","tags":["registry"],"requestBody":{"content":{"application/json":{"example":{"image":"cloud"},"schema":{"$ref":"#/components/schemas/registryMint"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/registryToken"}}},"description":"ok"}},"x-app":"registry"}},"/v1/releases":{"get":{"operationId":"get_v1_releases","summary":"Returns the versions that actually reached the cluster.","description":"Returns the versions that actually reached the cluster.\n\nIt lists the org's releases: the deployments that were genuinely applied to the\ncluster, with the app they belong to, their version, environment, status and when\nthey were released. A deployment that failed or is still building is NOT a\nrelease and is excluded — reaching the cluster is what makes one. Requires a\nvalidated principal; 403 without one.","tags":["releases"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/releaseBoard"}}},"description":"ok"}},"x-app":"platform"}},"/v1/replay":{"post":{"operationId":"post_v1_replay","summary":"Record a session-replay snapshot batch","description":"Accepts a batch of rrweb events from a browser recorder and hands it to the session-replay pipeline, which stores the recording and derives the session summary a player reads back.\n\nONE REQUEST IS ONE BATCH, and it is all-or-nothing: the recording is made durable before this answers, so a 200 {\"accepted\":1} means stored and never \"buffered somewhere\". There is no partial count, because a half-written recording is not a recording.\n\n`sessionId` is REQUIRED and bounded — at most 70 characters of ASCII letters, digits or '-'. It is the key every batch of one visit is grouped and ordered by, so an id outside that grammar is refused 400 here rather than accepted and dropped further down. `windowId` separates two tabs of one session and `distinctId` attributes the recording to a person; both are optional. `events` is the rrweb batch, each element a raw eventWithTime object, carried VERBATIM — the summary (click, keypress and mouse-activity counts, size) is derived downstream from exactly these bytes, so nothing is re-encoded or dropped.\n\nTHE CALLER'S CREDENTIAL DECIDES THE TENANT, and the body never does: the recording lands in the org the presented credential resolves to. It takes the SAME credentials as /v1/event — a validated bearer, an org API key, or a publishable pk- key on Authorization: Bearer, x-hanzo-ingest-key or ?ingest_key= — so a browser bundle already holding a pk- for events needs nothing new to record. A caller that presents nothing is 401 `ingest_key_required`; one whose key resolves to no project is 403 `ingest_key_unknown`; a reduced principal (a Hanzo Team workspace token) is 403 `insufficient_capability`, because a full-fidelity screen recording has no projected form that is safe for a guest to write into a host org.\n\nBOUNDS: 413 over 512 KiB of body, and that is the only bound on one batch — a recorder is expected to chunk a long session rather than send it whole, and the cap is the size one message can carry rather than an arbitrary number. 503 when the pipeline cannot take the batch: honest unavailability the caller can retry, never a 200 over a discarded recording.","tags":["replay"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/replayBody"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureResult"}}},"description":"Success"}},"x-app":"analytics"}},"/v1/rerank":{"post":{"operationId":"post_v1_rerank","summary":"Implements POST /v1/rerank (Cohere/Jina-compatible).","description":"Implements POST /v1/rerank (Cohere/Jina-compatible).\n\nBody: {\"model\": \"...\", \"query\": \"...\", \"documents\": [\"...\", ...]|[{\"text\":\"...\"}],\n\n\t\"top_n\"?: int, \"return_documents\"?: bool}\n\nResponse: {\"object\":\"list\",\"model\":...,\"results\":[{\"index\",\"relevance_score\",\"document\"?}],\"usage\":{...}}\n\nBackend selection is provider-driven (one endpoint, one contract):\n  - If the model routes to a native rerank provider (Jina/Cohere/Voyage) the\n    request is proxied to that provider's /rerank endpoint.\n  - Otherwise scores are computed as a real bi-encoder ranking: embed the\n    query and documents through the resolved embedding model and rank by\n    cosine similarity. No rerank-specific key required.","tags":["rerank"],"x-app":"github.com/hanzoai/ai"}},"/v1/research/artifacts":{"get":{"operationId":"get_v1_research_artifacts","summary":"Returns the caller org's research-diary feed newest-first — the snapshots and reports tied to its runs, as metadata and content addresses; the bytes themselves are fetched by hash.","description":"Returns the caller org's research-diary feed newest-first —\nthe snapshots and reports tied to its runs, as metadata and content addresses;\nthe bytes themselves are fetched by hash. ?run= narrows to one run, ?project=\nto one project (default the caller's project scope), and ?since= to a unix second.","tags":["research"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows to one project. Empty takes the caller's project scope.","schema":{"type":"string"}},{"name":"run","in":"query","required":false,"description":"Run narrows to one run's artifacts by its stable id.","schema":{"type":"string"}},{"name":"since","in":"query","required":false,"description":"Since bounds the feed to artifacts recorded at or after this unix second.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/artifactsOut"}}},"description":"ok"}},"x-app":"research"},"post":{"operationId":"post_v1_research_artifacts","summary":"Records one research-diary artifact — a board snapshot or a generated report — CONTENT-ADDRESSED inside the trust boundary.","description":"Records one research-diary artifact — a board snapshot or a\ngenerated report — CONTENT-ADDRESSED inside the trust boundary. The caller submits\nthe bytes as base64 `content`; the SERVER hashes them and THAT hash is the identity\nand the ref, so the address can never be poisoned by a client-asserted one. A\nclient-supplied sha256, if present, must match the bytes. The project is the\nSERVER's value and visibility is forced private. Re-posting the same bytes is a\nno-op that reports created=false.","tags":["research"],"requestBody":{"content":{"application/json":{"example":{"content":"iVBORw0KGgo=","kind":"snapshot","run_id":"benchmark:zen-1:mmlu"},"schema":{"$ref":"#/components/schemas/ResearchArtifact"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/artifactOut"}}},"description":"ok"}},"x-app":"research"}},"/v1/research/artifacts/{sha256}":{"get":{"operationId":"get_v1_research_artifacts_by_sha256","summary":"Fetch one recorded artifact's bytes by its content hash.","description":"Streams the artifact's stored bytes — the retrieval half of hash-addressing, where the diary feed hands out hashes and this hands back what they name. The Content-Type is image/png when the artifact was recorded as a snapshot and application/octet-stream otherwise; it comes from the recorded KIND, not from sniffing the bytes, so an artifact filed as a report always arrives as opaque bytes.\n\nThe hash is an address, and the read is NOT global. The store file IS the org, so the same bytes recorded by two tenants are two artifacts, and a hash that exists but belongs to somebody else is a 404 exactly like one that was never recorded — knowing a content hash is never enough to read it. A caller with no validated org is refused 403 outright.\n\nProject narrows further INSIDE that org: the artifact's project must equal the caller's, which is `?project=` when given and otherwise the caller's own project scope, defaulting to the default project. So an artifact filed under a named project is not found until the caller names that project — a mismatch is the same 404 an unknown hash gets, never a distinguishable refusal.\n\nThe address can be trusted because the WRITE derived it: the server hashes the bytes it stores, inside the trust boundary, and refuses a client-supplied sha256 that disagrees with them, so poisoning a first write would take a preimage. This read does not re-hash — it looks the hash up as a key.\n\nOne shape to expect: this route writes its errors IN-BAND as {\"error\": …} at the real status code, not the {status, error} envelope the typed ops beside it return. It is mounted under an error-flattening filter that would otherwise rewrite its 4xx, so the body is written before that filter runs. A store that cannot be opened is a 500.","tags":["research"],"parameters":[{"name":"sha256","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"research"}},"/v1/research/experiments":{"get":{"operationId":"get_v1_research_experiments","summary":"Returns the caller org's CANONICAL experiments — the deterministic deduped view over the versioned history.","description":"Returns the caller org's CANONICAL experiments — the deterministic\ndeduped view over the versioned history. With no ?project= it reads the org's\nwhole set across projects (the ops board's cross-project view, since a project is\na sub-scope of the one tenant); ?project= narrows to one and ?kind= to one\ndiscriminator.","tags":["research"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows to one project. Empty reads the org's whole set across projects.","schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"description":"Kind narrows to one discriminator: benchmark, kernel-perf, training, ablation or policy-eval.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/experimentsOut"}}},"description":"ok"}},"x-app":"research"},"post":{"operationId":"post_v1_research_experiments","summary":"Appends one batch of experiment and attempt versions to the caller org's evidence store, idempotently by content, then rolls it up to the analytics plane best-effort.","description":"Appends one batch of experiment and attempt versions to the\ncaller org's evidence store, idempotently by content, then rolls it up to the\nanalytics plane best-effort. The project is the SERVER's value and visibility is\nforced private — an upload grants no training or publication right, which is a\nseparate call. A run carrying a BYO endpoint is SSRF-checked before the store is\ntouched. The answer carries BOTH the canonical (deduped) and retained (full\nhistory) counts, so a caller sees the versioned truth rather than a dedup that\nreads as loss.","tags":["research"],"requestBody":{"content":{"application/json":{"example":{"attempts":[],"experiments":[{"id":"benchmark:zen-1:mmlu","kind":"benchmark","metric":"accuracy","subject":"zen-1","ts":1750000000,"value":0.81}]},"schema":{"$ref":"#/components/schemas/IngestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ingestOut"}}},"description":"ok"}},"x-app":"research"}},"/v1/research/grants":{"post":{"operationId":"post_v1_research_grants","summary":"Records the SEPARATE authorization an upload never implies: a record's visibility (private, org or public) and, for a run, its training and commons-publication consent.","description":"Records the SEPARATE authorization an upload never\nimplies: a record's visibility (private, org or public) and, for a run, its\ntraining and commons-publication consent. Address a run by its stable id or an\nartifact by its sha256; an artifact grant sets visibility only. The ORG is the\ntenant boundary and comes from the validated principal, so a caller can only ever\ngrant within its own org; `project` locates WHICH record inside it and defaults to\nthe caller's project scope.","tags":["research"],"requestBody":{"content":{"application/json":{"example":{"id":"benchmark:zen-1:mmlu","trainable":true,"visibility":"public"},"schema":{"$ref":"#/components/schemas/GrantRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/grantOut"}}},"description":"ok"}},"x-app":"research"}},"/v1/research/projects":{"get":{"operationId":"get_v1_research_projects","summary":"Returns every research project in the caller's org with its real totals — canonical and retained side by side — which is the ops board's \"every project + real totals\" view.","description":"Returns every research project in the caller's org with its\nreal totals — canonical and retained side by side — which is the ops board's\n\"every project + real totals\" view.","tags":["research"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsOut"}}},"description":"ok"}},"x-app":"research"}},"/v1/research/totals":{"get":{"operationId":"get_v1_research_totals","summary":"Returns the caller org's headline aggregate plus a per-kind breakdown — the observatory's poll target.","description":"Returns the caller org's headline aggregate plus a per-kind\nbreakdown — the observatory's poll target. Canonical and retained counts travel\ntogether, so a deduped view never reads as loss. ?project= narrows to one project.","tags":["research"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows the aggregate to one project. Empty aggregates the whole org.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResearchTotals"}}},"description":"ok"}},"x-app":"research"}},"/v1/responses":{"post":{"operationId":"post_v1_responses","summary":"Implements POST /v1/responses.","description":"Implements POST /v1/responses. The converted request is passed to\nChatCompletions and an installed ResponseWriter converts its OpenAI chat JSON\nor SSE back into Responses JSON/SSE on the fly.","tags":["responses"],"x-app":"github.com/hanzoai/ai"}},"/v1/risk/datasets":{"get":{"operationId":"riskDatasets","summary":"List this org's datasets","description":"Datasets lists this org's datasets, each with its newest version. An org that\nhas declared none gets an empty list; a store that cannot be reached gets a\nrefusal, never an empty list, because the two read identically and only one of\nthem is true.","tags":["risk"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDatasetList"}}},"description":"ok"}},"x-app":"dataset"},"post":{"operationId":"riskCreateDataset","summary":"Declare the next version of a dataset","description":"Declares the next version of a dataset from a bound query over\nthis org's own feature surface.\n\nIt mints a VERSION and writes no rows: a version is declared, then materialised\nonce, then never rewritten. Version numbers are monotone and never reused, so\n\"version 3 of signups\" means one thing forever — which is the whole reason a\nmodel can cite one.\n\nThe window is bounded by the source's retention, the horizon by a year, the\nrows by the plane's cap, and the number of datasets and versions per org by\ntheir own limits. Every refusal names which bound it hit.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"from":"2026-01-01T00:00:00Z","horizon":14,"kind":"person","name":"signups","to":"2026-04-01T00:00:00Z"},"schema":{"$ref":"#/components/schemas/riskDatasetSpec"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDataset"}}},"description":"ok"}},"x-app":"dataset"}},"/v1/risk/datasets/{name}":{"delete":{"operationId":"riskDeleteDataset","summary":"Dispose of one dataset and every version of it","description":"Disposes of one dataset and every version of it: the rows are\ndropped and the register is marked with what went.\n\nThis is the ONLY expiry in this plane. Neither table carries a TTL, deliberately:\na table TTL is a fleet-wide clock no tenant can hold longer or shorten, which is\nthe opposite of a retention decision belonging to the tenant whose records they\nare. The drop is a partition drop on (org, dataset), so the tenant is the first\ncomponent of the thing being dropped and a disposal cannot be spelled across one.\n\nThe BYTES are what goes. The register keeps one `disposed` row per version — the\nname, the number, the spec, the digest and who disposed of it when — for two\nreasons: a retention obligation is answered by a record of the deletion, not by\nsilence; and version numbers must stay monotone, so that after `orders` is\ndisposed of and declared again the next version is 4 and not 1. A number that\ncould be reused would make every citation of `orders v3` ambiguous forever.\n\nIt is not reversible and there is no soft state in between. A version a model\ncited has no rows once this returns, and every read of it says so.","tags":["risk"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset, from the path.","schema":{"type":"string"},"example":"signups"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDatasetDisposal"}}},"description":"ok"}},"x-app":"dataset"},"get":{"operationId":"riskDataset","summary":"Describe every version of one dataset","description":"Dataset describes every version of one dataset, newest first — the whole\nhistory, because the point of a version is that the older ones are still there\nand a model fitted last quarter cites one of them.\n\nA name this org does not own answers 404, exactly as an unknown name does, so a\nprobe learns nothing about another tenant's datasets.","tags":["risk"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset, from the path.","schema":{"type":"string"},"example":"signups"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDatasetVersions"}}},"description":"ok"}},"x-app":"dataset"}},"/v1/risk/datasets/{name}/export":{"get":{"operationId":"riskExportDataset","summary":"Read a version's rows back, one page at a time","description":"Reads a published version's rows back, one bounded page at a\ntime, in the version's own stable row order.\n\nOnly a published version can be exported. Rows written by an attempt that never\ncompleted are inert — no register row names them — and they are disposed of with\nthe dataset.","tags":["risk"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset, from the path.","schema":{"type":"string"},"example":"signups"},{"name":"version","in":"query","required":false,"description":"Version is the version to read. Zero takes the newest published one.","schema":{"type":"integer"},"example":1},{"name":"split","in":"query","required":false,"description":"Split narrows to train, val or test. Empty reads every split.","schema":{"type":"string"},"example":"train"},{"name":"offset","in":"query","required":false,"description":"Offset is where the page starts, in the version's own row order (by id,\nwhich is derived from the row and therefore stable forever).","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit is how many rows to return. Zero and anything above the plane's bound\ntake the bound.","schema":{"type":"integer"},"example":500}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDatasetRows"}}},"description":"ok"}},"x-app":"dataset"}},"/v1/risk/datasets/{name}/lineage":{"get":{"operationId":"riskDatasetLineage","summary":"Show where a version's rows came from, and whether that can still be demonstrated","description":"Shows where a version's rows came from and whether that can\nstill be demonstrated.\n\nThe answer is MEASURED, not recalled: the plane asks the source the same\nbounded question again and compares it to the fingerprint taken when the\nversion was built. Anything but exact agreement is reported as drift — the\nsource is fed by a rollup that runs behind the events, so \"it holds more now\"\nis the ordinary case and it means re-running the spec would not reproduce this\nversion. An admitted gap is actionable; an unfalsifiable claim is not.\n\nIT IS A PRICED, BOUNDED READ, because it is the same statement a\nmaterialisation is charged for: an exact distinct-count over up to 400 days of\nthis org's feature surface. It takes the org's ONE source-scan slot, so a\ntenant looping it spends one scan and not a thousand; it counts against the\nplane's ceiling, so the fleet's warehouse is bounded too; and it runs under\nthis plane's own deadline rather than the caller's patience.","tags":["risk"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset, from the path.","schema":{"type":"string"},"example":"signups"},{"name":"version","in":"query","required":false,"description":"Version is the version to trace. Zero takes the newest published one.","schema":{"type":"integer"},"example":1}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLineage"}}},"description":"ok"}},"x-app":"dataset"}},"/v1/risk/datasets/{name}/materialize":{"post":{"operationId":"riskMaterializeDataset","summary":"Materialise the declared version into immutable rows","description":"Builds the declared version into immutable rows and answers\n202 as soon as the attempt is on record.\n\nIt never holds the request open for the work: a materialisation is a bounded\nwarehouse scan, and letting an HTTP client's timeout be a data plane's timeout\nis how one tenant's retry loop becomes everyone's outage. ONE materialisation\nruns per org at a time; a second is refused rather than queued, because a queue\nadmits the same work later and the honest answer to \"again\" while one is\nrunning is that one is running.\n\nOnly a DECLARED version is admitted. A published version is immutable, and a\nversion whose earlier attempt did not complete is never re-attempted — that\nwould union two runs' rows under one number and make the digest a lie. In both\ncases the answer is to declare a new version, which is what a second run over a\nmoving source honestly is.","tags":["risk"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset, from the path.","schema":{"type":"string"},"example":"signups"}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDataset"}}},"description":"accepted"}},"x-app":"dataset"}},"/v1/risk/features":{"get":{"operationId":"riskFeatures","summary":"The feature catalogue: what the model reads, and what your surface carries","description":"Features is the feature catalogue in its two honest lenses.\n\nThe MODEL lens is the governed inventory: one entry per dimension of the model\nspace, each carrying the typology it serves, the supervisor's own words for the\nindicator, and the published standard those words come from — so a coverage\nclaim is checkable rather than asserted. It is the same for every organisation.\n\nThe SURFACE lens is what THIS organisation's own event surface actually carries,\nmeasured over the window: how many of its buckets carry each dimension at all,\nand what the dimension reads where it is present. A dimension present in no\nbucket is BLIND, and saying so is the difference between no risk and no data.","tags":["risk"],"parameters":[{"name":"days","in":"query","required":false,"description":"Days is how far back to measure the organisation's own coverage, 1 to 400.\nZero takes thirty.","schema":{"type":"integer"},"example":30}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskCatalog"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/health":{"get":{"operationId":"get_v1_risk_health","summary":"Whether the risk model plane can actually work right now","description":"Reports whether the per-organisation model plane is genuinely usable: that the plane was built, that the per-organisation stores can be written, and whether the event surface the feature plane is rolled up from is reachable. It is a REAL probe, not status theatre.\n\n200 only when the plane can work. Otherwise 503 CARRYING THE REPORT — which part failed and the real error — and that body is why this is not a typed op: a typed op reaches a non-2xx by returning an error, and the envelope that produces would drop exactly the detail the probe exists to deliver.\n\nAn unreachable event surface is REPORTED and is not a failure. Scoring reads in-memory aggregates and never the warehouse, so a warm that cannot run degrades how much history a model has seen and does not stop it deciding.\n\nIt also reports how many organisations' models are resident, how many have been evicted to hold that bound, and how many of the resident ones are at their own aggregate bound. Eviction is lossless — learned state is written to that organisation's own store first and its aggregates rebuild from its own record — so a climbing count is a capacity signal, not a loss. A STRAINED model is different: it has started forgetting its own least-recently-active subjects, and each forgotten subject reads as inactive until it is active again. That is a control degrading, and it is reported here because it is otherwise silent.\n\nIt answers about the process, not about a tenant: it takes no organisation and names none.","tags":["risk"],"x-app":"risk"}},"/v1/risk/labels":{"get":{"operationId":"riskLabels","summary":"Read the assertions this tenant has recorded","description":"Reads the assertions this tenant has recorded, newest event first.\n\nIt reads the RECORD — the tenant's own store — and not the columnar copy, so\nwhat it returns is what would be produced in an audit. Narrow it by entity, by\nasserter, or by event window.","tags":["risk"],"parameters":[{"name":"kind","in":"query","required":false,"description":"Kind and Subject narrow to one entity.","schema":{"type":"string"}},{"name":"subject","in":"query","required":false,"schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source narrows to one asserter — the read that answers \"what has commerce\ntold us\", separately from \"what has an analyst told us\".","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From and To bound the EVENT time, half-open, RFC 3339.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the page. Out of range takes the plane's own bound.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelsOut"}}},"description":"ok"}},"x-app":"label"},"post":{"operationId":"riskLabel","summary":"Assert ground truth about events","description":"Records a batch of ground truth against the entities it judges.\n\nEach assertion carries TWO times — when the judged event happened, and when\nthe assertion became knowable — and both are required. The second is what\nkeeps a chargeback that landed in June out of a model that had to decide in\nFebruary.\n\nIt is idempotent on the CONTENT of an assertion, so a webhook that redelivers\nis safe. It never overwrites: a source that corrects itself later files a NEW\nassertion, which wins from the moment it became knowable and leaves every\nearlier observation instant seeing exactly what it saw.\n\nThe asserter is stamped from the validated credential and is not a body field.","tags":["risk"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelOut"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/labels/coverage":{"get":{"operationId":"riskLabelCoverage","summary":"How much of the window has matured, and how much of that is judged","description":"Reports how much of a window has matured and how much of that is\njudged, per source.\n\nIt is the gate on training. A supervised fit over a window whose judged count\nis near zero produces a number, and the number is meaningless; this op is what\nlets that be stated before the fit rather than discovered after it.\n\nIt reads the RECORD plane and folds every assertion at that event's OWN as-of,\nso the counts obey exactly the leakage rule a materialisation would. It counts\nonly what was ASSERTED: what share of the whole event STREAM carries a label is\na question about the feature plane's denominator and is not answerable here.","tags":["risk"],"parameters":[{"name":"from","in":"query","required":false,"description":"From and To bound the EVENT window, half-open, RFC 3339.\n\nUnstated, the window is the 90 days ENDING where maturity begins — `to` is\nthe horizon ago, not now. A default window running to now under a default\nhorizon could not contain one matured event, so every count below it would\nbe zero however much ground truth the tenant held.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"horizon","in":"query","required":false,"description":"Horizon is the maturity horizon in days the coverage is measured under.\nUnstated takes 120. It also moves the default window, which ends where\nmaturity begins.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelCoverage"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/labels/dispose":{"post":{"operationId":"riskDisposeLabels","summary":"Dispose of this tenant's expired assertions, whole records only","description":"Applies this tenant's retention, and only this tenant's.\n\nIt is bounded three ways, each a compliance property rather than a\nconvenience. It refuses a boundary younger than the platform floor, because a\nlabel can be the input to an adverse action and five years is what the\nretention ledger holds such a record for. It never touches a record under\nlitigation hold. And it disposes of whole records rather than redacting\nfields.\n\nIt removes the derived columnar copy BEFORE the record, and refuses the whole\ndisposal if the warehouse cannot be reached. The other order would leave rows\nin the warehouse that nothing can identify any more, which is a disposal that\ndid not happen and says it did.","tags":["risk"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDisposeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDisposeOut"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/labels/hold":{"post":{"operationId":"riskHoldLabels","summary":"Place or release a litigation hold on named records","description":"Places or releases a litigation hold on named records.\n\nA hold is a fact about the RECORD, not about the world: it says retention may\nnot dispose of this row, and it asserts nothing about what happened. So it is\nnot a field on an assertion and it is not folded into the content digest —\ncarried there it was silently a no-op on any record that already existed, since\nre-filing the same assertion with a hold flag produced the same digest, the\ninsert was ignored, and the caller was answered `duplicate` while the hold it\nasked for was never placed. This op is the one way a hold moves, in either\ndirection, and the move is written to the audit log.\n\nEvery named id is this tenant's or is nothing. The statement runs against the\ntenant's own file, which holds no other tenant's rows and has no column that\ncould name one.","tags":["risk"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskHoldIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskHoldOut"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/labels/resolve":{"post":{"operationId":"riskResolveLabels","summary":"Resolve the label in force for named events, as of each event's own horizon","description":"Answers, for each named event, which assertion was in force AS OF that\nevent's own horizon — and what disagreed with it.\n\nThis is the join surface: the dataset materialiser calls it to attach ground\ntruth to training rows, and the evaluator calls it to score a past decision\nagainst what was knowable when the decision had to be made. One mechanism for\nboth, so a model can never be trained under one leakage rule and scored under\nanother.\n\nThree answers are distinct and all three are honest: a resolved label, an\nevent that has not matured, and a matured event nobody has judged. The last is\nnever reported as unproductive.","tags":["risk"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskResolveIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskResolveOut"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/labels/vocabulary":{"get":{"operationId":"riskLabelVocabulary","summary":"The closed vocabularies and the precedence rule that resolves a conflict","description":"Publishes the closed vocabularies and the precedence rule that\nresolves a conflict between two sources.\n\nA precedence rule nobody can read is a rule nobody can audit or dispute, and\nthe whole defensibility of a contested label rests on being able to say why\none assertion beat another. The order returned here is derived from the same\ndeclaration the resolver reads — it is not a description of it.","tags":["risk"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelVocabulary"}}},"description":"ok"}},"x-app":"label"}},"/v1/risk/learn":{"post":{"operationId":"riskLearn","summary":"Teach your organisation's own model from its own events","description":"Learn records a batch of events into the caller organisation's own aggregates\nand lets its model learn from them. It answers how many it learned from.\n\nIT DOES NOT SCORE, AND THAT IS THE POINT. An observation is a value you record;\nlearning is a transformation over observations; a verdict is a query against the\nresult. This op is the first two. [ops.score] is the third, it is pure, and it\nis the ONE door to a verdict. They were one call, which meant you could not\nrecord without training and could not train without being answered — and the\nmodel ran twice over every event to produce a verdict the response carried and\nno caller read.\n\nTO OBSERVE AND JUDGE, COMPOSE THE TWO, and mind the order. Score FIRST, then\nlearn: the score is then the model's opinion of an event it has not yet learned\nfrom, which is the question worth asking. The other order answers for a model\nthat has already absorbed the event it is judging.\n\nThis is the training path, and there is no job behind it: the model IS a set of\nmass counters over half-space trees, so learning is an increment and the model\nis current the instant the last event lands. Nothing from any other\norganisation is in it, and nothing from this organisation leaves it.\n\nA RETRY IS INERT. The record deduplicates on the event id you send, and an event\nalready in it moves nothing, costs nothing and is not counted — so a client that\ntimed out can send the same batch again and its model holds what it holds.\nWithout an id of your own there is nothing to converge on: two identical bodies\nare two events.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"events":[{"id":"tx_9","kind":"account","nano":420000000,"subject":"u_412"}]},"schema":{"$ref":"#/components/schemas/riskLearnIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLearnOut"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/policy":{"get":{"operationId":"riskPolicy","summary":"Your organisation's decision-regime history, and which version is in force","description":"Policy reports the caller organisation's own decision-regime history: every\ndistinct regime it has adopted, which version is in force, and what retention\nhas taken.\n\nWHY IT EXISTS. Every score cites the version it was decided under\n([riskScoreOut.Policy]), and the threshold that score was measured against is\nderived from the appetite that version states. Restate the appetite and, without\nthis record, every earlier decision becomes unreconstructible — the cut it was\njudged by no longer exists anywhere. An adverse decision that cannot be\nexplained against the policy in force when it was taken cannot be defended.\n\nIt covers ONE organisation. The history is on that organisation's own shelf, so\nanother's versions are not filtered out of the answer — they are not in the file\nthe answer is read from.","tags":["risk"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskPolicyOut"}}},"description":"ok"}},"x-app":"risk"},"put":{"operationId":"riskSetPolicy","summary":"State the decision regime: the appetite, the sample, and whether the model is live","description":"States the decision regime the caller organisation's model decides\nunder: how much of its own stream may be sent for examination, how much of the\nrest is sampled to measure what was missed, and whether the model may change an\noutcome at all.\n\nThe appetite is the decision a model is not permitted to make for itself: its\noutput is a probability, so how likely it is to MISS something is a matter of\npolicy that has to be stated, measured and reviewed rather than absorbed into a\nconstant. The alert threshold is derived from it as a quantile of the scores\nactually observed, which is what keeps its meaning as the distribution drifts.\n\nIt is DURABLE BEFORE IT IS IN FORCE. The regime is recorded as a new version on\nthe organisation's own shelf before anything in memory moves, so a policy that\ncannot be written down is refused rather than answered from state the next\nrollout would silently undo.\n\nARMING IS AN ADMIN ACT AND TUNING IS NOT. Setting `live` requires an admin of\nthis organisation; stating the appetite and the sample is self-service for any\nmember. Taking the model live decides whether it may change an OUTCOME at all —\na payment frozen, a grant refused — for every customer this organisation has,\nand that is a decision an organisation takes rather than one of its members.\n\nA RESTATEMENT OF THE REGIME IN FORCE MINTS NOTHING and answers the version\nalready in force. Compare the version you receive with the version you had:\nunchanged means the numbers were the same, which is why there is no flag for it.\n\nLearned state survives the change. The model's identity covers its SHAPE — the\ninventory and the geometry — and not its appetite, so restating policy unlearns\nnothing. It also does not REPORT the learned state: what the model is is read\nfrom the model.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"live":false,"review":0.01,"sample":0.001},"schema":{"$ref":"#/components/schemas/riskAppetiteIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskPolicyOut"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/reference":{"get":{"operationId":"riskReferenceSets","summary":"Lists every set this plane publishes, with its version and how fresh it is.","description":"Lists every set this plane publishes, with its version and how\nfresh it is.\n\nRead the Stale and Refused lists first: they are the two ways this plane can\nbe quietly wrong, and they are reported rather than inferred. A set in\nRefused answers nothing — it has never loaded, it is held by another\ncomponent, or it names a source we hold no licence for.","tags":["risk"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceSetsOut"}}},"description":"ok"}},"x-app":"reference"}},"/v1/risk/reference/refresh":{"post":{"operationId":"riskRefreshReference","summary":"Takes a new version of one set.","description":"Takes a new version of one set. SuperAdmin only.\n\nIt is platform work, not tenant work: it writes the shared baseline every\norganisation reads, so it is gated to the platform's own identity. Nothing\nhere can write an organisation's overrides, and nothing an organisation sends\ncan reach this route.\n\nIdempotent. A version is the content digest of what was taken, so refreshing\nan unchanged publisher writes no rows and reports unchanged. Resumable: a run\nthat died half-way is continued from where it stopped rather than restarted.\n\nA set whose source needs a licence we do not hold is refused with the reason,\nrather than being quietly skipped.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"set":"domain"},"schema":{"$ref":"#/components/schemas/RefreshReferenceIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshReferenceOut"}}},"description":"ok"}},"x-app":"reference"}},"/v1/risk/reference/resolve":{"post":{"operationId":"riskResolveReference","summary":"Looks keys up against the reference plane.","description":"Looks keys up against the reference plane.\n\nYour organisation's own overrides are consulted FIRST and win outright; the\nshared baseline answers everything they do not cover. Every answer names the\nversion that produced it, when that version was current and whether it is\nstale, so a decision can record exactly what it consulted.\n\nRead Refusal before reading Hit. A set that has never loaded, one held by the\ncomponent that screens against it, and one whose source needs a licence we do\nnot hold all answer with a refusal — and a miss on a refusing set means\nnothing is known, not that the key is clean.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"keys":["user@tempbox.example","3.5.140.1"],"sets":["domain","net"]},"schema":{"$ref":"#/components/schemas/ResolveReferenceIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveReferenceOut"}}},"description":"ok"}},"x-app":"reference"}},"/v1/risk/reference/{set}":{"delete":{"operationId":"riskClearReference","summary":"Removes one of your organisation's overrides.","description":"Removes one of your organisation's overrides.\n\nIt removes an entry your organisation wrote, never a baseline member: the\npublished set is not writable from here, so a removal can only ever restore\nthe baseline's own answer.","tags":["risk"],"parameters":[{"name":"set","in":"path","required":true,"schema":{"type":"string"},"example":"domain"},{"name":"key","in":"query","required":false,"schema":{"type":"string"},"example":"partner.example"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClearReferenceOut"}}},"description":"ok"}},"x-app":"reference"},"get":{"operationId":"riskReference","summary":"Reference describes one set and lists your org's overrides in it.","description":"Reference describes one set and lists your org's overrides in it.\n\nThe set half is public data about a published list — its version, its\npublishers, their licences and how current each one is. The overrides half is\nyours alone: it is read from your organisation's own store, and no other\norganisation's entries can appear in it.","tags":["risk"],"parameters":[{"name":"set","in":"path","required":true,"schema":{"type":"string"},"example":"domain"},{"name":"after","in":"query","required":false,"description":"After pages the override listing: the last key of the previous page.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the override listing: default 200, maximum 1000.","schema":{"type":"integer"},"example":50}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceOut"}}},"description":"ok"}},"x-app":"reference"},"put":{"operationId":"riskSetReference","summary":"Writes your organisation's own allow and deny entries over a set.","description":"Writes your organisation's own allow and deny entries over a set.\n\nIdempotent on the key: writing the same entry twice is one entry, and writing\nit again replaces the verdict and the note. The whole batch is one\ntransaction, so a batch that would cross the per-set bound writes nothing\nrather than half of itself — a half-applied deny list is worse than a refused\none, because nobody can tell which half applied.\n\nYour entries are held in your organisation's own store and are never visible\nto another organisation, and they never change what any other organisation\nsees. The shared baseline is not writable from here at all.","tags":["risk"],"parameters":[{"name":"set","in":"path","required":true,"schema":{"type":"string"},"example":"domain"}],"requestBody":{"content":{"application/json":{"example":{"entries":[{"key":"partner.example","note":"our reseller","verdict":"allow"}],"set":"domain"},"schema":{"$ref":"#/components/schemas/SetReferenceIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetReferenceOut"}}},"description":"ok"}},"x-app":"reference"}},"/v1/risk/score":{"post":{"operationId":"riskScore","summary":"Score one event against your organisation's own model","description":"Score judges one event against the caller organisation's OWN model and learns\nnothing from it. It is how a candidate is tried against real behaviour before\nanything depends on the answer, and it is the model's analogue of testing a\nrule.\n\nBecause it records nothing, the aggregates it reads do not include the event:\nthe numbers are the organisation's history as it stands. A model still warming\ndeclines with a reason rather than answering zero, because silence must never\nread as a clean result.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"event":{"id":"tx_9","kind":"account","nano":420000000,"subject":"u_412"}},"schema":{"$ref":"#/components/schemas/riskScoreIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskScoreOut"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/search":{"post":{"operationId":"riskSearch","summary":"Search exhaustively for the model shape that fits your own history","description":"Search runs an exhaustive search for the model shape that best fits the caller\norganisation's own history, and answers 202 with the run to read back.\n\nEvery candidate is replayed over that organisation's OWN feature surface in its\nown sandbox — its own aggregates, its own model, neither of them the live one —\nso a run cannot move a live threshold and cannot see another organisation's\ndata. The result is the learning curve for each shape and the one that fit\nbest, ranked on how closely it honoured the stated appetite, whether it warmed\nat all, whether it saturated, and how much of the coordinate space it left\nblind.\n\nAn empty history is REFUSED rather than reported as zero alerts, because \"no\nalerts\" is exactly what a quiet model looks like.","tags":["risk"],"requestBody":{"content":{"application/json":{"example":{"days":30},"schema":{"$ref":"#/components/schemas/riskSearchIn"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskSearchRun"}}},"description":"accepted"}},"x-app":"risk"}},"/v1/risk/search/{id}":{"get":{"operationId":"riskSearchResult","summary":"Read back one exhaustive search","description":"Reads back one search run: every shape tried over this\norganisation's own history, best first, and the one that fit.\n\nA run another organisation started is simply not there — the same 404 an\nunknown id gives, so the read is not a probe oracle.","tags":["risk"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the run, taken from the path. A run another organisation started is\nsimply not there — the same answer an unknown id gives.","schema":{"type":"string"},"example":"srch_2f6a1c"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskSearchReport"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/state":{"get":{"operationId":"riskState","summary":"Report your organisation's model: what it learned, and what it realised","description":"State reports the caller organisation's own model: what it has learned, whether\nit is live or still in shadow, the threshold in force, the appetite it stated\nbeside the share it actually realised, every refusal by reason, every feature\nthat read blind, and how much of the organisation's own event surface has been\nfolded in.\n\nIt covers ONE organisation. A caller cannot learn another's volumes, alert rate\nor behaviour from it, because the state is read out of a model that holds only\nits own.","tags":["risk"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskModelState"}}},"description":"ok"}},"x-app":"risk"}},"/v1/risk/state/model":{"post":{"operationId":"riskPublishModel","summary":"Publish your organisation's model as a named, immutable value","description":"Publishes your organisation's model as a NAMED VALUE, so a decision\ntaken today can be reconstructed tomorrow and a change made today can be undone.\n\nIt answers with a NAME and not with the state. The masses stay on your\norganisation's own encrypted store and are referred to by an address computed\nfrom their own content: the shape, the geometry seed, the position in the window,\nthe threshold, the masses themselves as IEEE-754 bits, and the fold watermark\nbehind them. That is what makes the value nameable without making the caller its\ncustodian.\n\nIT IS IDEMPOTENT ON THE VALUE. A model that has not changed publishes to the name\nit already has and mints nothing, reporting minted=false — so publishing at every\nboundary that matters is free. Ten values are retained per organisation, bounded\nin BYTES rather than in rows, and the oldest is disposed of past that.\n\nA model that has learned nothing is refused: planted is not learned, and a value\nthat reproduces nothing is not a value.\n\nIt is POST and PUT on one address because they are one plane's two verbs over one\nkind of thing: POST mints a value from the model in force, PUT puts a value in\nforce. They were /v1/risk/state/snapshot and /v1/risk/state/restore — two addresses\nnamed after the operation rather than after the thing, which is how a reader ends\nup asking what the difference between a snapshot and a value is.","tags":["risk"],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskPublishOut"}}},"description":"created"}},"x-app":"risk"},"put":{"operationId":"riskAdoptModel","summary":"Put one of your organisation's own published model values in force","description":"Puts one of your organisation's OWN PUBLISHED VALUES in force, by name —\nwhich is what an instant rollback is, what promoting a challenger is, and what\ninstalling the shape a search found is.\n\nIT TAKES AN ADDRESS AND NEVER STATE. The masses are read from your own store, so\nnothing about your model has to be held by whatever is making this call. That\ncloses the sharpest edge the previous shape had: a body of counters is something\na caller can COMPOSE, and a region filled until activity inside it reads as\nordinary is a model that has been shaped rather than learned. The engine's mass\ninvariant was the only thing standing between a composed body and the model; with\nan address there is no body to compose.\n\nIT ADOPTS THE SHAPE, NOT ONLY THE MASSES. A value records the model space its\nmasses were taken in, and a value whose space differs from the one in force\nREPLANTS your model into that space before restoring them. That is what makes\nPOST /v1/risk/search actionable: a search answers with the shape that fits your own\nhistory best and publishes it fitted, and its address is what you name here. Before\nthis, a winning shape was advice nobody could take — the adoption path refused every\nshape change, and a winner is a different shape by definition.\n\nWHAT ADOPTING A SEARCHED SHAPE COSTS, SAID PLAINLY: the value a search fits has\nlearned the window the search replayed and nothing older, so installing it trades\nhistory for fit. Your appetite is untouched — that is your policy record's, with its\nown versions — and so is the geometry, which stays your own.\n\nAn address your organisation has not published is NOT FOUND. That includes one\nanother organisation published, and it is not a lookup that failed: the store is\nper organisation and the address is a name, never an authority.","tags":["risk"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskAdoptIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskModelState"}}},"description":"ok"}},"x-app":"risk"}},"/v1/router/artifact-meta":{"delete":{"operationId":"delete_v1_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/data":{"delete":{"operationId":"delete_v1_router_data","summary":"Data","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_data","summary":"Data","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/defaults":{"delete":{"operationId":"delete_v1_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/history":{"get":{"operationId":"get_v1_router_history","summary":"Returns the router-improvement time-series.","description":"Returns the router-improvement time-series. Two scopes, one route,\nmirroring /v1/router/stats:\n\n  - ?scope=platform — PUBLIC-safe aggregate over ALL orgs, no authentication. Emits\n    the daily reward/cost-saved/adoption series (task mix included, model ids NOT)\n    and the retrain timeline. This is what world.hanzo.ai polls.\n  - default (org scope) — requires a signed-in principal, scoped to the caller's OWN\n    org (a super admin may pass ?org= to target another or \"\" for all).\n\nWindow: ?days=N (default 30, capped at 90). Aggregates only.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/judge-panel":{"get":{"operationId":"get_v1_router_judge-panel","summary":"Returns the LIVE Mean-Field Judge Panel state: the configured panel + dynamic judge posture (enabled/sample) resolved from the \"*\" GlobalDefaultOwner row, the live in-process per-judge calibration (weight/mean/n), and the static published benchmark.","description":"Returns the LIVE Mean-Field Judge Panel state: the configured\npanel + dynamic judge posture (enabled/sample) resolved from the \"*\"\nGlobalDefaultOwner row, the live in-process per-judge calibration (weight/mean/n),\nand the static published benchmark. PUBLIC-safe and platform-global (model ids +\nscalars only), so it rides the same unauthenticated, balance-exempt class as\n/v1/router/stats?scope=platform — the world widget polls it with no auth. The judge\nstate is a single in-process population (not per-org), so there is nothing to scope;\n?scope=platform is accepted for symmetry with router-stats.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/ledger":{"delete":{"operationId":"delete_v1_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/policy":{"delete":{"operationId":"delete_v1_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/rewards":{"delete":{"operationId":"delete_v1_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"get":{"operationId":"get_v1_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"patch":{"operationId":"patch_v1_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"},"put":{"operationId":"put_v1_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/org/settings[/list]). It dispatches IN-PROCESS through dispatchGateway —\nthe SAME canonical ZAP gateway registry (zap_registry.go) that the MsgType 200\nhandler serves over the gateway transport. The native ZAP\nhandler is the ONE and ONLY implementation of these routes; this is purely the\napi.hanzo.ai HTTP binding, so there is NO beego twin to drift from and the\nsplit-brain the router refactor removed stays removed.\n\nWhy a bridge and not a twin controller method: every other migrated route\n(get-records, get-connections, …) carries BOTH a beego controller method and a\nZAP handler — the exact dual-impl drift that silently NULLed customer router\nsettings (the update-router-policy data-wipe). Routing these nouns through the\nZAP handler over one adapter keeps a single source of truth.\n\nIdentity is the request's own Bearer credential (Authorization header), which\nthe native handlers resolve exactly as the gateway does — every caller\n(console, chat, app) already sends it. The dispatched handler returns a ZAP\nmessage whose status is field 0 and body is field 4 (BuildCloudResponse /\nBuildGatewayResponse layout); both are relayed verbatim. The route is mapped\n\"*\" (any verb) because the native handler is method-aware: /v1/router/policy\nsplits GET (read) vs PUT (write), /v1/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/router/stats":{"get":{"operationId":"get_v1_router_stats","summary":"Returns the router observability aggregate.","description":"Returns the router observability aggregate. Two scopes, one route:\n\n  - ?scope=platform — PUBLIC-safe aggregate over ALL orgs, no authentication.\n    Emits rates, shares, per-task/per-model counts, throughput, and the cost\n    RATIO (saved_pct) + counterfactual model id, but NEVER absolute $ levels,\n    org identity, raw events, or feature vectors. This is what world.hanzo.ai\n    polls.\n  - default (org scope) — requires a signed-in principal; scoped to the caller's\n    OWN org (a super admin may pass ?org= to target another org or \"\" for all).\n    Carries the absolute $/MTok indices for the admin savings panel.\n\nWindow: ?since= (RFC3339) or ?hours= (default 24, capped). Aggregates only.","tags":["router"],"x-app":"github.com/hanzoai/ai"}},"/v1/run":{"post":{"operationId":"post_v1_run","summary":"Runs a container image and gives back a URL.","description":"Runs a container image and gives back a URL.\n\nThe one-call shortcut over project → app → deploy: give it a `name` and an\n`image` and it creates or updates an image-source application in your org's\nDEFAULT project, deploys it through the same operator Service-CR writer\neverything else uses, and answers its id, name, live URL, status and shape.\nRe-running the same name UPDATES it in place, so the call is idempotent by name.\n\nWhat it produces is a first-class application, not a special object: it is\nlistable, stoppable and redeployable through the /v1/platform routes like any\nother app.\n\n`minScale` is the replica floor. `maxScale` above it declares an autoscaling\nceiling; `maxScale: 0` means no autoscaler at all — a fixed run at the floor.\nBoth are clamped to the deployment's limits. `runtime` and `shape` are accepted\nfor the client contract and echoed back: the image is the runtime unit and sizing\nis the operator's default.\n\nIt is BILLING-GATED before it touches the cluster: a flat per-run fee is\nauthorized against the org's own prepaid balance first, so an org that cannot pay\nis refused without anything being created. An unreachable cluster is 503 — a run\nnever reports a URL it did not create. Secret env is sealed into KMS and fails\nclosed without it.\n\nRequires a validated principal; 403 without one. The org is resolved from that\nvalidated identity and is what both pays and owns the namespace — it is never\nread from the body.","tags":["run"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runView"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/runner":{"post":{"operationId":"post_v1_runner","summary":"Triggers a native build — an image, or the binaries a repo declares.","description":"Triggers a native build — an image, or the binaries a repo declares.\n\nThe fabric's own build trigger, and what `hanzo build`, git-push-to-deploy and\ncloud's own self-release all call. It answers 202 with the build job id: a queued\nbuild, not a pushed artifact.\n\nTwo lanes, and a build is exactly one of them. The IMAGE lane takes `repo` and\nthe output `image` and launches a BuildKit Job that pushes it. The ARTIFACT lane\ntakes `binaries` — the same recipe the repo's hanzo.yml declares — and publishes\nto object storage instead; it must carry no `image`, because a build produces\nbinaries or an image, never both. `release: true` is the third mode: cloud\nself-publishing its own image, version computed, built, smoke-tested, tagged and\nannounced.\n\nPRIVILEGED, with exactly two credentials and never a third: the shared\nbuild-callback token compared in constant time — the machine path, which a user\nnever holds — or a validated IAM principal who is an ADMIN of their org, which is\nthe `hanzo build` user path and means one IAM login authorizes a build with no\nseparate build token. A plain member is refused.\n\nBoth paths are bounded the same way: the output must push to a registry the\nfabric owns, and on the IAM path the image's registry namespace must MATCH the\ncaller's own validated org — so an org admin can only publish into their own\nbrand and can never overwrite another's through the shared push credential. The\nsame confinement applies to the artifact lane's repo owner.\n\n`release: true` is the exception, and takes SUPERADMIN. It publishes the\nplatform's own image — the binary the whole fleet runs — so what it lands reaches\nevery org at the next reconcile, and no role inside the caller's own org can\nauthorize that. An org admin is refused however the registry namespace lines up,\nand the build token, which carries no identity at all, may enqueue an ordinary\nbuild but never a release.\n\nThe output image is parsed and validated as a single well-formed OCI ref before\nany authorization decision reads it, so a crafted ref cannot smuggle a\nbuild-exporter attribute past the check.","tags":["runner"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runnerBuildReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runnerBuildResp"}}},"description":"accepted"}},"x-app":"platform"}},"/v1/runner/releases":{"get":{"operationId":"get_v1_runner_releases","summary":"Lists the self-publish releases this process has run.","description":"Lists the self-publish releases this process has run.\n\nIt lists the platform's own release runs with their current state, so a release\nthat answered 202 with an id can be followed to its end. SuperAdmin only — this\nis the platform's own publishing record, not a tenant surface.\n\nThe record lives in THIS process's memory, so it covers the releases this\ninstance started and does not survive a restart.","tags":["runner"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/selfReleaseList"}}},"description":"ok"}},"x-app":"platform"}},"/v1/runner/releases/{id}":{"get":{"operationId":"get_v1_runner_releases_by_id","summary":"Returns one self-publish release by the id its 202 returned.","description":"Returns one self-publish release by the id its 202 returned.\n\nIt returns the state of one release run — which is the whole reason the trigger\nanswers with an id, because without this a release that died in the detached\npipeline would look exactly like one still in flight. SuperAdmin only.\n\nA 404 means the id is unknown OR has aged out of this process's in-memory record.\nThat is the honest answer either way: the process genuinely cannot tell the two\napart.","tags":["runner"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the build id the release trigger answered with, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseState"}}},"description":"ok"}},"x-app":"platform"}},"/v1/s3":{"get":{"operationId":"get_v1_s3","summary":"Lists the caller org's object-storage buckets.","description":"Lists the caller org's object-storage buckets. A bucket lives in an\nalready-live shared object store and is reached through the public gateway.\nThe names here are the friendly ones the org provisioned; the physical bucket\nis org-namespaced underneath, which is what keeps two tenants' buckets\ndistinct.","tags":["s3"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_s3","summary":"Provision an object storage bucket for your org","description":"Creates an S3-compatible bucket inside the already-running shared object store and answers with the endpoint that reaches it.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["s3"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/s3/buckets":{"get":{"operationId":"get_v1_s3_buckets","summary":"List your org's buckets","description":"Returns the caller's own buckets under the friendly names they were created with, each with its creation time.\n\nAnother tenant's bucket is not refused, it is INVISIBLE — a bucket outside the caller's namespace is skipped during the listing rather than reported, so the operation cannot be used to discover that a name is taken elsewhere.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"x-app":"storage"},"post":{"operationId":"post_v1_s3_buckets","summary":"Create a bucket in your org","description":"Creates a new bucket in the caller's own namespace and answers 201 with its friendly name and creation time.\n\nThe name is validated exactly as sent and never quietly normalised: it must match `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`, so a mixed-case name is a clean 400 rather than a bucket created as `photos` that the caller keeps asking for as `Photos`. A name already in use in the caller's own namespace is 409.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"x-app":"storage"}},"/v1/s3/buckets/{bucket}":{"delete":{"operationId":"delete_v1_s3_buckets_by_bucket","summary":"Delete an empty bucket","description":"Removes one of the caller's buckets, and only when it is already EMPTY — a bucket with objects in it answers 409 instead.\n\nThat refusal is deliberate rather than a limitation: this API does not cascade a delete of a tenant's objects behind a single bucket call, so emptying the bucket stays an explicit act. A bucket that does not exist is 404, and a successful delete answers 204 with no body.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"storage"}},"/v1/s3/buckets/{bucket}/objects":{"get":{"operationId":"get_v1_s3_buckets_by_bucket_objects","summary":"Browse one level of a bucket","description":"Lists one folder level of a bucket: each entry's key, whether it is a folder, its size, last-modified time and ETag. `prefix` scopes the read to a sub-folder.\n\nKeys come back RELATIVE to the requested prefix, not absolute, which is what lets a client render a breadcrumb without re-deriving it. The default is the folder view — sub-prefixes are returned as directory entries — and `recursive=true` flattens it to every key beneath the prefix instead.\n\nThe listing is bounded at 1000 entries so a large bucket cannot exhaust memory; treat a full page as \"there may be more\" rather than as the whole bucket.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"storage"},"post":{"operationId":"post_v1_s3_buckets_by_bucket_objects","summary":"Get a URL to upload one object directly","description":"Returns a short-lived presigned PUT URL, with the method, the cleaned key and the seconds until it expires. The client uploads to that URL DIRECTLY — the bytes never pass through this API, and the storage credential never leaves the server.\n\nThe URL is signed against the public storage host and scoped to exactly one bucket and key, and it expires five minutes after it is issued. The key is path-cleaned before signing, so a traversal cannot escape the bucket. A deployment with no public storage endpoint answers 503, because there is no host to sign a browser-followable URL against.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"storage"}},"/v1/s3/buckets/{bucket}/objects/{wildcard1}":{"delete":{"operationId":"delete_v1_s3_buckets_by_bucket_objects_by_wildcard1","summary":"Delete one object","description":"Removes the single object at the trailing path from one of the caller's buckets and answers 204 with no body. The key is path-cleaned first, so the delete cannot reach outside the bucket it names.\n\nIt removes one object and never a prefix: a trailing path that looks like a folder deletes the placeholder at that key, not the objects beneath it.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"storage"},"get":{"operationId":"get_v1_s3_buckets_by_bucket_objects_by_wildcard1","summary":"Get a URL to download one object directly","description":"Returns a short-lived presigned GET URL for the object at the trailing path, with the method, the key and its remaining lifetime. As with upload, the client fetches from that URL directly and the storage credential stays on the server.\n\nThe URL carries a content disposition of attachment with the object's file name, so a browser following it downloads the object rather than rendering it in place. Signed against the public host, scoped to the one bucket and key, and good for five minutes; a deployment with no public storage endpoint answers 503.\n\nA validated principal is required, and every bucket and key is resolved inside the caller's own org: physical bucket names are derived from the org, so a tenant cannot name another's storage. The operation is billed per call — the balance is checked BEFORE anything is touched, so an unfunded org is refused with nothing done, and the debit happens only after the work succeeds. Object storage that is not configured answers 503 under this subsystem's own name rather than falling through to another.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"storage"}},"/v1/s3/health":{"get":{"operationId":"get_v1_s3_health","summary":"Whether object storage is usable here","description":"A real readiness probe rather than a liveness stub: 200 only when the storage credentials are present, and it additionally reports whether presigning is available — the capability the two URL-issuing operations need and refuse without.\n\nAn unconfigured deployment answers 503 with `ready:false` and the reason, which is the same state in which every data-plane operation here refuses. Not token-gated, so the platform can probe it without a credential, and it carries no credential, bucket or tenant detail.","tags":["s3"],"x-app":"storage"}},"/v1/s3/{name}":{"delete":{"operationId":"delete_v1_s3_by_name","summary":"Deletes one bucket from the shared object store and removes its metadata row.","description":"Deletes one bucket from the shared object store and removes its\nmetadata row. Answers 204 with no body; a second call is a 404.","tags":["s3"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"uploads"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_s3_by_name","summary":"Returns one bucket's metadata.","description":"Returns one bucket's metadata. It carries the bucket's status and the\ngateway address it is reached at, and no username: the object store\nauthenticates with a shared, out-of-band key rather than a per-bucket\ncredential.","tags":["s3"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"uploads"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/sandboxes":{"get":{"operationId":"get_v1_sandboxes","summary":"The sandboxes this org holds","description":"Lists the caller org's sandboxes, newest first. `project` and `status` narrow it, and both are read from the QUERY STRING.\n\nIt answers from the org's own store rather than from the cluster, so a sandbox whose pod has since died still appears, carrying the status it was last known to have. That is deliberate: a lease you are being charged for should not vanish from the list because the thing behind it fell over.","tags":["sandboxes"],"x-app":"sandboxes"},"post":{"operationId":"post_v1_sandboxes","summary":"Lease a sandbox","description":"Creates a sandbox and returns it. `class` is one of `exec`, `dev` or `desktop`; `dev` and `desktop` are attached to a `project`, which is required for them and names the volume the work persists on. `ttlSec` bounds the lease, and `image` overrides the class default.\n\nThis is the ONLY path that creates cluster objects. The isolation boundary is the pod's runtime class, one field, so what a sandbox is confined by is a deployment decision rather than anything this operation negotiates.","tags":["sandboxes"],"x-app":"sandboxes"}},"/v1/sandboxes/{id}":{"delete":{"operationId":"delete_v1_sandboxes_by_id","summary":"End a sandbox","description":"Stops the sandbox's pod and drops the lease. The VOLUME survives by default, so a `dev` or `desktop` sandbox can be leased again over the same project and find its checkout where it left it.\n\n`purge=1` deletes the volume too. It is opt-in because it is the one part of this that cannot be undone.","tags":["sandboxes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandboxes"},"get":{"operationId":"get_v1_sandboxes_by_id","summary":"One sandbox","description":"Returns one of the caller org's sandboxes. An id belonging to another org answers 404 and not 403 — a 403 would confirm the id exists, and whether a given sandbox exists is itself a cross-tenant fact.","tags":["sandboxes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandboxes"}},"/v1/sandboxes/{id}/exec":{"post":{"operationId":"post_v1_sandboxes_by_id_exec","summary":"Run a command in a sandbox","description":"Runs a command inside the sandbox and returns its exit code, stdout and stderr. A non-zero exit is a SUCCESSFUL call carrying a failed program — the HTTP status stays 200, because \"the tests failed\" and \"the sandbox is broken\" are different facts.\n\nNOTHING RUNS IN cloud. The command is streamed to the Kubernetes exec subresource of the sandbox's pod, which runs under the gVisor runtime class. The sandbox is addressed by pod NAME through the apiserver, never by address.","tags":["sandboxes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandboxes"}},"/v1/sandboxes/{id}/fs":{"get":{"operationId":"get_v1_sandboxes_by_id_fs","summary":"Read a file, or list a directory","description":"Reads one file from the sandbox's project directory as text, or lists the entries when the path names a directory. Paths resolve under the project root and a path that climbs out is refused rather than rewritten.","tags":["sandboxes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandboxes"},"post":{"operationId":"post_v1_sandboxes_by_id_fs","summary":"Write a file","description":"Writes the request body to one file in the sandbox's project directory, creating parent directories. Same confinement as the read above.","tags":["sandboxes"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandboxes"}},"/v1/sbom":{"post":{"operationId":"post_v1_sbom","summary":"Ingest persists a CycloneDX SBOM's components keyed by image digest.","description":"Ingest persists a CycloneDX SBOM's components keyed by image digest. Gated to a\nvalidated SuperAdmin (owner == AdminOrg) — the canonical cloud super-admin\ncheck, which the build fleet / CI carries. Re-ingest is idempotent: rows share\nthe (digest, name, version, purl) ORDER BY, so ReplacingMergeTree keeps the\nlatest by ingested_at (and resolve reads FINAL).","tags":["sbom"],"requestBody":{"content":{"application/json":{"example":{"document":{"components":[]},"format":"cyclonedx","imageDigest":"sha256:abc","imageRef":"registry.hanzo.ai/hanzo/cloud:v1"},"schema":{"$ref":"#/components/schemas/SbomIngest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SbomIngested"}}},"description":"created"}},"x-app":"sbom"}},"/v1/sbom/health":{"get":{"operationId":"get_v1_sbom_health","summary":"Health is a pure liveness probe: the service is up; datastore reflects whether the datastore store is connected.","description":"Health is a pure liveness probe: the service is up; datastore reflects whether\nthe datastore store is connected. Not JWT-gated, always 200 (a disconnected\ndatastore is degraded-but-alive; the data endpoints report that as 503).","tags":["sbom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SbomHealth"}}},"description":"ok"}},"x-app":"sbom"}},"/v1/sbom/{wildcard1}":{"get":{"operationId":"get_v1_sbom_by_wildcard1","summary":"Resolve everything inside a container image","description":"Answers with the component set of one container image — each component's name, version, type, package URL and license — addressed by either the image digest or the image ref. The captured segment is greedy and percent-decoded, so a ref carrying slashes and a tag is passed whole.\n\nThis read is GLOBAL, not tenant-scoped, and deliberately so: a bill of materials belongs to a content-addressed digest rather than to an org, so every caller deploying the same image resolves the same components, and nothing tenant-owned is exposed by it. Ingest is the gated half of the pair.\n\nA miss is not the end of the lookup. The registry is the source of truth, so an unmaterialized ref is pulled from the SBOM attached to that image, persisted, and answered from the store — the first read of a freshly built image pays for the pull, later ones do not. A bare digest with no repository is not pullable and answers an honest 404, as does a ref with no attached document. Repeated ingests collapse to the latest, components come back ordered by type then name, and a result over 5000 components is capped with `truncated` set. When the datastore is not connected the answer is 503 rather than a fabricated empty image.","tags":["sbom"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sbom"}},"/v1/scrape":{"post":{"operationId":"post_v1_scrape","summary":"Fetch one page and get its extracted markdown, in the firecrawl envelope.","description":"Takes {url} and answers {success, data:{markdown, metadata}} — the exact contract a firecrawl client decodes. The fetch, extraction and optional browser render run in-process; there is no crawler pod to be down.\n\nThe shared service key is required as an Authorization Bearer, compared in constant time: unset on the deployment is 503, missing or wrong is 401. Unlike search, a validated principal does NOT substitute for it — this is the service-to-service door.\n\nA page is archived under the caller's own org and project, taken from the verified principal when there is one, so a scrape lands in the same corpus /v1/crawl fills and a URL already read under that scope is answered from the archive without touching the network. A service caller carrying no principal shares the unscoped prefix.\n\nThe URL is caller-supplied and fetched from INSIDE the cluster, which makes this a request-forgery primitive by construction: in-namespace service DNS and a cloud metadata endpoint that hands credentials to anyone who asks are both a resolution away. Only http and https are accepted, and every address actually dialled must be public unicast — loopback, link-local, private and multicast are refused. The check lives in the DIALER rather than on the hostname, because resolving a name to validate it and then letting the transport resolve it again is a gap DNS rebinding walks straight through; redirects re-enter the same dialer, so a public URL that bounces to the metadata address is refused at the hop that matters.\n\nThe one thing to get right: FAILURE IS 200. A missing or unparseable url, a body over the 1 MiB read cap, and a fetch that could not be completed all answer HTTP 200 with success:false and a reason — a firecrawl client reads data.success, not the status line. Only the two auth refusals use a status code, so a caller that branches on HTTP status alone will read every failed scrape as a success.","tags":["scrape"],"x-app":"websearch"}},"/v1/search":{"get":{"operationId":"get_v1_search","summary":"Lists the caller org's search indexes.","description":"Lists the caller org's search indexes. An index is a logical\nresource inside an already-live shared backend, so every one of them is\nreached through the public gateway rather than at an instance of its own.","tags":["search"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_search","summary":"Provision a search index for your org","description":"Creates a search index inside the already-running shared search backend and answers with the endpoint that reaches it.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["search"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/search/indexes":{"get":{"operationId":"get_v1_search_indexes","summary":"Lists the search indexes with their document counts and timestamps.","description":"Lists the search indexes with their document counts and timestamps.\n\nIt reads the in-cluster Meilisearch service and reshapes its /stats and\n/indexes replies into the rows the console's Search panel renders. The read is\ndegrade-friendly by design: an unreachable Meilisearch answers 200 with an\nEMPTY list, so the panel shows an honest empty state instead of an error.\ncreatedAt falls back to now and lastIndexedAt to null when the index list is\nunavailable.","tags":["search"],"parameters":[{"name":"Authorization","in":"header","required":false,"description":"Authorization carries the surface's bearer key (`Bearer \u003ckey\u003e`); the bare\nkey is accepted too. Search and vector are two surfaces with two keys.\nIt is not `validate:\"required\"` on purpose: requireKey answers absence\nitself, so an unconfigured surface 503s and a missing bearer 401s — a\nvalidation refusal would rewrite both statuses.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/searchIndexList"}}},"description":"ok"}},"x-app":"product"}},"/v1/search/stats":{"get":{"operationId":"get_v1_search_stats","summary":"Totals the documents across every search index.","description":"Totals the documents across every search index.\n\ntotalDocuments is summed from Meilisearch's own per-index counts. The other\nthree fields are structurally zero rather than estimated: Meilisearch keeps no\nquery-history counters, so searches, sessions and the per-day series are not\nderivable from the index and this surface reports the honest zero instead of a\nfabricated number. An unreachable Meilisearch answers 200 with all zeros.","tags":["search"],"parameters":[{"name":"Authorization","in":"header","required":false,"description":"Authorization carries the surface's bearer key (`Bearer \u003ckey\u003e`); the bare\nkey is accepted too. Search and vector are two surfaces with two keys.\nIt is not `validate:\"required\"` on purpose: requireKey answers absence\nitself, so an unconfigured surface 503s and a missing bearer 401s — a\nvalidation refusal would rewrite both statuses.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/searchStats"}}},"description":"ok"}},"x-app":"product"}},"/v1/search/{name}":{"delete":{"operationId":"delete_v1_search_by_name","summary":"Deletes one search index from the shared backend and removes its metadata row.","description":"Deletes one search index from the shared backend and removes its\nmetadata row. Answers 204 with no body; a second call is a 404.","tags":["search"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"products"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_search_by_name","summary":"Returns one search index's metadata.","description":"Returns one search index's metadata. It carries the index's status\nand the gateway address it is reached at, and no username: the backend\nauthenticates with a shared, out-of-band key rather than a per-index\ncredential.","tags":["search"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"products"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/security/findings":{"get":{"operationId":"get_v1_security_findings","summary":"The org's findings, across scans or within one","description":"Lists the caller org's findings — rule, severity, path, line, masked preview and fingerprint — newest first. `scanId` narrows to a single scan, `minSeverity` (critical | high | medium | low) drops everything below that rank, and `limit` caps the page; a minSeverity outside that set is refused with 400 rather than quietly ignored, so a filter typo cannot read as \"no findings\". Strictly org-scoped, and a caller with no validated org is refused.","tags":["security"],"x-app":"security"}},"/v1/security/findings/{id}":{"get":{"operationId":"get_v1_security_findings_by_id","summary":"One finding","description":"Returns a single finding: which rule fired, where (path and line), the masked preview and the SHA-256 fingerprint of the secret — the raw secret is not stored and cannot be read back. Scoped to the caller's org, and a finding belonging to another org is the same 404 as one that never existed.","tags":["security"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"security"}},"/v1/security/health":{"get":{"operationId":"get_v1_security_health","summary":"Liveness, and how many detection rules are loaded","description":"Reports that the scanning subsystem is serving and how many secret-detection rules the engine holds. It has no external dependency — the answer is ok whenever the findings store opened — so it measures this process rather than anything downstream. Reads no tenant: a prober that sends no principal is answered, not refused.","tags":["security"],"x-app":"security"}},"/v1/security/rules":{"get":{"operationId":"get_v1_security_rules","summary":"The secret-detection catalog the engine scans with","description":"Returns every rule a scan can fire — the id, name and severity a finding cites — so a caller can render or triage results without hard-coding the catalog. It is the same for everyone and discloses nothing tenant-specific, so it carries no org scope.","tags":["security"],"x-app":"security"}},"/v1/security/scans":{"get":{"operationId":"get_v1_security_scans","summary":"The org's scan history","description":"Lists the caller org's scans, newest first, each as the same summary the submission answered — files read, findings fired, tally by severity. `limit` caps the page. Strictly org-scoped: a caller only ever sees its own scans, and one with no validated org is refused.","tags":["security"],"x-app":"security"},"post":{"operationId":"post_v1_security_scans","summary":"Scan submitted source for hardcoded secrets","description":"Runs the detection engine over a batch of {path, content} files and answers 201 with the scan summary: how many files were read, how many findings fired, and the tally by severity.\n\nTHE SUBMITTED CONTENT IS NEVER STORED. It is scanned in memory; what persists is the finding — its rule, its path and line, a MASKED preview (first and last characters kept, the middle starred) and the SHA-256 fingerprint of the raw secret. The fingerprint is what makes the same secret recognisable across scans and after rotation without the secret ever being written down.\n\nRequires a validated org, which scopes the stored scan and every finding on it; a caller with no org is refused. `project` in the body names the sub-scope and is refused with 400 if it is not a valid slug; omit it and the caller's project header is used instead, where an unusable value is simply ignored. Bounded at 500 files and 8 MiB of total content per submission — split a larger tree across scans. One scan is one metered unit, and the scan is recorded in the audit log with its tally, never with its findings.","tags":["security"],"x-app":"security"}},"/v1/security/scans/{id}":{"get":{"operationId":"get_v1_security_scans_by_id","summary":"One scan and every finding on it","description":"Returns the scan summary together with all of its findings, so the detail view is one round-trip rather than a list call per scan. The findings carry masked previews and fingerprints, never secrets.\n\nScoped to the caller's org: a scan id belonging to another org is the same 404 as an id that never existed, so a probe learns nothing about what exists elsewhere. No validated org is refused.","tags":["security"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"security"}},"/v1/sentry/discover":{"post":{"operationId":"post_v1_sentry_discover","summary":"Aggregates a project's captured errors into a table — the caller names the filters, the groupings and the aggregations, and gets back the columns and rows they asked for.","description":"Aggregates a project's captured errors into a table — the caller\nnames the filters, the groupings and the aggregations, and gets back the\ncolumns and rows they asked for.\n\nThe project is mandatory and is checked against the caller's own org before it\nscopes anything, so a project id belonging to someone else reads as absent\nrather than as data.","tags":["sentry"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDiscoverIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yDiscoverOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/events/{id}":{"get":{"operationId":"get_v1_sentry_events_by_id","summary":"Returns one captured error event of a project, by its id.","description":"Returns one captured error event of a project, by its id.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the event id.","schema":{"type":"string"}},{"name":"project","in":"query","required":true,"description":"Project is the project the event belongs to, by its id. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryEventOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/issues":{"get":{"operationId":"get_v1_sentry_issues","summary":"Lists the caller's org's grouped error issues, optionally narrowed to one project and one time window, and filtered by status, level, environment, service, a free-text query and a sort.","description":"Lists the caller's org's grouped error issues, optionally\nnarrowed to one project and one time window, and filtered by status, level,\nenvironment, service, a free-text query and a sort.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status narrows to one lifecycle state: unresolved, resolved or ignored.","schema":{"type":"string"}},{"name":"level","in":"query","required":false,"description":"Level narrows to one severity, e.g. error, warning, info.","schema":{"type":"string"}},{"name":"environment","in":"query","required":false,"description":"Environment narrows to one deployment environment.","schema":{"type":"string"}},{"name":"serviceName","in":"query","required":false,"description":"ServiceName narrows to one reporting service.","schema":{"type":"string"}},{"name":"query","in":"query","required":false,"description":"Query narrows to issues whose text contains it.","schema":{"type":"string"}},{"name":"sort","in":"query","required":false,"description":"Sort orders the page, e.g. lastSeen, firstSeen, count.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how many issues to skip. Zero starts at the first.","schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many issues come back. Zero means the default.","schema":{"type":"integer"}},{"name":"project","in":"query","required":false,"description":"Project narrows the org's issues to one project, by its id.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Period is the window to read, relative to now — 1h, 24h, 7d, 14d, 30d.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorIssuesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/issues/{id}":{"get":{"operationId":"get_v1_sentry_issues_by_id","summary":"Returns one grouped issue of the caller's org with its latest occurrence sample.","description":"Returns one grouped issue of the caller's org with its latest\noccurrence sample.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the issue id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorGettableIssueOut"}}},"description":"ok"}},"x-app":"o11y"},"put":{"operationId":"put_v1_sentry_issues_by_id","summary":"Changes an issue's lifecycle — resolve, ignore, reopen or assign — and returns the updated issue.","description":"Changes an issue's lifecycle — resolve, ignore, reopen or\nassign — and returns the updated issue. Fields left unset are left unchanged.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the issue id.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryUpdateIssueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorIssueOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/issues/{id}/events":{"get":{"operationId":"get_v1_sentry_issues_by_id_events","summary":"Lists one issue's captured occurrences, scoped to a project — a project is an isolation unit, so the caller declares which project's occurrences to read.","description":"Lists one issue's captured occurrences, scoped to a project\n— a project is an isolation unit, so the caller declares which project's\noccurrences to read.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the issue id.","schema":{"type":"string"}},{"name":"project","in":"query","required":true,"description":"Project is the project whose occurrences to read, by its id. Required.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many occurrences come back. Zero means the default.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryIssueEventsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/logs":{"get":{"operationId":"get_v1_sentry_logs","summary":"Lists a project's captured error events, newest first, optionally narrowed to those whose message or exception text contains a search string.","description":"Lists a project's captured error events, newest first, optionally\nnarrowed to those whose message or exception text contains a search string.","tags":["sentry"],"parameters":[{"name":"project","in":"query","required":true,"description":"Project is the project to read, as its id. Required.","schema":{"type":"string"}},{"name":"query","in":"query","required":false,"description":"Query narrows the page to events whose text contains it.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Period is the window to read, relative to now — 1h, 24h, 7d, 14d, 30d.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many events come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yLogsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/projects":{"get":{"operationId":"get_v1_sentry_projects","summary":"Lists the caller's org's Sentry projects, each with its freshly-derived DSN.","description":"Lists the caller's org's Sentry projects, each with its\nfreshly-derived DSN.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryProjectsOut"}}},"description":"ok"}},"x-app":"o11y"},"post":{"operationId":"post_v1_sentry_projects","summary":"Creates a Sentry project under the caller's org and returns it, DSN included.","description":"Creates a Sentry project under the caller's org and\nreturns it, DSN included. Only the name, and optionally a slug and platform,\nare the caller's to set; the org, id and key are server-assigned.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["sentry"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryPostableProject"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryProjectOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/projects/{id}":{"delete":{"operationId":"delete_v1_sentry_projects_by_id","summary":"Deletes one Sentry project of the caller's org.","description":"Deletes one Sentry project of the caller's org. Its DSN\nstops resolving immediately, so ingest for that id fails closed exactly as an\nunknown project does; retained events are not touched. Answers 204.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the project id.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y"},"get":{"operationId":"get_v1_sentry_projects_by_id","summary":"Returns one Sentry project of the caller's org, DSN included.","description":"Returns one Sentry project of the caller's org, DSN included.\n\nCallers need the viewer role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the project id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryProjectOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/projects/{id}/keys/rotate":{"post":{"operationId":"post_v1_sentry_projects_by_id_keys_rotate","summary":"Rotates a project's DSN key — bumping its rotation watermark so keys below it stop verifying — and returns the project with its new DSN.","description":"Rotates a project's DSN key — bumping its rotation\nwatermark so keys below it stop verifying — and returns the project with its\nnew DSN.\n\nCallers need the editor role; the runtime's own gate enforces it.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the project id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryProjectOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/stats":{"get":{"operationId":"get_v1_sentry_stats","summary":"Returns a project's event-rate timeseries: one bucket per interval over the requested period, counting the events in it.","description":"Returns a project's event-rate timeseries: one bucket per interval over\nthe requested period, counting the events in it.","tags":["sentry"],"parameters":[{"name":"project","in":"query","required":true,"description":"Project is the project to read, as its id. Required.","schema":{"type":"string"}},{"name":"field","in":"query","required":false,"description":"Field is the dimension to count over. Empty counts all events.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Period is the window to read, relative to now — 1h, 24h, 7d, 14d, 30d.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yStatsOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/traces":{"get":{"operationId":"get_v1_sentry_traces","summary":"Lists the traces a project's captured errors reference, each with how many errors landed on it, when they started and stopped, and the latest message seen — the entry point for \"which requests are failing\".","description":"Lists the traces a project's captured errors reference, each with how\nmany errors landed on it, when they started and stopped, and the latest\nmessage seen — the entry point for \"which requests are failing\".","tags":["sentry"],"parameters":[{"name":"project","in":"query","required":true,"description":"Project is the project to read, as its id. Required.","schema":{"type":"string"}},{"name":"period","in":"query","required":false,"description":"Period is the window to read, relative to now — 1h, 24h, 7d, 14d, 30d.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many traces come back.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTracesOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/traces/{id}":{"get":{"operationId":"get_v1_sentry_traces_by_id","summary":"Returns one trace's captured errors for a project — every error event that carried the trace id, in the order the events plane holds them.","description":"Returns one trace's captured errors for a project — every error event\nthat carried the trace id, in the order the events plane holds them.","tags":["sentry"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the trace id.","schema":{"type":"string"}},{"name":"project","in":"query","required":true,"description":"Project is the project the trace's errors belong to. Required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yTraceOut"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sentry/{project}/envelope/":{"post":{"operationId":"post_v1_sentry_by_project_envelope","summary":"Receive a Sentry envelope on the clean root","description":"The same envelope ingest as the DSN path, spelled the way this platform names things: one /v1/, the product, the project. Point an SDK's DSN here and the wire is identical.\n\nAUTHENTICATED BY THE DSN PUBLIC KEY and exempt from the principal gate for the same reason — a Sentry SDK has no Hanzo session to present. The project segment is a UUID enforced by the route, and the exemption matches method plus prefix plus suffix, so every Sentry READ (issues, discover, events, logs, traces, stats) stays gated.","tags":["sentry"],"parameters":[{"name":"project","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/sentry/{project}/store/":{"post":{"operationId":"post_v1_sentry_by_project_store","summary":"Receive a single Sentry event on the clean root","description":"The legacy single-event ingest on the clean /v1/sentry root — one JSON event rather than a framed batch. Same DSN-key authentication, same gate exemption, same UUID-enforced project segment as the envelope route beside it.","tags":["sentry"],"parameters":[{"name":"project","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/sentry/{wildcard1}":{"delete":{"operationId":"delete_v1_sentry_by_wildcard1","summary":"Delete a Sentry project","description":"The one delete on the Sentry surface: removing a PROJECT, answering 204. Error issues, events and traces are not individually deletable — they are append-only telemetry, and their lifetime is retention's business, not an API call's.\n\nRequires a validated, org-scoped principal with edit rights; a viewer is refused. The delete is confined to the org minted from that principal's claim, so a project id belonging to another tenant is not found rather than removed. Deleting a project retires the DSN that fed it, so any SDK still pointed at that key stops being accepted. Before the runtime is initialized, 503.","tags":["sentry"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"},"get":{"operationId":"get_v1_sentry_by_wildcard1","summary":"Read the caller org's errors on the Sentry surface","description":"Serves the Sentry-compatible read surface — projects, error issues and one issue's occurrences, a single event, error logs, error-correlated traces and one trace's waterfall, and the event-rate stats — so a Sentry client or the error console reads its errors at the paths it already speaks.\n\nIt is the SAME runtime the observability surface serves, reached under a second path family, and there is NO rewrite: the runtime carries these routes literally. That is what makes this a product face rather than a translation layer. One runtime, two path families.\n\nA validated principal is required and the read is scoped to that principal's own org. Errors are a tenant's OWN data, so org membership is the whole admission test and there is deliberately no admin term on it — gating the product on platform sudo would make the only way to see your own errors a scope that shows you everyone's. Before the runtime is initialized, 503.","tags":["sentry"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"},"patch":{"operationId":"patch_v1_sentry_by_wildcard1","summary":"Not served — the Sentry surface has no partial update","description":"The Sentry face carries NO route for a partial update. The wildcard admits every method, so this operation exists as an address, but nothing behind it answers and a request lands on the runtime as an unrouted path.\n\nIt is documented rather than silently omitted because the useful thing to say is where to go instead: an issue's lifecycle — resolve, ignore, assign — is a REPLACE on that issue, not a patch, and it is the only mutable state on this surface. A client that reaches for a partial update here is looking for that call.","tags":["sentry"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"},"post":{"operationId":"post_v1_sentry_by_wildcard1","summary":"Send events to the Sentry surface, or write on it","description":"Carries every write on the Sentry-compatible surface: the SDK's error ingest, and the authenticated writes the console makes — creating a project, rotating a project's DSN key, and running a discover query over the events plane.\n\nTHE TWO ARE AUTHENTICATED DIFFERENTLY, and that is the rule to get right. An envelope or store submission presents a DSN public key, never a Hanzo session, so it is exempt from the principal gate and verified by the ingest key check instead — which derives the org from the DSN and fails closed. A keyless submission is a 401 from that verifier, not a 403 from the gate, and telling those two apart is how you tell the hops apart. Every other write here needs a validated, org-scoped principal, and creating or rotating requires an editor rather than a viewer.\n\nThe ingest exemption is matched by method plus prefix plus suffix, never a bare prefix, and the project segment must be a UUID — so no read is reachable through it. Before the runtime is initialized, 503.","tags":["sentry"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"},"put":{"operationId":"put_v1_sentry_by_wildcard1","summary":"Move an error issue through its lifecycle","description":"The one replace on the Sentry surface: updating an error ISSUE — resolving it, ignoring it, or assigning it — and answering the updated issue.\n\nNothing else here takes a replace. A project is created and deleted but never replaced, and the event and trace planes are append-only telemetry, so an issue's lifecycle is the only mutable state this face exposes.\n\nRequires a validated, org-scoped principal with edit rights; a viewer is refused. The write is confined to the org minted from that principal's claim, so an issue id belonging to another tenant is simply not found. Before the runtime is initialized, 503.","tags":["sentry"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"o11y"}},"/v1/settings/{product}":{"get":{"operationId":"get_v1_settings_by_product","summary":"Reads the caller org's configuration for one product, with every secret field MASKED — only the names of the set secrets come back, never their values, which live in KMS.","description":"Reads the caller org's configuration for one product, with every\nsecret field MASKED — only the names of the set secrets come back, never their\nvalues, which live in KMS. A product the org has never configured is not a 404:\nit answers 200 with an empty config object, so the console's Settings tab always\nrenders and merges its own display defaults on top.","tags":["settings"],"parameters":[{"name":"product","in":"path","required":true,"description":"Product is the catalog slug, from the path. Must match ^[a-z0-9][a-z0-9._-]{0,62}$.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/settingsView"}}},"description":"ok"}},"x-app":"settings"},"put":{"operationId":"put_v1_settings_by_product","summary":"Writes the caller org's configuration for one product and answers the stored result, secrets masked.","description":"Writes the caller org's configuration for one product and answers the\nstored result, secrets masked. Secret VALUES are sealed into KMS under\norgs/{org}/settings/{product}/{key} and never touch this deployment's database;\nwith no KMS configured a write that carries any secret is refused whole (503)\nrather than dropping it or persisting it in the clear. A secret the body omits\nkeeps its stored value, so a partial write never silently clears one.","tags":["settings"],"parameters":[{"name":"product","in":"path","required":true,"description":"Product is the catalog slug, from the PATH. zip binds the path last, so the\nURL names the product being written whatever a body field claims.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/settingsReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/settingsView"}}},"description":"ok"}},"x-app":"settings"}},"/v1/share":{"get":{"operationId":"get_v1_share","summary":"Returns the tunnel shares the caller's org currently has open, across every environment that org has enabled.","description":"Returns the tunnel shares the caller's org currently has open, across\nevery environment that org has enabled. It is a READ and it degrades honestly: an\nunconfigured deployment, an org that has not provisioned yet, and an unreachable\ncontroller all answer an EMPTY list at 200 rather than an error, so the console\nnever error-toasts on load.","tags":["share"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sharesOut"}}},"description":"ok"}},"x-app":"share"}},"/v1/share/enable":{"post":{"operationId":"post_v1_share_enable","summary":"Enable provisions the caller org's tunnel account and returns the credential the `hanzo share` CLI needs to run a tunnel.","description":"Enable provisions the caller org's tunnel account and returns the credential the\n`hanzo share` CLI needs to run a tunnel. It is idempotent: the account is keyed\ndeterministically off the VALIDATED org, so a repeat call hands back the same\naccount rather than creating a second one, and a caller can only ever provision\ntheir OWN org's account. 503 when the deployment has no share controller\nconfigured; 502 when that controller is unreachable.","tags":["share"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/enableResp"}}},"description":"ok"}},"x-app":"share"}},"/v1/sites":{"get":{"operationId":"get_v1_sites","summary":"Returns the org's deployed sites at the pretty URLs they serve at.","description":"Returns the org's deployed sites at the pretty URLs they serve at.\n\nIt reads the SAME org-scoped store as /v1/projects and keeps only the projects\nthat are actually `live`, so a draft or a failed build is not advertised as a\nsite.\n\nScope: a validated principal is required (403 without one) and the list is\nkeyed by that principal's org.","tags":["sites"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsSite"},"type":"array"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_sites","summary":"Generates a self-contained, mobile-responsive static site from a natural-language brief and deploys it live in one call.","description":"Generates a self-contained, mobile-responsive static site from a\nnatural-language brief and deploys it live in one call.\n\nOne inference call turns `brief` (capped at 8 KiB) into a file manifest, which\nthen runs through the SAME validation, guards and viewport guarantee as a\nhand-supplied manifest: index.html required at the root, absolute and\ntraversal paths rejected, per-file and total size capped, and a mobile\nviewport meta tag injected into every HTML document that lacks one. The\ngenerated site is fully inline — no CDNs, no remote fonts or images — so it is\nCSP-safe. `slug` and `name` are optional: the model's own title is preferred,\nand a slug is derived or minted when none is given.\n\nIt writes into the SAME org-scoped store as /v1/projects — it ensures a\nproject (framework `static`) for the resolved slug and records a deployment —\nso this is a second door onto one publish pipeline, not a second copy of\nproject state. Ordering is the billing contract: the hosting gate runs BEFORE\nany inference or upload, so a denied gate generates and uploads NOTHING, and\nthe debit lands once, only after the site is actually live. The tokens are\nbilled to the same ledger the hosting fee was reserved against.\n\nAnswers 503 when object storage or inference is unconfigured, and 400 when the\nmodel's manifest cannot be parsed or fails the guards.\n\nScope: a validated principal is required (403 without one) and the site is\npublished into THAT principal's org.","tags":["sites"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsBuildSite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsSiteDeploy"}}},"description":"ok"}},"x-app":"projects"}},"/v1/sites/deploy":{"post":{"operationId":"post_v1_sites_deploy","summary":"Deploys a caller-supplied file manifest — the deploy_site capability an agent calls — and answers with where it went live.","description":"Deploys a caller-supplied file manifest — the deploy_site\ncapability an agent calls — and answers with where it went live.\n\n`files` is a list of {path, content} pairs, the same shape the brief build\nemits, and it runs through the SAME guards: index.html required at the root,\nabsolute and traversal paths rejected, per-file and total size capped, and a\nmobile viewport meta tag injected into every HTML document that lacks one — so\na hand-built site is exactly as safe and as responsive as a generated one.\n`slug` and `name` are optional; a slug is derived from the name or minted.\n\nIt writes into the SAME org-scoped store as /v1/projects, ensuring a project\n(framework `static`) for the resolved slug and recording a deployment. The\nhosting gate runs before the upload and the debit lands once, after the site\nis live — a failed upload is never billed. Answers 503 when object storage is\nunconfigured.\n\nScope: a validated principal is required (403 without one) and the site is\npublished into THAT principal's org.","tags":["sites"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeploySite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsSiteDeploy"}}},"description":"ok"}},"x-app":"projects"}},"/v1/sites/{slug}/publish":{"post":{"operationId":"post_v1_sites_by_slug_publish","summary":"Promotes a build output into a new release AND goes live with it — create+activate in one call, which is the 99% path.","description":"Promotes a build output into a new release AND goes live with it —\ncreate+activate in one call, which is the 99% path.\n\nIt is exactly the two halves in sequence with no extra semantics, so the\nstaged flow and the one-shot flow can never drift apart: `source` is promoted\nunder the same org-relative rule and the same guards CreateRelease applies,\nthen the site's pointer is flipped to it, the public host is claimed and the\nedge is purged. Idempotent on unchanged bytes — same manifest, same release id,\nno copy — and billed once, after the release exists.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["sites"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site to publish, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsPublish"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"ok"}},"x-app":"projects"}},"/v1/sites/{slug}/releases":{"get":{"operationId":"get_v1_sites_by_slug_releases","summary":"Returns a site's releases newest-first, marking the active one — the rollback menu.","description":"Returns a site's releases newest-first, marking the active one —\nthe rollback menu.\n\nEach row carries the release id to activate, the source it was promoted from,\nits object and byte counts, and the URL if it is the one serving. Retention\nbounds the list, so it is the set that can actually still be rolled back to,\nnot a full history.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["sites"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the project to act on, from the path. It is unique within the\ncaller's org and nowhere else, so another tenant's slug is a 404.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsRelease"},"type":"array"}}},"description":"ok"}},"x-app":"projects"},"post":{"operationId":"post_v1_sites_by_slug_releases","summary":"Promotes a build output into a new immutable release WITHOUT serving it — the staged half of publishing, for when you want to check a release before it goes live.","description":"Promotes a build output into a new immutable release WITHOUT\nserving it — the staged half of publishing, for when you want to check a\nrelease before it goes live. Answers 201.\n\n`source` is a path RELATIVE to your org's own storage space: the org segment\nis prepended server-side from the validated principal and the bucket is never\nin the request at all, so a server-side copy can only ever reach bytes your\norg already owns. The prefix is listed, content-addressed (SHA-256 over the\nsorted manifest of key/size/etag), and copied into an immutable\n`\u003corg\u003e/.releases/\u003cslug\u003e/\u003cid\u003e/` prefix; the row is written LAST, so a partial\ncopy is unreachable rather than merely unlikely. Re-publishing an unchanged\nsource is idempotent BY CONSTRUCTION — same bytes, same id, no copy at all.\n\nThe source must contain index.html at its root and stay under the same file\nand byte caps an artifact deploy does (413 past them); a source that changes\nmid-copy is a 409 and the release is abandoned. Each publish also reclaims\nreleases past the retention depth, so a site's release space stays bounded.\nThis is the billable half — the hosting gate runs before any copy, and the\ndebit lands once the release exists.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["sites"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site to publish, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsPublish"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"created"}},"x-app":"projects"}},"/v1/sites/{slug}/releases/{release}/activate":{"post":{"operationId":"post_v1_sites_by_slug_releases_by_release_activate","summary":"Points the site at an existing release — the go-live, and equally the ROLLBACK.","description":"Points the site at an existing release — the go-live, and\nequally the ROLLBACK.\n\nAim it at an older release and the site serves that one again: releases are\nimmutable and retained to the retention depth, so nothing is rebuilt or\nre-copied and the flip is one atomic statement. Before the flip, two\nconditions run in the order that gives each its own honest answer — the ROW\nsays whether this release exists for this tenant at all (404, with no signal\nabout a foreign id), and only then do the BYTES say whether it can still serve\n(410 GONE when retention has reclaimed them; that rollback target is not\ncoming back, so publish again). Going live also claims the public host and\npurges the edge, so the release is reachable and no cached predecessor is\nserved. NOT billed: no new content is produced, only a pointer moved.\n\nScope: a validated principal is required (403 without one) and the site is\nresolved within that principal's org, so another tenant's slug is a 404.","tags":["sites"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the site the release belongs to, from the path.","schema":{"type":"string"}},{"name":"release","in":"path","required":true,"description":"Release is the content-addressed release id (\"rel_\" + 32 hex chars), from\nthe path. Anything that is not that shape is not found, rather than being\ninterpolated into a storage prefix.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsRelease"}}},"description":"ok"}},"x-app":"projects"}},"/v1/skills":{"get":{"operationId":"get_v1_skills","summary":"Lists the skills the caller's org can reach — the brand's embedded catalogue plus the org's own authored ones — with each one's activation flag.","description":"Lists the skills the caller's org can reach — the brand's embedded\ncatalogue plus the org's own authored ones — with each one's activation flag.\nA skill is discovery and activation metadata attached to an agent, never called\ndirectly, so every entry here is non-dispatchable. It is GET /v1/tools narrowed\nto one source, not a second store: a name a caller sees here is the same entry,\nwith the same activation state, that discovery reports.","tags":["skills"],"parameters":[{"name":"activated","in":"query","required":false,"description":"Activated keeps only the tools activated for the caller's org and project,\nand only when it is exactly the string \"true\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sourceToolList"}}},"description":"ok"}},"x-app":"tools"},"post":{"operationId":"post_v1_skills","summary":"Adds or revises one of the caller org's own skills, and answers 201 with the stored record.","description":"Adds or revises one of the caller org's own skills, and answers 201\nwith the stored record. The id is derived from the name, so writing the same\nname again REVISES that skill rather than accumulating near-duplicates that\nwould then collide in the registry. An org's skills are private to it by\nconstruction — they live in a different store from the brand's embedded\ncatalogue and have no path into the public gallery — and a brand skill always\nwins a name collision against an org's.","tags":["skills"],"requestBody":{"content":{"application/json":{"example":{"content":"# Triage\n…","description":"how we triage","name":"triage"},"schema":{"$ref":"#/components/schemas/skillIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/skillWritten"}}},"description":"created"}},"x-app":"tools"}},"/v1/skills/authored":{"get":{"operationId":"get_v1_skills_authored","summary":"Lists the caller org's OWN skills with their SKILL.md bodies.","description":"Lists the caller org's OWN skills with their SKILL.md\nbodies. GET /v1/skills is the registry view — the brand's catalogue plus this\norg's, with activation flags and no bodies; this is the EDITABLE set, so it\ncarries the content that view omits and nothing the org did not write.","tags":["skills"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authoredSkillList"}}},"description":"ok"}},"x-app":"tools"}},"/v1/skills/{id}":{"delete":{"operationId":"delete_v1_skills_by_id","summary":"Removes one of the caller org's authored skills.","description":"Removes one of the caller org's authored skills. Scoped to the\ncaller's org, so an id belonging to another tenant is never reached. Removing\nwhat is not there is not an error — the caller's intent is \"gone\", and it is.","tags":["skills"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the skill to remove, from the path. It is the skill's name.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/skillDeleted"}}},"description":"ok"}},"x-app":"tools"}},"/v1/social/accounts":{"get":{"operationId":"get_v1_social_accounts","summary":"List the social accounts connected to your org","description":"Returns the org's connected accounts — each one's id, network, handle, status and timestamps. `provider` filters to one network; `limit` bounds the page, defaulting to 200 and capped at 1000.\n\nAn account's provider access token is NEVER included in any response on this surface. Only the publisher reads it.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"},"post":{"operationId":"post_v1_social_accounts","summary":"Connect a social account to your org","description":"Records a social account for the org and answers 201 with the stored row, including the generated id later calls address it by.\n\n`provider` must be one of x, facebook, instagram, linkedin, tiktok, youtube or threads, defaulting to x when omitted. `status` is one of connected, disconnected or error, defaulting to connected. The handle is trimmed and bounded at 1024 characters.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"}},"/v1/social/accounts/{id}":{"delete":{"operationId":"delete_v1_social_accounts_by_id","summary":"Disconnect one account","description":"Removes one connected account from the org and answers 204 with no body; an id that is not there is 404.\n\nIt removes the account record only. Posts that already published through it keep their published state and their recorded external ids — this does not retract anything from the network.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"},"get":{"operationId":"get_v1_social_accounts_by_id","summary":"Read one connected account","description":"Returns one of the org's connected accounts by id — its network, handle, status and timestamps — or 404. The provider access token is not part of the response.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"},"put":{"operationId":"put_v1_social_accounts_by_id","summary":"Replace one connected account","description":"Replaces the account's network, handle and status with what the body carries, and answers with the stored row.\n\nThis is a REPLACEMENT, not a merge, which is the rule most easily got wrong: a field the body omits is written as its default, so leaving out the handle blanks it and leaving out the status resets it to connected. Send the whole record. The same vocabularies as create apply, and an unknown network or status is refused rather than coerced.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"}},"/v1/social/posts":{"get":{"operationId":"get_v1_social_posts","summary":"List your org's posts","description":"Returns the org's posts — content, channel, status, scheduled time, media and timestamps. `status` filters to one of draft, scheduled, published or failed; `limit` bounds the page, defaulting to 200 and capped at 1000.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"},"post":{"operationId":"post_v1_social_posts","summary":"Create a post, and publish it if it is already due","description":"Stores a post for the org and answers 201 with the stored row.\n\nA post created as scheduled for a time that has already passed is published IMMEDIATELY, and the row returned carries that outcome — this is the one behaviour a reader would otherwise miss. A future-scheduled post is left for the scheduler, and a draft is left alone. Publishing never fails the creation: the post is stored either way, and a publish that could not run leaves the row for the scheduler to retry.\n\n`content` is required and bounded at 8192 characters; `channel` is one of the seven supported networks, defaulting to x; `status` is one of draft, scheduled, published or failed, defaulting to draft; up to 10 media URLs are kept, each bounded at 1024 characters.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"}},"/v1/social/posts/{id}":{"delete":{"operationId":"delete_v1_social_posts_by_id","summary":"Delete one post","description":"Removes one post from the org and answers 204 with no body; an id that is not there is 404.\n\nIt deletes the record here only. A post that has already published is not retracted from the network by deleting it.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"},"get":{"operationId":"get_v1_social_posts_by_id","summary":"Read one post","description":"Returns one of the org's posts by id, with its current status, scheduled time, media and — once it has published — the account and external id it published under. 404 when there is no such post for this org.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"},"put":{"operationId":"put_v1_social_posts_by_id","summary":"Replace one post","description":"Replaces the post's content, channel, status, scheduled time and media with what the body carries, and answers with the stored row.\n\nA REPLACEMENT, not a merge: an omitted field is written as its default, so omitting media clears it and omitting the status resets the post to draft. `content` is required on every update. Unlike create, this never triggers a publish — moving a post's scheduled time into the past here leaves it for the scheduler; publish now is its own operation.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"}},"/v1/social/posts/{id}/publish":{"post":{"operationId":"post_v1_social_posts_by_id_publish","summary":"Publish one post now","description":"Publishes the post immediately to the connected accounts on its channel and answers with the updated row, carrying the account and external id it published under.\n\nIt is IDEMPOTENT: a post that has already published, or that another caller is publishing right now, comes back unchanged rather than being posted twice. That claim is taken before any network call, which is what makes a double submit safe.\n\nThe two failure shapes differ on purpose. Having no connected account for the channel is the caller's to fix, so it is recorded ON the post as failed with the reason and answers normally. A deployment that lacks the network's own credentials cannot publish for anyone, so that is a 503 naming exactly what is missing.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"social"}},"/v1/social/providers":{"get":{"operationId":"get_v1_social_providers","summary":"Which networks this deployment can actually publish to","description":"Reports each supported network's publish-readiness: whether this deployment holds the OAuth application credentials for it and, when it does not, exactly which environment variables are missing.\n\nThis is a live read of the deployment's own configuration, not a static list of networks — it answers \"can I connect this today\", which is what a connect affordance and a pre-cutover checklist both need. It says nothing about whether the caller has connected an account; that is the accounts listing.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"}},"/v1/social/summary":{"get":{"operationId":"get_v1_social_summary","summary":"Counts across your org's social presence","description":"Returns four counts for the caller's org: total posts, how many are scheduled, how many have published, and how many accounts are connected. It is the dashboard roll-up, computed over the org's own rows in one read.\n\nA validated principal is required; 403 without one. Every row is keyed by the caller's org taken from that principal and never from the request, so an id belonging to another tenant reads as not found rather than as a refusal.","tags":["social"],"x-app":"social"}},"/v1/sql":{"get":{"operationId":"get_v1_sql","summary":"ListSQL lists the caller org's Hanzo SQL databases.","description":"ListSQL lists the caller org's Hanzo SQL databases. Each one is a DEDICATED\nPostgreSQL instance the org alone runs, so the host is that instance's own\nin-cluster Service and the port is 5432.","tags":["sql"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_sql","summary":"Provision a PostgreSQL database for your org","description":"Launches your org's OWN PostgreSQL instance and answers with its `postgres://` connection string. The instance is yours alone: a deployment in your own tenant namespace, so its admin credential is naturally scoped to you and no other tenant shares the process. Off-cluster, where there is no orchestrator to launch one, this fails closed with 503 rather than handing back a shared one.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["sql"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/sql/{name}":{"delete":{"operationId":"delete_v1_sql_by_name","summary":"DropSQL deprovisions one Hanzo SQL database.","description":"DropSQL deprovisions one Hanzo SQL database. It reverts any app instance\nbound to it back to Base BEFORE tearing down the org's dedicated Postgres\ninstance — never a live app pointed at a deleted backend — then deletes the\nsealed credential and removes the metadata row. Answers 204 with no body; a\nsecond call is a 404, not a second delete.","tags":["sql"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"orders"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_sql_by_name","summary":"GetSQL returns one Hanzo SQL database's metadata.","description":"GetSQL returns one Hanzo SQL database's metadata. It carries the database's\nstatus, its instance address and the admin user Postgres booted with — never\nthe password, which is returned once at create and otherwise lives only in\nHanzo KMS. A still-booting instance reads \"provisioning\", reconciled from the\noperator's live view rather than from the row.","tags":["sql"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"orders"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/store/":{"get":{"operationId":"get_v1_store","summary":"List your org's storefronts as a page","description":"Answers a pagination envelope — page, display, the rows, and a total count — read from the caller org's OWN namespaced database, so one tenant can never list another's stores. Sorting defaults to the store slug and is overridable with sort; display is the page size and page applies only alongside it, and either one that is not a positive integer is refused rather than silently ignored. The limit query overrides the reported COUNT only and never the rows returned. A request that resolves no org namespace is served an empty page, never an unscoped scan. Readable with an admin token, a store-scoped token, or the anonymous published storefront key.","tags":["store"],"x-app":"commerce"},"post":{"operationId":"post_v1_store","summary":"Create a storefront","description":"Creates a store from the body inside the caller org's own namespaced database, so the row is physically isolated to that tenant from its first write, and answers it at 201 with a Location header naming its id. Requires an admin or store-write token: the anonymous published storefront key may READ stores but never create one. A body that fails to decode is 400.","tags":["store"],"x-app":"commerce"}},"/v1/store/access":{"get":{"operationId":"get_v1_store_access","summary":"Whether a store is entitled to trade, and why","description":"Answers allowed, the store id, and a status of trial, active, payment_required, store_required or unavailable — the entitlement check a merchant surface gates on. The rule that surprises people is that entitlement is PER STORE, not per org: the store needs its own current subscription on the entry plan, either trialing with a trial end still ahead or active with a period end still ahead, so an org-wide balance or a sibling store's plan unlocks nothing here. The store comes from the X-Store-Id header and otherwise falls back to the org's first store; neither resolving is store_required with allowed false, and a backing-store failure is 503 with status unavailable — a retry signal, not a denial.","tags":["store"],"x-app":"commerce"}},"/v1/store/current":{"get":{"operationId":"get_v1_store_current","summary":"Resolve your org's active storefront without naming an id","description":"Returns the caller org's store resolved FROM THE AUTHENTICATED ORG rather than from a path id — which is how an admin dashboard or a storefront edge learns the store id it should then read and write against. An X-Store-Id header selects a specific store, resolved only inside the caller's own namespace, so a foreign id cannot cross the tenant boundary and answers 404 instead. With no header the org's first store is returned, and an org that has none yet has its canonical default provisioned lazily and idempotently, carrying no payment credentials. Only when there is no org in context, or provisioning fails, does it fall back to a placeholder store literally named default, which a storefront edge should treat as unconfigured.","tags":["store"],"x-app":"commerce"}},"/v1/store/token":{"post":{"operationId":"post_v1_store_token","summary":"Mint your org's least-privilege storefront read key","description":"Answers a freshly minted token carrying ONLY the published-read permission — enough for a logged-out shopper's storefront to read your published catalog and nothing more, with no write and no admin scope. It is org-bound, signed with the org's own secret and subject to the org id, so unlike a shared service token it can never act on another tenant. Minting ROTATES rather than accumulates: the previous storefront token is dropped first and is invalid immediately, so re-minting is how you revoke. Admin is enforced by the handler as well as the route, because the route's token gate does not apply on the identity path and a plain member must not be able to mint their org's key.","tags":["store"],"x-app":"commerce"}},"/v1/store/{storeid}":{"delete":{"operationId":"delete_v1_store_by_storeid","summary":"Delete a storefront, keeping a recoverable copy","description":"Removes the addressed store and answers 204 with no body. Before the live row goes, the entity is written once more under a tombstone kind, so the deletion leaves a recoverable copy rather than destroying the record outright; the store's listing overrides live inside that row and go with it. The id is resolved inside the caller org's own namespace, so an unknown or foreign id is 404. Requires an admin or store-write token.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_store_by_storeid","summary":"Fetch one storefront","description":"Reads the addressed store from the caller org's own namespaced database, so an id belonging to another tenant is simply absent there and answers 404 rather than leaking its existence. The body is the stored entity including its embedded listing override map. Readable with an admin or store-read token and also with the anonymous published storefront key, which is what lets a logged-out storefront resolve the store it is rendering.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_store_by_storeid","summary":"Change part of a storefront","description":"Loads the stored store and decodes the body over it, so only the fields the body names change and everything else keeps its stored value — the difference from the full replace, which clears what it is not told. Answers the merged entity. The id is resolved inside the caller org's own namespace, so an unknown or foreign id is 404. Requires an admin token, or one holding both store read and store write.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_store_by_storeid","summary":"Method-override tunnel for clients that cannot send PUT, PATCH or DELETE","description":"Re-dispatches the request into the handler the intended verb would have reached, taking that verb from a _method form value or query parameter and then from the X-HTTP-Method-Override header, the header winning when both are present. Only PUT, PATCH and DELETE are accepted; anything else resolves to 405. The trap is the default: naming NO override at all is treated as a partial update, never as a create. Authorization is whatever the underlying operation requires, since the real handler runs.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_store_by_storeid","summary":"Replace a storefront outright","description":"This is a true REPLACEMENT, not a merge: the stored key is preserved but the body is decoded onto a fresh entity, so every field the body omits is written back as its zero value. Use the partial update when you mean to change part of a store. The id is resolved inside the caller org's own namespace, so an unknown or foreign id is a 404 before anything is written. Requires an admin token, or one holding both store read and store write.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/authorize":{"post":{"operationId":"post_v1_store_by_storeid_authorize","summary":"Authorize a new order against a storefront, holding the funds without settling them","description":"Tallies a new order for the addressed store from the user, payment and order body, reserves its items, runs the processor authorization and answers the saved order with a Location header pointing at it. The gate is a token carrying admin or published scope, so a published storefront key is enough; no token is 401 and a token with neither bit is 403. The store is loaded BEFORE any payment work and its currency OVERRIDES whatever the body asked for, so a store that will not load ends the call with 500 and nothing is charged. On any authorization failure the reservations are released and the order and payment are persisted as cancelled, so a failed attempt still leaves a durable record. Capture is a separate call.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/authorize/{orderid}":{"post":{"operationId":"post_v1_store_by_storeid_authorize_by_orderid","summary":"Authorize an order that already exists, holding the funds without settling them","description":"Continues the order named in the path rather than minting a new one, holding funds for it. The order is loaded from the caller org's own store, so an id belonging to another tenant is a 404. The rule most callers get wrong is that the body's order object is MERGED onto the loaded order before the tally — this is not a read-only reference, and a field sent here overwrites what is stored. The gate, the store resolution and the currency override behave exactly as on the bodiless-id sibling, and settling is still the capture call's job.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/bundle/{key}":{"get":{"operationId":"get_v1_store_by_storeid_bundle_by_key","summary":"Fetch a bundle as this storefront sells it","description":"Returns the stored bundle with the store's listing for it laid over the top — every non-empty listing field wins, and the currency is forced to the store's own — so the caller reads what this storefront actually sells rather than the catalog-wide record. The overlay is keyed by the item's ID: a listing filed only under a slug or SKU does not reach it, unlike the listing reads, which do fall back to those. An unknown store or key is 404. Readable with an admin token or the anonymous published storefront key.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/capture/{orderid}":{"post":{"operationId":"post_v1_store_by_storeid_capture_by_orderid","summary":"Capture a previously authorized order and settle the payment","description":"Settles the order named in the path — the second half of the two-step flow — and answers the updated order with a Location header. Dispatch follows the order's STORED payment type, and a successful capture is the moment the rest of the system learns about the sale: order and payment rows are updated, coupon redemptions, referral, cart and stats are written, the confirmation email goes out, and the paid and completed events are emitted. A capture failure releases the order's inventory reservations and answers 400, so a failed settlement never leaves items held.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/charge":{"post":{"operationId":"post_v1_store_by_storeid_charge","summary":"Authorize and capture a new order in one call","description":"Runs authorization and capture back to back against a freshly created order — the one-step flow for callers with no reason to hold funds. It takes the authorize body and inherits every authorize rule: the store's currency wins over the body, the items are reserved before the processor is called, and the amount bounds the processor enforces still apply. There is no order id on this address, so it can never continue an existing order. Either half failing answers 400, and the capture side effects — confirmation email, redemptions, stats, the paid and completed events — run only when both halves succeed.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/authorize":{"post":{"operationId":"post_v1_store_by_storeid_checkout_authorize","summary":"Authorize a new order against a storefront, holding the funds — the checkout spelling","description":"Authorizes a new order for the addressed store and holds the funds, answering the saved order with a Location header. It binds the identical handler as the shorter authorize address, so the two are ONE operation at two spellings and not two behaviours; the checkout prefix is the newer one. Every rule carries over: admin or published scope on the token, the store loaded first with its currency overriding the body, items reserved before the processor call, and reservations released with the order persisted cancelled on failure. Nothing is settled here.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/authorize/{orderid}":{"post":{"operationId":"post_v1_store_by_storeid_checkout_authorize_by_orderid","summary":"Authorize an existing order, holding the funds — the checkout spelling","description":"Continues the order named in the path rather than minting one, and shares its handler byte for byte with the unprefixed authorize-by-id address. The order is loaded from the caller org's own store, so another tenant's id is a 404, and the body's order object is merged onto the loaded row before the tally — a field sent here overwrites what is stored. Store resolution, the token gate and the currency override behave as on every other authorize address; settle with the capture address and the same order id.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/capture/{orderid}":{"post":{"operationId":"post_v1_store_by_storeid_checkout_capture_by_orderid","summary":"Capture a previously authorized order and settle it — the checkout spelling","description":"Settles the authorized order named in the path and answers the updated order with a Location header, running the same handler as the unprefixed capture address. Dispatch follows the order's stored payment type. Success is what triggers the downstream work — order and payment updates, redemptions, referral, cart and stats, the confirmation email, and the paid and completed events — while a failure releases the order's inventory reservations and answers 400.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/charge":{"post":{"operationId":"post_v1_store_by_storeid_checkout_charge","summary":"Authorize and capture a new order in one call — the checkout spelling","description":"Performs authorization and capture back to back against a newly created order for the addressed store, on the same handler as the unprefixed charge address. It takes the authorize body and inherits every authorize rule, including the store's currency winning over the body and the items being reserved before the processor is called. There is no order id on this address, so it can never continue an existing order. Either half failing answers 400, and the capture side effects run only when both succeed.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/paypal/cancel/{payKey}":{"post":{"operationId":"post_v1_store_by_storeid_checkout_paypal_cancel_by_paykey","summary":"PayPal cancel by pay key — refuses, exactly as the unprefixed address does","description":"Meant to void the payments carrying the given pay key, stamp them cancelled and cancel the order, but the shared checkout handler resolves its order from an ORDER ID path parameter this route does not carry. The result is an untyped order and a cancel dispatch that refuses with 400 before the pay key lookup ever runs. Token gate, namespacing and store resolution happen first, so a missing token is still 401 and an unloadable store still 500. It is the same handler as the unprefixed cancel address, with the same outcome.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/paypal/confirm/{payKey}":{"post":{"operationId":"post_v1_store_by_storeid_checkout_paypal_confirm_by_paykey","summary":"PayPal confirm by pay key — refuses, exactly as the unprefixed address does","description":"Meant to mark the payments carrying the given pay key as paid and set the order to paid, it cannot reach that work from this address: the shared checkout handler takes its order from an ORDER ID path parameter this route does not carry, so the order is always fresh and untyped and the confirm dispatch refuses with 400 before the pay key is queried. The token gate, the namespace middleware and the store lookup all run ahead of that, so authentication and store failures surface first. Behaviour is identical to the unprefixed confirm address; the checkout prefix changes nothing here.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/checkout/paypal/pay":{"post":{"operationId":"post_v1_store_by_storeid_checkout_paypal_pay","summary":"Start a PayPal authorization for a new order — the checkout spelling","description":"Begins a PayPal authorization by running the ordinary store authorize flow, since the route binds that exact handler — body, store resolution, tally, reservations and failure behaviour are the authorize address's, unchanged. The processor is chosen from the body's payment type, so this path reaches PayPal only when that type says so. A successful PayPal authorization stamps a pay key onto the payment, which is the key the confirm and cancel addresses filter on. Build against the plain authorize address instead.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/listing":{"get":{"operationId":"get_v1_store_by_storeid_listing","summary":"The storefront's whole listing override map","description":"Returns every override this store applies to catalog items — name, price, list price, media, availability and the hidden flag — keyed by product or variant id, in one read. A listing is an OVERRIDE, not a product: the catalog item exists independently and this map only says how this storefront presents it. Read from the caller org's own namespaced database, so a store id belonging to another tenant is 404. Readable with an admin token or the anonymous published storefront key.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/listing/{key}":{"delete":{"operationId":"delete_v1_store_by_storeid_listing_by_key","summary":"Remove a listing override","description":"Drops the key from the store's listing map and re-saves the store, answering 204 with no body. It UN-OVERRIDES rather than deletes: the product, variant or bundle itself is untouched and simply reverts to its catalog values on this storefront. A key that is not present is 404, and so is a store id outside the caller org's namespace. Admin-gated.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"get":{"operationId":"get_v1_store_by_storeid_listing_by_key","summary":"Fetch one listing override, by item id or by its slug or SKU","description":"Looks the key up in the store's listing map first and, failing that, matches it against each listing's slug and then its SKU — so a storefront holding only a product's URL slug can still resolve the override. That fallback is unique to the listing reads; the item overlay routes match by id alone. A key matching none of the three is 404, as is a store id outside the caller org's namespace. Readable with an admin token or the anonymous published storefront key.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"patch":{"operationId":"patch_v1_store_by_storeid_listing_by_key","summary":"Confirm a listing override exists and re-save the store","description":"Requires the key to already be present — an absent one is 404 — and answers the store's listing map at 200. Read the behaviour before relying on it: the decoded body is applied to a COPY taken out of the map and is never assigned back, so the stored listing is unchanged and the map returned is exactly the map that was already there. An actual edit to an existing listing has to go through the upsert, which does write its result back into the store. A body that fails to decode is still 400. Admin-gated and namespaced to the caller's org.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"post":{"operationId":"post_v1_store_by_storeid_listing_by_key","summary":"Add a listing override under a new key","description":"Creates the override and answers the store's ENTIRE listing map at 201 with a Location header — not just the entry that was added. A key already present is refused 400: creation never silently overwrites, so changing an existing listing has to be an explicit replace. The stored listing has its currency stamped from the store's own, which the replace path does not do. The key is matched exactly here, with none of the slug or SKU fallback the read allows. Admin-gated and resolved inside the caller org's namespace.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"},"put":{"operationId":"put_v1_store_by_storeid_listing_by_key","summary":"Upsert a listing override","description":"Decodes the body over the existing listing when the key is present, so fields it omits keep their stored values, and builds the listing from the body alone when the key is new. Answers 200 when it replaced something and 201 with a Location header when it created it; either way the body is the store's entire listing map, not the single entry. Unlike creation, this path does NOT restamp the listing's currency from the store. Admin-gated, with the store resolved inside the caller org's namespace.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/paypal/cancel/{payKey}":{"post":{"operationId":"post_v1_store_by_storeid_paypal_cancel_by_paykey","summary":"PayPal cancel by pay key — refuses, because a pay key alone does not identify the order","description":"Intended to void the payments carrying the given pay key, stamp them cancelled and cancel the order, it never reaches that work: the shared checkout handler reads its order from an ORDER ID path parameter this route does not carry, leaving an untyped order that the cancel dispatch refuses with 400 before the pay key lookup runs. Authentication, namespacing and store resolution happen ahead of the refusal, so a missing token is 401 and an unloadable store 500. Cancelling a real PayPal authorization needs an address that carries the order id.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/paypal/confirm/{payKey}":{"post":{"operationId":"post_v1_store_by_storeid_paypal_confirm_by_paykey","summary":"PayPal confirm by pay key — refuses, because a pay key alone does not identify the order","description":"Intended to mark every payment carrying the given pay key as paid and flip the order to paid, it cannot do that from this address and does not pretend to: the shared checkout handler resolves its order from an ORDER ID path parameter that this route does not carry, so it always works against a fresh untyped order and the confirm dispatch refuses it with 400 before the pay key is ever queried. The token gate, the namespace and the store lookup all run ahead of that, so a missing token is still 401 and an unloadable store still 500. Drive a PayPal return through an address that carries the order id.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/paypal/pay":{"post":{"operationId":"post_v1_store_by_storeid_paypal_pay","summary":"Start a PayPal authorization for a new order","description":"Runs the ordinary store authorize flow — the route binds that very handler, so the body, the store resolution, the tally, the reservations and the failure behaviour are the authorize address's, unchanged. It reaches PayPal only when the body's payment type says so; nothing about this path forces the processor, so a card-typed payment posted here authorizes on the card processor instead. A successful PayPal authorization stamps a pay key onto the payment, which is the key the confirm and cancel addresses filter on. It is the older entry point; the plain authorize address is the one to build against.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/product/{key}":{"get":{"operationId":"get_v1_store_by_storeid_product_by_key","summary":"Fetch a product as this storefront sells it","description":"Returns the stored product with the store's listing for it laid over the top — non-empty listing fields replace the catalog values and the currency is forced to the store's own — which is what lets two storefronts sell the same catalog product at their own price, name and media. The overlay is keyed by the product's ID, so a listing filed only under a slug or SKU does not apply here. An unknown store or key is 404. Readable with an admin token or the anonymous published storefront key.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/trial":{"post":{"operationId":"post_v1_store_by_storeid_trial","summary":"Start this store's no-card trial on the entry plan","description":"Creates a trialing subscription for the addressed store on the entry plan and grants that plan's trial credit, answering 201 when this call actually started one and 200 with a reason otherwise — not_new when the store already has billing history, trial_not_configured when no entry plan is wired. The window is always the SEVEN-DAY no-card trial, because this address never presents a card; the longer card-present window is reached only by adding a card afterwards. Entitlement is per store while the billing subject is the org, so every store an org owns takes its own trial. Admin-gated and namespaced to the caller's org: no resolvable store is 404 with store_required, and a backing-store failure is 503.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/store/{storeid}/variant/{key}":{"get":{"operationId":"get_v1_store_by_storeid_variant_by_key","summary":"Fetch a variant as this storefront sells it","description":"Returns the stored variant with the store's listing for it overlaid — non-empty listing fields replace the catalog values and the currency is forced to the store's own — which is what makes per-storefront pricing of a shared variant possible. The overlay is keyed by the variant's ID, never by its slug or SKU. An unknown store or key is 404. Readable with an admin token or the anonymous published storefront key.","tags":["store"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce"}},"/v1/summary":{"get":{"operationId":"get_v1_summary","summary":"Reports whether the platform is up.","description":"Reports whether the platform is up. It returns the public status\ndocument: the incidents currently open against Hanzo's own services, derived\nfrom the fleet health probes, plus the address of the human status page. No\nauthentication is required and no tenant data is involved — the answer is the\nsame for every caller.\n\nA service that fails its health probe becomes one incident naming that service.\nWhen the availability source itself cannot be read the endpoint answers 503\nrather than an empty incident list, because \"we cannot tell\" and \"everything is\nfine\" are different answers and only one of them is true.","tags":["summary"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.StatusSummary"}}},"description":"ok"}},"x-app":"o11y"}},"/v1/sync":{"get":{"operationId":"get_v1_sync","summary":"List returns every sync link the caller's org has, each with its two endpoints, its direction and trigger policy, and the time it last reconciled.","description":"List returns every sync link the caller's org has, each with its two endpoints, its\ndirection and trigger policy, and the time it last reconciled. Scoped to the\ncaller's own org — another tenant's links are structurally unreachable.","tags":["sync"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncList"}}},"description":"ok"}},"x-app":"sync"},"post":{"operationId":"post_v1_sync","summary":"Create declares a sync between two endpoints and returns it.","description":"Create declares a sync between two endpoints and returns it. It is an UPSERT:\nre-declaring the same source and target updates that link rather than piling up\nduplicates, so a console that re-submits is safe. The org comes from the validated\nprincipal, never from the request, so a sync can only ever bind endpoints inside\nthe caller's own org. A git source must be an https clone URL on the provider's own\nhost with no embedded credentials; a target left empty is derived as a native\nrepository named after the source. With run=true the first reconcile is queued in\nthe background, so a large initial import never blocks this response.","tags":["sync"],"requestBody":{"content":{"application/json":{"example":{"run":true,"source":{"locator":"https://github.com/acme/site","provider":"github"}},"schema":{"$ref":"#/components/schemas/syncReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncView"}}},"description":"ok"}},"x-app":"sync"}},"/v1/sync/{id}":{"delete":{"operationId":"delete_v1_sync_by_id","summary":"Delete removes one sync and tears down the outbound mirror it derived, answering 204.","description":"Delete removes one sync and tears down the outbound mirror it derived, answering\n204. The teardown is the point: without it an unsynced repository would keep\nforce-pushing to the upstream it is no longer linked to. Org-scoped, so another\ntenant's id is the same 404 an unknown id gives.","tags":["sync"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sync to act on, from the path.","schema":{"type":"string"},"example":"sync_1"}],"responses":{"204":{"description":"no content"}},"x-app":"sync"},"get":{"operationId":"get_v1_sync_by_id","summary":"Get returns one sync by id.","description":"Get returns one sync by id. It is org-scoped: an id belonging to another tenant is\nthe same 404 an unknown id gives, so a probe learns nothing about what exists.","tags":["sync"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sync to act on, from the path.","schema":{"type":"string"},"example":"sync_1"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncView"}}},"description":"ok"}},"x-app":"sync"},"patch":{"operationId":"patch_v1_sync_by_id","summary":"Patch updates one sync's mutable policy — direction, trigger and actor — in place.","description":"Patch updates one sync's mutable policy — direction, trigger and actor — in place.\nThe endpoints and the kind are immutable: re-pointing a sync is a delete and a\ncreate, so a link can never silently start syncing somewhere else. A field the\nrequest omits is left as it was. Changing the direction immediately reconciles the\nderived outbound mirror, so turning push off stops the upstream being written to\nrather than merely recording the intent.","tags":["sync"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sync to update, from the path.","schema":{"type":"string"},"example":"sync_1"}],"requestBody":{"content":{"application/json":{"example":{"direction":"pull","id":"sync_1"},"schema":{"$ref":"#/components/schemas/patchSyncIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncView"}}},"description":"ok"}},"x-app":"sync"}},"/v1/sync/{id}/run":{"post":{"operationId":"post_v1_sync_by_id_run","summary":"Run reconciles one sync now — the manual re-sync, and the initial import for a link created without run=true.","description":"Run reconciles one sync now — the manual re-sync, and the initial import for a link\ncreated without run=true. The work is handed to a bounded background worker and the\ncall answers 202 immediately, so a large mirror-in never holds the request open;\nqueued=true means accepted, not finished.","tags":["sync"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sync to act on, from the path.","schema":{"type":"string"},"example":"sync_1"}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/syncQueued"}}},"description":"accepted"}},"x-app":"sync"}},"/v1/tasks":{"delete":{"operationId":"delete_v1_tasks","summary":"Redirect to the tasks API root","description":"Answers 307 with Location /v1/tasks/ — this address serves nothing itself. The redirect is a routing fact derived from the engine's subtree, decided before any handler runs, so it is the same on every method.\n\nA 307 preserves both the method and the body, so a client that follows redirects re-sends the request unchanged to /v1/tasks/ and nothing is lost. A client that does NOT follow redirects sees only the 307 and performs no work — address /v1/tasks/ directly and the hop disappears.","tags":["tasks"],"x-app":"tasks"},"get":{"operationId":"get_v1_tasks","summary":"Redirect to the tasks API root","description":"Answers 307 with Location /v1/tasks/ — this address serves nothing itself. The redirect is a routing fact derived from the engine's subtree, decided before any handler runs, so it is the same on every method.\n\nA 307 preserves both the method and the body, so a client that follows redirects re-sends the request unchanged to /v1/tasks/ and nothing is lost. A client that does NOT follow redirects sees only the 307 and performs no work — address /v1/tasks/ directly and the hop disappears.","tags":["tasks"],"x-app":"tasks"},"patch":{"operationId":"patch_v1_tasks","summary":"Redirect to the tasks API root","description":"Answers 307 with Location /v1/tasks/ — this address serves nothing itself. The redirect is a routing fact derived from the engine's subtree, decided before any handler runs, so it is the same on every method.\n\nA 307 preserves both the method and the body, so a client that follows redirects re-sends the request unchanged to /v1/tasks/ and nothing is lost. A client that does NOT follow redirects sees only the 307 and performs no work — address /v1/tasks/ directly and the hop disappears.","tags":["tasks"],"x-app":"tasks"},"post":{"operationId":"post_v1_tasks","summary":"Redirect to the tasks API root","description":"Answers 307 with Location /v1/tasks/ — this address serves nothing itself. The redirect is a routing fact derived from the engine's subtree, decided before any handler runs, so it is the same on every method.\n\nA 307 preserves both the method and the body, so a client that follows redirects re-sends the request unchanged to /v1/tasks/ and nothing is lost. A client that does NOT follow redirects sees only the 307 and performs no work — address /v1/tasks/ directly and the hop disappears.","tags":["tasks"],"x-app":"tasks"},"put":{"operationId":"put_v1_tasks","summary":"Redirect to the tasks API root","description":"Answers 307 with Location /v1/tasks/ — this address serves nothing itself. The redirect is a routing fact derived from the engine's subtree, decided before any handler runs, so it is the same on every method.\n\nA 307 preserves both the method and the body, so a client that follows redirects re-sends the request unchanged to /v1/tasks/ and nothing is lost. A client that does NOT follow redirects sees only the 307 and performs no work — address /v1/tasks/ directly and the hop disappears.","tags":["tasks"],"x-app":"tasks"}},"/v1/tasks/{wildcard1}":{"delete":{"operationId":"delete_v1_tasks_by_wildcard1","summary":"Delete an engine resource","description":"Removes a resource the engine owns — a namespace and the like — inside the caller's own tenant shard.\n\nIt is the narrowest of the three working methods: most of the engine's surface is read on GET and acted on with POST, so a delete that finds no route for its path answers the same plain-text 404 any unrouted path does.\n\nThis single address fronts the whole durable-workflow engine — namespaces, workflows, schedules, batches, deployments, nexus, task queues, workers, activities and the rest — matched by path segment inside the engine's own router, which is why the document publishes one wildcard rather than sixty-four operations.\n\nMost of it requires a validated principal and is refused 403 otherwise; the settings and cluster probes are open, because capability flags and cluster health carry no tenant data. A principal that is validated but carries NO org is refused too — that request would otherwise read the shared unscoped store instead of anyone's shard, so it fails closed. Admitted, the org, project and user are threaded into the engine, and every read and write lands in that tenant's own shard.\n\nTwo things about the answers differ from the rest of this API and will bite a generic client: errors here carry `code` as a NUMBER rather than the usual `status`, and the address serves several content types — JSON, plain-text refusals, and an event stream at the events path. Until the engine is wired the whole surface answers 503.","tags":["tasks"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"get":{"operationId":"get_v1_tasks_by_wildcard1","summary":"Read workflow state from the durable engine","description":"Reads from the durable engine: list namespaces, workflows, schedules, batches, deployments, task queues, workers and search attributes, fetch one workflow with its history, or subscribe to the realtime event stream. The cluster and settings probes are on this method too.\n\nThis single address fronts the whole durable-workflow engine — namespaces, workflows, schedules, batches, deployments, nexus, task queues, workers, activities and the rest — matched by path segment inside the engine's own router, which is why the document publishes one wildcard rather than sixty-four operations.\n\nMost of it requires a validated principal and is refused 403 otherwise; the settings and cluster probes are open, because capability flags and cluster health carry no tenant data. A principal that is validated but carries NO org is refused too — that request would otherwise read the shared unscoped store instead of anyone's shard, so it fails closed. Admitted, the org, project and user are threaded into the engine, and every read and write lands in that tenant's own shard.\n\nTwo things about the answers differ from the rest of this API and will bite a generic client: errors here carry `code` as a NUMBER rather than the usual `status`, and the address serves several content types — JSON, plain-text refusals, and an event stream at the events path. Until the engine is wired the whole surface answers 503.","tags":["tasks"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"patch":{"operationId":"patch_v1_tasks_by_wildcard1","summary":"Not served by the engine","description":"Published because this address accepts every method, but the engine routes no PATCH: the answer is a plain-text 404, not a 405, and no state changes.\n\nThere is no partial update on this surface. State advances by appending events, so the operations that change a running workflow — signal, cancel, terminate, reset — are all POST.\n\nThis single address fronts the whole durable-workflow engine — namespaces, workflows, schedules, batches, deployments, nexus, task queues, workers, activities and the rest — matched by path segment inside the engine's own router, which is why the document publishes one wildcard rather than sixty-four operations.\n\nMost of it requires a validated principal and is refused 403 otherwise; the settings and cluster probes are open, because capability flags and cluster health carry no tenant data. A principal that is validated but carries NO org is refused too — that request would otherwise read the shared unscoped store instead of anyone's shard, so it fails closed. Admitted, the org, project and user are threaded into the engine, and every read and write lands in that tenant's own shard.\n\nTwo things about the answers differ from the rest of this API and will bite a generic client: errors here carry `code` as a NUMBER rather than the usual `status`, and the address serves several content types — JSON, plain-text refusals, and an event stream at the events path. Until the engine is wired the whole surface answers 503.","tags":["tasks"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"post":{"operationId":"post_v1_tasks_by_wildcard1","summary":"Start workflows and act on running ones","description":"Everything that changes the engine's state: register a namespace, start a workflow or signal-with-start one, and signal, query, cancel, terminate or reset a workflow that is already running. The MCP tool surface is on this method as well, and is the one part of it that refuses a non-POST with a plain-text 405.\n\nThe engine is event-sourced and exactly-once, so an action is durable once it is accepted and survives a process crash — a started workflow resumes rather than restarts.\n\nThis single address fronts the whole durable-workflow engine — namespaces, workflows, schedules, batches, deployments, nexus, task queues, workers, activities and the rest — matched by path segment inside the engine's own router, which is why the document publishes one wildcard rather than sixty-four operations.\n\nMost of it requires a validated principal and is refused 403 otherwise; the settings and cluster probes are open, because capability flags and cluster health carry no tenant data. A principal that is validated but carries NO org is refused too — that request would otherwise read the shared unscoped store instead of anyone's shard, so it fails closed. Admitted, the org, project and user are threaded into the engine, and every read and write lands in that tenant's own shard.\n\nTwo things about the answers differ from the rest of this API and will bite a generic client: errors here carry `code` as a NUMBER rather than the usual `status`, and the address serves several content types — JSON, plain-text refusals, and an event stream at the events path. Until the engine is wired the whole surface answers 503.","tags":["tasks"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"},"put":{"operationId":"put_v1_tasks_by_wildcard1","summary":"Not served by the engine","description":"Published because this address accepts every method, but the engine routes no PUT: the answer is a plain-text 404, not a 405, and no state changes.\n\nNothing here is updated by replacement. The engine is event-sourced — a workflow is changed by signalling, cancelling, terminating or resetting it, all of which are POST — so a client reaching for PUT wants POST.\n\nThis single address fronts the whole durable-workflow engine — namespaces, workflows, schedules, batches, deployments, nexus, task queues, workers, activities and the rest — matched by path segment inside the engine's own router, which is why the document publishes one wildcard rather than sixty-four operations.\n\nMost of it requires a validated principal and is refused 403 otherwise; the settings and cluster probes are open, because capability flags and cluster health carry no tenant data. A principal that is validated but carries NO org is refused too — that request would otherwise read the shared unscoped store instead of anyone's shard, so it fails closed. Admitted, the org, project and user are threaded into the engine, and every read and write lands in that tenant's own shard.\n\nTwo things about the answers differ from the rest of this API and will bite a generic client: errors here carry `code` as a NUMBER rather than the usual `status`, and the address serves several content types — JSON, plain-text refusals, and an event stream at the events path. Until the engine is wired the whole surface answers 503.","tags":["tasks"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tasks"}},"/v1/team/account":{"post":{"operationId":"post_v1_team_account","summary":"Read the caller's account and switch workspace","description":"The account control plane the Team client speaks: one POST carries a `method` verb and its `params`, and answers {\"result\": …}. The verbs are the session's own reads and the workspace switch — getLoginInfoByToken, getUserWorkspaces, selectWorkspace, getWorkspaceInfo, getMemberships, getPerson, getSocialIds, getRegionInfo, isReadOnlyGuest — plus sendInvite, which adds a member to a workspace and is refused for a caller who is not its owner or admin.\n\nA REFUSAL IS HTTP 200 carrying {\"error\": {severity, code, params}} — the platform Status the client translates — not a 4xx. An unreadable body, an unauthorized session and an unknown verb all arrive that way, so a caller that reads only the status code reads every failure here as a success.\n\nNO CREDENTIAL IS EVER HANDLED HERE. login, signUp, the OTP verbs, password change and reset, join and the guest-token exchange each answer Unauthorized with \"sign in at hanzo.id\" — a stated policy, not an unknown method, so the door being shut is a fact a test can pin. Sessions come from the OAuth pair under /account/auth.\n\nAuth is the team session token: Authorization: Bearer, else the HttpOnly account-token cookie. The tenant is that token's SIGNED org claim, never a header, and selectWorkspace resolves only among the orgs the token proves membership of. It also demands an explicit workspaceUrl — it never falls back to a first workspace, and a slug that resolves in two of the caller's orgs answers Ambiguous rather than picking one.","tags":["team"],"x-app":"team"}},"/v1/team/account/auth/{provider}":{"get":{"operationId":"get_v1_team_account_auth_by_provider","summary":"Start a sign-in at hanzo.id","description":"STARTS the OAuth hop: answers 302 to hanzo.id's authorize endpoint and sets the short-lived HttpOnly state cookie that binds the flow to this browser. NO TOKEN COMES BACK FROM THIS CALL — the session is minted by the callback below, and a client that expects JSON here gets a redirect with no body.\n\nA browser is the intended caller. Anything else must follow the Location AND keep the Set-Cookie, because the callback refuses a flow whose state it cannot match. That cookie carries the random nonce plus the client's navigateUrl, so the round trip needs no second channel, and it lives ten minutes — the whole budget for the hop.\n\nThe provider segment only picks a hint: the redirect_uri is ALWAYS the canonical openid callback, the one IAM has registered. Measured end to end, hanzo.id strips that hint today, so /auth/google and /auth/openid land on the same Hanzo sign-in page — the federation shortcut is an upstream fix, not a second door here.","tags":["team"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/team/account/auth/{provider}/callback":{"get":{"operationId":"get_v1_team_account_auth_by_provider_callback","summary":"Complete a sign-in and hand the browser its session","description":"COMPLETES the OAuth hop: hanzo.id redirects the browser here with ?code and ?state, and the answer is another 302 — back to the client's login route carrying the freshly minted team session token in the query. Never JSON, and never a token in this response's own body.\n\nTHE STATE IS CHECKED FIRST, before the code is even looked at: the flow cookie is read and cleared one-shot, and a callback whose ?state does not equal the nonce it held is bounced with error=state_mismatch and NEVER exchanged. That is what makes a forged or replayed callback inert. Only then is the code exchanged server-side — team is a confidential client with a client_secret, so there is no PKCE and the code never passes through the browser's JS.\n\nThe tenant is derived from the IAM access token VERIFIED RS256 against the JWKS, the same trust anchor the identity boundary uses; a token whose owner claim is empty fails closed with no login at all. Every org that token proves gets a workspace ensured, so a member of two orgs is a counted seat in both. The IAM access token is also parked in an HttpOnly cookie for the same-origin agents proxy — page JS never reads it.\n\nEVERY failure is a redirect, not a status: a denied consent, a missing code, a failed exchange, an unreadable userinfo, an unverifiable org and a token-mint failure each bounce to the login page with an ?error code naming the step.","tags":["team"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/team/account/cookie":{"delete":{"operationId":"delete_v1_team_account_cookie","summary":"Signs this browser out of team by expiring the HttpOnly account-token cookie the OAuth callback set.","description":"Signs this browser out of team by expiring the HttpOnly\naccount-token cookie the OAuth callback set. It is the counterpart of the\ncookie PUT, it takes nothing — the cookie it clears is named by this service,\nnever by the caller — and it is unconditional: a caller with no cookie, an\nexpired one or a forged one all get the same acknowledgement, because clearing\nsomething that is not there is the same outcome as clearing something that is.\n\nIt clears ONLY the team session cookie. The IAM access-token cookie the same\ncallback set is a different credential with a different lifetime and is left\nalone, so this is a team sign-out, not a platform one.","tags":["team"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/cookieAck"}}},"description":"ok"}},"x-app":"team"},"put":{"operationId":"put_v1_team_account_cookie","summary":"Store the session token as this browser's cookie","description":"Writes the team session token into the HttpOnly `account-token` cookie — Secure, SameSite=Lax, whole-origin scope, thirty days — and answers {\"result\": true}. This is how the client turns the token it caught off the OAuth bounce into a credential page JS can no longer read, which IS the security property: script that cannot see the cookie cannot exfiltrate it, and every later call on the files, billing and collaborator planes authenticates from it when no bearer is sent.\n\nThe token is VERIFIED — signature and expiry, against this service's own signing secret — BEFORE it is stored. Anything this service did not sign is 401 and nothing is written; persisting a caller-supplied value unchecked would be a session-fixation door, where an attacker pins a cookie the victim's browser then presents as its own.\n\nThe token may arrive as `token` in the JSON body or, when the body is absent or unparseable, from the Authorization bearer — an unreadable body is NOT an error here. The sibling DELETE clears this same cookie and signs the browser out of team only: the IAM cookie set alongside it is a different credential with its own lifetime and is left alone.","tags":["team"],"x-app":"team"}},"/v1/team/account/providers":{"get":{"operationId":"get_v1_team_account_providers","summary":"Returns the identity providers this deployment starts a login with.","description":"Returns the identity providers this deployment starts a login\nwith. It is always exactly one — hanzo.id. Which identities that door accepts\n(Google, GitHub, passkey, password) is IAM's question, answered on IAM's own\npage next to the identity check and the training-data consent that must\nprecede a first session; listing them here would be a second place holding\nthat answer, and the two drift the moment IAM gains or drops one.","tags":["team"],"responses":{"200":{"content":{"application/json":{"example":[{"displayName":"Hanzo","name":"openid"}],"schema":{"items":{"$ref":"#/components/schemas/ProviderInfo"},"type":"array"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/billing/plan":{"get":{"operationId":"get_v1_team_billing_plan","summary":"Returns the plan and seat counts for the caller's OWN org, resolved from the VERIFIED team session token — never a client header.","description":"Returns the plan and seat counts for the caller's OWN org, resolved\nfrom the VERIFIED team session token — never a client header. Seats and guests\nare the org's distinct active human members (a bot member is not a seat); the\nplan comes from the licensing entitlement and is empty when that read is\nunavailable, so the page shows an honest dash rather than a fabricated tier. A\ncaller with no verified session gets 401, and a real seat-read failure is a\n502 rather than a false \"0 members\".","tags":["team"],"responses":{"200":{"content":{"application/json":{"example":{"active":true,"guestLimit":3,"guests":1,"plan":"pro","seats":3,"upgradeUrl":"https://billing.hanzo.ai"},"schema":{"$ref":"#/components/schemas/planInfo"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/billing/ui":{"get":{"operationId":"get_v1_team_billing_ui","summary":"Open the wallet page","description":"Serves the usage-and-wallet page the Team front links to — HTML, not JSON. It is a static React build compiled into this binary, so there is no upstream to be down and no build step at request time.\n\nSESSION-GATED: without a verified team session token — bearer, else the HttpOnly account cookie — the caller gets 401 and not one byte of the page, so an anonymous browser meets a refusal rather than a shell that then fails to load anything.\n\nThe page is markup only. It reads money SAME-ORIGIN from cloud's own balance and usage endpoints, which pin the org from the IAM cookie the team OAuth callback set — nothing under this path proxies a money read, so there is no second auth mechanism here to get wrong. A deployment whose page was never built answers 503 naming the missing bundle, never a blank 200.","tags":["team"],"x-app":"team"}},"/v1/team/billing/ui/{wildcard1}":{"get":{"operationId":"get_v1_team_billing_ui_by_wildcard1","summary":"Load an asset of the wallet page","description":"Serves one file of the embedded wallet bundle — a content-hashed script or stylesheet under assets/, an icon, or the page shell itself.\n\nA path that names NO REAL FILE falls back to the shell instead of 404ing, which is what makes a deep link into the page's own routes survive a hard refresh. So a 200 here is not proof the asset exists — a typo answers HTML.\n\nassets/ is immutable for a year (the names carry the content hash); the shell is no-cache, so a deploy is picked up on the next load. Gated exactly like the page: 401 without a verified session, 503 when the bundle was never built.","tags":["team"],"parameters":[{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/team/bots":{"get":{"operationId":"get_v1_team_bots","summary":"Returns the caller org's bot members — the org's agents projected as the workspace Employees they become, each with the member account uuid and Person reference the roster addresses it by.","description":"Returns the caller org's bot members — the org's agents projected as\nthe workspace Employees they become, each with the member account uuid and\nPerson reference the roster addresses it by. An agents subsystem that is not\nmounted answers an empty list, never an error.","tags":["team"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/botRoster"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/bots/sync":{"post":{"operationId":"post_v1_team_bots_sync","summary":"SyncBots re-projects the caller org's agents as workspace members into EVERY workspace of the org, and removes the ones whose agent is gone.","description":"SyncBots re-projects the caller org's agents as workspace members into EVERY\nworkspace of the org, and removes the ones whose agent is gone. It is\nidempotent, and admin only: mutating a workspace's roster requires the\ngateway-minted admin flag, which a client can never forge. It answers how many\nroster entries the reconcile touched.","tags":["team"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/botSync"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/files/{workspace}":{"post":{"operationId":"post_v1_team_files_by_workspace","summary":"Upload a file into a workspace","description":"Stores one file in a workspace's blob store and answers the blob id it is addressable by, as plain text — the front discards that body, it is there for a caller driving this by hand.\n\nThe body is a multipart form with a `file` part, and THAT PART'S FILENAME IS THE BLOB ID: the client mints it (a uuid v4) and the server stores under it, so a part whose filename is not a uuid is refused rather than assigned one. A file over 100 MiB is 413 and an empty one is 400.\n\nThe caller must hold a verified session or workspace token AND be a member of the workspace; an unknown workspace, another tenant's workspace and a workspace the caller is not in all answer the same 404, so a probe learns nothing about what exists. The stored key embeds the verified org and the workspace, so an upload cannot land in another tenant's box whatever id it names. A storage backend that is unavailable fails closed with 502 rather than reporting a write it never made.","tags":["team"],"parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/team/files/{workspace}/{filename}":{"delete":{"operationId":"delete_v1_team_files_by_workspace_by_filename","summary":"Removes one blob from a workspace's file store.","description":"Removes one blob from a workspace's file store. The caller must\nhold a verified session AND be a member of the workspace; anything else — an\nunknown workspace, another tenant's workspace, a workspace the caller is not\nin — answers the same 404, so a probe learns nothing about what exists.\n\nIt is IDEMPOTENT: deleting a present or an absent blob both answer 204, so a\ndelete never confirms a blob's existence and a foreign blob id (a physical key\nthe caller can never name into another tenant's box) is a harmless no-op. A\nstorage backend that is unavailable fails closed with 502 rather than lying\nabout success.","tags":["team"],"parameters":[{"name":"workspace","in":"path","required":true,"description":"Workspace is the workspace uuid the blob belongs to, from the path.","schema":{"type":"string"},"example":"6579…"},{"name":"filename","in":"path","required":true,"description":"Filename is the last path segment, which the front sets to the blob id\nwhen it sends no explicit `file`.","schema":{"type":"string"}},{"name":"file","in":"query","required":false,"description":"File is the blob id, and wins over the path segment when both are present.","schema":{"type":"string"},"example":"0d4f…"}],"responses":{"204":{"description":"no content"}},"x-app":"team"},"get":{"operationId":"get_v1_team_files_by_workspace_by_filename","summary":"Download a workspace file","description":"Streams one blob's raw BYTES back — this is the read side of the workspace file store, not a JSON envelope around it.\n\nTHE BLOB IS NAMED BY THE `file` QUERY PARAMETER, NOT BY :filename. The path segment is only the name a browser saves the download under; a request without ?file= is a 400 no matter what the path says.\n\nThe Content-Type is derived from the STORED BYTES, never from the name: only png, jpeg, gif and webp, recognized by their magic bytes, are served inline under their true type, and everything else is served inert as application/octet-stream with an attachment disposition. Every response carries nosniff, so a file uploaded under an .svg or .html name cannot be talked into executing in a viewer's origin. Blobs are immutable, so a hit caches privately for a year.\n\nSame gate as the upload: verified token, membership of the workspace. A genuine miss, another tenant's workspace, a workspace the caller is not in, and a blob id belonging to a different workspace are ONE answer — 404 — because the physical key is org- and workspace-scoped and a foreign id is simply a key that does not exist. An unavailable backend is a 502, never an empty 200.","tags":["team"],"parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/team/transactor/api/v1/statistics":{"get":{"operationId":"get_v1_team_transactor_api_v1_statistics","summary":"Statistics returns the transactor's live sessions for the workspace the caller's credential names — the endpoint the front's workspace switcher and server panel poll on the transactor base.","description":"Statistics returns the transactor's live sessions for the workspace the caller's\ncredential names — the endpoint the front's workspace switcher and server panel\npoll on the transactor base. `token` carries the same two lanes the socket's path\nsegment does: a workspace UUID names the workspace and is authorized against the\nmembership rows, an HS256 workspace token names it in its signed claims.\nactiveSessions carries ONLY that one workspace, never another tenant's sessions.\nAn unverifiable credential, or one the caller is no member under, is 401.","tags":["team"],"parameters":[{"name":"token","in":"query","required":false,"description":"Token is the workspace token minted by selectWorkspace.","schema":{"type":"string"},"example":"eyJhbGciOiJIUzI1NiJ9…"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/statsOut"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/transactor/statistics":{"get":{"operationId":"get_v1_team_transactor_statistics","summary":"Statistics returns the transactor's live sessions for the workspace the caller's credential names — the endpoint the front's workspace switcher and server panel poll on the transactor base.","description":"Statistics returns the transactor's live sessions for the workspace the caller's\ncredential names — the endpoint the front's workspace switcher and server panel\npoll on the transactor base. `token` carries the same two lanes the socket's path\nsegment does: a workspace UUID names the workspace and is authorized against the\nmembership rows, an HS256 workspace token names it in its signed claims.\nactiveSessions carries ONLY that one workspace, never another tenant's sessions.\nAn unverifiable credential, or one the caller is no member under, is 401.","tags":["team"],"parameters":[{"name":"token","in":"query","required":false,"description":"Token is the workspace token minted by selectWorkspace.","schema":{"type":"string"},"example":"eyJhbGciOiJIUzI1NiJ9…"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/statsOut"}}},"description":"ok"}},"x-app":"team"}},"/v1/team/transactor/{token}":{"get":{"operationId":"get_v1_team_transactor_by_token","summary":"Open the workspace data-plane socket","description":"Upgrades to the WebSocket the Team client runs an entire workspace over: every frame is a ZAP envelope wrapping one JSON-RPC message — findAll/findOne reads against the workspace's documents, tx writes that broadcast to the other live sessions, hello negotiating JSON rather than msgpack. The response is a protocol upgrade, so there is no body to read.\n\nTHE PATH SEGMENT IS THE CREDENTIAL. It is the workspace token selectWorkspace minted — bearer-equivalent, and sitting in a URL that proxies and access logs record, which is exactly why it expires in twelve hours and is re-minted on demand rather than being long-lived like the session token. It is decoded and verified (signature and expiry) BEFORE the upgrade, so a bad one is a 401 and never a socket that is accepted and then dropped, and it must carry both an account and a workspace claim. Nothing ambient authorizes this socket: a WebSocket is exempt from CORS, so a cookie-borne credential would make the Origin check the only access control on the whole data plane.\n\nThe tenant is the token's SIGNED org claim and it keys every store path, so no header can name another workspace's data. The upgrade ALSO refuses a browser Origin outside the team surfaces with 403 — otherwise any page could open an authenticated socket with a token it lured out of a logged-in browser — while a request with no Origin at all is admitted, because that is what a non-browser client sends.\n\nOn connect the workspace's system spaces are seeded once and the roster is reconciled every time, so the org's human members and its bots are present as workspace people without a separate sync call.","tags":["team"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team"}},"/v1/templates":{"get":{"operationId":"get_v1_templates","summary":"Lists the public starter-kit catalog plus, for a validated caller, that org's own private kits.","description":"Lists the public starter-kit catalog plus, for a validated caller, that\norg's own private kits. No request field can widen the scope: the org comes\nfrom the validated principal, so an anonymous or cross-org caller structurally\nsees the public catalog only.","tags":["templates"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kitList"}}},"description":"ok"}},"x-app":"templates"},"post":{"operationId":"post_v1_templates","summary":"Creates a starter kit PRIVATE to the caller's org and answers 201 with the stored kit.","description":"Creates a starter kit PRIVATE to the caller's org and answers 201 with\nthe stored kit. The owner is stamped by the server, so a body \"org\" is never\ntrusted; publishing over a public-catalog slug is 409, so a slug still names\nexactly one kit.","tags":["templates"],"requestBody":{"content":{"application/json":{"example":{"framework":"Next.js 14","slug":"acme-portal","title":"Acme Internal Portal"},"schema":{"$ref":"#/components/schemas/publishKitIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StarterKit"}}},"description":"created"}},"x-app":"templates"}},"/v1/templates/{slug}":{"delete":{"operationId":"delete_v1_templates_by_slug","summary":"Deletes the caller org's OWN starter kit.","description":"Deletes the caller org's OWN starter kit. A slug they do not own is a\n404, never a delete: the DELETE binds org.","tags":["templates"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the starter kit to act on, from the path.","schema":{"type":"string"},"example":"acme-portal"}],"responses":{"204":{"description":"no content"}},"x-app":"templates"},"get":{"operationId":"get_v1_templates_by_slug","summary":"Returns one starter kit: the caller org's own by that slug, else the public catalog's.","description":"Returns one starter kit: the caller org's own by that slug, else the public\ncatalog's. A slug another org owns reads as not found.","tags":["templates"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the starter kit to act on, from the path.","schema":{"type":"string"},"example":"folio"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StarterKit"}}},"description":"ok"}},"x-app":"templates"},"put":{"operationId":"put_v1_templates_by_slug","summary":"Overwrites the caller org's OWN starter kit at the path slug, answering the stored kit.","description":"Overwrites the caller org's OWN starter kit at the path slug, answering\nthe stored kit. A slug they do not own is 404, never a create: the UPDATE binds\norg, so a PUT can never reach another org's kit.","tags":["templates"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the kit to replace, from the path.","schema":{"type":"string"},"example":"acme-portal"}],"requestBody":{"content":{"application/json":{"example":{"slug":"acme-portal","title":"Acme Internal Portal v2"},"schema":{"$ref":"#/components/schemas/replaceKitIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StarterKit"}}},"description":"ok"}},"x-app":"templates"}},"/v1/tools":{"get":{"operationId":"get_v1_tools","summary":"Lists every tool the caller's org and project can reach, from every source, each flagged with whether it is activated.","description":"Lists every tool the caller's org and project can reach, from every\nsource, each flagged with whether it is activated. This is the discovery\nsurface: one flat set of names spanning connector actions, user functions,\nzap-service routes, agents, skills and the org's own external MCP servers,\ndeduplicated by name so the highest-precedence source wins a collision. It\nlists; it does not call — dispatch is POST /v1/tools/call.","tags":["tools"],"parameters":[{"name":"source","in":"query","required":false,"description":"Source keeps only tools from one source — connector, function, zap-service,\nagent, skill or mcp. Empty keeps every source.","schema":{"type":"string"}},{"name":"activated","in":"query","required":false,"description":"Activated keeps only the tools activated for the caller's org and project,\nand only when it is exactly the string \"true\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/toolList"}}},"description":"ok"}},"x-app":"tools"}},"/v1/tools/activation":{"get":{"operationId":"get_v1_tools_activation","summary":"Reports which tools are switched on for the caller's org and project.","description":"Reports which tools are switched on for the caller's org and\nproject. Activation is what makes a tool dispatchable and what makes it visible\nto an agent, so this is the set the MCP tool list is drawn from — every other\ntool in the registry is discoverable but refused at call time.","tags":["tools"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/activationSet"}}},"description":"ok"}},"x-app":"tools"},"put":{"operationId":"put_v1_tools_activation","summary":"Switches tools on and off for the caller's org and project, and answers with the resulting activated set.","description":"Switches tools on and off for the caller's org and project, and\nanswers with the resulting activated set. It is the ONE write path that turns\nskills, plugins and connectors into callable tools — an unactivated tool is\nlisted by discovery but refused 403 at dispatch. Activate is applied before\nDeactivate, so a name in both lists ends up off. More than 256 toggles in one\nrequest is refused 413.","tags":["tools"],"requestBody":{"content":{"application/json":{"example":{"activate":["cloud_get_ping"],"deactivate":[]},"schema":{"$ref":"#/components/schemas/activationReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/activationSet"}}},"description":"ok"}},"x-app":"tools"}},"/v1/tools/call":{"post":{"operationId":"post_v1_tools_call","summary":"Runs one of the caller's activated tools and answers with its output.","description":"Runs one of the caller's activated tools and answers with its output.\n\nThis is the door onto the tool plane's DYNAMIC half — the half no build-time\ncatalogue can hold, because it is per-tenant: an org's connected connector\nactions, its authored skills, its agents and functions, and the tools of every\nexternal MCP server it registered. A tool's existence, its price and its\nactivation are all rows, not code, so they cannot be known until the caller is.\n\nOne policy, the registry's: resolve by precedence, refuse an unactivated tool\n403, settle a priced one through the x402 seam or fail closed 402, then\ndispatch to the winning source bound to the caller's own (org, project). One\nmetered unit, one audit record. A caller can only ever dispatch its own tools.\n\nDiscovery is GET /v1/tools — ?activated=true for the callable set.","tags":["tools"],"requestBody":{"content":{"application/json":{"example":{"arguments":{"channel":"#general","text":"hi"},"name":"slack_post_message"},"schema":{"$ref":"#/components/schemas/toolCall"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/toolResult"}}},"description":"ok"}},"x-app":"tools"}},"/v1/tools/catalog":{"get":{"operationId":"get_v1_tools_catalog","summary":"Lists the MCP servers the public registries publish, as we hold them: our canonical copy of registry.modelcontextprotocol.io, plus what we decided about each entry.","description":"Lists the MCP servers the public registries publish, as we hold\nthem: our canonical copy of registry.modelcontextprotocol.io, plus what we\ndecided about each entry.\n\nThis is the SHELF an org picks from. A listing with a streamable-http endpoint\ncan be enabled as-is — POST /v1/mcp/servers with its id — and its tools then\njoin the org's tool plane and the fleet's MCP door. A listing that only ships a\nstdio package needs a process to run it, which is why the transports are on\nevery entry rather than implied.\n\nHidden entries are absent: they are the ones we took off the shelf. A platform\nSuperAdmin sees them, because the same query answers \"what is on the shelf\" and\n\"what is in the catalog\" and two queries would drift apart.\n\nIt is PAGED — 50 by default, 200 at most. The public registry publishes tens of\nthousands of servers, so an unbounded answer is a twenty-megabyte response and a\nstorefront that renders in a minute. total is the whole match, not the page.","tags":["tools"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q matches the name, title or description, case-insensitively.","schema":{"type":"string"}},{"name":"featured","in":"query","required":false,"description":"Featured keeps only the listings we put on the front of the shelf, and only\nwhen it is exactly the string \"true\".","schema":{"type":"string"}},{"name":"official","in":"query","required":false,"description":"Official keeps only the vendors' OWN servers — not third-party copies of\nthem — and only when it is exactly the string \"true\".","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page: default 50, maximum 200. A value that is not a\npositive integer reads as the default.","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Offset skips that many listings.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcpCatalog"}}},"description":"ok"}},"x-app":"tools"}},"/v1/tools/catalog/sync":{"post":{"operationId":"post_v1_tools_catalog_sync","summary":"Pulls the public MCP registry into our canonical copy and reports what changed.","description":"Pulls the public MCP registry into our canonical copy and reports\nwhat changed. SuperAdmin only; every other caller is refused.\n\nIt is IDEMPOTENT: a listing is keyed by the publisher's own reverse-DNS name,\nso a second pass over an unchanged registry rewrites the same rows and reports\nadded=0, updated=0. It never deletes — a listing that vanishes upstream may be\none an org has already enabled, and dropping its description would not drop its\nserver. And it never touches CURATION: hidden, featured, an admin-set official\nand a logo survive every sync, because the write does not name those columns.","tags":["tools"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcpCatalogSync"}}},"description":"ok"}},"x-app":"tools"}},"/v1/tools/catalog/{id}":{"get":{"operationId":"get_v1_tools_catalog_by_id","summary":"Returns one catalog entry in full: the publisher's description, its repository and site, every package form with the runtime that launches it, and every hosted endpoint.","description":"Returns one catalog entry in full: the publisher's description, its\nrepository and site, every package form with the runtime that launches it, and\nevery hosted endpoint. It is what a branding page renders, and what tells a\ncaller whether the listing can be enabled here and now (a streamable-http\nremote) or needs somewhere to run first (a stdio package).\n\nA HIDDEN listing is not served to an org — a shelf that renders what it does\nnot list would be a way around the shelf — but is served to a SuperAdmin, who\nis the one deciding whether to put it back.","tags":["tools"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the listing, from the path. It is the publisher's reverse-DNS name\nwith its one slash written as an underscore — \"com.stripe_mcp\".","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPListing"}}},"description":"ok"}},"x-app":"tools"},"patch":{"operationId":"patch_v1_tools_catalog_by_id","summary":"Sets what WE say about one catalog entry — hidden, featured, official, logo — and answers with the stored listing.","description":"Sets what WE say about one catalog entry — hidden, featured,\nofficial, logo — and answers with the stored listing. SuperAdmin only; every\nother caller is refused.\n\nCuration is the half of a catalog row a sync cannot write, and this is the only\nthing that writes it. The upstream half is never editable here: a description\nthat disagreed with the publisher's would be a fork of their listing, and the\nnext sync would silently undo it.","tags":["tools"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the listing to curate, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"featured":true,"official":false},"schema":{"$ref":"#/components/schemas/curateReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPListing"}}},"description":"ok"}},"x-app":"tools"}},"/v1/traces/health":{"get":{"operationId":"get_v1_traces_health","summary":"How many spans this deployment holds for your org","description":"Reports the native trace store's live state for the calling tenant: the subsystem version and `spans`, the count actually held right now. Not a dependency probe — the store is in-process, so this answers 200 whenever the process is up.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`.","tags":["traces"],"x-app":"metrics"}},"/v1/traces/query":{"get":{"operationId":"get_v1_traces_query","summary":"Recent spans for your org over a time range","description":"Answers `{count, spans}`, newest first, filtered on each span's START time. `start` and `end` are nanosecond bounds where 0 — which is what an absent, empty or unparseable value becomes — means UNBOUNDED, so a malformed bound widens the listing instead of failing it. `limit` defaults to 100 when absent or non-positive.\n\nIt lists SPANS, not traces: several spans of one trace each count separately and each take a slot against `limit`. Assembling one trace is /v1/traces/trace. The tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`.","tags":["traces"],"x-app":"metrics"}},"/v1/traces/trace":{"get":{"operationId":"get_v1_traces_trace","summary":"Every span of one trace — the waterfall","description":"Answers `{spans}`: every span the org holds for the trace id in `id`, in the order they were appended, which is what a waterfall view renders. Unlike the other reads there is no count, no time range and no limit — a trace is addressed by id or not at all.\n\nAn id with no spans answers an EMPTY list, never a 404: the store cannot tell a trace that never existed from one whose spans retention has already dropped, so it does not pretend to. The tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`, and a trace id belonging to another org is simply not in this org's store.","tags":["traces"],"x-app":"metrics"}},"/v1/traces/write":{"post":{"operationId":"post_v1_traces_write","summary":"Append spans for your org","description":"Takes `{spans:[{traceId, spanId, parentId, name, startNs, endNs, attrs}]}`, appends each, and answers `{written}` — the number of spans sent. Every span is indexed by its trace id as it lands, which is what makes the waterfall read possible without a second store.\n\nTimes are NANOSECONDS since the Unix epoch. Retention is a bounded ring of 1048576 spans per org: past that the OLDEST are evicted to keep the newest 1048576, and the trace index is rebuilt — so a long-lived trace can lose its early spans while its later ones survive, and a waterfall read is best-effort against retention, not a guarantee.\n\nThe tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`. A body that does not decode is 400.","tags":["traces"],"x-app":"metrics"}},"/v1/tracker/projects":{"get":{"operationId":"get_v1_tracker_projects","summary":"Returns every tracker project in the caller's org, newest first.","description":"Returns every tracker project in the caller's org, newest first.\n\nA project is the board: it owns a KEY (the uppercase handle that prefixes every\nissue identifier, \"ENG-14\") and the issues filed under it. The listing is\norg-scoped server-side — the org is the validated bearer claim, never a\nclient-supplied header — so one org can never see another's boards.","tags":["tracker"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/trackerProject"},"type":"array"}}},"description":"ok"}},"x-app":"tracker"},"post":{"operationId":"post_v1_tracker_projects","summary":"Open a tracker board in your org","description":"Creates a board and returns it, including the KEY that will prefix every issue identifier filed under it — the same key GET, PATCH and DELETE address the board by, and the ENG in ENG-14.\n\n`name` is required. `key` is optional and is UPPERCASED: omit it and one is derived from the name — its first four letters and digits, or PRJ when that yields nothing usable. A key that is not 2-8 characters starting with a letter is 400.\n\nTHE KEY IS UNIQUE PER ORG AND A COLLISION IS REFUSED, NOT MERGED: a second board on a key already taken is 409, and the derived key is not made unique for you, so two similarly named boards collide and the second caller must name a key. Re-POSTing is therefore not idempotent — it fails rather than returning the existing board.\n\nThe org is the validated bearer's own, never a client header, and the board is stored under the caller's selected IAM PROJECT: the same key in two IAM projects is two unrelated boards. 403 without a validated org.\n\nFree by default. The create runs the shared per-org balance gate at a fee of zero unless a deployment prices it, and a priced deployment out of balance refuses with the nested {\"error\":{\"code\",\"message\"}} body at 402/503 rather than a flat error.","tags":["tracker"],"x-app":"tracker"}},"/v1/tracker/projects/{key}":{"delete":{"operationId":"delete_v1_tracker_projects_by_key","summary":"Removes one tracker project of the caller's org AND every issue filed under it, and answers 204 with no body.","description":"Removes one tracker project of the caller's org AND every issue\nfiled under it, and answers 204 with no body. 404 when the org has no project\nunder that key.\n\nThe cascade is the point: an issue has no meaning without the board whose key\nnames it, so deleting the board deletes them together rather than leaving\norphans addressable by an identifier that no longer resolves.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the project's org-unique handle: 2-8 uppercase alphanumerics starting\nwith a letter (\"ENG\", \"OPS2\"). Matched case-insensitively.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"tracker"},"get":{"operationId":"get_v1_tracker_projects_by_key","summary":"Returns one tracker project of the caller's org by its key — its name, description and timestamps.","description":"Returns one tracker project of the caller's org by its key —\nits name, description and timestamps. 404 when the org has no project\nunder that key.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the project's org-unique handle: 2-8 uppercase alphanumerics starting\nwith a letter (\"ENG\", \"OPS2\"). Matched case-insensitively.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trackerProject"}}},"description":"ok"}},"x-app":"tracker"},"patch":{"operationId":"patch_v1_tracker_projects_by_key","summary":"Renames a tracker project or rewrites its description, and returns the updated project.","description":"Renames a tracker project or rewrites its description, and\nreturns the updated project. Both fields are optional: one the caller omits\nkeeps its stored value.\n\nThe project KEY is never editable — it prefixes every issue identifier already\nfiled under the board, so changing it would rewrite the human handle of every\nissue in it.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the project to update, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"name":"Platform Engineering"},"schema":{"$ref":"#/components/schemas/projectPatch"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trackerProject"}}},"description":"ok"}},"x-app":"tracker"}},"/v1/tracker/projects/{key}/issues":{"get":{"operationId":"get_v1_tracker_projects_by_key_issues","summary":"Returns the issues of one tracker project, optionally filtered by status, kind, repo, source and whether they are scheduled.","description":"Returns the issues of one tracker project, optionally filtered by\nstatus, kind, repo, source and whether they are scheduled.\n\nThis is the ONE place a surface takes its slice of the shared issue table: the\nboard passes no filter or a status, the timeline passes scheduled=true, a git\nrepository's Issues tab passes kind=issue\u0026repo=\u003cr\u003e and its Pull Requests tab\nkind=pr\u0026repo=\u003cr\u003e. A filter value outside its closed set is refused with 400\nrather than silently returning an empty board.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the project whose issues to list, from the path.","schema":{"type":"string"},"example":"ENG"},{"name":"status","in":"query","required":false,"description":"Status keeps only issues in that board column: backlog, todo, in_progress,\ndone or canceled. An unknown value is refused with 400.","schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"description":"Kind keeps only work items of that shape: issue, pr or epic. An unknown\nvalue is refused with 400.","schema":{"type":"string"},"example":"pr"},{"name":"repo","in":"query","required":false,"description":"Repo keeps only issues bound to that git repository.","schema":{"type":"string"},"example":"hanzoai/cloud"},{"name":"source","in":"query","required":false,"description":"Source keeps only issues opened from that surface: team, git, crm,\nhelpdesk, cms or agent. An unknown value is refused with 400.","schema":{"type":"string"}},{"name":"scheduled","in":"query","required":false,"description":"Scheduled keeps only issues that carry a date — a start, a due date or\nboth. This is the timeline's slice of the board: pass scheduled=true to\nget exactly the rows a gantt has somewhere to draw, instead of fetching\nevery issue and discarding the undated ones client-side.","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/issueView"},"type":"array"}}},"description":"ok"}},"x-app":"tracker"},"post":{"operationId":"post_v1_tracker_projects_by_key_issues","summary":"File an issue on a tracker board","description":"Files a work item on one board and returns it, carrying the `identifier` — KEY-\u003cnumber\u003e — it will be known by everywhere else.\n\nTHE NUMBER IS THE SERVER'S TO ASSIGN and is not accepted from the caller: it is the board's highest plus one, taken inside the insert's own transaction, and it counts PER BOARD — ENG-1 and OPS-1 are different issues.\n\n`title` is required; everything else is optional and defaults. `kind` (issue, pr, epic) says what the item IS, `source` (team, git, crm, helpdesk, cms, agent) says which surface OPENED it, and the two are orthogonal — an issue escalated from support is kind=issue\u0026source=helpdesk. `status` defaults to backlog, `priority` to none. A value outside one of these closed sets is 400, never silently defaulted. `labels` may not contain a comma, the storage separator.\n\n`startAt` and `dueAt` place the item on the TIMELINE, in unix seconds, and both default to unset. A due date on its own is a milestone — an interval of zero length — and the two together are a bar; a start with no due date is work under way with no deadline. There is no separate milestone resource: a milestone is this row, dated. A negative bound, or a due date before its start, is 400 rather than a silently reordered interval.\n\n`repo` and `extRef` RECORD an external binding; they do not create one. Filing here writes to your tracker and reaches no external system — nothing is pushed to GitHub. The GitHub integration runs the other way, mirroring upstream issues INTO this tracker.\n\n404 when the caller's org has no board under that key. The org is the validated bearer's own and the board is resolved within the caller's selected IAM project; 403 without a validated org. Free by default, on the same balance gate as the board create — an epic, a pull request and an issue are priced identically, since the fee is per work item rather than per kind.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"tracker"}},"/v1/tracker/projects/{key}/issues/{num}":{"delete":{"operationId":"delete_v1_tracker_projects_by_key_issues_by_num","summary":"Removes one issue from a tracker project and answers 204 with no body.","description":"Removes one issue from a tracker project and answers 204 with no\nbody. 404 when the project or the issue does not exist in the caller's org.\n\nThe issue's number is NOT reused: the next issue on the board takes the next\nnumber, so a deleted identifier stays retired rather than silently pointing at\ndifferent work.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the issue's project, from the path.","schema":{"type":"string"}},{"name":"num","in":"path","required":true,"description":"Num is the issue's number within that project — the digits of KEY-14.\nPositive; anything else is refused with 400.","schema":{"type":"integer"}}],"responses":{"204":{"description":"no content"}},"x-app":"tracker"},"get":{"operationId":"get_v1_tracker_projects_by_key_issues_by_num","summary":"Returns one issue of one tracker project by its per-project number — title, description, status, priority, assignee, labels, kind, source and its git bindings.","description":"Returns one issue of one tracker project by its per-project number —\ntitle, description, status, priority, assignee, labels, kind, source and its\ngit bindings. 404 when the project or the issue does not exist in the caller's\norg.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the issue's project, from the path.","schema":{"type":"string"}},{"name":"num","in":"path","required":true,"description":"Num is the issue's number within that project — the digits of KEY-14.\nPositive; anything else is refused with 400.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueView"}}},"description":"ok"}},"x-app":"tracker"},"patch":{"operationId":"patch_v1_tracker_projects_by_key_issues_by_num","summary":"Edits one issue in place and returns it — retitle it, rewrite its body, move it between board columns, reprioritize, reassign, reschedule, or replace its labels.","description":"Edits one issue in place and returns it — retitle it, rewrite its\nbody, move it between board columns, reprioritize, reassign, reschedule, or\nreplace its labels. Every field is optional: one the caller omits keeps its\nstored value, and `labels` REPLACES the set rather than adding to it.\n\n`startAt` and `dueAt` are the issue's place on the timeline, in unix seconds;\n0 clears one. They are validated as the interval they RESULT in, so moving\nonly the due date is still checked against the stored start — a due date\nbefore its start is 400, never a bar drawn backwards.\n\nThe issue's kind, source and git bindings are not editable here: they record\nwhere the work item came FROM, which is a fact about its origin rather than\nits current state.","tags":["tracker"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the issue's project, from the path.","schema":{"type":"string"},"example":"ENG"},{"name":"num","in":"path","required":true,"description":"Num is the issue's number within that project, from the path.","schema":{"type":"integer"},"example":14}],"requestBody":{"content":{"application/json":{"example":{"assignee":"z","key":"ENG","num":14,"status":"in_progress"},"schema":{"$ref":"#/components/schemas/issuePatch"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueView"}}},"description":"ok"}},"x-app":"tracker"}},"/v1/traffic/globe":{"get":{"operationId":"get_v1_traffic_globe","summary":"Returns the PUBLIC live request-geo aggregate for the world.hanzo.ai \"Hanzo mode\" globe: WHERE requests to api.hanzo.ai are coming from, as country/region points with per-service-class counts, plus headline throughput rates.","description":"Returns the PUBLIC live request-geo aggregate for the\nworld.hanzo.ai \"Hanzo mode\" globe: WHERE requests to api.hanzo.ai are coming from,\nas country/region points with per-service-class counts, plus headline throughput\nrates.\n\nIt is AUTH-exempt and BALANCE-exempt exactly like /v1/router/stats?scope=platform:\n  - auth: the controller name \"traffic/globe\" is neither a get-/update- CRUD name\n    nor a super-admin/present-credential endpoint, so the authz filter passes it\n    through, and this handler requires no principal.\n  - balance: isBalanceExempt(\"/v1/traffic/...\") returns true.\n\nIt exposes ONLY aggregates — counts, rates, and country/region centroids — and\nNEVER any IP, per-request row, org, or user dimension (see object/traffic.go).\nMarketing telemetry; nothing sensitive.","tags":["traffic"],"x-app":"github.com/hanzoai/ai"}},"/v1/translate":{"post":{"operationId":"post_v1_translate","summary":"Translate a string or a batch into one target language","description":"Returns one translation per input string, in input order, each carrying where it sits on the review ladder and whether it came from your memory rather than an engine — plus a usage block of REAL counts (strings, cached, translated, and the source characters that actually reached an engine). Send `text` for one string or `batch` for many, never both. When you name no `source`, the detected one is reported back.\n\nTHE TRANSLATION MEMORY IS CONSULTED FIRST AND IT IS NORMATIVE, NOT A CACHE. Every string keys on (source text, target, glossary version, tier); a hit is returned VERBATIM and never re-translated, which is what makes a locale rebuild idempotent under a non-deterministic model and the bill proportional to what actually changed. Misses go to the engine and are written back at state `machine`. Editing a glossary term changes the key, so a stale rendering can never be served.\n\nIT CANNOT TRAMPLE REVIEWED WORK. A write from this route may create an entry or refresh one still at `machine`, and nothing else — a string a human moved to approved or published through the memory review lane survives every rebuild, and comes back here unchanged. The memory is the caller's OWN org's, a separate store per org: the source text you send is customer content and lands nowhere else. Read it back or review it at /v1/translate/memory.\n\n`tier` picks the engine and defaults to quality — the model plane, which carries context, terminology and tone, and which bills its own tokens, so nothing is charged twice here. `bulk` is the high-volume engine and is metered HERE, on the source characters that reached it: a fully-cached rebuild reports zero characters and costs zero. BULK NEVER FALLS BACK TO QUALITY — on a deployment that does not serve it the answer is 503 for that tier, so a caller is never quietly served, or charged, at a tier it did not ask for. A bulk request beyond its balance is refused with the nested {\"error\":{\"code\",\"message\"}} body at 402/503.\n\n`target` IS CHECKED FOR SHAPE, NOT FOR SUPPORT: anything BCP-47-shaped is accepted (`es`, `pt-BR`), anything else is 400. There is no unsupported-language error — a well-formed tag no engine can actually render is passed straight through, and whatever comes back is what gets stored and returned. `format` (text, html, markdown) tells the engine what markup to preserve; `glossary` fixes terms verbatim.\n\nRequires a validated principal — 401 without one, and the org is always that principal's. At most 512 strings per call and 32768 characters per string; an engine that fails or answers a reply that does not cover every input is 502, and nothing is stored.","tags":["translate"],"x-app":"translate"}},"/v1/translate/memory":{"get":{"operationId":"get_v1_translate_memory","summary":"List returns the org's own translation-memory entries, newest first, optionally narrowed to one target language and/or one position on the review ladder.","description":"List returns the org's own translation-memory entries, newest first, optionally\nnarrowed to one target language and/or one position on the review ladder. It is\nthe review lane's read: what a human reviewer works through.\n\nThe org is ALWAYS the validated principal's org, never a request field, so one\ntenant can never read another's memory — the entries hold customer source text.","tags":["translate"],"parameters":[{"name":"target","in":"query","required":false,"description":"Target narrows to one target language tag (BCP-47, e.g. \"es\" or \"pt-BR\").","schema":{"type":"string"}},{"name":"state","in":"query","required":false,"description":"State narrows to one position on the review ladder: machine, suggested,\napproved or published.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. Non-positive or unparseable means the server\ndefault (200); the ceiling is 1000.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryPage"}}},"description":"ok"}},"x-app":"translate"},"put":{"operationId":"put_v1_translate_memory","summary":"Review records a human decision on one translation-memory entry, and returns the entry as stored.","description":"Review records a human decision on one translation-memory entry, and returns the\nentry as stored. A human write always wins over the stored value, and once it lands\nat approved or published no machine write can move it again — which is what makes a\nlocale rebuild safe to run against reviewed work.\n\nThe org is ALWAYS the validated principal's org, never a request field, so a review\ncan only ever land in the caller's own memory.","tags":["translate"],"requestBody":{"content":{"application/json":{"example":{"source":"Hello","state":"approved","target":"es","text":"Hola"},"schema":{"$ref":"#/components/schemas/ReviewRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryEntry"}}},"description":"ok"}},"x-app":"translate"}},"/v1/upload":{"post":{"operationId":"post_v1_upload","summary":"Upload a file into an execution session","description":"Takes a multipart upload and writes the file into the session's sandbox, so a later run can read it. Answers the session id and the identifier the file is addressed by; `session_id` in the form joins an existing session instead of opening one.\n\nThe body is multipart/form-data, which is why this is not a typed operation: every non-empty typed body is decoded as JSON.","tags":["upload"],"x-app":"exec"}},"/v1/usage":{"post":{"operationId":"post_v1_usage","summary":"Ingests a batch of account-usage samples — what a developer's OWN AI accounts have consumed of their OWN plans, metered from each provider's own login — and appends them to the warehouse series.","description":"Ingests a batch of account-usage samples — what a developer's OWN AI\naccounts have consumed of their OWN plans, metered from each provider's own\nlogin — and appends them to the warehouse series. Answers 202.\n\nSend either a `samples` array or one sample's fields at the top level. Every\nsample needs a provider, a machine and a known window class; an unknown window or\nkind is refused rather than silently rewritten, because a dash filled with a class\nnobody reported is worse than an error. There is no timestamp field: the server\nowns the observation clock, and a sample says which window it measured with\nwindowStart or resetsAt.\n\nIt is FAIL-SOFT on storage: a warehouse outage costs a poll of history\n(stored:false), never a failed request. It records usage ONLY — the link registry\nis refreshed separately via POST /v1/links, so there is one and only one way to\nupdate an account row.","tags":["usage"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reportReq"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reportResp"}}},"description":"accepted"}},"x-app":"usage"}},"/v1/usage/activity":{"get":{"operationId":"get_v1_usage_activity","summary":"Activity returns the per-day usage series for ONE authorized subject — the points a contribution heatmap and a timeline are drawn from, gap-filled so every day in the range is present.","description":"Activity returns the per-day usage series for ONE authorized subject — the points a\ncontribution heatmap and a timeline are drawn from, gap-filled so every day in the\nrange is present. Authorization is resolved server-side from the validated\nprincipal, so a caller can never widen the subject past what they are entitled to:\na non-admin reads only themselves and their own org. subject=project answers empty\nwith a note, because the usage ledger records no project column yet. When the\nwarehouse is not connected the series answers empty with available=false rather\nthan fabricated days.","tags":["usage"],"parameters":[{"name":"subject","in":"query","required":false,"description":"Subject is what the series is about: \"user\" (default), \"org\" or \"project\".","schema":{"type":"string"},"example":"user"},{"name":"id","in":"query","required":false,"description":"ID names the subject within what the caller is entitled to see. Omitted (or\n\"me\") it is the caller themselves, or their own org. Another user requires org\nadmin and must belong to the caller's org; another org requires a SuperAdmin.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"From is the first day of the range, \"2006-01-02\". Defaults to 90 days back.","schema":{"type":"string"},"example":"2026-01-01"},{"name":"to","in":"query","required":false,"description":"To is the last day of the range, \"2006-01-02\". Defaults to today; the span is\nclamped to 366 days.","schema":{"type":"string"},"example":"2026-03-31"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityView"}}},"description":"ok"}},"x-app":"leaderboard"}},"/v1/usage/analytics":{"get":{"operationId":"get_v1_usage_analytics","summary":"Is the entitlement-GATED per-provider breakdown of the caller org's LLM usage — the paid lens over the same warehouse ledger GET /v1/usage/summary reads its totals from.","description":"Is the entitlement-GATED per-provider breakdown of the caller org's LLM\nusage — the paid lens over the same warehouse ledger GET /v1/usage/summary reads\nits totals from. Basic own-org usage stays ungated at /v1/usage/summary.\n\nA plan that does not grant the analytics datastore is refused with 402, and an\nunresolvable plan fails closed to the free floor, which does not grant it. The\nwindow is clamped forward to the plan's retention entitlement, so a tenant can\nnever read older than its plan allows even with a custom start. The response is\nmarked no-store.\n\nINTERIM (mirrors apps/world's limits echo): no org→plan resolver exists in cloud\nyet — the subscription lookup is owned by the billing plane and the gateway\nprincipal carries no plan claim — so the caller passes the plan and the gate\nresolves THAT plan's access.","tags":["usage"],"parameters":[{"name":"end","in":"query","required":false,"description":"End is the exclusive window end, RFC3339. Read only when Range is custom.","schema":{"type":"string"}},{"name":"plan","in":"query","required":false,"description":"Plan is the plan id whose entitlement decides access and retention. INTERIM:\ncloud has no org-to-plan resolver yet, so the caller names the plan; when\nthat resolver lands this becomes the caller org's own plan.","schema":{"type":"string"}},{"name":"range","in":"query","required":false,"description":"Range is the window: 24h, 7d, 30d, or custom. Empty means 24h.","schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the inclusive window start, RFC3339. Read only when Range is\ncustom, and clamped forward to the plan's retention floor.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/usageAnalyticsView"}}},"description":"ok"}},"x-app":"usage"}},"/v1/usage/analytics/access":{"get":{"operationId":"get_v1_usage_analytics_access","summary":"Echoes a plan's resolved analytics entitlement so a dashboard can configure itself against the LIVE catalog instead of hardcoding tier numbers.","description":"Echoes a plan's resolved analytics entitlement so a dashboard can\nconfigure itself against the LIVE catalog instead of hardcoding tier numbers. An\nempty plan resolves the free floor, and a catalog resolution failure serves that\nsame floor rather than erroring — so this always answers 200. It is a read-only\ncontract echo and carries no tenant data.","tags":["usage"],"parameters":[{"name":"plan","in":"query","required":false,"description":"Plan is a plan id from the live @hanzo/plans catalog. Empty resolves the\nfree floor, and so does an id the catalog does not know — this never fails\non an unknown plan.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/usageAnalyticsAccess"}}},"description":"ok"}},"x-app":"usage"}},"/v1/usage/leaderboard":{"get":{"operationId":"get_v1_usage_leaderboard","summary":"Leaderboard ranks AI usage over a window, either the users of the caller's own org or organizations against each other, and always reports the caller's own standing even when it falls outside the returned page.","description":"Leaderboard ranks AI usage over a window, either the users of the caller's own org\nor organizations against each other, and always reports the caller's own standing\neven when it falls outside the returned page. Identities are private by default: a\ncaller sees themselves, plus the peers or orgs that opted into public listing, and\nonly an admin sees their own org's members named. Cross-org spend is restricted to\nplatform admins. When the warehouse is not connected the board answers empty with\navailable=false rather than a fabricated rank.","tags":["usage"],"parameters":[{"name":"scope","in":"query","required":false,"description":"Scope picks the board: \"personal\" (default) ranks the caller among their own\norg's users, \"org\" is that same org board named for an admin, \"global\" ranks\norganizations against each other.","schema":{"type":"string"},"example":"personal"},{"name":"metric","in":"query","required":false,"description":"Metric is the value ranked: tokens (default), requests, or cost.","schema":{"type":"string"},"example":"tokens"},{"name":"period","in":"query","required":false,"description":"Period is the window ranked: day, week, month (default) or all.","schema":{"type":"string"},"example":"week"},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned, clamped to 100. Defaults to 10, which is also\nwhat a non-positive or unparseable value takes.","schema":{"type":"integer"},"example":10}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaderboardView"}}},"description":"ok"}},"x-app":"leaderboard"}},"/v1/usage/leaderboard/optin":{"get":{"operationId":"get_v1_usage_leaderboard_optin","summary":"Returns the caller's own public-listing preference and their org's, each with whether the caller may change it.","description":"Returns the caller's own public-listing preference and their org's,\neach with whether the caller may change it. Public listing is opt-in and private\nby default, so a fresh caller reads listed=false for both.","tags":["usage"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/optinView"}}},"description":"ok"}},"x-app":"leaderboard"},"put":{"operationId":"put_v1_usage_leaderboard_optin","summary":"Sets the CALLER's own public-listing preference on the leaderboard.","description":"Sets the CALLER's own public-listing preference on the leaderboard.\nSelf only: the row written is keyed by the caller's validated ledger identity, so\nthis can never edit another member's visibility whatever the request says. A\ncaller opting in with no handle is given their username, so a listed row never\nrenders as \"Anonymous\" to its own owner.","tags":["usage"],"requestBody":{"content":{"application/json":{"example":{"handle":"ada","listed":true},"schema":{"$ref":"#/components/schemas/userOptinReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/userOptinView"}}},"description":"ok"}},"x-app":"leaderboard"}},"/v1/usage/leaderboard/optin/org":{"put":{"operationId":"put_v1_usage_leaderboard_optin_org","summary":"Sets the ORG's listing on the cross-org global board.","description":"Sets the ORG's listing on the cross-org global board. Only an admin of\nthe caller's own org — an org admin or a platform SuperAdmin — may change it, and\nthe org written is the caller's validated tenant, never a value from the request.\nListing consents to publishing the org's usage VOLUME; cross-org spend stays\nrestricted to platform admins regardless.","tags":["usage"],"requestBody":{"content":{"application/json":{"example":{"display":"Acme","listed":true},"schema":{"$ref":"#/components/schemas/orgOptinReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/orgOptinView"}}},"description":"ok"}},"x-app":"leaderboard"}},"/v1/usage/rollup/backfill":{"post":{"operationId":"post_v1_usage_rollup_backfill","summary":"Backfill seeds the derived usage rollup from ledger history — the rows written before the incremental view existed, which that view can never capture.","description":"Backfill seeds the derived usage rollup from ledger history — the rows written\nbefore the incremental view existed, which that view can never capture. SuperAdmin\nonly. Because the rollup accumulates, a second unguarded run would double every\nday it re-reads, so it refuses with 409 when the rollup already holds rows unless\nforce=true is passed; forcing WILL double-count.","tags":["usage"],"requestBody":{"content":{"application/json":{"example":{"before":"2026-01-01T00:00:00Z"},"schema":{"$ref":"#/components/schemas/backfillQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/backfillResult"}}},"description":"ok"}},"x-app":"leaderboard"}},"/v1/usage/samples":{"get":{"operationId":"get_v1_usage_samples","summary":"Is the PER-PROVIDER view: one connected account's own consumption of its own plan — \"my plan is 47% through its 6h window, resets at 14:20\".","description":"Is the PER-PROVIDER view: one connected account's own consumption of its\nown plan — \"my plan is 47% through its 6h window, resets at 14:20\".\n\n`current` is the newest instance of each lane (the headline); `windows` is the\nhistory behind it. Both come from ONE deduped read, so they can never disagree.\nThe rows are the caller's OWN linked accounts, scoped to the validated principal\nand its subject — never another user's, and never another org's.","tags":["usage"],"parameters":[{"name":"account","in":"query","required":false,"description":"Account narrows to ONE linked account of that provider. Empty covers every\naccount the caller has linked there.","schema":{"type":"string"}},{"name":"provider","in":"query","required":false,"description":"Provider is the upstream to read, e.g. anthropic. Required.","schema":{"type":"string"}},{"name":"range","in":"query","required":false,"description":"Range is the window to read: 1h, 24h, 7d or 30d. Empty means 24h, and any\nother label is refused rather than silently replaced.","schema":{"type":"string"}},{"name":"window","in":"query","required":false,"description":"Window narrows to ONE window class: 6h, day, week or month. Empty covers\nevery class.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dashResp"}}},"description":"ok"}},"x-app":"usage"}},"/v1/usage/summary":{"get":{"operationId":"get_v1_usage_summary","summary":"Answers GET /v1/usage/summary: the caller's own usage footprint over one window — the categorized spend roll-up from the commerce ledger, the org's LLM usage totals from the warehouse, and the caller's OWN linked provider accounts beside the org's Hanzo-routed usage.","description":"Answers GET /v1/usage/summary: the caller's own usage footprint over one\nwindow — the categorized spend roll-up from the commerce ledger, the org's LLM\nusage totals from the warehouse, and the caller's OWN linked provider accounts\nbeside the org's Hanzo-routed usage.\n\nEvery source degrades INDEPENDENTLY to honest zeros and says so in `sources` and\nin its own `available` flag, so a partial deploy reports \"no data\" rather than\nfabricating spend. The account rows and the Hanzo rows are concatenated and never\nsummed: a plan's percent is not money.\n\nThe response is org-scoped from the validated principal and marked no-store — a\nsigned-out caller is refused.","tags":["usage"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is the window: 24h, 7d, 30d, or custom. Empty means 24h. A label this\nsurface does not know is refused rather than silently replaced.","schema":{"type":"string"}},{"name":"start","in":"query","required":false,"description":"Start is the inclusive window start, RFC3339. Read only when Range is\ncustom.","schema":{"type":"string"}},{"name":"end","in":"query","required":false,"description":"End is the exclusive window end, RFC3339. Read only when Range is custom.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/usageSummary"}}},"description":"ok"}},"x-app":"usage"}},"/v1/validators":{"get":{"operationId":"get_v1_validators","summary":"Returns the validator slots the caller's org has claimed.","description":"Returns the validator slots the caller's org has claimed.\n\nOne entry per claimed slot with its node identity, its live-ish node status and\nthe owner-gated registration queued for it, if any. Slots are org-scoped by the\nvalidated identity, so a caller can only ever see their own — a slot claimed by\nanother org is not merely hidden from this list, it is unreachable through the\nwhole surface.","tags":["validators"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit is how many slots to return, as a decimal string in the `?limit=`\nquery. Absent, unparseable or non-positive means 200; over 1000 is clamped\nto 1000. It is a string rather than a number because the parse that has\nalways served this route trims surrounding whitespace, and one parse rule is\nbetter than two.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/validatorList"}}},"description":"ok"}},"x-app":"validators"},"post":{"operationId":"post_v1_validators","summary":"Claims a validator slot and provisions its node, after proving the caller's wallet owns the slot's NFT.","description":"Claims a validator slot and provisions its node, after proving the\ncaller's wallet owns the slot's NFT.\n\nThe pipeline, all server-enforced: burn the single-use challenge (so a replayed\nor forged nonce dies before any chain read), recover the signer from the message\nthis server rebuilds, require that wallet to hold Validator-tier GenesisNFT\n#tokenId on Ethereum mainnet, generate a fresh luxd staking identity and seal it\ninto KMS, write a LuxNetwork CR for a NEW node, and ENQUEUE an owner-gated\nregistration. The registration is never auto-submitted to any P-Chain — the\nowner co-signs it out of band — and the stake weight is set at co-sign time,\nnever derived from the NFT.\n\nIt fails CLOSED at every gate: a bad signature, a non-owner, a non-tier slot or\nan unavailable KMS all leave no claim persisted and no key material exposed.\nRe-claiming a slot this org already holds re-applies the node CR and returns 200\nwith the existing identity (keys and NodeID are stable); a slot held by another\norg is 409. A cluster-less deployment still claims the slot, seals the keys and\nqueues the registration, reporting the node as \"node_pending\".","tags":["validators"],"requestBody":{"content":{"application/json":{"example":{"nonce":"5f3a…","signature":"0x…","tokenId":7},"schema":{"$ref":"#/components/schemas/validatorClaim"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/slotView"}}},"description":"ok"}},"x-app":"validators"}},"/v1/validators/challenge":{"get":{"operationId":"get_v1_validators_challenge","summary":"Issues the single-use nonce and the exact message a wallet must sign to claim a validator slot.","description":"Issues the single-use nonce and the exact message a wallet must sign\nto claim a validator slot.\n\nThe nonce is bound to (validated org, slot) and stored server-side, so a\nsignature obtained for one org or one slot can never be replayed for another,\nand the message POST /v1/validators verifies is rebuilt from those same server\nfacts rather than trusted from the caller. Redeem it with\nPOST /v1/validators before it expires; it can be redeemed once.\n\nA tokenId outside the Validator tier is refused here rather than after signing.","tags":["validators"],"parameters":[{"name":"tokenId","in":"query","required":false,"description":"TokenID is the Validator-tier GenesisNFT token id, as a decimal string in\nthe `?tokenId=` query. A value that is not a positive integer is 400. It is\na string rather than a number because the parse that has always served this\nroute trims surrounding whitespace, and one parse rule is better than two.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/challengeView"}}},"description":"ok"}},"x-app":"validators"}},"/v1/validators/{tokenId}":{"get":{"operationId":"get_v1_validators_by_tokenid","summary":"Returns one claimed validator slot, scoped to the caller's org.","description":"Returns one claimed validator slot, scoped to the caller's org.\n\nA slot another org holds, and a slot nobody holds, are both 404 — never a\ndifferent status, so this route cannot be used to probe which slots are taken.","tags":["validators"],"parameters":[{"name":"tokenId","in":"path","required":true,"description":"TokenID is the slot's GenesisNFT token id, from the path, as a decimal\nstring. A value that is not a positive integer is 400. It is a string\nrather than a number because the parse that has always served this route\ntrims surrounding whitespace, and one parse rule is better than two.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/slotView"}}},"description":"ok"}},"x-app":"validators"}},"/v1/vector":{"get":{"operationId":"get_v1_vector","summary":"Lists the caller org's vector collections.","description":"Lists the caller org's vector collections. A collection is a\nlogical resource inside an already-live shared backend, so every one of them\nis reached through the public gateway rather than at an instance of its own.","tags":["vector"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning"},"post":{"operationId":"post_v1_vector","summary":"Provision a vector collection for your org","description":"Creates a vector collection inside the already-running shared vector backend and answers with the endpoint that reaches it.\n\n`name` is the org-unique slug every physical name derives from, and must match ^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. `instance` optionally BINDS the add-on to one of your app instances: the DSN is injected into that instance's addons secret as \u003cKIND\u003e_URL, switching the app off its built-in store and onto this one. Omit it and the connection string is yours to wire.\n\nTHE CREDENTIAL COMES BACK ONCE. The connection string and password are in this response and nowhere else — every read beside it omits the password — so a caller that does not keep them has to provision again. Where KMS is configured the password is sealed there and only a reference is persisted; where it is not, it is returned this once and stored nowhere. It is never held in plaintext.\n\nScoped to the caller's validated org (403 without one), which also namespaces the physical resource under a fixed-width hash, so two tenants can never fold onto one backend resource — a residual collision fails closed with 409 rather than silently sharing. A name already taken in your org is 409; an invalid name or instance slug is 400; a backend that refuses the create is 502. Where a later step fails after the backend resource already exists, it is torn back down rather than left orphaned.\n\nBilling is gated BEFORE anything is created: an unfunded org — or, in the fail-closed default, an unreachable meter — gets the fleet-wide 402/503 and nothing is provisioned. The fee is per-kind and set by the deployment.","tags":["vector"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"Success"}},"x-app":"provisioning"}},"/v1/vector/collections":{"get":{"operationId":"get_v1_vector_collections","summary":"Lists the vector collections with their size and geometry.","description":"Lists the vector collections with their size and geometry.\n\nIt reads the in-cluster Qdrant service: the collection list, then each\ncollection's detail for its point count, vector dimension and distance metric.\nPer-collection detail is best-effort — one collection that fails to describe\nitself keeps its name and defaults (dimension 0, cosine) rather than blanking\nthe whole panel — and an unreachable Qdrant answers 200 with an EMPTY list.","tags":["vector"],"parameters":[{"name":"Authorization","in":"header","required":false,"description":"Authorization carries the surface's bearer key (`Bearer \u003ckey\u003e`); the bare\nkey is accepted too. Search and vector are two surfaces with two keys.\nIt is not `validate:\"required\"` on purpose: requireKey answers absence\nitself, so an unconfigured surface 503s and a missing bearer 401s — a\nvalidation refusal would rewrite both statuses.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/vectorCollectionList"}}},"description":"ok"}},"x-app":"product"}},"/v1/vector/stats":{"get":{"operationId":"get_v1_vector_stats","summary":"Totals the collections, vectors and storage across the vector store.","description":"Totals the collections, vectors and storage across the vector store.\n\nEvery figure is summed from the same per-collection detail\nGET /v1/vector/collections returns, so the two panels can never disagree. An\nunreachable Qdrant answers 200 with all zeros rather than an error.","tags":["vector"],"parameters":[{"name":"Authorization","in":"header","required":false,"description":"Authorization carries the surface's bearer key (`Bearer \u003ckey\u003e`); the bare\nkey is accepted too. Search and vector are two surfaces with two keys.\nIt is not `validate:\"required\"` on purpose: requireKey answers absence\nitself, so an unconfigured surface 503s and a missing bearer 401s — a\nvalidation refusal would rewrite both statuses.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/vectorStats"}}},"description":"ok"}},"x-app":"product"}},"/v1/vector/{name}":{"delete":{"operationId":"delete_v1_vector_by_name","summary":"Deletes one vector collection from the shared backend and removes its metadata row.","description":"Deletes one vector collection from the shared backend and removes\nits metadata row. Answers 204 with no body; a second call is a 404.","tags":["vector"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"embeddings"}],"responses":{"204":{"description":"no content"}},"x-app":"provisioning"},"get":{"operationId":"get_v1_vector_by_name","summary":"Returns one vector collection's metadata.","description":"Returns one vector collection's metadata. It carries the\ncollection's status and the gateway address it is reached at, and no username:\nthe backend authenticates with a shared, out-of-band key rather than a\nper-collection credential, so there is no per-resource user to report.","tags":["vector"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the resource's org-unique slug, from the path. Lower-cased and\ntrimmed before lookup, exactly as it was at create.","schema":{"type":"string"},"example":"embeddings"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionedResource"}}},"description":"ok"}},"x-app":"provisioning"}},"/v1/videos/generations":{"post":{"operationId":"post_v1_videos_generations","summary":"Implements POST /v1/videos/generations — the ASYNC create.","description":"Implements POST /v1/videos/generations — the ASYNC create.\n\nBody: {\"model\": \"...\", \"prompt\": \"...\", \"size\"?: \"1280x720\", \"seconds\"?: int}\n\nIt authenticates the caller, resolves the model to its upstream provider via\nthe shared routing table (zen3-video* / wan2-2-t2v-a14b → the spark-video\nbackend), reserves the per-video budget (the balance gate), creates ONE\nupstream job, registers it in the in-pod store, and returns the OpenAI-shaped\nvideo object with status \"queued\" IMMEDIATELY. The client then polls\nGET /v1/videos/{id} and downloads GET /v1/videos/{id}/content. Nothing is\nbilled here — the debit lands on completion.","tags":["videos"],"x-app":"github.com/hanzoai/ai"}},"/v1/videos/{id}":{"get":{"operationId":"get_v1_videos_by_id","summary":"Implements GET /v1/videos/{id} — poll a job's status.","description":"Implements GET /v1/videos/{id} — poll a job's status.\n\nIt authenticates the caller, verifies they OWN the job (the caller's billing\nsubject must equal the job's), performs ONE upstream status poll, and — the\nfirst time the job is observed completed — settles the reservation with the\nactual cost and records the billable usage event (exactly once). Returns the\nOpenAI-shaped video object.","tags":["videos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/videos/{id}/content":{"get":{"operationId":"get_v1_videos_by_id_content","summary":"Implements GET /v1/videos/{id}/content — download the finished MP4.","description":"Implements GET /v1/videos/{id}/content — download the finished MP4.\n\nIt authenticates + ownership-checks the caller, then proxies the upstream\n/content endpoint (bounded by the download concurrency ceiling) and streams the\nraw video bytes back inline. A successful download also bills the job once (for\nthe client that downloads without first polling to completion) — idempotent\nwith the poll path via job.markCompleted.","tags":["videos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/vpcs":{"get":{"operationId":"get_v1_vpcs","summary":"Returns every VPC the caller's org owns, under the friendly names the org created them with.","description":"Returns every VPC the caller's org owns, under the friendly names the\norg created them with. DigitalOcean is one account for the whole deployment, so\nthe account-wide inventory is filtered to the caller's own \"o\"\u003corgHash\u003e- name\nprefix and the prefix is stripped — another org's VPC is not merely hidden, it\nis never in the answer.","tags":["vpcs"],"responses":{"200":{"content":{"application/json":{"example":{"vpcs":[{"cidr":"10.10.0.0/16","id":"vpc-1","name":"web","region":"nyc3","status":"active","subnets":[]}]},"schema":{"$ref":"#/components/schemas/vpcList"}}},"description":"ok"}},"x-app":"do"},"post":{"operationId":"post_v1_vpcs","summary":"Creates a VPC in the caller's org namespace and answers 201 with it.","description":"Creates a VPC in the caller's org namespace and answers 201 with it.\nThe physical DigitalOcean name is derived server-side from the validated org,\nso a tenant can only ever create inside its own namespace; a name that already\nexists there is a 409.","tags":["vpcs"],"requestBody":{"content":{"application/json":{"example":{"ip_range":"10.10.0.0/16","name":"web","region":"nyc3"},"schema":{"$ref":"#/components/schemas/createVPCReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/vpcView"}}},"description":"created"}},"x-app":"do"}},"/v1/vpcs/{id}":{"delete":{"operationId":"delete_v1_vpcs_by_id","summary":"Removes one of the caller org's VPCs and answers 204.","description":"Removes one of the caller org's VPCs and answers 204. Ownership is\nconfirmed by re-fetching the resource and checking its physical name carries\nthe caller's org prefix BEFORE anything is deleted, so a cross-tenant id is a\n404 rather than a delete of another org's VPC.","tags":["vpcs"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DigitalOcean resource id (a UUID), from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"do"},"get":{"operationId":"get_v1_vpcs_by_id","summary":"Returns one of the caller org's VPCs by id.","description":"Returns one of the caller org's VPCs by id. A VPC that exists but sits\nin another org's namespace is reported 404, never 403 — the answer must not\ntell one tenant that another tenant's resource exists.","tags":["vpcs"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the DigitalOcean resource id (a UUID), from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/vpcView"}}},"description":"ok"}},"x-app":"do"}},"/v1/wallets":{"get":{"operationId":"get_v1_wallets","summary":"Returns the caller org's wallets, newest first, optionally NARROWED within the org by project, agent or account.","description":"Returns the caller org's wallets, newest first, optionally NARROWED\nwithin the org by project, agent or account. The org is always the bound\nisolation boundary — the filters only ever narrow inside it, so a caller can\nnever widen past its own org.","tags":["wallets"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows to wallets scoped to one project. Must be a url-safe segment.","schema":{"type":"string"}},{"name":"agent","in":"query","required":false,"description":"Agent narrows to wallets scoped to one agent. Must be a url-safe segment.","schema":{"type":"string"}},{"name":"account","in":"query","required":false,"description":"Account narrows to wallets under one account id. Must be a url-safe segment.","schema":{"type":"string"},"example":"acct_9f8c1d"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/walletList"}}},"description":"ok"}},"x-app":"wallets"},"post":{"operationId":"post_v1_wallets","summary":"Provisions a new signing identity under one of the caller org's accounts and answers the stored wallet including its on-chain address.","description":"Provisions a new signing identity under one of the caller org's\naccounts and answers the stored wallet including its on-chain address. The\ncustody backend generates the key material — a KMS-sealed secp256k1 key, an\nMPC threshold key on the ring, or a Safe smart wallet owned by one — and the\nHANDLE to it is kept server-side and never returned. A custody kind the\ndeployment has not wired fails CLOSED with 503: a signature is never\nfabricated. The wallet is scoped to the org, the caller's ambient project, and\noptionally an agent and the named account; those narrowings are what its key\nref is derived from, so each must be a url-safe segment.","tags":["wallets"],"requestBody":{"content":{"application/json":{"example":{"accountId":"acct_9f8c1d","chain":"eip155:36963","custody":"kms","name":"ops hot wallet","tier":"hot"},"schema":{"$ref":"#/components/schemas/createWalletIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/wallets/accounts":{"get":{"operationId":"get_v1_wallets_accounts","summary":"Returns the caller org's wallet accounts, newest first.","description":"Returns the caller org's wallet accounts, newest first. Accounts\nare physically org-scoped, so another tenant's are not reachable from here.","tags":["wallets"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accountList"}}},"description":"ok"}},"x-app":"wallets"},"post":{"operationId":"post_v1_wallets_accounts","summary":"Opens a named wallet account for the caller's org.","description":"Opens a named wallet account for the caller's org. An account is\na GROUPING of wallets, not a key or a balance: wallets are created under one\nand can be listed by it. The org is stamped by the server from the validated\nprincipal, so a request can never open an account in another tenant.","tags":["wallets"],"requestBody":{"content":{"application/json":{"example":{"name":"treasury"},"schema":{"$ref":"#/components/schemas/createAccountIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletAccount"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/wallets/{id}":{"get":{"operationId":"get_v1_wallets_by_id","summary":"Returns one of the caller org's wallets: its scope, custody kind, tier, chain and on-chain address.","description":"Returns one of the caller org's wallets: its scope, custody kind,\ntier, chain and on-chain address. The custody handle to the signing material is\nnever part of the answer. A wallet id another org owns reads as not found, so\nthe response cannot confirm that it exists.","tags":["wallets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wal_4b1e77"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/wallets/{id}/keys":{"post":{"operationId":"post_v1_wallets_by_id_keys","summary":"Rolls one wallet's signing material through its own custody backend and answers the wallet with whatever address that produced.","description":"Rolls one wallet's signing material through its own custody backend\nand answers the wallet with whatever address that produced. For KMS custody a\nfresh secp256k1 key is generated and sealed, which CHANGES the address — funds\nand approvals at the old address do not move. For a Safe the address is\ncounterfactual and the owner shares are ring-managed, so rotation is a no-op\nand the address is unchanged. A backend that is not configured fails closed\nwith 503 rather than leaving the wallet half-rotated.","tags":["wallets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wal_4b1e77"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/wallets/{id}/sign":{"post":{"operationId":"post_v1_wallets_by_id_sign","summary":"Produces a secp256k1 signature from one of the caller org's wallets over a 32-byte digest, through whichever custody backend that wallet uses.","description":"Produces a secp256k1 signature from one of the caller org's wallets over\na 32-byte digest, through whichever custody backend that wallet uses. Give it\neither a `digest` (32 bytes as hex, signed verbatim) or a `message` (hashed\nwith Keccak256 first) — exactly one is required. The private key never leaves\nits backend: KMS custody opens the sealed key in-process, MPC custody produces\na threshold signature on the ring. The answer carries the digest that was\nsigned alongside the signature, so a caller can verify what it got.","tags":["wallets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wal_4b1e77"}],"requestBody":{"content":{"application/json":{"example":{"id":"wal_4b1e77","message":"approve withdrawal 42"},"schema":{"$ref":"#/components/schemas/signIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/signature"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/wallets/{id}/transactions":{"post":{"operationId":"post_v1_wallets_by_id_transactions","summary":"Composes a Safe transaction on the MPC ring and answers its EIP-712 hash together with the owner approval the ring's threshold signature produced.","description":"Composes a Safe transaction on the MPC ring and answers its\nEIP-712 hash together with the owner approval the ring's threshold signature\nproduced. Only a wallet whose custody is \"safe\" can do this — any other custody\nis a 400, because the backend itself is asked whether it can propose rather\nthan the kind being switched on. The ring computes the Safe-tx hash bound to\nthe Safe contract and the chain id, so the hash a caller gets back is the one\nthe Safe will verify. This PROPOSES: it does not execute the transaction.","tags":["wallets"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wal_4b1e77"}],"requestBody":{"content":{"application/json":{"example":{"chainId":36963,"data":"0x","id":"wal_4b1e77","nonce":7,"to":"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984","value":"0"},"schema":{"$ref":"#/components/schemas/safeTxIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/safeProposal"}}},"description":"ok"}},"x-app":"wallets"}},"/v1/webhooks":{"get":{"operationId":"get_v1_webhooks","summary":"Returns every webhook endpoint the caller's org has registered, newest first, each with its 7-day delivery and failure counts.","description":"Returns every webhook endpoint the caller's org has registered,\nnewest first, each with its 7-day delivery and failure counts. Signing secrets\nare redacted here — a secret leaves the server only on create and on rotate.\nThe listing is physically org-scoped, so another tenant's endpoints are not\nreachable from this route at all.","tags":["webhooks"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/endpointList"}}},"description":"ok"}},"x-app":"webhooks"},"post":{"operationId":"post_v1_webhooks","summary":"Registers a new webhook subscription for the caller's org and answers 201 with the endpoint INCLUDING its freshly minted signing secret.","description":"Registers a new webhook subscription for the caller's org and\nanswers 201 with the endpoint INCLUDING its freshly minted signing secret.\nThis is one of only two responses that ever carry that secret (the other is\nrotate) — store it now, because no later read returns it. The org is stamped by\nthe server from the validated principal, so a body can never register an\nendpoint in another tenant.","tags":["webhooks"],"requestBody":{"content":{"application/json":{"example":{"description":"order pipeline","events":["commerce.order.\u003e"],"url":"https://acme.example/hooks/hanzo"},"schema":{"$ref":"#/components/schemas/createEndpointIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Endpoint"}}},"description":"created"}},"x-app":"webhooks"}},"/v1/webhooks/{id}":{"delete":{"operationId":"delete_v1_webhooks_by_id","summary":"Removes one of the caller org's webhook endpoints and answers 204 with no body.","description":"Removes one of the caller org's webhook endpoints and answers\n204 with no body. Delivery stops immediately and the endpoint's signing secret\nis gone with it; its recorded delivery history goes too. An id another org owns\nreads as not found.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"responses":{"204":{"description":"no content"}},"x-app":"webhooks"},"get":{"operationId":"get_v1_webhooks_by_id","summary":"Returns one of the caller org's webhook endpoints with its 7-day delivery and failure counts, signing secret redacted.","description":"Returns one of the caller org's webhook endpoints with its 7-day\ndelivery and failure counts, signing secret redacted. An id another org owns\nreads as not found, so the response cannot confirm that it exists.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Endpoint"}}},"description":"ok"}},"x-app":"webhooks"},"put":{"operationId":"put_v1_webhooks_by_id","summary":"Replaces the editable fields of one of the caller org's endpoints — url, events, status and description — and answers the stored row with its secret redacted.","description":"Replaces the editable fields of one of the caller org's\nendpoints — url, events, status and description — and answers the stored row\nwith its secret redacted. It is a full replace, not a patch: an omitted field\nis written as its empty value, and an omitted or empty events list resubscribes\nthe endpoint to EVERY event. The signing secret and the creation time are\nimmutable here; rotate the secret with POST /v1/webhooks/{id}/secret.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"requestBody":{"content":{"application/json":{"example":{"events":["commerce.order.paid"],"id":"wh_9f8c1d2e","status":"disabled","url":"https://acme.example/hooks/v2"},"schema":{"$ref":"#/components/schemas/updateEndpointIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Endpoint"}}},"description":"ok"}},"x-app":"webhooks"}},"/v1/webhooks/{id}/deliveries":{"get":{"operationId":"get_v1_webhooks_by_id_deliveries","summary":"Returns one endpoint's per-attempt delivery log, newest first — the record of what was sent, what the subscriber answered, and how long it took.","description":"Returns one endpoint's per-attempt delivery log, newest first —\nthe record of what was sent, what the subscriber answered, and how long it\ntook. One event that retried three times appears as three rows sharing a\ndelivery id. It is org-scoped exactly like every other route here: the endpoint\nlookup only ever finds THIS org's endpoint, so another org's id is a 404 and\nnever a window onto its logs.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"},{"name":"limit","in":"query","required":false,"description":"Limit caps how many attempts come back: default 50, maximum 200. A value\nthat is not a positive integer reads as the default.","schema":{"type":"integer"},"example":100},{"name":"status","in":"query","required":false,"description":"Status narrows the log to one outcome: \"ok\", \"retrying\" or \"failed\".\nEmpty returns every attempt.","schema":{"type":"string"},"example":"failed"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deliveryList"}}},"description":"ok"}},"x-app":"webhooks"}},"/v1/webhooks/{id}/secret":{"post":{"operationId":"post_v1_webhooks_by_id_secret","summary":"Mints a NEW HMAC signing secret for the endpoint and answers the endpoint WITH it — the only other response besides create that ever carries a secret.","description":"Mints a NEW HMAC signing secret for the endpoint and answers the\nendpoint WITH it — the only other response besides create that ever carries a\nsecret. The old secret stops working the instant this returns: every subsequent\ndelivery signs with the new one, with no overlap window. Call it when the\nsubscriber is ready to swap the value on its side, not before.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Endpoint"}}},"description":"ok"}},"x-app":"webhooks"}},"/v1/webhooks/{id}/test":{"post":{"operationId":"post_v1_webhooks_by_id_test","summary":"Sends ONE signed test event to the endpoint right now and answers the outcome inline, so the console can show whether the subscriber is reachable without waiting for real traffic.","description":"Sends ONE signed test event to the endpoint right now and answers\nthe outcome inline, so the console can show whether the subscriber is reachable\nwithout waiting for real traffic. It takes the same attempt path the bus\ndispatcher takes — one attempt, 10s timeout, no retry ladder — and records the\nresult in the endpoint's delivery log. It works on a DISABLED endpoint too:\nvalidating one you have paused is the whole point.","tags":["webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/testResult"}}},"description":"ok"}},"x-app":"webhooks"}},"/v1/websearch/search":{"delete":{"operationId":"delete_v1_websearch_search","summary":"Keyless web meta-search, in the SearXNG JSON envelope.","description":"Answers {query, number_of_results, results:[{url, title, content, engine}]} — the exact /search?format=json contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q` is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query kept — distinct queries are distinct results) and capped at 20. Ranking is deterministic rather than scored: the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal — the same gate the whole data plane uses — passes straight through, and a caller without one must present the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503 rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer — nothing was found — and not an outage. The array is always present, never null.\n\nThe one thing to get right: every method answers identically. This is one handler registered for all of them, and it reads only the query string, so a body sent on the write verbs is ignored rather than refused.","tags":["websearch"],"x-app":"websearch"},"get":{"operationId":"get_v1_websearch_search","summary":"Keyless web meta-search, in the SearXNG JSON envelope.","description":"Answers {query, number_of_results, results:[{url, title, content, engine}]} — the exact /search?format=json contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q` is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query kept — distinct queries are distinct results) and capped at 20. Ranking is deterministic rather than scored: the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal — the same gate the whole data plane uses — passes straight through, and a caller without one must present the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503 rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer — nothing was found — and not an outage. The array is always present, never null.\n\nThe one thing to get right: every method answers identically. This is one handler registered for all of them, and it reads only the query string, so a body sent on the write verbs is ignored rather than refused.","tags":["websearch"],"x-app":"websearch"},"patch":{"operationId":"patch_v1_websearch_search","summary":"Keyless web meta-search, in the SearXNG JSON envelope.","description":"Answers {query, number_of_results, results:[{url, title, content, engine}]} — the exact /search?format=json contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q` is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query kept — distinct queries are distinct results) and capped at 20. Ranking is deterministic rather than scored: the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal — the same gate the whole data plane uses — passes straight through, and a caller without one must present the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503 rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer — nothing was found — and not an outage. The array is always present, never null.\n\nThe one thing to get right: every method answers identically. This is one handler registered for all of them, and it reads only the query string, so a body sent on the write verbs is ignored rather than refused.","tags":["websearch"],"x-app":"websearch"},"post":{"operationId":"post_v1_websearch_search","summary":"Keyless web meta-search, in the SearXNG JSON envelope.","description":"Answers {query, number_of_results, results:[{url, title, content, engine}]} — the exact /search?format=json contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q` is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query kept — distinct queries are distinct results) and capped at 20. Ranking is deterministic rather than scored: the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal — the same gate the whole data plane uses — passes straight through, and a caller without one must present the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503 rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer — nothing was found — and not an outage. The array is always present, never null.\n\nThe one thing to get right: every method answers identically. This is one handler registered for all of them, and it reads only the query string, so a body sent on the write verbs is ignored rather than refused.","tags":["websearch"],"x-app":"websearch"},"put":{"operationId":"put_v1_websearch_search","summary":"Keyless web meta-search, in the SearXNG JSON envelope.","description":"Answers {query, number_of_results, results:[{url, title, content, engine}]} — the exact /search?format=json contract a SearXNG client decodes, so an agent tool configured against SearXNG reaches this with no change. `q` is the query and `language` narrows it; both are read from the QUERY STRING.\n\nServed in-process by a Go meta-search over keyless public engines, never a third-party search API and never a search key. The enabled engines run concurrently and their hits are merged, deduplicated by normalised URL (host and path, trailing slash and fragment dropped, query kept — distinct queries are distinct results) and capped at 20. Ranking is deterministic rather than scored: the first configured engine's hits lead.\n\nTWO WAYS IN, one-way equivalent, and no third: a validated principal — the same gate the whole data plane uses — passes straight through, and a caller without one must present the shared service key as X-API-Key, compared in constant time. A deployment with no key configured answers 503 rather than opening the surface to everyone, and a missing or wrong key is 401. It is never an open proxy. There is no tenant scoping beyond that gate, and there is nothing to scope: the results are public web pages, identical for every caller.\n\nIt fails SOFT on the engines and closed only on the gate. An engine that errors or is served a bot-challenge page contributes zero results and never fails the request, so an empty `results` is a real answer — nothing was found — and not an outage. The array is always present, never null.\n\nThe one thing to get right: every method answers identically. This is one handler registered for all of them, and it reads only the query string, so a body sent on the write verbs is ignored rather than refused.","tags":["websearch"],"x-app":"websearch"}},"/v1/wecom-bot/callback/{botId}":{"get":{"operationId":"get_v1_wecom-bot_callback_by_botid","summary":"Verify WeChat work bot callback URL","description":"Verify WeChat work bot callback URL","tags":["wecom-bot"],"parameters":[{"name":"botId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"},"post":{"operationId":"post_v1_wecom-bot_callback_by_botid","summary":"Process WeChat work bot messages","description":"Process WeChat work bot messages","tags":["wecom-bot"],"parameters":[{"name":"botId","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"github.com/hanzoai/ai"}},"/v1/world":{"get":{"operationId":"get_v1_world","summary":"Answers GET /v1/world — the product's front door, naming every wire this surface answers on.","description":"Answers GET /v1/world — the product's front door, naming every wire this\nsurface answers on.\n\nIt exists because two of those wires are INVISIBLE to the generated document.\n/v1/world/mcp and /v1/world/zap are carved off the cloud catch-all by the\ningress and answered by world-gw, so the cloud router never serves them — and\nopenapi.Describe renders prose only for a route the router actually serves,\nwhich is the very property that keeps the document from being able to claim an\noperation nothing answers. Both addresses are real and public, so without this\nop the only way to learn they exist is to read the ingress config. This is\nwhere that fact lives, in the product's own surface.\n\nPublic on purpose: discovery precedes credentials. It reports addresses and\nprotocols only — never feed data, and never the caller's plan, which\nGET /v1/world/limits owns — so there is nothing here to leak.","tags":["world"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/worldIndex"}}},"description":"ok"}},"x-app":"world"}},"/v1/world/limits":{"get":{"operationId":"get_v1_world_limits","summary":"Echoes a World plan's rate limits, alert quota and model-API grant, read straight from the live @hanzo/plans catalog, so agents and dashboards configure themselves against the catalog instead of hardcoding tier numbers.","description":"Echoes a World plan's rate limits, alert quota and model-API grant, read\nstraight from the live @hanzo/plans catalog, so agents and dashboards configure\nthemselves against the catalog instead of hardcoding tier numbers.\n\nAn empty or unknown plan resolves world-free, and a catalog failure serves that\nsame free floor rather than erroring — so this always answers 200, and it can only\never under-grant. It reports the contract; it does not enforce it.","tags":["world"],"parameters":[{"name":"plan","in":"query","required":false,"description":"Plan is a World plan id from the live @hanzo/plans catalog, e.g. world-pro.\nEmpty means world-free, and so does an id the catalog does not know — this\nnever fails on an unknown plan.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/limitsView"}}},"description":"ok"}},"x-app":"world"}},"/v1/world/news":{"get":{"operationId":"get_v1_world_news","summary":"Returns the caller's merged world-news feed: every source their project's pipeline names — GDELT once per keyword, plus each allowlisted RSS or Atom feed — fetched concurrently, narrowed by the pipeline's keyword/region/source filters, deduplicated by link and sorted freshest first, capped at 50 items.","description":"Returns the caller's merged world-news feed: every source their project's\npipeline names — GDELT once per keyword, plus each allowlisted RSS or Atom feed —\nfetched concurrently, narrowed by the pipeline's keyword/region/source filters,\ndeduplicated by link and sorted freshest first, capped at 50 items.\n\nA project with no stored pipeline gets a sensible default set of world feeds\nrather than an empty answer. A source that fails or times out is SKIPPED: the feed\ndegrades to honest partial results and never 5xxs because one outlet was down.\nReading also publishes the result to the /v1/world/stream subscribers of the same\n(org, project), so a dashboard's own refresh updates every open tab.","tags":["world"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/newsResponse"}}},"description":"ok"}},"x-app":"world"}},"/v1/world/pipeline":{"get":{"operationId":"get_v1_world_pipeline","summary":"Returns the caller project's news pipeline: which feeds it reads and how the merged result is filtered.","description":"Returns the caller project's news pipeline: which feeds it reads and how\nthe merged result is filtered. A project that has never written one is answered\nwith the built-in world feeds and `default: true`, so a fresh project sees the\nsame feed /v1/world/news would actually serve rather than an empty configuration.","tags":["world"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pipelineView"}}},"description":"ok"}},"x-app":"world"},"put":{"operationId":"put_v1_world_pipeline","summary":"Replaces the caller project's news pipeline and returns what was stored.","description":"Replaces the caller project's news pipeline and returns what was\nstored. It is a WHOLE replacement, not a patch: a field the request leaves out is\nstored empty, so sending only feeds clears the filters.\n\nEvery feed URL is validated HERE, at the write boundary — http(s) only, and the\nhost must be on the server's allowlist — so a stored pipeline can never name a\nhost the fetcher would later refuse, and the allowlist is one decision in one\nplace rather than a check at each fetch.","tags":["world"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pipelineReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pipelineView"}}},"description":"ok"}},"x-app":"world"}},"/v1/world/stream":{"get":{"operationId":"get_v1_world_stream","summary":"Live news refreshes for the caller's org and project, as Server-Sent Events.","description":"Holds the connection open as text/event-stream and pushes a `news` event — the same {items:[…]} body GET /v1/world/news answers — each time the caller's (org, project) feed refreshes, with a `: ping` heartbeat comment every 25s. Delivery is best-effort: a slow consumer is dropped on buffer overrun and reconnects, re-fetching GET /v1/world/news, which stays the source of truth. Requires a validated principal; 403 without one.","tags":["world"],"x-app":"world"}},"/v1/x402/settlements/{id}":{"get":{"operationId":"get_v1_x402_settlements_by_id","summary":"Settlement reads one x402 payment receipt by id.","description":"Settlement reads one x402 payment receipt by id.\n\nIt is scoped to the caller's PAYER org — the ledger that was debited — so one\ntenant can never read another's settlement, and an id that exists but belongs\nto somebody else is a 404 exactly like one that does not exist. A caller with\nno billable identity is refused outright.","tags":["x402"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the settlement id from the URL — the deterministic keccak(from|nonce)\nkey an x402 receipt is issued under (the `id` field of a Receipt, and the\n`transaction` of the SettlementResponse on the PAYMENT-RESPONSE header a paid\nrequest answers with).","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Receipt"}}},"description":"ok"}},"x-app":"x402"}},"/ws/query_progress":{"get":{"operationId":"get_ws_query_progress","summary":"Watch one running query's progress over a websocket","description":"The same progress read as /v1/o11y/query_progress, delivered over a websocket: the Upgrade IS the contract, so there is no JSON response to declare and no typed operation to make of it.\n\nIt sits outside /v1/o11y on purpose — the upgrade handshake is a transport concern, not a resource — and it was unreachable from the composed binary until the route table named it, because the old wildcard covered only the o11y prefix.\n\nA validated, org-scoped principal is required.","x-app":"o11y"}},"/{org}/{project}/{repo}/git-receive-pack":{"post":{"operationId":"post_by_org_by_project_by_repo_git-receive-pack","summary":"Accept a push, and turn it into a build","description":"The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero — the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed at the git host's root with the PROJECT as a middle path segment — the canonical-URL form of the project-scoped remote, since a git client has no header to carry a project. Served only on the dedicated git host; elsewhere it falls through. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/{org}/{project}/{repo}/git-upload-pack":{"post":{"operationId":"post_by_org_by_project_by_repo_git-upload-pack","summary":"Serve a clone or fetch","description":"The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol, streamed straight through git itself — request body to git's stdin, git's stdout to the response — so a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type other than `application/x-git-upload-pack-request` is 400. Addressed at the git host's root with the PROJECT as a middle path segment — the canonical-URL form of the project-scoped remote, since a git client has no header to carry a project. Served only on the dedicated git host; elsewhere it falls through. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/{org}/{project}/{repo}/info/refs":{"get":{"operationId":"get_by_org_by_project_by_repo_info_refs","summary":"Advertise a repository's refs to a git client","description":"The ref-advertisement phase of git's smart-HTTP protocol — the first request a clone, a fetch and a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push, and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private repository reached without its org is 404, indistinguishable from one that does not exist. Addressed at the git host's root with the PROJECT as a middle path segment — the canonical-URL form of the project-scoped remote, since a git client has no header to carry a project. Served only on the dedicated git host; elsewhere it falls through. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"project","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/{org}/{repo}":{"get":{"operationId":"get_by_org_by_repo","summary":"Open a repository's home page","description":"A repository at a glance: its branches, the tree at the tip, its most recent commits, its README rendered, and the HTTPS and SSH clone URLs. `?ref=` selects a branch, tag or commit; the default branch is used when it is omitted. A repository with no commits yet renders its clone instructions rather than an error, which is what a caller who has just created one needs to see. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/{org}/{repo}/blob/{wildcard1}":{"get":{"operationId":"get_by_org_by_repo_blob_by_wildcard1","summary":"View a file in a repository","description":"One file's contents at one revision, with its size and line count. A BINARY file is reported as binary rather than dumped into the page. The path after /blob/ is the file and `?ref=` selects the branch, tag or commit. An unknown ref or a path that is not a file in it is 404. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/{org}/{repo}/commits":{"get":{"operationId":"get_by_org_by_repo_commits","summary":"Read a repository's commit log","description":"The hundred most recent commits on one ref, each with its author, message and date. `?ref=` selects the branch, tag or commit, defaulting to the repository's default branch; an unknown one is 404. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/{org}/{repo}/git-receive-pack":{"post":{"operationId":"post_by_org_by_repo_git-receive-pack","summary":"Accept a push, and turn it into a build","description":"The pack-transfer phase of a push, and the point at which a push becomes an EVENT. NEVER ANONYMOUS: a push always requires an authenticated org, and the org in the path must equal it.\n\nOnce the pack is on disk the repository's storage usage is metered and a build is fired for every branch whose tip actually moved, computed from the before/after branch diff rather than from what the client claimed. That runs on a cancel-immune context, so a client that hangs up the moment its push lands still gets its build, and it runs even when git itself exited non-zero — the refs on disk are the ground truth. Repacking housekeeping is detached and never blocks the response.\n\nA Content-Type other than `application/x-git-receive-pack-request` is 400. Addressed at the git host's root, so `git clone https://\u003cgit-host\u003e/\u003corg\u003e/\u003crepo\u003e.git` works with the canonical URL and no prefix. Served ONLY on the dedicated git host; on the API and console hosts it falls through, so a bare /:org/:repo can never shadow another surface. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/{org}/{repo}/git-upload-pack":{"post":{"operationId":"post_by_org_by_repo_git-upload-pack","summary":"Serve a clone or fetch","description":"The pack-transfer phase of a clone or fetch: the request and the response are git's binary pack protocol, streamed straight through git itself — request body to git's stdin, git's stdout to the response — so a multi-gigabyte clone never lands in this process's memory.\n\nA PUBLIC repository is fetched anonymously; a private one requires its own org, and a wrong or absent org is 404 rather than a hint that the repository exists. A Content-Type other than `application/x-git-upload-pack-request` is 400. Addressed at the git host's root, so `git clone https://\u003cgit-host\u003e/\u003corg\u003e/\u003crepo\u003e.git` works with the canonical URL and no prefix. Served ONLY on the dedicated git host; on the API and console hosts it falls through, so a bare /:org/:repo can never shadow another surface. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"x-app":"git"}},"/{org}/{repo}/info/refs":{"get":{"operationId":"get_by_org_by_repo_info_refs","summary":"Advertise a repository's refs to a git client","description":"The ref-advertisement phase of git's smart-HTTP protocol — the first request a clone, a fetch and a push all make. `?service=` selects which: `git-upload-pack` advertises for a fetch, `git-receive-pack` for a push, and any other value is 400.\n\nANONYMOUS ONLY FOR FETCH, AND ONLY ON A PUBLIC REPOSITORY. The push advertisement always requires an authenticated org, and where a path org is present it must equal the authenticated one. A private repository reached without its org is 404, indistinguishable from one that does not exist. Addressed at the git host's root, so `git clone https://\u003cgit-host\u003e/\u003corg\u003e/\u003crepo\u003e.git` works with the canonical URL and no prefix. Served ONLY on the dedicated git host; on the API and console hosts it falls through, so a bare /:org/:repo can never shadow another surface. This is git's own wire protocol, not an API call to make by hand: point a git client at the clone URL and it makes this request itself.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}},"/{org}/{repo}/tree/{wildcard1}":{"get":{"operationId":"get_by_org_by_repo_tree_by_wildcard1","summary":"Browse a directory inside a repository","description":"The contents of one directory at one revision, with breadcrumbs back up and links onward into subdirectories and files. The path after /tree/ is the directory and `?ref=` selects the branch, tag or commit, defaulting to the repository's own default branch. An unknown ref is 404, as is a repository with no commits. A public repository is readable by anyone; a private one only by its own org. A repository that does not exist and one belonging to another org answer the SAME 404, so the page is never an existence oracle. This is a server-rendered browser page, not JSON — the console repo-browser reads the same repository through the JSON ops under /v1/git. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML. Served only on the dedicated git host, where a browse URL matches the clone URL; on the API and console hosts it falls through to their own routes, so it can never shadow them.","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"wildcard1","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git"}}},"components":{"schemas":{"AccessChange":{"properties":{"affected":{"description":"Affected lists the usernames that were updated.","items":{"type":"string"},"type":"array"},"failed":{"description":"Failed lists the usernames that were NOT updated. Non-empty means the org is in\na mixed state and the action should be retried.","items":{"type":"string"},"type":"array"},"org":{"description":"Org is the tenant acted on.","type":"string"},"suspended":{"description":"Suspended is the state applied: true for suspend, false for reactivate.","type":"boolean"}},"type":"object"},"AccessOut":{"properties":{"data":{"$ref":"#/components/schemas/AccessChange"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Account":{"properties":{"name":{"type":"string"},"number":{"type":"string"},"party":{"type":"string"},"type":{"type":"string"}},"type":"object"},"Accounts":{"properties":{"account":{"$ref":"#/components/schemas/SourceState","description":"Account is the state of the caller's own linked-account side."},"hanzo":{"$ref":"#/components/schemas/SourceState","description":"Hanzo is the state of the org's Hanzo-routed side."},"rows":{"description":"Rows is the two row sets CONCATENATED, never summed — each row says which\nside it came from. A percent is not money and a provider's own spend is not\na Hanzo charge, so adding them would produce a number that means nothing.","items":{"$ref":"#/components/schemas/TotalView"},"type":"array"}},"type":"object"},"AccountsTotal":{"properties":{"accounts":{"description":"Accounts is how many linked accounts the total folds.","type":"integer"},"completionTokens":{"description":"CompletionTokens is the total completion-token count.","type":"integer"},"costCents":{"description":"CostCents is the total cost in cents.","type":"integer"},"promptTokens":{"description":"PromptTokens is the total prompt-token count.","type":"integer"},"requests":{"description":"Requests is the total request count the gateway routed.","type":"integer"},"totalTokens":{"description":"TotalTokens is the total token count.","type":"integer"}},"type":"object"},"AccountsUsage":{"properties":{"accounts":{"description":"Accounts is one row per linked account the gateway actually routed through.","items":{"$ref":"#/components/schemas/RoutedUsage"},"type":"array"},"scope":{"description":"Scope is always \"user\": the caller's own linked accounts.","type":"string"},"source":{"description":"Source is always \"routed\": the gateway's own routed ledger.","type":"string"},"total":{"$ref":"#/components/schemas/AccountsTotal","description":"Total is the honest sum across the rows."}},"type":"object"},"ActionOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Result"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"ActionRecord":{"properties":{"args":{"type":"string"},"createdAt":{"type":"integer"},"err":{"type":"string"},"id":{"type":"string"},"ok":{"type":"boolean"},"result":{"type":"string"},"stepId":{"type":"string"},"tool":{"type":"string"}},"type":"object"},"ActivityPoint":{"properties":{"costCents":{"description":"CostCents is the day's spend in whole US cents. A series is only ever returned\nfor a subject the caller is authorized to see, so this is never withheld: 0 means\nno spend that day.","type":"integer"},"day":{"description":"Day is the UTC calendar day this point covers, \"2006-01-02\".","type":"string"},"requests":{"description":"Requests is the subject's request count on this day. 0 is a real, quiet day: the\nseries is gap-filled, so every day in the range is present whether or not\nanything happened.","type":"integer"},"tokens":{"description":"Tokens is prompt+completion tokens on this day — normally the heatmap's\nintensity, scaled against ActivityTotals.MaxTokens.","type":"integer"}},"type":"object"},"ActivityRow":{"properties":{"action":{"type":"string"},"actor":{"type":"string"},"at":{"type":"string"},"detail":{"type":"string"},"id":{"type":"integer"},"key":{"type":"string"}},"type":"object"},"ActivityTotals":{"properties":{"activeDays":{"description":"ActiveDays counts the days with any usage at all — the streak/consistency number.\nCompare it against len(days) for the share of days the subject showed up.","type":"integer"},"costCents":{"description":"CostCents is the window's spend in whole US cents, the sum of Days[].CostCents.","type":"integer"},"maxRequests":{"description":"MaxRequests is the same ceiling for a request-based heatmap — the busiest single\nday's request count, 0 for an idle window.","type":"integer"},"maxTokens":{"description":"MaxTokens is the busiest single day's token count: the ceiling to normalize a\ntoken heatmap against, so the darkest cell is that day. 0 for an idle window,\nwhich a client must not divide by.","type":"integer"},"requests":{"description":"Requests is the sum of Days[].Requests over the whole window.","type":"integer"},"tokens":{"description":"Tokens is the sum of Days[].Tokens over the whole window.","type":"integer"}},"type":"object"},"ActivityView":{"properties":{"available":{"description":"Available is false when nothing could be read: the warehouse is not connected,\nthe rollup is not ready, or the subject is one the ledger cannot attribute (see\nNote). Days is then empty because there is no answer, not because there was no\nactivity.","type":"boolean"},"days":{"description":"Days is the gap-filled series, one point per calendar day from From up to (not\nincluding) To, in ascending order, zero-valued days included. Always a list,\nnever null.","items":{"$ref":"#/components/schemas/ActivityPoint"},"type":"array"},"from":{"description":"From is the first day in Days, \"2006-01-02\" inclusive.","type":"string"},"id":{"description":"ID is the subject the server actually read, after resolving \"me\"/empty to the\ncaller and bounding it to what they may see — a ledger \"owner/name\" for a user,\nan org id for an org. Echoed so a client can confirm whose series it holds.","type":"string"},"note":{"description":"Note explains an empty-but-not-broken answer in plain words — today only\nsubject=project, which the usage ledger records no column for. Present only when\nthere is something to say; show it instead of an empty chart.","type":"string"},"source":{"description":"Source names the table the series was aggregated from (the derived daily rollup,\nhanzo.usage_rollup_daily).","type":"string"},"subject":{"description":"Subject echoes what the series is about: user|org|project.","type":"string"},"to":{"description":"To is the EXCLUSIVE upper bound, \"2006-01-02\" — the day AFTER the last point in\nDays. A request for to=2026-03-31 answers to=2026-04-01 with 2026-03-31 last.","type":"string"},"totals":{"$ref":"#/components/schemas/ActivityTotals","description":"Totals are the window's sums plus the busiest-day ceilings a heatmap scales\nagainst. Derived from Days — nothing here is read separately."}},"type":"object"},"AdCampaign":{"properties":{"account":{"description":"provider ad-account ref (Meta act_\u003cid\u003e)","type":"string"},"budget":{"type":"integer"},"createdAt":{"type":"integer"},"externalId":{"description":"provider campaign id after a launch","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"objective":{"type":"string"},"platform":{"type":"string"},"spend":{"type":"integer"},"status":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"Answer":{"properties":{"cold":{"description":"Cold reports that this request paid to PREPARE the revision — the tree\nwrite, the dependency fetch and the language server's first index. It is\nthe billed event, surfaced so a caller can see what it was charged for.","type":"boolean"},"completions":{"items":{"$ref":"#/components/schemas/Completion"},"type":"array"},"diagnostics":{"items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"hover":{"type":"string"},"lang":{"type":"string"},"locations":{"items":{"$ref":"#/components/schemas/Location"},"type":"array"},"op":{"type":"string"},"path":{"type":"string"},"repo":{"type":"string"},"rev":{"type":"string"},"symbols":{"items":{"$ref":"#/components/schemas/Symbol"},"type":"array"}},"type":"object"},"AppView":{"properties":{"app":{"description":"service / CR name, e.g. iam","type":"string"},"cluster":{"type":"string"},"declaredTag":{"type":"string"},"drift":{"$ref":"#/components/schemas/Verdict"},"endpoints":{"items":{"type":"string"},"type":"array"},"env":{"description":"main|test|dev","type":"string"},"health":{"description":"green|yellow|red|\"\" (unknown)","type":"string"},"id":{"description":"\u003corg\u003e/\u003capp\u003e/\u003cenv\u003e, e.g. hanzoai/iam/main","type":"string"},"latestTag":{"type":"string"},"namespace":{"type":"string"},"org":{"description":"image namespace, e.g. hanzoai","type":"string"},"phase":{"description":"operator status.phase (Running/…)","type":"string"},"registry":{"type":"string"},"repo":{"description":"owner/repo, e.g. hanzoai/iam","type":"string"},"role":{"description":"operator spec.role (sql|kv|generic|ingress|…) or \"\" — the one declared class field","type":"string"},"runningTag":{"type":"string"}},"type":"object"},"Artifact":{"properties":{"cosign_cert":{"type":"string"},"cosign_signature":{"type":"string"},"download_url":{"description":"DownloadURL is a short-lived signed URL to the artifact bytes. The\nscaffold returns the ArtifactRef as-is; production issues a signed URL.","type":"string"},"release":{"$ref":"#/components/schemas/Release"}},"type":"object"},"AskAnswer":{"properties":{"answer":{"type":"string"},"citations":{"items":{"$ref":"#/components/schemas/Citation"},"type":"array"},"degraded":{"type":"boolean"},"question":{"type":"string"}},"type":"object"},"AskRequest":{"properties":{"from":{"description":"From is the RFC3339 start of the metric window. Empty means all time, treated as a\nsingle reporting period (see monthsBetween).","type":"string"},"question":{"description":"Question is the plain-language question about the org's books, e.g. \"what is my\nMRR?\". Longer than 2000 characters is truncated, never refused.","type":"string"},"to":{"description":"To is the RFC3339 end of the metric window. Empty means up to now.","type":"string"}},"type":"object"},"AskResponse":{"properties":{"answer":{"description":"Answer is one or two sentences answering the question, every number in it taken\nfrom Figures.","type":"string"},"figures":{"description":"Figures are the grounded numbers the answer states, each already formatted.","items":{"$ref":"#/components/schemas/Figure"},"type":"array"},"followups":{"description":"Followups are sharper questions to ask next, chosen from the same intent.","items":{"type":"string"},"type":"array"},"sources":{"description":"Sources name the books reports the figures were computed from — \"pnl\",\n\"position\", \"trial\".","items":{"type":"string"},"type":"array"}},"type":"object"},"Attempt":{"properties":{"answer":{"type":"string"},"benchmark":{"type":"string"},"correct":{"type":"boolean"},"gold":{"type":"string"},"item":{"type":"string"},"model":{"type":"string"},"response":{"type":"string"},"revision":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"ts":{"type":"integer"}},"type":"object"},"Audience":{"properties":{"createdAt":{"description":"CreatedAt and UpdatedAt are unix seconds, both server-assigned.","type":"integer"},"event":{"description":"Event is the analytics event a member must have fired. EMPTY MEANS NO\nFILTER: the audience is then every mailable customer in the org, and no\nwarehouse is consulted.","type":"string"},"id":{"description":"ID is the server-assigned audience id (\"aud_\" + 128 random bits).","type":"string"},"name":{"description":"Name is the audience's label. Required, trimmed, capped at 1024 bytes.","type":"string"},"updatedAt":{"type":"integer"},"windowDays":{"description":"WindowDays is how far back the event counts, ending now. 0 means 30 and\nnothing above 3650 is honoured. Ignored when Event is empty.","type":"integer"}},"type":"object"},"AudienceList":{"properties":{"data":{"description":"Data is the page; an empty array when the org has saved no audience.","items":{"$ref":"#/components/schemas/Audience"},"type":"array"}},"type":"object"},"AudiencePreview":{"properties":{"available":{"description":"Available is false when the roster or the warehouse could not be read; the\ncounts are then zero because nothing was measured, not because the cohort\nis empty, and Reason says which read failed.","type":"boolean"},"count":{"description":"Count is the cohort size: distinct warehouse identifiers for an event\naudience, mailable customers for an event-less (whole-org) one.","type":"integer"},"deliverable":{"description":"Deliverable is how many de-duplicated addresses a send would reach, and\nUnmatched how many cohort identifiers named no customer. Unmatched is\nreported rather than hidden: it is the honest explanation for a cohort of\n500 that mails 3.","type":"integer"},"reason":{"type":"string"},"sample":{"description":"Sample is up to 1000 cohort IDENTIFIERS — never addresses, which product\nanalytics does not hold. Empty for an event-less (whole-org) audience.","items":{"type":"string"},"type":"array"},"source":{"description":"Source names where the cohort was read: the events table for an event\naudience, \"iam:\u003corg\u003e\" for the whole-org one.","type":"string"},"unmatched":{"type":"integer"}},"type":"object"},"AuthoredPlugin":{"properties":{"createdAt":{"description":"CreatedAt is when the plugin was last built, Unix seconds.","type":"integer"},"id":{"description":"ID is the plugin's id within the org, and the id a delete addresses.","type":"string"},"name":{"description":"Name is the plugin's name: one lowercase path segment, the id it runs by.","type":"string"},"org":{"description":"Org is the org that built the plugin — the validated caller's.","type":"string"},"provider":{"description":"Provider is the connectors provider whose credential this plugin uses at\nrun time. Absent for a plugin that needs none. The credential itself is\nnever here — it stays under KMS custody in the connectors plane.","type":"string"},"source":{"description":"Source is the TypeScript as authored (or as generated from a spec).","type":"string"}},"type":"object"},"Backend":{"properties":{"url":{"description":"URL is the upstream server, http(s)://host[:port].","type":"string"},"weight":{"description":"Weight is this member's share of the round-robin; must be \u003e= 0.","type":"integer"}},"type":"object"},"BackfillIn":{"properties":{"org":{"description":"Org is the tenant to migrate. Required — there is no fleet-wide form of this\ncutover, because each org must be reconciled on its own.","type":"string"}},"type":"object"},"BackfillOut":{"properties":{"data":{"$ref":"#/components/schemas/Backfilled"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Backfilled":{"properties":{"entryId":{"description":"EntryID is the finance ledger entry created, or \"\" when the balance was\nnon-positive and there was nothing to carry.","type":"string"},"migratedCents":{"description":"MigratedCents is the balance carried across, read from commerce BEFORE the move.","type":"integer"},"org":{"description":"Org is the tenant migrated.","type":"string"}},"type":"object"},"BalanceLine":{"properties":{"account":{"type":"string"},"amount":{"description":"cents, display sign","type":"integer"},"name":{"type":"string"},"type":{"type":"string"}},"type":"object"},"BalanceSheet":{"properties":{"asOf":{"type":"string"},"assets":{"items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"balanced":{"description":"TotalAssets == TotalLiabilities + TotalEquity","type":"boolean"},"equity":{"items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"liabilities":{"items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"totalAssets":{"type":"integer"},"totalEquity":{"type":"integer"},"totalLiabilities":{"type":"integer"}},"type":"object"},"BankQuestion":{"properties":{"connector":{"type":"string"},"createdAt":{"type":"string"},"externalId":{"type":"string"},"prompt":{"type":"string"},"status":{"type":"string"}},"type":"object"},"BankTally":{"properties":{"ingested":{"description":"transactions seen","type":"integer"},"posted":{"description":"vouchers newly posted (outflow + reconciled)","type":"integer"},"questions":{"description":"unmatched inflows that raised a question","type":"integer"},"reconciled":{"description":"inflows cleared against Square-clearing","type":"integer"},"skipped":{"description":"already-processed idempotent no-ops","type":"integer"},"transfers":{"description":"own-account moves recorded (no P\u0026L)","type":"integer"}},"type":"object"},"BankTxnRow":{"properties":{"amountCents":{"type":"integer"},"connector":{"type":"string"},"currency":{"type":"string"},"description":{"type":"string"},"direction":{"type":"string"},"externalId":{"type":"string"},"matchedVoucher":{"type":"string"},"merchant":{"type":"string"},"postedAt":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Blog":{"properties":{"caseStudy":{"type":"string"},"how":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"why":{"type":"string"}},"type":"object"},"Blueprint":{"properties":{"brand":{"type":"string"},"enabled":{"type":"boolean"},"principles":{"description":"the 64-principle spine (Zen of Hanzo archetypes)","items":{"$ref":"#/components/schemas/Principle"},"type":"array"},"sections":{"items":{"$ref":"#/components/schemas/Section"},"type":"array"},"steps":{"items":{"$ref":"#/components/schemas/JourneyStep"},"type":"array"},"strategies":{"items":{"$ref":"#/components/schemas/Strategy"},"type":"array"},"templates":{"items":{"$ref":"#/components/schemas/Template"},"type":"array"},"title":{"type":"string"},"version":{"type":"string"}},"type":"object"},"BoardView":{"properties":{"auditUrl":{"type":"string"},"configured":{"type":"boolean"},"engine":{"type":"string"},"manageUrl":{"type":"string"},"switches":{"items":{"$ref":"#/components/schemas/SwitchView"},"type":"array"}},"type":"object"},"BookRequest":{"properties":{"override":{"description":"Override books this bill even when one of the SAME economic identity\n(vendor, total, issue date) already posted — the explicit human confirmation that a\nsame-looking bill is a genuine second spend, not the same receipt re-scanned.","type":"boolean"},"scanId":{"description":"ScanID is the scanned document's file hash, as GET /v1/books/inbox and the scan\ndraft report it. It is the idempotency key: re-booking the same scan writes nothing.","type":"string"},"voucher":{"$ref":"#/components/schemas/Voucher","description":"Voucher is the reviewed voucher to post. Its source is FORCED to (scan, scanId)\nserver-side, so it can never be booked under another source's key."}},"type":"object"},"BookResponse":{"properties":{"posted":{"description":"Posted is true when this call wrote the voucher, false when the same scan had\nalready booked and nothing was written.","type":"boolean"},"scanId":{"description":"ScanID echoes the scan that was booked.","type":"string"}},"type":"object"},"BotRun":{"properties":{"runId":{"description":"RunID is the run's id in the bot runtime, and the node id its live VNC session\nis registered under.","type":"string"},"sessionUrl":{"description":"SessionURL is the live session the hanzo.app /vnc panel embeds to watch or\nattach to this run. Derived here from the run id, never sent by the runtime.","type":"string"},"startedAt":{"description":"StartedAt is when the run began, RFC 3339, as the runtime stamped it.","type":"string"},"status":{"description":"Status is the run's state as the runtime reports it; \"running\" when the runtime\nnames none of its own.","type":"string"},"surface":{"description":"Surface is what the bot drives: the desktop or terminal sandbox it runs in.","type":"string"},"task":{"description":"Task is the instruction the bot is executing.","type":"string"}},"type":"object"},"BotRuns":{"properties":{"bots":{"description":"Bots is the org's live runs. Always an array, never null.","items":{"$ref":"#/components/schemas/BotRun"},"type":"array"}},"type":"object"},"BotStopped":{"properties":{"runId":{"description":"RunID is the run that was stopped.","type":"string"},"status":{"description":"Status is the run's terminal state: \"stopped\".","type":"string"}},"type":"object"},"Breakdown":{"properties":{"available":{"description":"Available is false when the product-event table could not be read.","type":"boolean"},"items":{"description":"Items is the ranked buckets, most pageviews first. Empty rather than absent.","items":{"$ref":"#/components/schemas/BreakdownRow"},"type":"array"},"reason":{"description":"Reason says why the lens is unavailable. Omitted when it is available.","type":"string"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"}},"type":"object"},"BreakdownRow":{"properties":{"key":{"description":"Key is the bucket: a requested path, a referrer domain (\"(direct)\" for none or\na same-origin one), or a utm_source (\"(none)\" when absent).","type":"string"},"pageviews":{"description":"Pageviews is how many $pageview events fell in this bucket.","type":"integer"},"pct":{"description":"Pct is this bucket's share of ALL in-window pageviews, 0..100, one decimal —\nnot of the returned rows, so a top-N shows the long tail honestly.","type":"number"},"visitors":{"description":"Visitors is how many distinct people they came from.","type":"integer"}},"type":"object"},"CalendarPost":{"properties":{"body":{"description":"Body is the post text. Required.","type":"string"},"channel":{"description":"Channel is the target network: x, facebook, instagram, linkedin, tiktok,\nyoutube or threads. Required — a post must name where it goes.","type":"string"},"createdAt":{"description":"CreatedAt and UpdatedAt are unix seconds, both server-assigned.","type":"integer"},"error":{"description":"Error is the exact reason the last publish attempt failed — the honest\nrecord behind a \"failed\" status, never a faked success.","type":"string"},"id":{"description":"ID is the server-assigned post id (\"cal_\" + 128 random bits).","type":"string"},"publishedAt":{"description":"PublishedAt is when the publish succeeded; 0 until it does.","type":"integer"},"scheduledAt":{"description":"ScheduledAt is the unix publish time; 0 leaves the post a draft, and any\nvalue makes it \"scheduled\" for the durable sweep to pick up.","type":"integer"},"status":{"description":"Status is draft, scheduled, published, failed or canceled. Server-owned.","type":"string"},"title":{"description":"Title is the post's internal label, capped at 1024 bytes.","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"Campaign":{"properties":{"budget":{"description":"Budget and Spend are minor units (USD cents), clamped to \u003e= 0.","type":"integer"},"channel":{"description":"Channel is the delivery surface: email, sms, social, meta, google or\ntiktok. Empty means email.","type":"string"},"createdAt":{"description":"CreatedAt and UpdatedAt are unix seconds, both server-assigned.","type":"integer"},"id":{"description":"ID is the server-assigned campaign id (\"camp_\" + 128 random bits).","type":"string"},"name":{"description":"Name is the campaign's label. Required, trimmed, capped at 1024 bytes.","type":"string"},"objective":{"description":"Objective is the free-text goal (\"signups\"), capped at 1024 bytes.","type":"string"},"scheduledAt":{"description":"ScheduledAt is the unix send time; 0 means unscheduled. Setting it on a\ncampaign with no explicit status makes that status \"scheduled\".","type":"integer"},"spend":{"type":"integer"},"status":{"description":"Status is the lifecycle: draft, scheduled, active, paused or completed.\nEmpty means draft.","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"CampaignList":{"properties":{"data":{"description":"Data is the page; an empty array when the org has no matching campaign.","items":{"$ref":"#/components/schemas/Campaign"},"type":"array"}},"type":"object"},"CaptureBatch":{"properties":{"batch":{"items":{"$ref":"#/components/schemas/CaptureEvent"},"type":"array"},"events":{"items":{"$ref":"#/components/schemas/CaptureEvent"},"type":"array"}},"type":"object"},"CaptureEvent":{"properties":{"anonymousId":{"type":"string"},"channel":{"type":"string"},"clip":{"$ref":"#/components/schemas/ClipBody"},"currency":{"type":"string"},"distinctId":{"type":"string"},"environment":{"type":"string"},"error":{"$ref":"#/components/schemas/Exception"},"event":{"type":"string"},"groupId":{"type":"string"},"groupType":{"type":"string"},"kind":{"type":"string"},"level":{"type":"string"},"library":{"type":"string"},"libraryVersion":{"type":"string"},"log":{"$ref":"#/components/schemas/LogBody"},"messageId":{"type":"string"},"metric":{"$ref":"#/components/schemas/MetricBody"},"path":{"type":"string"},"personId":{"type":"string"},"product":{"type":"string"},"productId":{"type":"string"},"properties":{"additionalProperties":{},"type":"object"},"quantity":{"type":"integer"},"refCode":{"type":"string"},"referrer":{"type":"string"},"release":{"type":"string"},"resource":{"type":"string"},"revenue":{"type":"number"},"service":{"type":"string"},"sessionId":{"type":"string"},"signupWeek":{"type":"string"},"site":{"type":"string"},"span":{"$ref":"#/components/schemas/SpanBody"},"spanId":{"type":"string"},"timestamp":{"type":"string"},"traceId":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"},"utm":{"$ref":"#/components/schemas/UTM"}},"type":"object"},"CaptureResult":{"properties":{"accepted":{"type":"integer"},"dropped":{"type":"integer"}},"type":"object"},"Cart":{"properties":{"createdAt":{"description":"CreatedAt is when the cart was opened, RFC3339.","type":"string"},"currency":{"description":"Currency is the ISO 4217 code every amount below is denominated in.","type":"string"},"discountCents":{"description":"DiscountCents is what coupons and promotions took off, in whole cents.","type":"integer"},"email":{"description":"Email is the shopper's address, when the cart carries one.","type":"string"},"id":{"description":"ID is the cart's id — what every other cart op addresses it by, and what a\nstorefront persists against the browser session.","type":"string"},"items":{"description":"Items are the cart's lines, in the order they were added.","items":{"$ref":"#/components/schemas/CartItem"},"type":"array"},"lineTotalCents":{"description":"LineTotalCents is the sum of the lines before any discount, in whole cents.","type":"integer"},"order":{"description":"Order is the order this cart became, once checkout completed it. Empty\nuntil then, and its presence is what makes a cart final.","type":"string"},"shippingCents":{"description":"ShippingCents is the shipping charge, in whole cents. It stays zero until a\nshipping option is priced at checkout.","type":"integer"},"status":{"description":"Status is \"active\" for a cart still being filled, \"ordered\" once checkout\nturned it into an order, and \"discarded\" when the shopper abandoned it.","type":"string"},"store":{"description":"Store is the storefront the cart is being filled on.","type":"string"},"subtotalCents":{"description":"SubtotalCents is LineTotalCents less DiscountCents, in whole cents.","type":"integer"},"taxCents":{"description":"TaxCents is the sales tax, in whole cents. It stays zero until checkout\nresolves the shopper's tax region.","type":"integer"},"totalCents":{"description":"TotalCents is what the shopper pays: subtotal plus shipping plus tax, in\nwhole cents.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when the cart was last amended, RFC3339.","type":"string"},"user":{"description":"User is the signed-in shopper this cart belongs to, empty for a guest cart.","type":"string"}},"type":"object"},"CartItem":{"properties":{"free":{"description":"Free reports a line that costs nothing because a coupon or a promotion made\nit so, rather than because its price is zero.","type":"boolean"},"id":{"description":"ID is the line's identity — the variant id when the line is a variant,\notherwise the product id. It is what a subsequent set call addresses.","type":"string"},"kind":{"description":"Kind is \"variant\" when this line is a specific sellable variant and\n\"product\" when it is the product itself.","type":"string"},"name":{"description":"Name is the item's display name, cached onto the line when it was added so\na cart renders without a second read.","type":"string"},"priceCents":{"description":"PriceCents is the unit price in whole cents, cached at the moment the line\nwas added. The line's contribution to the cart is this times Quantity.","type":"integer"},"quantity":{"description":"Quantity is how many units of this item the cart holds.","type":"integer"},"sku":{"description":"SKU is the line's stock-keeping unit — the variant's when it has one,\notherwise the product's. Empty when neither carries one.","type":"string"}},"type":"object"},"CartItemSet":{"properties":{"id":{"description":"ID is the cart to amend, from the path.","type":"string"},"product":{"description":"Product names the catalog product to set, by its id or its URL slug. Give\nthis or Variant, never both; a request naming neither is refused.","type":"string"},"quantity":{"description":"Quantity is how many of that item the cart should hold AFTER this call — it\nis the resulting count, not a delta, so sending 3 twice leaves 3 and not 6.\nZERO REMOVES the line, which is the only way to take an item out.","type":"integer"},"variant":{"description":"Variant names the specific sellable variant to set, by its id or its SKU.\nPrefer it over Product for anything sold in sizes, colours or tiers — the\nprice and the stock are the variant's, not the product's.","type":"string"}},"type":"object"},"CartOpen":{"properties":{"currency":{"description":"Currency is the ISO 4217 code the cart is priced in, lower-cased. Empty\nmeans usd.","type":"string"},"email":{"description":"Email is the shopper's address, for a cart that belongs to someone who has\nnot signed in. It is what a guest checkout and an abandoned-cart follow-up\nkey on. Empty is fine.","type":"string"},"store":{"description":"Store is the storefront this cart is being filled on. Empty uses the org's\ndefault store, which is what a single-storefront merchant always wants.","type":"string"},"user":{"description":"User is the id of the signed-in shopper this cart belongs to, when there is\none. Empty means a guest cart identified only by its own id.","type":"string"}},"type":"object"},"Catalog":{"properties":{"connectorCount":{"type":"integer"},"connectors":{"items":{"$ref":"#/components/schemas/ConnectorMetadata"},"type":"array"}},"type":"object"},"CatalogEntry":{"properties":{"labels":{"description":"Labels is the starter's second suggested taxonomy, same treatment as Tags.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the starter's suggested handle. It is NOT taken in your org: the\ncatalog is shared reference content, so this name is free until you import it,\nand posting it under a name you already use appends a version to yours.","type":"string"},"prompt":{"description":"Prompt is the starter's full template body, ready to POST as-is. Entries too\nlarge to create (over 64 KiB) are dropped from this list rather than offered.","type":"string"},"tags":{"description":"Tags is the starter's suggested taxonomy, carried through unchanged if you\nimport it.","items":{"type":"string"},"type":"array"},"type":{"description":"Type labels the template's kind, defaulted to \"text\" for entries that declare\nnone.","type":"string"}},"type":"object"},"CategorySpend":{"properties":{"category":{"description":"Category is the bucket the ledger's own tag mapped to. An untagged or\nunrecognised line gets its own honest bucket rather than being folded away.","type":"string"},"cents":{"description":"Cents is what the org spent in that bucket over the window, in US cents.","type":"integer"},"count":{"description":"Count is how many ledger lines rolled up into it.","type":"integer"}},"type":"object"},"Channel":{"properties":{"disabled":{"type":"boolean"},"id":{"description":"the social integration id to target in a post","type":"string"},"name":{"type":"string"},"provider":{"description":"\"x\" | \"instagram\" | \"tiktok\" | ...","type":"string"}},"type":"object"},"ChannelMetric":{"properties":{"externalId":{"description":"ExternalID is the provider-side id of the execution the spend belongs to.\nAbsent until the channel has launched.","type":"string"},"kind":{"description":"Kind is which channel this row is: paid, organic or email. It is also the\nrow's identity — a campaign carries at most one channel per kind.","type":"string"},"platform":{"description":"Platform is the provider the spend was read from: meta, google, x, instagram,\nor the email provider.","type":"string"},"spendCents":{"description":"SpendCents is what the provider itself reports this channel spent, in CENTS.\n0 when the channel never launched, when no executor is wired for it, or when\nthe read failed — SpendError tells the last case apart from a genuine zero.","type":"integer"},"spendError":{"description":"SpendError is why this channel's spend could not be read (connector not\nconnected, provider error), as one secret-free line. Present only on failure;\nthe campaign total then simply omits this channel rather than failing.","type":"string"},"status":{"description":"Status is the channel's launch state on the campaign — pending, live, paused,\nfailed or unavailable. Only a live channel is asked for its spend at all.","type":"string"}},"type":"object"},"ChannelResult":{"properties":{"channel":{"description":"the social integration id targeted","type":"string"},"error":{"description":"short reason, when it failed","type":"string"},"externalId":{"description":"social post id, when it went out","type":"string"},"provider":{"description":"\"x\" | \"instagram\" | ... when known","type":"string"},"status":{"description":"\"distributed\" | \"scheduled\" | \"failed\"","type":"string"}},"type":"object"},"ChannelSpec":{"properties":{"account":{"description":"Account is the provider account this channel runs under: an ad-account, a page\nor a mailing-list id. An executor may replace it at launch with the account it\nactually used.","type":"string"},"detail":{"description":"Detail is the last outcome in one secret-free line — the failure reason, or\nwhat the executor reported. Absent when there is nothing to explain.","type":"string"},"externalId":{"description":"ExternalID is the provider-side id of the running execution, recorded by the\norchestrator at launch and handed back verbatim to read spend or to pause.\nServer-owned and absent until this channel has launched; anything a caller\nsends for it is dropped.","type":"string"},"kind":{"description":"Kind is the channel and the identity a campaign holds at most one of: paid,\norganic or email. It picks the executor the launch fans out to.","type":"string"},"platform":{"description":"Platform is the provider within the kind — meta, google, x, instagram, or the\nemail provider.","type":"string"},"status":{"description":"Status is this channel's own launch outcome, not the campaign's: pending (added,\nnever launched), live, paused, failed (Detail says why) or unavailable (no\nexecutor wired on this deployment). Server-owned — a caller can never assert it.","type":"string"}},"type":"object"},"Citation":{"properties":{"endLine":{"type":"integer"},"file":{"type":"string"},"line":{"type":"integer"},"repo":{"type":"string"},"symbol":{"type":"string"}},"type":"object"},"ClearReferenceOut":{"properties":{"cleared":{"description":"Cleared is false when your org held no such override — which is not an\nerror, it is the honest answer to a removal that had nothing to remove.","type":"boolean"},"key":{"description":"Key is the entry named.","type":"string"},"overrides":{"description":"Overrides is how many your org still holds in this set.","type":"integer"},"set":{"description":"Set is the set cleared in.","type":"string"}},"type":"object"},"ClipBody":{"properties":{"bytes":{"type":"integer"},"duration":{"type":"integer"},"object":{"type":"string"}},"type":"object"},"Cluster":{"properties":{"id":{"type":"string"},"idlePVCs":{"type":"integer"},"monthlyCents":{"type":"integer"},"name":{"type":"string"},"nodePools":{"type":"integer"},"nodes":{"type":"integer"},"pods":{"type":"integer"},"pools":{"items":{"$ref":"#/components/schemas/NodePool"},"type":"array"},"pvcs":{"type":"integer"},"pvs":{"type":"integer"},"region":{"type":"string"},"scanError":{"type":"string"},"scanned":{"type":"boolean"},"status":{"type":"string"},"version":{"type":"string"}},"type":"object"},"CodeFile":{"properties":{"id":{"description":"ID is the file's path RELATIVE to its session's artifact directory, which is\nalso how it is fetched: GET /v1/download/{session}/{id}.","type":"string"},"name":{"description":"Name is the display name. On an ANSWER it carries the `{session}/{id}`\nidentifier whole, because the client matches on that prefix.","type":"string"},"session_id":{"description":"SessionID is the other accepted spelling of the same fact on the way IN. Both\nare read; whichever is set wins.","type":"string"},"storage_session_id":{"description":"StorageSessionID names the session holding the bytes, and is the spelling the\nanswer always uses.","type":"string"}},"type":"object"},"CodeResult":{"properties":{"files":{"description":"Files are what this run CREATED OR CHANGED, decided by mtime against a marker\ntaken before the program started — so it is the run's output, not a listing of\nthe directory. Fetch each from GET /v1/download/{session}/{id}.","items":{"$ref":"#/components/schemas/CodeFile"},"type":"array"},"session_id":{"description":"SessionID is the sandbox this run used — the one that was passed in, or the\nfresh one that was leased. Pass it to the next run to keep the filesystem.","type":"string"},"stderr":{"description":"Stderr is what the program wrote to standard error, INCLUDING a compiler's\ndiagnostics and the trace of a program that exited non-zero. Its presence is\nnot a failed call.","type":"string"},"stdout":{"description":"Stdout is what the program wrote to standard output.","type":"string"}},"type":"object"},"CodeRun":{"properties":{"args":{"description":"Args become the PROGRAM's argv, never the compiler's. For the compiled\nlanguages the toolchain builds first and these are passed to the binary it\nproduced.","items":{"type":"string"},"type":"array"},"code":{"description":"Code is the WHOLE program, not a fragment: it is written to a single file and\nthat file is what runs, so a compiled language needs its entry point and an\ninterpreted one runs top to bottom.","type":"string"},"files":{"description":"Files are inputs the host already put in some session. Each names the session\nits bytes live in, which is usually — and ideally — the session this run wants.","items":{"$ref":"#/components/schemas/CodeFile"},"type":"array"},"lang":{"description":"Lang selects the toolchain, and with it the filename the code is written to\nand the line that runs it: py, js, ts, bash, r, php, go, rs, c, cpp, java, d,\nf90. Anything else is refused rather than guessed at — a run in the wrong\nlanguage fails somewhere deep in a compiler, which reads as an outage.","type":"string"},"runtime_session_hint":{"description":"RuntimeSessionHint is the stateful-session hint. It is carried so a client\nthat sends it is not silently misread, and it selects nothing here: every\nsession in this implementation is already a warm sandbox, so there is no\nsecond kind of runtime for a hint to choose between.","type":"string"},"session_id":{"description":"SessionID continues an EXISTING sandbox, which is what makes runs stateful:\nthe same filesystem, so one run's output file is the next run's input. Empty\nleases a fresh sandbox and the id it got comes back on the result.","type":"string"},"user_id":{"description":"UserID attributes the run inside the caller's org. It is a label, never a\ntenant: the org is resolved from the validated principal and a value here\ncannot widen what the run may reach.","type":"string"}},"required":["lang","code"],"type":"object"},"CodingStartIn":{"properties":{"agentRef":{"type":"string"},"base":{"type":"string"},"project":{"type":"string"},"prompt":{"type":"string"},"replyChannel":{"type":"string"},"replyThread":{"type":"string"},"repo":{"type":"string"},"subject":{"type":"string"},"targetId":{"type":"string"},"timeoutSeconds":{"type":"integer"}},"type":"object"},"CodingStarted":{"properties":{"branch":{"type":"string"},"repo":{"type":"string"},"routed":{"type":"boolean"},"sessionId":{"type":"string"},"targetId":{"type":"string"}},"type":"object"},"CollectOut":{"properties":{"balanceUsedCents":{"description":"BalanceUsedCents is how much was covered by prepaid balance.","type":"integer"},"cardChargedCents":{"description":"CardChargedCents is how much was charged to the card on file.","type":"integer"},"creditUsedCents":{"description":"CreditUsedCents is how much was covered by credit grants.","type":"integer"},"invoice":{"$ref":"#/components/schemas/InvoiceOut","description":"Invoice is the invoice AFTER the attempt — its status is the authority on\nwhat happened, not this struct's other fields."},"paid":{"description":"Paid reports whether the invoice is now settled in full. A false here with\nno error is a DECLINE: the invoice stays open and may be collected again.","type":"boolean"},"processorRef":{"description":"ProcessorRef is the processor's reference for any card charge — the field\nthat proves money moved at the gateway rather than only in our ledger.","type":"string"},"reason":{"description":"Reason explains a decline or partial collection. Empty on success.","type":"string"}},"type":"object"},"CommerceOverview":{"properties":{"aov":{"description":"AOV is average order value — Revenue/Orders, rounded to two places. Zero when\nthere were no orders.","type":"number"},"available":{"description":"Available is false when the product-event table could not be read — the lens is\nreported missing rather than as zeros that look like no sales.","type":"boolean"},"orders":{"description":"Orders is how many order_completed events landed in the window.","type":"integer"},"reason":{"description":"Reason says why the lens is unavailable. Omitted when it is available.","type":"string"},"revenue":{"description":"Revenue is the total those orders carried, in the events' own currency unit.","type":"number"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"}},"type":"object"},"Company":{"properties":{"arr":{"description":"ARR is annual recurring revenue in minor units (cents) of Currency.","type":"integer"},"city":{"description":"City is the head-office city.","type":"string"},"country":{"description":"Country is the head-office country.","type":"string"},"createdAt":{"description":"CreatedAt is the unix second the company was created. Server-owned.","type":"integer"},"currency":{"description":"Currency is the ISO code ARR is denominated in; a write that names none\nstores USD.","type":"string"},"domainName":{"description":"DomainName is the company's primary domain, e.g. \"acme.com\".","type":"string"},"employees":{"description":"Employees is the headcount.","type":"integer"},"id":{"description":"ID is the server-minted company id (\"comp_\" + 128 random bits).","type":"string"},"idealCustomerProfile":{"description":"ICP marks the company as an ideal-customer-profile fit.","type":"boolean"},"linkedinLink":{"description":"Linkedin is the company's LinkedIn URL.","type":"string"},"name":{"description":"Name is the company name.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second of the last write. Server-owned.","type":"integer"},"xLink":{"description":"XLink is the company's X (Twitter) URL.","type":"string"}},"type":"object"},"Completion":{"properties":{"detail":{"type":"string"},"kind":{"type":"integer"},"label":{"type":"string"}},"type":"object"},"Config":{"properties":{"max_age":{"description":"MaxAge caps message age, e.g. \"24h\" or \"7d\"; \"0\" (default) is unlimited.","type":"string"},"max_bytes":{"description":"MaxBytes caps the stream's total stored bytes; -1 (default) is unlimited.","type":"integer"},"max_msg_size":{"description":"MaxMsgSize caps one message's size in bytes; -1 (default) is the broker's limit.","type":"integer"},"max_msgs":{"description":"MaxMsgs caps the number of stored messages; -1 (default) is unlimited.","type":"integer"},"name":{"description":"Name is the stream name, unique within the org (alphanumeric, hyphens, underscores).","type":"string"},"num_replicas":{"description":"Replicas is the number of stream replicas (1–5); this plane runs 1.","type":"integer"},"retention":{"description":"Retention is the retention policy: limits (default), interest, or workqueue.","type":"string"},"storage":{"description":"Storage is the storage backend: file (default) or memory.","type":"string"},"subjects":{"description":"Subjects are the org-relative subjects bound to this stream (wildcards supported). Default: the stream name.","items":{"type":"string"},"type":"array"}},"type":"object"},"ConnectorAction":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"name":{"type":"string"},"props":{"items":{"$ref":"#/components/schemas/PropSpec"},"type":"array"}},"type":"object"},"ConnectorAuth":{"properties":{"required":{"type":"boolean"},"type":{"type":"string"}},"type":"object"},"ConnectorMetadata":{"properties":{"actions":{"items":{"$ref":"#/components/schemas/ConnectorAction"},"type":"array"},"auth":{"$ref":"#/components/schemas/ConnectorAuth"},"categories":{"items":{"type":"string"},"type":"array"},"description":{"type":"string"},"displayName":{"type":"string"},"logoUrl":{"type":"string"},"name":{"type":"string"},"triggers":{"items":{"$ref":"#/components/schemas/ConnectorTrigger"},"type":"array"},"version":{"type":"string"}},"type":"object"},"ConnectorTrigger":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"name":{"type":"string"},"props":{"items":{"$ref":"#/components/schemas/PropSpec"},"type":"array"},"strategy":{"type":"string"}},"type":"object"},"Consumer":{"properties":{"ack_floor":{"$ref":"#/components/schemas/Sequences","description":"AckFloor is the highest contiguously acknowledged sequence pair."},"config":{"$ref":"#/components/schemas/Durable","description":"Config is the consumer's configuration."},"created":{"description":"Created is when the consumer was created.","format":"date-time","type":"string"},"delivered":{"$ref":"#/components/schemas/Sequences","description":"Delivered is the highest delivered sequence pair."},"name":{"description":"Name is the consumer name.","type":"string"},"num_ack_pending":{"description":"AckPending is the number of delivered, not yet acknowledged messages.","type":"integer"},"num_pending":{"description":"Pending is the number of messages yet to be delivered.","type":"integer"},"num_redelivered":{"description":"Redelivered is the number of messages currently being redelivered.","type":"integer"},"num_waiting":{"description":"Waiting is the number of pull requests waiting for messages.","type":"integer"},"stream_name":{"description":"Stream is the stream this consumer reads.","type":"string"}},"type":"object"},"Contact":{"properties":{"city":{"description":"City is where the person is based.","type":"string"},"companyId":{"description":"CompanyID links the contact to one of the org's companies; empty when the\ncontact stands alone, and cleared when its company is deleted. A write\nnaming a company the org does not own is refused with 422.","type":"string"},"createdAt":{"description":"CreatedAt is the unix second the contact was created. Server-owned.","type":"integer"},"email":{"description":"Email is the person's email address.","type":"string"},"firstName":{"description":"FirstName is the person's given name.","type":"string"},"id":{"description":"ID is the server-minted contact id (\"cont_\" + 128 random bits).","type":"string"},"jobTitle":{"description":"JobTitle is the person's role at their company.","type":"string"},"lastName":{"description":"LastName is the person's family name.","type":"string"},"linkedinLink":{"description":"Linkedin is the person's LinkedIn URL.","type":"string"},"phone":{"description":"Phone is the person's phone number.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second of the last write. Server-owned.","type":"integer"},"xLink":{"description":"XLink is the person's X (Twitter) URL.","type":"string"}},"type":"object"},"ContextBundle":{"properties":{"budgetTokens":{"type":"integer"},"query":{"type":"string"},"repo":{"type":"string"},"spans":{"items":{"$ref":"#/components/schemas/Span"},"type":"array"},"usedTokens":{"type":"integer"}},"type":"object"},"CordonIn":{"properties":{"cordon":{"description":"Cordon true marks the node unschedulable; false restores it.","type":"boolean"},"drain":{"description":"Drain additionally evicts the pods already running there.","type":"boolean"},"id":{"description":"ID is the node's droplet id, from the path.","type":"string"}},"type":"object"},"Cost":{"properties":{"dropletsMonthly":{"type":"integer"},"loadBalancersMonthly":{"type":"integer"},"reclaimableMonthly":{"type":"integer"},"totalMonthly":{"type":"integer"},"volumesMonthly":{"type":"integer"},"wastedMonthly":{"description":"WastedMonthly is what the fleet pays every month for provisioned-but-empty space on\nthe volumes a kubelet actually measured.\n\nIt is NOT ReclaimableMonthly and must never be added to it. Reclaimable is money a\nbutton on this board collects, by deleting volumes proven to belong to no one.\nWasted is money locked inside volumes that are IN USE and holding live data:\nDigitalOcean can only ever grow a volume, so collecting it means copying a database\nonto a smaller one. See shrinkRecipe.\n\nIt is also a LOWER BOUND — unmeasured volumes contribute nothing.","type":"integer"}},"type":"object"},"Curriculum":{"properties":{"steps":{"items":{"$ref":"#/components/schemas/JourneyStep"},"type":"array"},"title":{"type":"string"},"version":{"type":"string"}},"type":"object"},"CustomerDetailData":{"properties":{"apiKeys":{"type":"integer"},"balanceCents":{"type":"integer"},"created":{"type":"string"},"display":{"type":"string"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"ownerEmail":{"type":"string"},"plan":{"type":"string"},"spendCents":{"type":"integer"},"status":{"type":"string"},"transactions":{"items":{"$ref":"#/components/schemas/CustomerTxn"},"type":"array"},"users":{"items":{"$ref":"#/components/schemas/CustomerUser"},"type":"array"}},"type":"object"},"CustomerDetailOut":{"properties":{"data":{"$ref":"#/components/schemas/CustomerDetailData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"CustomerRow":{"properties":{"balanceCents":{"type":"integer"},"created":{"type":"string"},"display":{"type":"string"},"lastActive":{"type":"string"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"ownerEmail":{"type":"string"},"plan":{"type":"string"},"spendCents":{"type":"integer"},"status":{"description":"\"active\" | \"suspended\"","type":"string"},"users":{"type":"integer"}},"type":"object"},"CustomerTxn":{"properties":{"cents":{"type":"integer"},"currency":{"type":"string"},"id":{"type":"string"},"notes":{"type":"string"},"time":{"type":"string"},"type":{"description":"\"deposit\" (credit) | \"withdraw\" (usage)","type":"string"}},"type":"object"},"CustomerUser":{"properties":{"created":{"type":"string"},"email":{"type":"string"},"forbidden":{"type":"boolean"},"hasApiKey":{"type":"boolean"},"isAdmin":{"type":"boolean"},"lastSignin":{"type":"string"},"name":{"type":"string"}},"type":"object"},"CustomersOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/CustomerRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"D1Query":{"properties":{"params":{"items":{},"type":"array"},"sql":{"type":"string"}},"type":"object"},"DefRow":{"properties":{"definition":{},"key":{"type":"string"},"updated_at":{"type":"string"},"updated_by":{"type":"string"},"version":{"type":"integer"}},"type":"object"},"DeliveryRow":{"properties":{"attempt":{"type":"integer"},"created":{"type":"string"},"delivery":{"type":"string"},"durationMs":{"type":"integer"},"endpoint":{"type":"string"},"error":{"type":"string"},"httpStatus":{"type":"integer"},"status":{"type":"string"},"subject":{"type":"string"}},"type":"object"},"DestinationField":{"properties":{"example":{"description":"a sample value of the right shape (\"G-XXXXXXX\"), when one helps","type":"string"},"key":{"description":"the camelCase key on both the connect body and the stored config","type":"string"},"label":{"description":"human label for the console card's input","type":"string"},"required":{"description":"when true, a connect that leaves it empty is refused 400","type":"boolean"}},"type":"object"},"DestinationStatus":{"properties":{"account":{"description":"Account is the operator's own label for the connected account, as supplied on\nconnect. Absent when unset.","type":"string"},"category":{"description":"groups the card: Analytics | Advertising","type":"string"},"config":{"additionalProperties":{"type":"string"},"description":"Config is the org's stored NON-SECRET configuration — the measurement/pixel\nids keyed by DestinationField.Key. A secret is never in here; secrets live in\nKMS and only their names are published, in Secrets.","type":"object"},"connected":{"description":"Connected is true when this org has a stored row for the platform — it has\nbeen configured here at least once. It says nothing about whether a\ncredential still resolves; that is Live.","type":"boolean"},"enabled":{"description":"Enabled is whether the fan-out forwards to this destination. False on a\ndestination that is connected but paused, and on one never connected.","type":"boolean"},"fields":{"description":"Fields are the non-secret inputs this platform needs, which the console card\nrenders and the connect body fills.","items":{"$ref":"#/components/schemas/DestinationField"},"type":"array"},"live":{"description":"Live is whether a credential resolves RIGHT NOW: a KMS-sealed secret for this\norg, else the integrations connection named by the platform's Fallback, else\nno credential needed at all (a public-ingest sink like Umami). False on a\nconnected destination whose secret has gone missing — Connected \u0026\u0026 !Live is\nexactly the \"reconnect me\" state.","type":"boolean"},"name":{"description":"the platform's display name (\"Google Analytics 4\")","type":"string"},"platform":{"description":"the platform slug, and the path segment every route addresses it by","type":"string"},"secrets":{"description":"Secrets are the KMS secret NAMES this platform custodies for the org — names\nonly, never values. The connect body accepts each under its camelCase form.","items":{"type":"string"},"type":"array"}},"type":"object"},"DeviceSignals":{"properties":{"arch":{"type":"string"},"cpuid":{"description":"CPUID is a CPU/board identifier string.","type":"string"},"disk_serial":{"description":"DiskSerial of the boot/root volume.","type":"string"},"hostname":{"description":"Hostname is a weak signal, used only as a tiebreaker.","type":"string"},"install_id":{"description":"InstallID is a per-install random the agent persists locally on first run.","type":"string"},"machine_id":{"description":"MachineID is a stable per-host id (e.g. /etc/machine-id, IOPlatformUUID,\nMachineGuid). Strongest single signal where present.","type":"string"},"macs":{"description":"MAC addresses of stable interfaces (order-insensitive; we sort).","items":{"type":"string"},"type":"array"},"os":{"description":"OS / Arch coarse platform tags.","type":"string"}},"type":"object"},"Diagnostic":{"properties":{"code":{"type":"object"},"message":{"type":"string"},"range":{"$ref":"#/components/schemas/Range"},"severity":{"type":"integer"},"source":{"type":"string"}},"type":"object"},"DoCost":{"properties":{"accountBalanceCents":{"type":"integer"},"avgDailyBurnCents":{"type":"integer"},"configured":{"type":"boolean"},"creditRemainingCents":{"type":"integer"},"error":{"type":"string"},"generatedAt":{"type":"string"},"history":{"items":{"$ref":"#/components/schemas/DoHistoryPoint"},"type":"array"},"monthToDateSpendCents":{"type":"integer"}},"type":"object"},"DoHistoryPoint":{"properties":{"amountCents":{"type":"integer"},"date":{"type":"string"},"description":{"type":"string"},"type":{"type":"string"}},"type":"object"},"DocField":{"properties":{"default":{"type":"string"},"fetchFrom":{"type":"string"},"fieldname":{"type":"string"},"fieldtype":{"type":"string"},"hidden":{"type":"boolean"},"inListView":{"type":"boolean"},"label":{"type":"string"},"options":{"type":"string"},"readOnly":{"type":"boolean"},"reqd":{"type":"boolean"},"unique":{"type":"boolean"}},"type":"object"},"DocPerm":{"properties":{"cancel":{"type":"boolean"},"create":{"type":"boolean"},"delete":{"type":"boolean"},"read":{"type":"boolean"},"role":{"type":"string"},"submit":{"type":"boolean"},"write":{"type":"boolean"}},"type":"object"},"DocType":{"properties":{"autoname":{"type":"string"},"createdAt":{"type":"integer"},"fields":{"items":{"$ref":"#/components/schemas/DocField"},"type":"array"},"isSingle":{"type":"boolean"},"isSubmittable":{"type":"boolean"},"module":{"type":"string"},"name":{"type":"string"},"permissions":{"items":{"$ref":"#/components/schemas/DocPerm"},"type":"array"},"titleField":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"Drift":{"properties":{"disabled":{"type":"integer"},"down":{"type":"integer"},"drifted":{"type":"boolean"},"name":{"type":"string"},"running":{"type":"integer"},"versions":{"items":{"type":"string"},"type":"array"}},"type":"object"},"DriftFlag":{"properties":{"kind":{"type":"string"},"message":{"type":"string"},"severity":{"type":"string"}},"type":"object"},"DropletIn":{"properties":{"disk":{"description":"Disk requests a PERMANENT resize that grows the disk. DO can never resize such a\ndroplet down again, so it defaults false — a CPU/RAM-only change, reversible.","type":"boolean"},"id":{"description":"ID is the DO droplet id, from the path. Numeric.","type":"string"},"size":{"description":"Size is the target DigitalOcean size slug on resize, e.g. \"s-4vcpu-8gb\".","type":"string"}},"type":"object"},"Durable":{"properties":{"ack_policy":{"description":"Ack is the acknowledgment policy: explicit (default), all, or none.","type":"string"},"ack_wait":{"description":"AckWait is how long the broker waits for an ack before redelivering, e.g. \"30s\" (default).","type":"string"},"deliver_policy":{"description":"Deliver is where delivery starts: all (default), last, new, by_start_sequence, by_start_time, or last_per_subject.","type":"string"},"description":{"description":"Description says what this consumer is for.","type":"string"},"durable_name":{"description":"Name is the durable consumer name (alphanumeric, hyphens, underscores).","type":"string"},"filter_subject":{"description":"Filter delivers only messages on this org-relative subject (wildcards supported).","type":"string"},"max_ack_pending":{"description":"MaxAckPending caps unacknowledged messages in flight (default 1000).","type":"integer"},"max_deliver":{"description":"MaxDeliver caps delivery attempts per message; -1 (default) is unlimited.","type":"integer"},"opt_start_seq":{"description":"StartSeq is the starting sequence for deliver_policy by_start_sequence.","type":"integer"},"opt_start_time":{"description":"StartTime is the starting instant for deliver_policy by_start_time.","format":"date-time","type":"string"},"replay_policy":{"description":"Replay is the replay pacing: instant (default) or original.","type":"string"}},"type":"object"},"Endpoint":{"properties":{"created":{"type":"string"},"deliveries7d":{"description":"Deliveries7d / Failures7d are cheap usage counters computed from the delivery log\nover usageWindow (not stored columns) and populated ONLY on list/get. They are 0\nwhen there is no delivery history — never omitempty, so the console always sees them.","type":"integer"},"description":{"type":"string"},"events":{"items":{"type":"string"},"type":"array"},"failures7d":{"type":"integer"},"id":{"type":"string"},"org":{"type":"string"},"secret":{"type":"string"},"status":{"type":"string"},"updated":{"type":"string"},"url":{"type":"string"}},"type":"object"},"EnrollInput":{"properties":{"address":{"description":"Address is a single recipient, normalized (lower-cased, trimmed) before\nuse. Give this OR audienceId, never both and never neither.","type":"string"},"audienceId":{"description":"AudienceID fans the sequence out over a saved audience, resolved live to\nthe org's mailable customers. Email only.","type":"string"},"channel":{"description":"Channel is the delivery surface; empty means email. An audience resolves\nmailboxes, so an audience enroll must be email.","type":"string"},"id":{"description":"ID is the sequence id from the path.","type":"string"}},"type":"object"},"EnrollResult":{"properties":{"alreadyEnrolled":{"description":"AlreadyEnrolled is how many this sequence had already taken and were left\nalone.","type":"integer"},"enrolled":{"description":"Enrolled is how many started a walk on this call.","type":"integer"},"enrollmentId":{"description":"EnrollmentID names the walk, and is present ONLY for a single-address\nenroll — a fan-out has many, and reporting one of them would be a lie.","type":"string"},"resolved":{"description":"Resolved is how many addresses the request named — 1 for an address, the\naudience's deliverable count for an audience.","type":"integer"}},"type":"object"},"Enrollment":{"properties":{"address":{"description":"Address is the normalized (lower-cased, trimmed) recipient.","type":"string"},"channel":{"description":"Channel is the delivery surface the steps go out on.","type":"string"},"currentStep":{"description":"CurrentStep is the index of the step that sends next.","type":"integer"},"enrolledAt":{"description":"EnrolledAt and UpdatedAt are unix seconds.","type":"integer"},"id":{"description":"ID is the server-assigned enrollment id (\"enr_\" + 128 random bits).","type":"string"},"nextRunAt":{"description":"NextRunAt is the unix time the current step comes due; 0 once the walk has\nended. It IS the schedule — durable in SQLite, so it survives restarts.","type":"integer"},"sequenceId":{"description":"SequenceID is the sequence being walked.","type":"string"},"status":{"description":"Status is active, completed or canceled.","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"EnrollmentList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Enrollment"},"type":"array"}},"type":"object"},"Entry":{"properties":{"archetype":{"type":"string"},"description":{"type":"string"},"forkable":{"description":"Forkable is NOT omitempty: false is an answer here, not a missing field.\nOmitted, a client could not tell \"you cannot fork this\" from \"nobody said\".","type":"boolean"},"id":{"type":"string"},"kind":{"description":"repo | site","type":"string"},"language":{"type":"string"},"license":{"type":"string"},"name":{"type":"string"},"note":{"description":"Note is why a row is NOT in the published catalog, set by the admission gate\n(gate.go) on the sites it holds back. It is the difference between a demo\nthat silently vanished from the public lens and one whose owner can read the\nreason and fix it. A published row never carries one.","type":"string"},"org":{"description":"hanzo | lux | zoo","type":"string"},"origin":{"description":"Origin is WHAT THIS IS TO YOU: template | community | third-party | product\n(origin.go owns the four nouns and derives them). Not omitempty, for the\nsame reason Forkable is not: every row has an answer, and a missing one is\nexactly the flattening this field exists to end.","type":"string"},"repo":{"description":"source","type":"string"},"scope":{"description":"Scope is provenance, not storage: \"public\" for a row from the published\ncorpus, \"org\" for one only this caller can see. A UI that cannot tell them\napart cannot warn before sharing a link.","type":"string"},"stars":{"type":"integer"},"template":{"description":"lineage, if forked from one","type":"string"},"title":{"type":"string"},"updated":{"type":"string"},"upstream":{"description":"Upstream/License credit the third-party work an entry was published from:\nthe difference between \"this org built it\" and \"somebody else built it and\nwe are showing it to you\".\n\nWHO built it is Org, above — the account that paid for the project. There\nwas once a separate admin-gated `official` boolean here claiming the same\nthing, and because it was gated it disagreed: apps Hanzo wrote and hosts\nwere published by a script holding an ordinary org token, so it stayed\nfalse on all of them and this directory filed our own work as somebody\nelse's. A field that restates an unforgeable fact can only ever be the\nwrong copy of it.","type":"string"},"url":{"description":"live, if it is deployed","type":"string"}},"type":"object"},"EnvVarJSON":{"properties":{"key":{"type":"string"},"secret":{"type":"boolean"},"value":{"type":"string"}},"type":"object"},"Event":{"properties":{"distinctId":{"type":"string"},"event":{"type":"string"},"properties":{"additionalProperties":{},"type":"object"},"time":{"type":"string"},"type":{"type":"string"}},"type":"object"},"Exception":{"properties":{"frames":{"items":{"$ref":"#/components/schemas/Frame"},"type":"array"},"handled":{"type":"boolean"},"message":{"type":"string"},"stack":{"type":"string"},"type":{"type":"string"}},"type":"object"},"Experiment":{"properties":{"canonical":{"type":"boolean"},"cost_usd":{"type":"number"},"endpoint":{"type":"string"},"git_branch":{"type":"string"},"git_dirty":{"type":"boolean"},"git_sha":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"lib_versions":{},"meta":{},"metric":{"type":"string"},"n":{"type":"integer"},"n_total":{"type":"integer"},"project":{"type":"string"},"publishable":{"type":"boolean"},"revision":{"description":"original | corrected | retracted","type":"string"},"status":{"description":"planning | running | complete | faulted","type":"string"},"subject":{"type":"string"},"task":{"type":"string"},"trainable":{"type":"boolean"},"ts":{"type":"integer"},"value":{"type":"number"},"visibility":{"type":"string"}},"type":"object"},"Extracted":{"properties":{"category":{"description":"proposed slug (software|cloud|office|…)","type":"string"},"currency":{"type":"string"},"issuedAt":{"description":"YYYY-MM-DD","type":"string"},"lineItems":{"items":{"$ref":"#/components/schemas/LineItem"},"type":"array"},"merchant":{"type":"string"},"note":{"type":"string"},"taxCents":{"type":"integer"},"totalCents":{"type":"integer"}},"type":"object"},"Field":{"properties":{"key":{"type":"string"},"label":{"type":"string"}},"type":"object"},"Figure":{"properties":{"label":{"description":"Label names the metric, e.g. \"MRR\" or \"Runway\".","type":"string"},"period":{"description":"Period is the window the figure covers, e.g. \"2026-07\" or \"all-time\".","type":"string"},"value":{"description":"Value is the figure already formatted through books' own money formatter, so a\nconsumer never re-derives it.","type":"string"}},"type":"object"},"Filing":{"properties":{"at":{"description":"At is the unix second the filing record was written.","type":"integer"},"note":{"description":"Note explains a filing Hanzo did not perform itself: what remains to be done\nand by whom.","type":"string"},"provider":{"description":"Provider is the filing partner that performed the filing, or \"manual\" when no\npartner is wired.","type":"string"},"ref":{"description":"Ref is the partner's or the state's filing reference. Empty when nothing was\nactually filed — no filing id is ever fabricated.","type":"string"},"status":{"description":"Status is manual (no partner wired — a registered agent files out-of-band),\nsubmitted (the partner accepted it, awaiting the state), filed (the state\naccepted it) or rejected.","type":"string"}},"type":"object"},"Filters":{"properties":{"keywords":{"description":"Keywords keeps only items whose TITLE contains one of these,\ncase-insensitively. They are also the GDELT queries the feed fans out to,\none per keyword of three characters or more — so a keyword both widens what\nis fetched and narrows what is kept.","items":{"type":"string"},"type":"array"},"regions":{"description":"Regions keeps only items whose TITLE contains one of these, matched\ncase-insensitively as a substring. Empty keeps every region.","items":{"type":"string"},"type":"array"},"sources":{"description":"Sources keeps only items whose outlet name contains one of these,\ncase-insensitively. Empty keeps every outlet.","items":{"type":"string"},"type":"array"}},"type":"object"},"FinanceCost":{"properties":{"configured":{"type":"boolean"},"digitalocean":{"$ref":"#/components/schemas/DoCost"},"error":{"type":"string"},"period":{"type":"string"},"totalCents":{"type":"integer"},"vendors":{"items":{"$ref":"#/components/schemas/Vendor"},"type":"array"}},"type":"object"},"FinanceData":{"properties":{"cost":{"$ref":"#/components/schemas/FinanceCost"},"derived":{"$ref":"#/components/schemas/FinanceDerived"},"generatedAt":{"type":"string"},"revenue":{"$ref":"#/components/schemas/FinanceRevenue"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"}},"type":"object"},"FinanceDerived":{"properties":{"grossMarginCents":{"type":"integer"},"grossMarginPct":{"type":"number"},"profitable":{"type":"boolean"},"runwayDays":{"type":"number"}},"type":"object"},"FinanceOut":{"properties":{"data":{"$ref":"#/components/schemas/FinanceData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"FinanceRevenue":{"properties":{"configured":{"type":"boolean"},"creditsConsumedCents":{"type":"integer"},"mrrCents":{"type":"integer"},"totalRevenueCents":{"type":"integer"}},"type":"object"},"FinancialPackage":{"properties":{"balanceSheet":{"$ref":"#/components/schemas/BalanceSheet"},"from":{"type":"string"},"generatedAt":{"type":"string"},"gl":{"items":{"$ref":"#/components/schemas/GLRow"},"type":"array"},"org":{"type":"string"},"pnl":{"$ref":"#/components/schemas/PnL"},"to":{"type":"string"},"trialBalance":{"$ref":"#/components/schemas/TrialBalance"}},"type":"object"},"Finding":{"properties":{"cluster":{"type":"string"},"detail":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"monthlyCents":{"type":"integer"},"resource":{"type":"string"},"severity":{"type":"string"},"title":{"type":"string"}},"type":"object"},"FingerprintRequest":{"properties":{"signals":{"$ref":"#/components/schemas/DeviceSignals","description":"Signals is the host material the client agent collected. Which fields\nactually participate in the binding is deliberately unspecified — send\neverything available and let the server decide."}},"type":"object"},"FingerprintResponse":{"properties":{"fingerprint":{"description":"Fingerprint is the OPAQUE binding value to pass to POST /v1/licensing/issue.\nIt is one-way: the raw signals cannot be recovered from it and are never\nechoed back.","type":"string"},"version":{"description":"Version is the binding algorithm revision, so a stored fingerprint stays\nrecognizable across a recipe rotation.","type":"string"}},"type":"object"},"Flow":{"properties":{"created":{"type":"integer"},"externalId":{"type":"string"},"folderId":{"type":"string"},"id":{"type":"string"},"metadata":{},"projectId":{"description":"projectId == org (server-derived)","type":"string"},"publishedVersionId":{"type":"string"},"status":{"type":"string"},"updated":{"type":"integer"}},"type":"object"},"FlowAction":{"properties":{"displayName":{"type":"string"},"name":{"type":"string"},"nextAction":{"$ref":"#/components/schemas/FlowAction"},"settings":{"$ref":"#/components/schemas/StepSettings"},"skip":{"type":"boolean"},"type":{"description":"PIECE | CODE | ROUTER | LOOP_ON_ITEMS","type":"string"},"valid":{"type":"boolean"}},"type":"object"},"FlowRun":{"properties":{"created":{"type":"integer"},"finishTime":{"type":"integer"},"flowId":{"type":"string"},"flowVersionId":{"type":"string"},"id":{"type":"string"},"startTime":{"type":"integer"},"status":{"type":"string"},"updated":{"type":"integer"}},"type":"object"},"FlowTrigger":{"properties":{"displayName":{"type":"string"},"name":{"type":"string"},"nextAction":{"$ref":"#/components/schemas/FlowAction"},"settings":{"$ref":"#/components/schemas/StepSettings"},"strategy":{"type":"string"},"type":{"description":"PIECE_TRIGGER | EMPTY","type":"string"},"valid":{"type":"boolean"}},"type":"object"},"FlowVersion":{"properties":{"created":{"type":"integer"},"displayName":{"type":"string"},"flowId":{"type":"string"},"id":{"type":"string"},"schemaVersion":{"type":"string"},"state":{"type":"string"},"trigger":{"$ref":"#/components/schemas/FlowTrigger"},"updated":{"type":"integer"},"valid":{"type":"boolean"}},"type":"object"},"Formation":{"properties":{"alreadyIncorporated":{"description":"AlreadyIncorporated declares an org that already has a legal entity, which\ntakes the import path (structure → import → company) instead of forming one.","type":"boolean"},"capTableImported":{"description":"CapTableImported reports whether the existing company's cap table has been\nimported onto the canonical cap table.","type":"boolean"},"createdAt":{"description":"CreatedAt is the unix second the formation was opened.","type":"integer"},"documentIds":{"description":"DocumentIDs are the data room ids of the GENERATED formation documents.","items":{"type":"string"},"type":"array"},"esignRef":{"description":"EsignRef is the e-signature provider's reference for the signature request.","type":"string"},"filing":{"$ref":"#/components/schemas/Filing","description":"Filing is the state-of-incorporation filing record, once documents exist."},"founders":{"description":"Founders is every founding stakeholder, with its equity split and KYC state.","items":{"$ref":"#/components/schemas/Founder"},"type":"array"},"genesis":{"$ref":"#/components/schemas/Genesis","description":"Genesis is the cap-table equity genesis, once recorded."},"imported":{"description":"Imported reports whether the existing company's corporate documents have been\ningested into the org's data room.","type":"boolean"},"importedDocs":{"description":"ImportedDocs are the data room ids of the documents ingested from Drive.","items":{"type":"string"},"type":"array"},"jurisdiction":{"description":"Jurisdiction is the state of formation: DE or WY.","type":"string"},"name":{"description":"Name is the company name the entity is being formed under.","type":"string"},"org":{"description":"Org is the owning org — the tenant key, and the reason there is exactly one\nformation per org.","type":"string"},"paid":{"description":"Paid reports whether the one-time formation fee has been charged.","type":"boolean"},"paymentRef":{"description":"PaymentRef is the billing reference recorded for the charged formation fee on\nthe org's own ledger.","type":"string"},"signed":{"description":"Signed reports whether the formation documents have come back signed — the\ne-signature provider's answer, which a real provider's webhook drives.","type":"boolean"},"stage":{"description":"Stage is the machine's current state: structure, founders, payment, documents,\nesign or genesis on the formation path, import on the skip path, and company\nat the terminal.","type":"string"},"structure":{"description":"Structure is the legal entity being formed: c-corp, llc or dao-llc.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second of the most recent write to the formation.","type":"integer"}},"type":"object"},"Founder":{"properties":{"decidedBy":{"description":"DecidedBy is who settled a terminal KYC status: the provider name, or a\nreviewer's user id.","type":"string"},"email":{"description":"Email is the founder's email, and the key a KYC decision addresses a founder\nby — POST /v1/company/kyc/decision matches on it.","type":"string"},"equityBps":{"description":"EquityBps is the founder's ownership in basis points, 0–10000 (1% == 100 bps,\nso 10000 is the whole company). The founders' shares seed the cap-table genesis.","type":"integer"},"kycRef":{"description":"KYCRef is the idv provider's session reference for this founder.","type":"string"},"kycStatus":{"description":"KYCStatus is the founder's identity-verification state: pending, verified (a\nreal idv provider reported a pass), reviewer_confirmed (a privileged reviewer\nconfirmed out-of-band) or failed. The payment stage is unreachable until every\nfounder passes.","type":"string"},"name":{"description":"Name is the founder's full legal name, as it appears on the formation documents.","type":"string"}},"type":"object"},"Frame":{"properties":{"column":{"type":"integer"},"file":{"type":"string"},"function":{"type":"string"},"line":{"type":"integer"}},"type":"object"},"Funnel":{"properties":{"available":{"type":"boolean"},"orders":{"type":"integer"},"pageviews":{"type":"integer"},"revenue":{"type":"number"},"signups":{"type":"integer"},"visitors":{"type":"integer"},"windowDays":{"type":"integer"}},"type":"object"},"GLRow":{"properties":{"account":{"type":"string"},"against":{"type":"string"},"credit":{"type":"integer"},"debit":{"type":"integer"},"id":{"type":"integer"},"postingAt":{"type":"string"},"remarks":{"type":"string"},"sourceId":{"type":"string"},"sourceKind":{"type":"string"}},"type":"object"},"GPU":{"properties":{"memory":{"description":"VRAM bytes, 0 = unknown","type":"integer"},"model":{"description":"\"GB10\", \"8060S\", \"RTX 4090\"","type":"string"},"vendor":{"description":"nvidia | amd | apple | intel | ...","type":"string"}},"type":"object"},"GenerateInput":{"properties":{"brief":{"description":"the brief/goal driving copy generation","type":"string"},"channels":{"description":"target channels (SocialPost)","type":"string"},"design":{"description":"studio design slug (asset source)","type":"string"},"doctype":{"description":"Campaign | SocialPost | Asset","type":"string"},"kind":{"description":"asset kind: ecom|product|lifestyle|hover|hero","type":"string"},"model":{"description":"optional zen model override (copy)","type":"string"},"product":{"description":"commerce product handle (copy context)","type":"string"},"project":{"description":"brand/site sub-scope (billing + tenancy axis)","type":"string"},"source_media":{"description":"asset source image (design CAD/photo)","type":"string"},"title":{"description":"optional explicit title","type":"string"},"tone":{"description":"tone override for a single draft","type":"string"},"voice":{"description":"brand-voice guidance for the copy director","type":"string"}},"type":"object"},"GenerateResult":{"properties":{"doctype":{"description":"the marketing type the draft was filed as","type":"string"},"name":{"description":"the new document's name — its address for every later call","type":"string"},"status":{"description":"always \"draft\"; the lifecycle owns the initial state","type":"string"}},"type":"object"},"Genesis":{"properties":{"at":{"description":"At is the unix second the genesis root was computed.","type":"integer"},"block":{"description":"Block is the L1 block the anchoring transaction landed in. Set only once the\nreceipt has been read; absent otherwise.","type":"integer"},"chainId":{"description":"ChainID is the EVM chain the root is committed to — the Hanzo L1 by default.","type":"integer"},"note":{"description":"Note explains an unanchored genesis honestly — anchor wiring absent, or the\nsubmit error — rather than reporting a commit that did not happen.","type":"string"},"root":{"description":"Root is the 0x-prefixed keccak256 root of the founding allocation. It is\nALWAYS computed, whether or not the on-chain anchor is wired, because the root\nis the tamper-evident witness.","type":"string"},"status":{"description":"Status is pending (root computed, not yet on-chain) or anchored (committed).","type":"string"},"txHash":{"description":"TxHash is the L1 transaction hash of the anchoring commit. Empty until anchored.","type":"string"}},"type":"object"},"GitOpsApp":{"properties":{"automated":{"type":"boolean"},"health":{"description":"Healthy|Degraded|Progressing|…","type":"string"},"history":{"items":{"$ref":"#/components/schemas/GitOpsDeploy"},"type":"array"},"name":{"type":"string"},"namespace":{"type":"string"},"operation":{"$ref":"#/components/schemas/GitOpsOperation"},"path":{"type":"string"},"project":{"type":"string"},"reconciledAt":{"type":"string"},"repoURL":{"type":"string"},"resources":{"type":"integer"},"revision":{"description":"the commit last applied","type":"string"},"selfHeal":{"type":"boolean"},"sync":{"description":"Synced|OutOfSync|Unknown","type":"string"},"targetRevision":{"type":"string"}},"type":"object"},"GitOpsDeploy":{"properties":{"automated":{"type":"boolean"},"deployedAt":{"type":"string"},"id":{"type":"integer"},"revision":{"type":"string"},"startedAt":{"type":"string"}},"type":"object"},"GitOpsOperation":{"properties":{"finishedAt":{"type":"string"},"message":{"type":"string"},"phase":{"type":"string"},"revision":{"type":"string"},"startedAt":{"type":"string"}},"type":"object"},"GitOpsPlane":{"properties":{"applications":{"items":{"$ref":"#/components/schemas/GitOpsApp"},"type":"array"},"installed":{"type":"boolean"},"reason":{"type":"string"}},"type":"object"},"GrantIn":{"properties":{"amountCents":{"description":"AmountCents is the credit, in whole cents. Must be positive and within the\nper-grant cap.","type":"integer"},"currency":{"description":"Currency is the ISO code, lower-cased. Empty means usd.","type":"string"},"org":{"description":"Org is the tenant to credit. Required.","type":"string"},"reason":{"description":"Reason is the operator's justification, recorded on the audit row.","type":"string"},"source":{"description":"Source is the money bucket: \"trial\" (default) for a non-cash comp that is never\nrefundable, or \"prepaid\" for real money. Anything unknown falls back to trial.","type":"string"},"user":{"description":"User optionally names a MEMBER to credit, by bare IAM username. Empty credits\nthe org. Which of the two the money actually lands on is decided by\naccount.Payer, not here: a pooled org keeps one balance whatever is named.","type":"string"}},"type":"object"},"GrantOut":{"properties":{"data":{"$ref":"#/components/schemas/GrantResult"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"GrantRequest":{"properties":{"id":{"description":"an experiment (run) stable id","type":"string"},"project":{"type":"string"},"publishable":{"type":"boolean"},"sha256":{"description":"OR an artifact content hash","type":"string"},"trainable":{"type":"boolean"},"visibility":{"type":"string"}},"type":"object"},"GrantResult":{"properties":{"balanceCents":{"description":"BalanceCents is the account balance AFTER the grant, in whole cents.","type":"integer"},"balanceExact":{"description":"BalanceExact is that same balance at full 18-decimal precision, so a sub-cent\ndebit is visible rather than rounded away.","type":"string"},"currency":{"description":"Currency is the lower-cased ISO code the grant was denominated in.","type":"string"},"grantedCents":{"description":"GrantedCents is the amount actually credited.","type":"integer"},"org":{"description":"Org is the tenant whose ledger was credited.","type":"string"},"source":{"description":"Source is the money bucket: \"trial\" (non-cash comp) or \"prepaid\" (real money).","type":"string"},"subject":{"description":"Subject is the ACCOUNT the credit landed on inside that ledger: the org slug for\na pooled org, \"\u003corg\u003e/\u003cname\u003e\" for a member of a per-member one. It is echoed\nbecause the operator does not choose it — account.Payer does — so naming a\nmember of a pooled org credits the pool and the receipt has to say so.","type":"string"},"transactionId":{"description":"TransactionID is the ledger entry id, for reconciliation against commerce.","type":"string"}},"type":"object"},"GrantRow":{"properties":{"actor":{"description":"staff email (or sub) who issued it","type":"string"},"amountCents":{"type":"integer"},"createdAt":{"type":"string"},"currency":{"type":"string"},"org":{"type":"string"},"reason":{"type":"string"},"result":{"description":"success | error","type":"string"},"source":{"description":"\"trial\" | \"prepaid\"","type":"string"},"transactionId":{"type":"string"}},"type":"object"},"GrantsOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GrantRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"Health":{"properties":{"status":{"description":"Status is ok when the message plane answers, degraded otherwise.","type":"string"},"uptime":{"description":"Uptime is how long this surface has been mounted.","type":"string"},"version":{"description":"Version is the connected broker's server version; empty while degraded.","type":"string"}},"type":"object"},"HealthView":{"properties":{"env":{"description":"Env is the deployment environment (\"dev\" | \"staging\" | \"prod\").","type":"string"},"service":{"description":"Service is always \"licensing\".","type":"string"},"signer":{"description":"Signer names the KMS provider signing licenses here. \"local\" means a\ndevelopment key: tokens it mints are not production credentials.","type":"string"},"status":{"description":"Status is \"ok\" whenever the process is up — this is not a dependency probe.","type":"string"}},"type":"object"},"Host":{"properties":{"addr":{"type":"string"},"error":{"description":"Err is set when a peer could not be reached. Its plugins are then\nunknown, which is NOT the same as none, so the list stays empty and the\ndrift below refuses to conclude anything from it.","type":"string"},"host":{"description":"Host is the pod's stable id, and Addr where it was reached. Self is true\nfor the host that answered the request.","type":"string"},"plugins":{"items":{"$ref":"#/components/schemas/Status"},"type":"array"},"self":{"type":"boolean"}},"type":"object"},"InboxItem":{"properties":{"category":{"type":"string"},"confidence":{"type":"string"},"createdAt":{"type":"string"},"extracted":{"$ref":"#/components/schemas/Extracted"},"filename":{"type":"string"},"id":{"description":"the file hash (== a scan's ScanID)","type":"string"},"status":{"type":"string"},"vendor":{"type":"string"}},"type":"object"},"IngestRequest":{"properties":{"attempts":{"items":{"$ref":"#/components/schemas/Attempt"},"type":"array"},"experiments":{"items":{"$ref":"#/components/schemas/Experiment"},"type":"array"}},"type":"object"},"Install":{"properties":{"created":{"items":{"type":"string"},"type":"array"},"existing":{"items":{"type":"string"},"type":"array"},"module":{"type":"string"}},"type":"object"},"Integrity":{"properties":{"brokenAt":{"description":"BrokenAt is the seq of the FIRST record that failed verification, or -1 when\nOK. Reason describes the break (recomputed-hash mismatch, prev-hash\ndiscontinuity, or a seq gap).","type":"integer"},"count":{"description":"Count is the number of records walked.","type":"integer"},"headHash":{"description":"HeadHash is the hash of the last record (or the genesis anchor for an empty\nchain). Pin this externally over time to detect tail-truncation.","type":"string"},"ok":{"description":"OK is true iff every record's stored hash equals the recomputed hash AND the\nchain links are continuous (each PrevHash == the prior record's Hash, seqs\ngapless from 0).","type":"boolean"},"reason":{"type":"string"}},"type":"object"},"InvoiceLineIn":{"properties":{"amount":{"description":"Amount is the line total in whole cents (250000 is $2,500.00).","type":"integer"},"description":{"description":"Description is the human-readable line, e.g. \"Advisory retainer — August\".","type":"string"},"quantity":{"description":"Quantity is the number of units, when the line is metered. Optional.","type":"integer"},"unitPrice":{"description":"UnitPrice is the per-unit price in cents, when the line is metered. Optional.","type":"integer"}},"type":"object"},"InvoiceOut":{"properties":{"amountDueCents":{"description":"AmountDueCents is what remains collectible.","type":"integer"},"amountPaidCents":{"description":"AmountPaidCents is what has been collected so far.","type":"integer"},"createdAt":{"description":"CreatedAt is when the draft was raised, RFC3339.","type":"string"},"currency":{"description":"Currency is the ISO 4217 code.","type":"string"},"customerEmail":{"description":"CustomerEmail is where it is sent.","type":"string"},"id":{"description":"ID is the invoice id — what the issue, collect and void ops address.","type":"string"},"lines":{"description":"Lines are the charges on the invoice.","items":{"$ref":"#/components/schemas/InvoiceLineIn"},"type":"array"},"number":{"description":"Number is the human-facing invoice number, e.g. \"INV-0042\". A draft has\nnone; issuing assigns it.","type":"string"},"paymentRef":{"description":"PaymentRef is the processor reference for the collection, once paid.","type":"string"},"status":{"description":"Status is draft, open, paid, void or uncollectible. A draft is not\ncollectible; issuing moves it to open.","type":"string"},"subtotalCents":{"description":"SubtotalCents is the sum of the lines.","type":"integer"},"userId":{"description":"UserID is the customer billed.","type":"string"}},"type":"object"},"InvoiceRow":{"properties":{"amountCents":{"type":"integer"},"currency":{"type":"string"},"display":{"type":"string"},"due":{"type":"string"},"id":{"type":"string"},"issued":{"type":"string"},"number":{"type":"string"},"org":{"type":"string"},"status":{"type":"string"}},"type":"object"},"InvoicesOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/InvoiceRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"IssueRequest":{"properties":{"fingerprint":{"description":"Fingerprint is a previously-registered device binding value, as returned by\nPOST /v1/licensing/fingerprint. Leave it empty and pass Signals to bind the\ndevice at issue time instead.","type":"string"},"holder":{"description":"Holder overrides who the token is issued to; defaults to the caller's own\nvalidated subject. It NAMES the token's bearer and grants nothing on its\nown — the entitlement checked is always the caller's org's.","type":"string"},"product":{"description":"Product is the licensed commerce product the caller wants a token for.","type":"string"},"release":{"description":"Release scopes the token to one signed binary release, recorded as a\n\"release:\u003cid\u003e\" feature so a single bad release can be revoked on its own.","type":"string"},"signals":{"$ref":"#/components/schemas/DeviceSignals","description":"Signals binds the device at issue time, as an alternative to a\npre-registered fingerprint. The raw signals are never stored or echoed —\nthey are folded immediately into the one-way binding value."},"ttl_seconds":{"description":"TTLSeconds requests a token lifetime in seconds. It is clamped to the\ndeployment maximum AND to the entitlement's own expiry — a token never\noutlives the subscription that paid for it.","type":"integer"}},"required":["product"],"type":"object"},"IssueResponse":{"properties":{"app_id":{"description":"AppID is the brand this token runs under (\"hanzo\" | \"lux\" | \"zoo\"). The\nengine refuses a token whose app_id is not the one it was built for.","type":"string"},"exp":{"description":"Exp is the token's expiry, Unix seconds.","type":"integer"},"features":{"description":"Features are the capability grants copied verbatim from the plan the org\nbought. The engine enforces exactly these.","items":{"type":"string"},"type":"array"},"fingerprint_bound":{"description":"Bound reports whether a device fingerprint was folded into the token. An\nunbound token runs on any machine; a bound one runs only on the machine it\nwas bound to.","type":"boolean"},"holder":{"description":"Holder is who the token was issued to.","type":"string"},"nonce":{"description":"Nonce uniquely identifies this token, and is what a per-token revocation\nnames.","type":"string"},"token":{"description":"Token is the signed license, `base64url(payload).base64url(ed25519_sig)`.\nIt is the credential the engine runs on — treat it as a secret.","type":"string"}},"type":"object"},"JWK":{"properties":{"crv":{"description":"Crv is always \"Ed25519\".","type":"string"},"kty":{"description":"Kty is always \"OKP\".","type":"string"},"use":{"description":"Use is always \"sig\".","type":"string"},"x":{"description":"X is the public key, base64url (the JWK convention).","type":"string"}},"type":"object"},"JournalEntry":{"properties":{"amount":{"description":"the entry's magnitude (display; postings hold the signed truth)"},"createdAt":{"type":"integer"},"id":{"type":"string"},"kind":{"type":"string"},"memo":{"type":"string"},"postings":{"items":{"$ref":"#/components/schemas/Posting"},"type":"array"},"program":{"description":"referral|affiliate|author for payouts; \"\" otherwise","type":"string"},"ref":{"description":"idempotency ref (unique within Kind+Program)","type":"string"}},"type":"object"},"JourneyStep":{"properties":{"args":{"additionalProperties":{"type":"object"},"type":"object"},"deps":{"description":"Dependencies are step ids that must be done/skipped before this step is\navailable. The wire key is `deps` (the blueprint contract); the Go field keeps\nits descriptive name.","items":{"type":"string"},"type":"array"},"detail":{"description":"the prose/juncture — what the Guide asks/explains here","type":"string"},"draft":{"type":"string"},"draftInto":{"type":"string"},"enabled":{"description":"Enabled is the admin on/off lever. A NIL pointer reads as ENABLED (absence ==\non): a legacy/org curriculum that omits the field keeps every step, and only an\nexplicit `enabled: false` (an admin disable) drops a step from the journey. See\non() in blueprint.go and the Blueprint.Curriculum() projection.","type":"boolean"},"id":{"type":"string"},"section":{"description":"the phase (section id) this step groups under","type":"string"},"signal":{"description":"Signal, when set, names a machine detector (detect.go). When the detector\nreports the org's real state present, the step auto-marks done.","type":"string"},"title":{"type":"string"},"tool":{"description":"Tool, when set, is the MCP tool the Business AI runs for \"do it for me\". Args\nare its default arguments; Draft is an optional AI prompt whose output fills the\nDraftInto arg (default \"brief\").","type":"string"}},"type":"object"},"KindTotal":{"properties":{"cost_usd":{"type":"number"},"experiments":{"type":"integer"},"kind":{"type":"string"}},"type":"object"},"LLM":{"properties":{"available":{"description":"Available is false when the warehouse was not connected or a query blipped.\nThe totals below are then honest zeros, NOT measured ones.","type":"boolean"},"completionTokens":{"description":"CompletionTokens is the output half.","type":"integer"},"costCents":{"description":"CostCents is what they cost the org, in US cents. This IS a Hanzo charge.","type":"integer"},"models":{"description":"Models is how many distinct models were used.","type":"integer"},"promptTokens":{"description":"PromptTokens is the input half of that total.","type":"integer"},"requests":{"description":"Requests is how many completions the org made in the window.","type":"integer"},"source":{"description":"Source names the warehouse table the totals came from.","type":"string"},"tokens":{"description":"Tokens is the total tokens those completions consumed.","type":"integer"}},"type":"object"},"LLMOverview":{"properties":{"available":{"description":"Available is true whenever the ledger answered — including with no usage in the\nwindow, which is honest zeros rather than a missing lens.","type":"boolean"},"completionTokens":{"description":"CompletionTokens is the output half of Tokens.","type":"integer"},"errorRate":{"description":"ErrorRate is Errors/Requests, 0..1, rounded to three places. Zero when there\nwere no requests.","type":"number"},"errors":{"description":"Errors is how many of Requests failed.","type":"integer"},"models":{"description":"Models is how many distinct models the org called.","type":"integer"},"promptTokens":{"description":"PromptTokens is the input half of Tokens.","type":"integer"},"providers":{"description":"Providers is how many distinct providers served them.","type":"integer"},"requests":{"description":"Requests is how many LLM calls the org made in the window.","type":"integer"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"},"spendCents":{"description":"SpendCents is what those calls cost, in cents.","type":"integer"},"tokens":{"description":"Tokens is prompt plus completion tokens over those calls.","type":"integer"}},"type":"object"},"LeaderboardRow":{"properties":{"anonymous":{"description":"Anonymous is true when this subject's identity was withheld and Handle is the\n\"Anonymous\" placeholder: the metric is real, the name is not. Render it as an\nunnamed row, never as someone actually called Anonymous.","type":"boolean"},"costCents":{"description":"CostCents is this subject's spend in whole US cents (not dollars, not\nmillicents). It is 0 unless the viewer is entitled to this subject's spend —\ntheir own row, an admin looking at their own org, an explicitly cost-ranked\nboard, or a platform admin on the global board — so 0 means \"withheld or zero\",\nand the two are deliberately indistinguishable.","type":"integer"},"handle":{"description":"Handle is the display identity to render: the peer's chosen handle if they opted\ninto public listing, their username if the viewer is an admin of their org, the\norg's chosen display name (or its id) on an org board, and the literal\n\"Anonymous\" when the identity is withheld. Never an email or a raw ledger id.","type":"string"},"metric":{"description":"Metric is the value the board was ranked by, copied from Requests, Tokens or\nCostCents according to the request's metric. It is what a client sizes bars\nagainst without having to know which metric was asked for.","type":"integer"},"rank":{"description":"Rank is this subject's 1-based standing in the window, 1 being the top. Rows are\nordered by the ranked metric descending (ties broken by request count), and a\nboard is always the top of the list — there is no offset paging — so the first\nrow is always rank 1 and rank is also the row's index + 1.","type":"integer"},"requests":{"description":"Requests is how many AI requests this subject made in the window. Volume is not\nsensitive, so it is reported for every row including anonymized ones.","type":"integer"},"self":{"description":"Self marks the caller's own row so a client can highlight it in place. At most\none row carries it, and it is never set on an org board — org rows carry no user\nidentity, so the caller's own org standing arrives in LeaderboardView.Self.","type":"boolean"},"tokens":{"description":"Tokens is prompt+completion tokens this subject spent in the window. Like\nRequests it is reported for every row.","type":"integer"}},"type":"object"},"LeaderboardView":{"properties":{"available":{"description":"Available is false when the usage warehouse is not connected or its rollup is not\nready. Rows is then empty because nothing could be read — not because nobody used\nanything. Show that difference; never render an unavailable board as a real one.","type":"boolean"},"end":{"description":"End is the EXCLUSIVE upper bound of the window, \"2006-01-02\" — the day after the\nlast one counted. A board through today reports tomorrow's date here.","type":"string"},"metric":{"description":"Metric echoes the value ranked: tokens|requests|cost.","type":"string"},"period":{"description":"Period is the window's canonical label: day|week|month|all. The server resolves\naliases (7d, 30d, today, …) to these, so this may differ from what was sent.","type":"string"},"rows":{"description":"Rows are the ranked subjects, best first, at most the requested limit of them.\nAlways a list, never null: an empty one means nothing was read, not an error.","items":{"$ref":"#/components/schemas/LeaderboardRow"},"type":"array"},"scope":{"description":"Scope echoes the board that was served: personal|org|global.","type":"string"},"self":{"$ref":"#/components/schemas/SelfRank","description":"Self is the caller's own standing, reported even when they fall outside Rows.\nAbsent when the caller's ledger identity cannot be resolved, or when the query\nbehind it failed — never faked to keep the shape tidy."},"source":{"description":"Source names the table these numbers were aggregated from (the derived daily\nrollup, hanzo.usage_rollup_daily), so an operator can tell exactly what was read.","type":"string"},"start":{"description":"Start is the first day counted, \"2006-01-02\" inclusive. Empty for period=all,\nwhich has no lower bound at all.","type":"string"},"subject":{"description":"Subject is what the rows stand for — \"user\" on a personal or org board, \"org\" on\nthe global one. It tells a client whether Handle names a person or a company.","type":"string"},"total":{"description":"Total is how many subjects were ranked in the window — the org's active users, or\nthe active/opted-in orgs on the global board. It is the universe the ranks are\nout of, so it is normally larger than len(rows).","type":"integer"}},"type":"object"},"Leg":{"properties":{"account":{"description":"Account is the chart-of-accounts number this side posts to, e.g. \"5300\".","type":"string"},"credit":{"description":"Credit is the leg's credit in exact cents. Set this or Debit, not both.","type":"integer"},"debit":{"description":"Debit is the leg's debit in exact cents. Set this or Credit, not both.","type":"integer"}},"type":"object"},"LineItem":{"properties":{"amountCents":{"type":"integer"},"description":{"type":"string"}},"type":"object"},"ListOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Host"},"type":"array"},"drift":{"items":{"$ref":"#/components/schemas/Drift"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"Listing":{"properties":{"category":{"type":"string"},"createdAt":{"type":"integer"},"currency":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"price":{"description":"exact per-call price; 0 is free."},"public":{"type":"boolean"},"publisherOrg":{"type":"string"},"recipient":{"description":"seller payout WALLET ID, in PublisherOrg.","type":"string"},"title":{"type":"string"},"tool":{"type":"string"}},"type":"object"},"LoadBalancer":{"properties":{"blockedReason":{"type":"string"},"cluster":{"type":"string"},"deletable":{"type":"boolean"},"droplets":{"type":"integer"},"id":{"type":"string"},"ip":{"type":"string"},"monthlyCents":{"type":"integer"},"name":{"type":"string"},"region":{"type":"string"},"service":{"description":"Service is the `namespace/name` of the live type=LoadBalancer Service that claims\nthis load balancer, proven from the cluster scan. Non-empty means IN USE.","type":"string"},"sizeUnit":{"type":"integer"},"status":{"type":"string"}},"type":"object"},"Location":{"properties":{"external":{"type":"boolean"},"path":{"type":"string"},"range":{"$ref":"#/components/schemas/Range"}},"type":"object"},"LogBody":{"properties":{"body":{"type":"string"},"number":{"type":"integer"},"severity":{"type":"string"}},"type":"object"},"MCPListing":{"properties":{"description":{"description":"Description is the publisher's one-line summary.","type":"string"},"featured":{"description":"Featured puts the listing on the front of the shelf. Curation.","type":"boolean"},"hidden":{"description":"Hidden keeps the listing out of the org-visible catalog. Curation: a sync\nnever changes it. Only a SuperAdmin sets it, and only a SuperAdmin sees a\nhidden entry listed.","type":"boolean"},"id":{"description":"ID addresses the listing in a URL. It is the reverse-DNS NAME with its one\nslash written as an underscore — reversible, because a namespace never\ncontains an underscore — so the id is readable and stable rather than a\nhash that means nothing to whoever reads a link.","type":"string"},"logo":{"description":"Logo is the brand mark to render for the listing — the publisher's icon when\nthe entry carries one, or the one an admin set. Curation.","type":"string"},"name":{"description":"Name is the publisher's reverse-DNS name, e.g. \"com.stripe/mcp\".","type":"string"},"official":{"description":"Official is whether this is the vendor's OWN server rather than someone\nelse's copy of it. Derived on every sync (see isOfficial) until a\nSuperAdmin sets it explicitly, after which the admin's answer stands.","type":"boolean"},"packages":{"description":"Packages are the runnable package forms — npm, pypi, oci — each with the\nruntime that launches it and the transport it then speaks.","items":{"$ref":"#/components/schemas/MCPPackage"},"type":"array"},"registry":{"description":"Registry is the upstream this row was synced from.","type":"string"},"remotes":{"description":"Remotes are the hosted endpoints the publisher serves the server at.","items":{"$ref":"#/components/schemas/MCPRemote"},"type":"array"},"repo":{"description":"Repo is the source repository URL, when the entry names one.","type":"string"},"site":{"description":"Site is the project's homepage, when the entry names one.","type":"string"},"synced":{"description":"Synced is when this row was last confirmed against upstream, Unix seconds.","type":"integer"},"title":{"description":"Title is the human-readable display name, when the entry carries one.","type":"string"},"transports":{"description":"Transports are the distinct transports this server can be reached over,\nsorted: some of \"stdio\", \"streamable-http\", \"sse\". A listing with\n\"streamable-http\" is one an org can enable here and now; a listing that is\nonly \"stdio\" needs a process to run it.","items":{"type":"string"},"type":"array"},"vendor":{"description":"Vendor is the namespace half of Name — the publisher, e.g. \"com.stripe\".","type":"string"},"version":{"description":"Version is the published version of this listing.","type":"string"}},"type":"object"},"MCPPackage":{"properties":{"identifier":{"description":"Identifier is the package name or download URL.","type":"string"},"registry":{"description":"Registry is where the package is fetched from: npm, pypi, oci, nuget, mcpb.","type":"string"},"runtime":{"description":"Runtime is the publisher's hint for what launches it: npx, uvx, docker.","type":"string"},"transport":{"description":"Transport is what the launched process speaks: usually \"stdio\".","type":"string"},"version":{"description":"Version is the exact published package version.","type":"string"}},"type":"object"},"MCPRemote":{"properties":{"transport":{"description":"Transport is \"streamable-http\" or \"sse\".","type":"string"},"url":{"description":"URL is the endpoint.","type":"string"}},"type":"object"},"MCPServer":{"properties":{"authHeader":{"description":"AuthHeader is the request header the KMS-held credential is injected into,\ne.g. \"Authorization\". Absent when the server needs no credential.","type":"string"},"createdAt":{"description":"CreatedAt is when the server was registered, Unix seconds.","type":"integer"},"hasSecret":{"description":"HasSecret is whether a credential is sealed in KMS for this server. The\nVALUE is never returned by any route.","type":"boolean"},"id":{"description":"ID is the server's id within the org. It also PREFIXES every tool name the\nserver contributes, which is what keeps two servers' \"search\" apart.","type":"string"},"listing":{"description":"Listing is the catalog entry this server was enabled from, when it was.\nEmpty means the org typed the URL in itself.","type":"string"},"name":{"description":"Name is the org's label for the server.","type":"string"},"org":{"description":"Org is the org that registered the server — the validated caller's.","type":"string"},"source":{"description":"Source is where the registration came from: \"catalog\" when it was enabled\noff the shelf, \"org\" when the org registered the URL itself. It is DERIVED\nfrom Listing rather than stored, because two columns for one fact is two\nchances to disagree.","type":"string"},"url":{"description":"URL is the server's JSON-RPC endpoint. Always a public http(s) host: the\nregistration boundary and the dialer both refuse anything else.","type":"string"}},"type":"object"},"MemoryEntry":{"properties":{"actor":{"description":"Actor is the validated user id that last wrote this entry by hand. Empty on an\nentry an engine produced, and on one written before attribution existed.","type":"string"},"glossary_version":{"description":"Glossary is the glossary VERSION the entry was translated under — the digest\nversion() derives from the terms, so changing a term changes the key and the\nstale rendering can never be served.","type":"string"},"source":{"description":"Source is the ORIGINAL string this entry translates. Part of the identity.","type":"string"},"state":{"description":"State is the entry's position on the review ladder: machine, suggested,\napproved or published.","type":"string"},"target":{"description":"Target is the target language tag (BCP-47, e.g. \"es\" or \"pt-BR\"). Part of the\nidentity.","type":"string"},"text":{"description":"Text is the stored translation. A memory hit returns it verbatim, which is the\nidempotence contract.","type":"string"},"tier":{"description":"Tier is the engine tier the entry belongs to, quality or bulk. Part of the\nidentity: the two tiers keep separate renderings of the same source.","type":"string"},"updated_at":{"description":"UpdatedAt is the unix second the entry last changed.","type":"integer"}},"type":"object"},"MemoryPage":{"properties":{"data":{"description":"Data is the matching memory entries, newest first.","items":{"$ref":"#/components/schemas/MemoryEntry"},"type":"array"}},"type":"object"},"Message":{"properties":{"data":{"description":"Data is the payload, base64-encoded.","type":"string"},"headers":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Headers are the message headers, when any were published.","type":"object"},"num_delivered":{"description":"Delivered is how many times a consumer has been handed this message (pulls only).","type":"integer"},"num_pending":{"description":"Remaining is how many messages follow this one for the consumer (pulls only).","type":"integer"},"sequence":{"description":"Sequence is the message's stream sequence.","type":"integer"},"subject":{"description":"Subject is the org-relative subject the message was stored under.","type":"string"},"timestamp":{"description":"Timestamp is when the broker stored the message.","format":"date-time","type":"string"}},"type":"object"},"MetricBody":{"properties":{"labels":{"additionalProperties":{},"type":"object"},"name":{"type":"string"},"value":{"type":"number"}},"type":"object"},"Metrics":{"properties":{"at":{"description":"unix seconds, server-stamped","type":"integer"},"gpuUtil":{"description":"0..1 aggregate utilization","type":"number"},"load1":{"type":"number"},"load15":{"type":"number"},"load5":{"type":"number"},"memFree":{"description":"bytes","type":"integer"},"memUsed":{"description":"bytes","type":"integer"}},"type":"object"},"MetricsData":{"properties":{"asOf":{"type":"string"},"currency":{"type":"string"},"customers":{"items":{"$ref":"#/components/schemas/SaaSCustomer"},"type":"array"},"gaps":{"items":{"type":"string"},"type":"array"},"generatedAt":{"type":"string"},"orgs":{"type":"integer"},"revenue":{"$ref":"#/components/schemas/SaaSRevenue"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"subscriptions":{"$ref":"#/components/schemas/SaaSSubs"},"usage":{"$ref":"#/components/schemas/SaaSUsage"},"window":{"type":"string"}},"type":"object"},"MetricsOut":{"properties":{"data":{"$ref":"#/components/schemas/MetricsData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"MetricsResponse":{"properties":{"arr":{"description":"ARR is annualized recurring revenue in cents (MRR × 12).","type":"integer"},"burn":{"description":"Burn is total expense in cents over the period.","type":"integer"},"cash":{"description":"Cash is the bank + processor-clearing balance in cents as of To.","type":"integer"},"cogs":{"description":"COGS is cost of goods sold in cents over the period.","type":"integer"},"deferredRevenue":{"description":"DeferredRevenue is the customer-wallet liability in cents as of To.","type":"integer"},"figures":{"description":"Figures is the same snapshot rendered through books' one money formatter.","items":{"$ref":"#/components/schemas/Figure"},"type":"array"},"from":{"description":"From is the RFC3339 start of the reporting window, exclusive; absent for all time.","type":"string"},"grossMarginBps":{"description":"GrossMarginBps is GrossProfit / Revenue in basis points (7000 = 70%).","type":"integer"},"grossProfit":{"description":"GrossProfit is Revenue − COGS, in cents.","type":"integer"},"monthlyBurn":{"description":"MonthlyBurn is net cash burned per month in cents; 0 when not losing cash.","type":"integer"},"months":{"description":"Months is the window length in whole months used to normalize MRR and burn.","type":"integer"},"mrr":{"description":"MRR is monthly recurring revenue in cents.","type":"integer"},"netIncome":{"description":"NetIncome is Revenue − Burn, in cents.","type":"integer"},"period":{"description":"Period is the human window label, e.g. \"2026-07\" or \"all-time\".","type":"string"},"revenue":{"description":"Revenue is recognized revenue in cents over the period.","type":"integer"},"runwayMonths":{"description":"RunwayMonths is Cash / MonthlyBurn; -1 means infinite (the org is not burning).","type":"integer"},"to":{"description":"To is the RFC3339 end of the reporting window, inclusive; absent for up to now.","type":"string"}},"type":"object"},"Middleware":{"properties":{"config":{"additionalProperties":{"type":"string"},"description":"Config is the transform's parameters: redirectScheme takes scheme (default\nhttps) and permanent (\"true\" ⇒ 301, else 302); stripPrefix REQUIRES\nprefixes (comma-separated, first match wins); addPrefix REQUIRES prefix;\nheaders is a header→value map set on the response.","type":"object"},"id":{"description":"ID identifies the transform within the org: [A-Za-z0-9-_.], at most 128\nchars. A create that omits it gets a generated one. Routes reference it by\nthis id.","type":"string"},"type":{"description":"Type is the transform: redirectScheme, stripPrefix, addPrefix or headers.","type":"string"}},"type":"object"},"ModelRow":{"properties":{"model":{"description":"Model is the model id, e.g. zen5-coder.","type":"string"},"pct":{"description":"Pct is this model's share of the window's returned spend, 0..100, one decimal.","type":"number"},"provider":{"description":"Provider is who served it.","type":"string"},"requests":{"description":"Requests is how many calls went to this model.","type":"integer"},"spendCents":{"description":"SpendCents is what they cost, in cents.","type":"integer"},"tokens":{"description":"Tokens is prompt plus completion tokens over those calls.","type":"integer"}},"type":"object"},"ModuleInfo":{"properties":{"doctypes":{"items":{"type":"string"},"type":"array"},"module":{"type":"string"}},"type":"object"},"ModuleState":{"properties":{"doctypes":{"items":{"type":"string"},"type":"array"},"installed":{"items":{"type":"string"},"type":"array"},"module":{"type":"string"}},"type":"object"},"MoneyOut":{"properties":{"data":{"$ref":"#/components/schemas/moneyBoard"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"MutationOut":{"properties":{"data":{"type":"object"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"NameIn":{"properties":{"name":{"description":"Name is the app, from the path.","type":"string"},"scope":{"description":"Scope \"host\" applies here only; default \"fleet\" applies everywhere.","type":"string"}},"type":"object"},"NewsItem":{"properties":{"image":{"description":"Image is a lead-image URL when the upstream carried one.","type":"string"},"lang":{"description":"Lang is the article's language code when the upstream reported one.","type":"string"},"link":{"description":"Link is the article's URL at the outlet.","type":"string"},"pubDate":{"description":"PubDate is when the outlet published it, RFC3339 UTC. Empty when the\nupstream gave no date this could parse — items with no date sort last.","type":"string"},"source":{"description":"Source is the outlet the item came from, as the upstream named it.","type":"string"},"title":{"description":"Title is the headline.","type":"string"},"tone":{"description":"Tone is GDELT's own sentiment score for the article, as text. Only GDELT\nitems carry it.","type":"string"}},"type":"object"},"Node":{"properties":{"blockedReason":{"type":"string"},"cluster":{"type":"string"},"clusterId":{"type":"string"},"createdAt":{"type":"string"},"id":{"type":"integer"},"localDiskGiB":{"type":"integer"},"memoryMiB":{"type":"integer"},"monthlyCents":{"type":"integer"},"mutable":{"description":"Mutable reports whether this droplet may be changed DIRECTLY — deleted or resized.\nOne predicate covers both because one fact decides both: a DOKS node belongs to a\nnode pool, and the pool is the only thing allowed to change it.","type":"boolean"},"name":{"type":"string"},"pods":{"type":"integer"},"privateIp":{"type":"string"},"publicIp":{"type":"string"},"ready":{"type":"boolean"},"region":{"type":"string"},"schedulable":{"type":"boolean"},"sizeSlug":{"type":"string"},"status":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"vcpus":{"type":"integer"},"volumes":{"type":"integer"}},"type":"object"},"NodePool":{"properties":{"blockedReason":{"type":"string"},"cluster":{"type":"string"},"clusterId":{"type":"string"},"clusterSchedulable":{"type":"integer"},"count":{"type":"integer"},"id":{"type":"string"},"name":{"type":"string"},"scalable":{"type":"boolean"},"size":{"type":"string"}},"type":"object"},"Opportunity":{"properties":{"amount":{"description":"Amount is the deal value in minor units (cents) of Currency.","type":"integer"},"closeDate":{"description":"CloseDate is the expected close as a unix second (0 = unset).","type":"integer"},"companyId":{"description":"CompanyID links the deal to one of the org's companies; empty when\nunlinked, and cleared when that company is deleted. A write naming a\ncompany the org does not own is refused with 422.","type":"string"},"createdAt":{"description":"CreatedAt is the unix second the opportunity was created. Server-owned.","type":"integer"},"currency":{"description":"Currency is the ISO code Amount is denominated in; a write that names none\nstores USD.","type":"string"},"id":{"description":"ID is the server-minted opportunity id (\"oppo_\" + 128 random bits).","type":"string"},"name":{"description":"Name is the deal name.","type":"string"},"pointOfContactId":{"description":"PointOfContact links the deal to one of the org's contacts; empty when\nunlinked, and cleared when that contact is deleted. A write naming a\ncontact the org does not own is refused with 422.","type":"string"},"stage":{"description":"Stage is the pipeline stage, always one of NEW, SCREENING, MEETING,\nPROPOSAL or CUSTOMER — stored upper-case whatever case the write used.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second of the last write. Server-owned.","type":"integer"}},"type":"object"},"Overlay":{"properties":{"beta":{"type":"boolean"},"betaOrgs":{"items":{"type":"string"},"type":"array"},"enabled":{"type":"boolean"},"id":{"type":"string"},"kind":{"type":"string"},"overrides":{},"updatedAt":{"type":"integer"}},"type":"object"},"Overview":{"properties":{"commerce":{"$ref":"#/components/schemas/CommerceOverview","description":"Commerce is the orders/revenue lens over product events."},"end":{"description":"End is the window's exclusive upper bound, RFC3339 UTC.","type":"string"},"interval":{"description":"Interval is the bucket width the window implies: hour or day.","type":"string"},"llm":{"$ref":"#/components/schemas/LLMOverview","description":"LLM is the LLM usage lens — real per-org data."},"range":{"description":"Range is the window that was actually applied: 24h, 7d, 30d or custom.","type":"string"},"scope":{"$ref":"#/components/schemas/Scope","description":"Scope names the tenant these numbers belong to."},"start":{"description":"Start is the window's inclusive lower bound, RFC3339 UTC.","type":"string"},"web":{"$ref":"#/components/schemas/WebOverview","description":"Web is the web-traffic lens over product events."}},"type":"object"},"PagesBuildConfig":{"properties":{"build_command":{"type":"string"},"destination_dir":{"type":"string"},"root_dir":{"type":"string"}},"type":"object"},"PagesD1Binding":{"properties":{"id":{"type":"string"}},"type":"object"},"PagesDeploy":{"properties":{"branch":{"type":"string"}},"type":"object"},"PagesDeploymentConfig":{"properties":{"compatibility_date":{"type":"string"},"compatibility_flags":{"items":{"type":"string"},"type":"array"},"d1_databases":{"additionalProperties":{"$ref":"#/components/schemas/PagesD1Binding"},"type":"object"},"env_vars":{"additionalProperties":{"$ref":"#/components/schemas/PagesEnvVar"},"type":"object"},"kv_namespaces":{"additionalProperties":{"$ref":"#/components/schemas/PagesKVBinding"},"type":"object"},"r2_buckets":{"additionalProperties":{"$ref":"#/components/schemas/PagesR2Binding"},"type":"object"}},"type":"object"},"PagesDeploymentConfigs":{"properties":{"preview":{"$ref":"#/components/schemas/PagesDeploymentConfig"},"production":{"$ref":"#/components/schemas/PagesDeploymentConfig"}},"type":"object"},"PagesEnvVar":{"properties":{"type":{"type":"string"},"value":{"type":"string"}},"type":"object"},"PagesKVBinding":{"properties":{"namespace_id":{"type":"string"}},"type":"object"},"PagesProjectCreate":{"properties":{"build_config":{"$ref":"#/components/schemas/PagesBuildConfig"},"deployment_configs":{"$ref":"#/components/schemas/PagesDeploymentConfigs"},"name":{"type":"string"},"production_branch":{"type":"string"}},"type":"object"},"PagesR2Binding":{"properties":{"name":{"type":"string"}},"type":"object"},"PaymentIn":{"properties":{"amountCents":{"description":"AmountCents is the amount to charge, in whole cents (5000 is $50.00).\nServer-side bounds apply and are authoritative — the default floor is $1\nand the ceiling $5,000, so a fat-fingered or hostile amount is refused\nbefore any money moves.","type":"integer"},"currency":{"description":"Currency is the ISO 4217 code, lower-cased. Empty means usd.","type":"string"},"idempotencyKey":{"description":"IdempotencyKey makes a retry safe: the same key never charges twice, it\nreplays the first result. Sending one is strongly recommended for an agent,\nwhich retries by construction. Empty falls back to a windowed key derived\nfrom the amount and currency, so a double-submit inside 15 minutes still\ncollapses onto one charge.","type":"string"},"sourceId":{"description":"SourceID is the single-use payment token that stands in for the card: a\nSquare Web Payments SDK nonce minted in the browser, or a Square sandbox\ntest nonce when the org's credentials are sandbox ones. The card number\nitself never reaches this process, which is what keeps it out of PCI scope.","type":"string"}},"type":"object"},"PaymentOut":{"properties":{"balanceCents":{"description":"BalanceCents is the org's balance AFTER this payment, read back from the\nsame key just credited so it matches what the balance endpoint reports.","type":"integer"},"id":{"description":"ID is the ledger transaction id for the credit. It is what getPayment\nreads back, and the customer-visible receipt for the money.","type":"string"},"processorRef":{"description":"ProcessorRef is the payment processor's own reference for the charge\n(Square's payment id). It is the field that proves money actually moved at\nthe gateway rather than only in our ledger — the thing to quote when\nreconciling against a processor dashboard.","type":"string"},"status":{"description":"Status is \"ok\" on a settled charge. A charge that did not settle is an\nerror with the processor's reason, never a status field to inspect.","type":"string"},"test":{"description":"Test reports which bucket this credited: true is a SANDBOX charge crediting\nthe test balance, false is live money. It is always stated so a receipt can\nnever be mistaken for the other kind.","type":"boolean"}},"type":"object"},"PaymentRecord":{"properties":{"amountCents":{"description":"AmountCents is the credited amount in whole cents.","type":"integer"},"createdAt":{"description":"CreatedAt is when the credit was written, RFC3339.","type":"string"},"currency":{"description":"Currency is the ISO 4217 code.","type":"string"},"id":{"description":"ID is the ledger transaction id.","type":"string"},"notes":{"description":"Notes is the ledger memo, carrying the processor and its reference.","type":"string"},"status":{"description":"Status is the payment's state. This ledger writes a deposit only AFTER the\nprocessor settled, so a payment that can be read is one that succeeded.","type":"string"},"subject":{"description":"Subject is the billing key this payment credited.","type":"string"},"test":{"description":"Test reports whether this was a sandbox charge (test balance) or live money.","type":"boolean"}},"type":"object"},"PnL":{"properties":{"expense":{"items":{"$ref":"#/components/schemas/PnLLine"},"type":"array"},"from":{"type":"string"},"income":{"items":{"$ref":"#/components/schemas/PnLLine"},"type":"array"},"netIncome":{"description":"TotalIncome − TotalExpense","type":"integer"},"to":{"type":"string"},"totalExpense":{"type":"integer"},"totalIncome":{"type":"integer"}},"type":"object"},"PnLLine":{"properties":{"account":{"type":"string"},"amount":{"description":"cents, display sign (income \u0026 expense both positive when normal)","type":"integer"},"name":{"type":"string"},"type":{"type":"string"}},"type":"object"},"Policy":{"properties":{"cache_paths":{"additionalProperties":{"type":"integer"},"description":"CachePaths overrides CacheTTLSec per path PREFIX (key \"/v1/models\" → seconds).\nThe longest matching prefix wins.","type":"object"},"cache_ttl_sec":{"description":"CacheTTLSec is the org's default edge-cache TTL for its responses, in seconds;\n0 means no caching. Unset inherits the platform default.","type":"integer"},"cors_origins":{"description":"CORSOrigins is the PLATFORM-scope CORS allowlist EdgeCORS admits: an exact\norigin, a bare host, or a \"*.host\" wildcard. Writable only by a SuperAdmin —\nCORS is evaluated before identity, so it has no tenant to scope to.","items":{"type":"string"},"type":"array"},"methods":{"description":"Methods is the allowlist of HTTP methods the edge accepts for this org. Empty\nmeans all are accepted.","items":{"type":"string"},"type":"array"},"mode":{"description":"Mode is the abuse gate's posture for THIS scope: \"shadow\" scores traffic and\nrecords the verdict without acting on it, \"live\" enforces it. Unset means\nshadow.\n\nIt is the one per-org field that does NOT inherit. Every other field here\nlayers a platform default under the org's own value, which is right for a\ndefault: a tenant that sets no rate ceiling should get the platform's. Mode\nis not a default, it is an ARMING DECISION — it is what makes a statistical\njudgement start refusing real traffic — and inheriting it means arming one\nscope arms every tenant that never asked for it, without a write to their\nrow and without anything in their config changing. So a tenant is live only\nif that tenant's OWN row says live, and the platform row's mode governs\nexactly one scope: the anonymous lane, which has no tenant of its own.\n\nIt is also not self-service. Writing it requires SuperAdmin (see the\n/v1/gateway config op): the subject of an abuse control does not get to\nswitch the control off.","type":"string"},"org_rpm":{"description":"OrgRPM is the org's OWN authenticated rate ceiling, requests per minute, as\nScopeRateLimit enforces it. Unset inherits the platform default, then the\nstatic boot default.","type":"integer"},"per_ip_rpm":{"description":"PerIPRPM is the PLATFORM-scope pre-auth flood cap: requests EdgeRateLimit\nadmits per WindowSec from one client IP. SuperAdmin-only, same reason.","type":"integer"},"updated_at":{"description":"UpdatedAt is the unix second this policy row was last written. Server-stamped;\na client-supplied value is ignored.","type":"integer"},"updated_by":{"description":"UpdatedBy is the validated user id that wrote this policy row. Server-stamped;\na client-supplied value is ignored.","type":"string"},"window_sec":{"description":"WindowSec is the window PerIPRPM is counted over, in seconds. SuperAdmin-only.","type":"integer"}},"type":"object"},"Position":{"properties":{"character":{"type":"integer"},"line":{"type":"integer"}},"type":"object"},"PostList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/CalendarPost"},"type":"array"}},"type":"object"},"Posting":{"properties":{"account":{"type":"string"},"amount":{"description":"signed 18-decimal USD (1e-18); Σ over an entry == 0"}},"type":"object"},"Price":{"properties":{"amount":{"description":"Amount is what ONE call costs, EXACTLY: an 18-decimal USD value, so a\nper-call price of $0.0025 is $0.0025 and not a cent-floored zero. Cents\ncannot hold a per-token price, and a tool plane is where per-token prices\nlive."},"currency":{"description":"Currency is the ISO 4217 code, e.g. \"USD\". Empty means USD.","type":"string"},"recipient":{"description":"Recipient is the payout wallet ref the marketplace seller is paid at.","type":"string"}},"type":"object"},"Principle":{"properties":{"change":{"description":"the Book of Changes reading","type":"string"},"domain":{"description":"the growth / go-to-market domain it governs","type":"string"},"hexagram":{"description":"the I-Ching hexagram (pinyin + gloss)","type":"string"},"n":{"description":"1..64, the hexagram number + canonical order","type":"integer"},"name":{"description":"the principle's short name","type":"string"},"principle":{"description":"the actionable growth law","type":"string"},"slug":{"description":"stable identifier a tactic files under","type":"string"},"sunTzu":{"description":"the Art of War teaching","type":"string"}},"type":"object"},"ProductRow":{"properties":{"orders":{"description":"Orders is how many order_completed events carried it.","type":"integer"},"productId":{"description":"ProductID is the product the order events named.","type":"string"},"revenue":{"description":"Revenue is the total they carried, in the events' own currency unit.","type":"number"},"units":{"description":"Units is the summed quantity sold.","type":"integer"}},"type":"object"},"ProgramApplication":{"properties":{"company":{"description":"Company is the applicant's company name.","type":"string"},"companyId":{"description":"CompanyID is the CRM Company minted for this lead at intake, so the startup\nalso appears in the org's standard CRM tabs. Empty when that best-effort\nprojection did not run.","type":"string"},"contactId":{"description":"ContactID is the CRM Contact minted for this lead at intake. Empty when that\nbest-effort projection did not run.","type":"string"},"contactName":{"description":"ContactName is the person who applied.","type":"string"},"createdAt":{"description":"CreatedAt is the unix second the application arrived. Server-owned.","type":"integer"},"email":{"description":"Email is the applicant's email — half of the (email, company) key a\nresubmission refreshes instead of duplicating.","type":"string"},"events":{"description":"Events is the append-only stage-transition log, oldest first.","items":{"$ref":"#/components/schemas/StageEvent"},"type":"array"},"id":{"description":"ID is the server-minted application id (\"appl_\" + 128 random bits).","type":"string"},"metadata":{"additionalProperties":{"type":"object"},"description":"Metadata is the FULL submitted form, every field, including the arrays the\npromoted columns above do not carry (tier1Investors, useCases) and the\ndeterministic tier1Matched list.","type":"object"},"reason":{"description":"Reason is why the application was rejected, required to reject. Empty\notherwise.","type":"string"},"role":{"description":"Role is the applicant's role at their company.","type":"string"},"screen":{"$ref":"#/components/schemas/ScreenResult","description":"Screen is the AI screen. It runs after intake, so a freshly created\napplication carries a \"pending\" screen."},"stage":{"description":"Stage is the pipeline stage: applied, screened, qualified, credits-offered,\nonboarded or rejected. Server-owned — it starts at \"applied\" and moves only\nthrough the transition machine.","type":"string"},"tier1":{"description":"Tier1 is whether the applicant is tier-1 backed, derived deterministically\nat intake from the submitted fund list — independent of the AI screen.","type":"boolean"},"updatedAt":{"description":"UpdatedAt is the unix second of the last write. Server-owned.","type":"integer"},"website":{"description":"Website is the applicant's website as submitted.","type":"string"}},"type":"object"},"ProjectSummary":{"properties":{"attempts":{"description":"canonical","type":"integer"},"attempts_retained":{"type":"integer"},"benchmarks":{"type":"integer"},"cost_usd":{"type":"number"},"experiments":{"description":"canonical","type":"integer"},"experiments_retained":{"type":"integer"},"kinds":{"items":{"type":"string"},"type":"array"},"models":{"type":"integer"},"project":{"type":"string"}},"type":"object"},"Promo":{"properties":{"active":{"description":"Active is false for a promo that is no longer offered; an inactive promo\nquotes as ineligible and refuses to redeem.","type":"boolean"},"code":{"description":"Code is the promo id, e.g. \"first1000\".","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds.","type":"integer"},"description":{"description":"Description is the human-readable offer.","type":"string"},"maxRedemptions":{"description":"MaxRedemptions is the hard fleet-wide cap; the redemption past it is\ndeclined.","type":"integer"},"percentOff":{"description":"PercentOff is the discount applied to ONE month's list price.","type":"integer"},"plans":{"description":"Plans is the csv of eligible plan ids (\"pro,max,team\").","type":"string"},"teamSeatCap":{"description":"TeamSeatCap is how many Team seats bill at the promo rate; seats beyond it\nbill at list.","type":"integer"}},"type":"object"},"PromoList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/PromoStatus"},"type":"array"}},"type":"object"},"PromoStatus":{"properties":{"promo":{"$ref":"#/components/schemas/Promo"},"redeemed":{"description":"Redeemed is how many orgs have taken it, Remaining how many are left under\nthe fleet-wide cap.","type":"integer"},"remaining":{"type":"integer"}},"type":"object"},"PropSpec":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"name":{"type":"string"},"required":{"type":"boolean"},"type":{"description":"string|number|boolean|object|array","type":"string"}},"type":"object"},"ProviderBreakdown":{"properties":{"available":{"description":"Available is false when the warehouse could not be read, which means \"no\nanswer\" and NOT \"no usage\" — Items is then empty for a reason.","type":"boolean"},"items":{"description":"Items is one row per provider, most tokens first.","items":{"$ref":"#/components/schemas/ProviderRow"},"type":"array"},"source":{"description":"Source names the warehouse table the rows came from.","type":"string"}},"type":"object"},"ProviderCredit":{"properties":{"burn_cents":{"type":"integer"},"grant_cents":{"type":"integer"},"has_credit":{"type":"boolean"},"is_paid_only":{"type":"boolean"},"provider":{"type":"string"},"remaining_cents":{"type":"integer"},"runway_days":{"description":"nil when burn is 0 / unknown (never a fabricated infinity)","type":"number"}},"type":"object"},"ProviderInfo":{"properties":{"displayName":{"description":"DisplayName is the human label for the sign-in button; this deployment\nsends \"Hanzo\". Omitted from the body when empty.","type":"string"},"name":{"description":"Name is the provider id, and it is the value that goes back in the URL to\nstart a login: GET /v1/team/account/auth/{provider}. This deployment\nsurfaces exactly one, \"openid\" — the hanzo.id door.","type":"string"}},"type":"object"},"ProviderRow":{"properties":{"costCents":{"description":"CostCents is what they cost the org, in US cents.","type":"integer"},"provider":{"description":"Provider is the upstream the requests were routed to, e.g. anthropic.","type":"string"},"requests":{"description":"Requests is how many completions the org made against that provider.","type":"integer"},"tokens":{"description":"Tokens is the total tokens those completions consumed, prompt plus\ncompletion.","type":"integer"}},"type":"object"},"ProvidersCreditOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ProviderCredit"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"PubkeyView":{"properties":{"alg":{"description":"Alg is always \"Ed25519\".","type":"string"},"keys":{"description":"Keys is the same key as a single-entry JWKS (OKP/Ed25519), for JWKS-shaped\nconsumers.","items":{"$ref":"#/components/schemas/JWK"},"type":"array"},"provider":{"description":"Provider names the KMS holding the private half (\"local\" | \"aws\" | ...).\n\"local\" means a development key — never trust it in production.","type":"string"},"public_key":{"description":"PublicKey is the 32-byte Ed25519 public key, standard base64. This is the\nform the engine embeds for offline verification.","type":"string"},"schema":{"description":"Schema is the license payload schema version this key signs.","type":"integer"},"token_format":{"description":"TokenFormat states the wire layout so an implementer can verify a token\nwithout this service's source.","type":"string"}},"type":"object"},"PublishInput":{"properties":{"doctype":{"type":"string"},"name":{"type":"string"},"scheduleAt":{"description":"\"\" = now","type":"string"}},"type":"object"},"PublishResult":{"properties":{"channels":{"items":{"type":"string"},"type":"array"},"externalIds":{"additionalProperties":{"type":"string"},"type":"object"},"results":{"items":{"$ref":"#/components/schemas/ChannelResult"},"type":"array"},"status":{"type":"string"}},"type":"object"},"Purge":{"properties":{"filter":{"description":"Filter purges only messages on this org-relative subject (wildcards supported).","type":"string"},"keep":{"description":"Keep retains that many newest messages.","type":"integer"},"name":{"description":"Name is the stream name, from the path.","type":"string"}},"type":"object"},"Query":{"properties":{"character":{"description":"Character is a 0-based UTF-16 code-unit offset within Line, per the LSP\nspecification — not a byte offset and not a rune index.","type":"integer"},"line":{"description":"Line is 0-based, per the LSP specification.","type":"integer"},"path":{"description":"Path is the repo-relative file, e.g. \"apps/lsp/lsp.go\".","type":"string"},"relation":{"description":"Relation refines locate: definition, reference, type or implementation.\nEmpty means definition. Every other op ignores it.","type":"string"},"repo":{"description":"Repo is the repository NAME within the caller's own org, e.g. \"cloud\".\nNot a URL and not an owner/name pair: the owner is the validated\nprincipal's org, so this names a repository the caller already owns.","type":"string"},"rev":{"description":"Rev is a branch, tag or commit sha. Empty means the default branch. It is\nresolved to a commit before anything else happens, so an answer is always\nabout one immutable tree.","type":"string"}},"type":"object"},"Question":{"properties":{"account":{"type":"string"},"amount":{"description":"formatted figure ($…)","type":"string"},"id":{"description":"the source transaction id it concerns","type":"string"},"kind":{"description":"outlier|reversal|roundoff|uncosted|overdrawn","type":"string"},"postedAt":{"type":"string"},"text":{"description":"the specific question to ask the founder","type":"string"}},"type":"object"},"QuestionsResponse":{"properties":{"questions":{"items":{"$ref":"#/components/schemas/Question"},"type":"array"}},"type":"object"},"Quote":{"properties":{"chargeCents":{"type":"integer"},"code":{"description":"Code, Plan and Seats echo what was quoted.","type":"string"},"discountCents":{"type":"integer"},"eligible":{"description":"Eligible says whether a redeem would be accepted right now; Reason says\nwhy not when it would not.","type":"boolean"},"listCents":{"description":"ListCents is the undiscounted month price, ChargeCents what would be\ncharged, DiscountCents the difference — all in USD cents.","type":"integer"},"plan":{"type":"string"},"reason":{"type":"string"},"remaining":{"description":"Remaining is how many redemptions are left under the fleet-wide cap.","type":"integer"},"seats":{"type":"integer"}},"type":"object"},"RaiseInvoiceIn":{"properties":{"currency":{"description":"Currency is the ISO 4217 code, lower-cased. Empty means usd.","type":"string"},"customerEmail":{"description":"CustomerEmail is where the invoice is sent. Optional.","type":"string"},"lines":{"description":"Lines are the charges. The invoice subtotal and amount due are COMPUTED\nfrom these — there is no total field to send, because a total that\ndisagreed with its own lines would bill a number nobody could derive.","items":{"$ref":"#/components/schemas/InvoiceLineIn"},"type":"array"},"userId":{"description":"UserID identifies the customer being billed, within the caller's own org.\nRequired — an invoice with no addressee is not an invoice.","type":"string"}},"type":"object"},"Range":{"properties":{"end":{"$ref":"#/components/schemas/Position"},"start":{"$ref":"#/components/schemas/Position"}},"type":"object"},"RateCard":{"properties":{"basis":{"description":"Basis names where the rates come from, so a published price can be\nexplained rather than merely asserted.","type":"string"},"microUsdPerGbHour":{"description":"MicroUSDPerGBHour is the price of one GiB of memory for one hour, in\nmillionths of a US dollar.","type":"integer"},"microUsdPerVcpuHour":{"description":"MicroUSDPerVCPUHour is the price of one vCPU for one hour, in millionths of\na US dollar.","type":"integer"}},"type":"object"},"ReadOut":{"properties":{"data":{"$ref":"#/components/schemas/Snapshot"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Receipt":{"properties":{"amount":{"description":"exact 18-dp USD (money.Amount string)","type":"string"},"from":{"description":"payer address","type":"string"},"id":{"type":"string"},"network":{"type":"string"},"nonce":{"type":"string"},"payee":{"description":"recipient address","type":"string"},"payeeOrg":{"type":"string"},"payer":{"description":"payer ORG (the debited ledger)","type":"string"},"resource":{"type":"string"},"settledAt":{"type":"integer"},"settledVia":{"description":"\"ledger\" (live) | \"chain\" (seam)","type":"string"},"txHash":{"type":"string"}},"type":"object"},"Record":{"properties":{"name":{"description":"the record name the customer creates","type":"string"},"type":{"description":"TXT | CNAME","type":"string"},"value":{"description":"the record value","type":"string"}},"type":"object"},"RecordsOut":{"properties":{"data":{"type":"object"},"integrity":{"$ref":"#/components/schemas/Integrity"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"RedeemInput":{"properties":{"code":{"description":"Code is the promo code from the path.","type":"string"},"instrument":{"description":"Instrument identifies the payment method. It is the anti-farming key: one\nredemption per instrument, fleet-wide, and it is REQUIRED — an absent\ninstrument is refused, never waved through.","type":"string"}},"type":"object"},"RedeemResult":{"properties":{"alreadyRedeemed":{"description":"AlreadyRedeemed is true when this org had already taken the promo and the\ncall was an idempotent replay.","type":"boolean"},"chargeCents":{"description":"ChargeCents is what month one costs after the discount, DiscountCents the\ndiscount that produced it. Both are quoted figures against the org's\nderived plan — NOTHING WAS CREDITED and no wallet moved.","type":"integer"},"discountCents":{"type":"integer"},"redemption":{"$ref":"#/components/schemas/Redemption"}},"type":"object"},"Redemption":{"properties":{"code":{"description":"Code is the promo redeemed.","type":"string"},"discountCents":{"description":"DiscountCents is the month-one discount this redemption CLAIMS, in USD\ncents. It is a recorded figure, NOT a balance: nothing was credited and no\nwallet moved. An admin granting against this claim is what would make it\nmoney, and that decision happens on the admin surface, not here.","type":"integer"},"plan":{"description":"Plan and Seats are what was redeemed against. Both are DERIVED server-side\n— Plan from the org's live paid subscription, Seats from claimSeats — and\nneither is ever read from the request.","type":"string"},"redeemedAt":{"description":"RedeemedAt is unix seconds.","type":"integer"},"seats":{"type":"integer"}},"type":"object"},"ReferenceAnswer":{"properties":{"age":{"description":"Age is how old that is, as a duration.","type":"string"},"asOf":{"description":"AsOf is when the oldest contributing publisher was current, RFC 3339.","type":"string"},"from":{"description":"From is override or baseline — which plane answered.","type":"string"},"hit":{"description":"Hit is whether the key is a member. It is meaningful ONLY when Refusal is\nempty: false with a refusal means the set could not be consulted, which is\nnot the same as the key being clean.","type":"boolean"},"key":{"description":"Key is the key as asked.","type":"string"},"matched":{"description":"Matched is the member that covered the key, which for a domain or a network\nis the enclosing entry rather than the key itself.","type":"string"},"refusal":{"description":"Refusal is why the set could not be consulted, when it could not: never\nloaded, held elsewhere, or a source we hold no licence for. Non-empty means\nHit must not be read as an answer.","type":"string"},"score":{"description":"Score is the published risk weight where the source expresses one.","type":"number"},"set":{"description":"Set is the set consulted.","type":"string"},"stale":{"description":"Stale is whether the set is past its freshness bound. A stale set still\nanswers — yesterday's list beats none — and this is how a decision knows it\nleaned on one.","type":"boolean"},"value":{"additionalProperties":{"type":"string"},"description":"Value is what the publisher says about the member — class, operator,\nscheme, region.","type":"object"},"verdict":{"description":"Verdict is the tenant's own allow or deny, present only for an override.\nThe baseline never carries one: it states facts and leaves the decision to\nthe caller's policy.","type":"string"},"version":{"description":"Version is the exact baseline version consulted, composed of each\ncontributing publisher and its content digest. It is what makes a decision\nreproducible: an auditor takes this string and knows precisely what was\nconsulted.","type":"string"}},"type":"object"},"ReferenceOut":{"properties":{"next":{"description":"Next is the key to page from, empty when this is the last page.","type":"string"},"overrides":{"description":"Overrides is YOUR org's entries over that baseline, in key order. They are\nheld in your organisation's own store and are not visible to any other.","items":{"$ref":"#/components/schemas/ReferenceOverride"},"type":"array"},"set":{"$ref":"#/components/schemas/ReferenceSet","description":"Set is the published set: its version, its freshness and its sources."}},"type":"object"},"ReferenceOverride":{"properties":{"at":{"description":"At is when it was written, RFC 3339.","type":"string"},"by":{"description":"By is who wrote it.","type":"string"},"key":{"description":"Key is the member this organisation is speaking about.","type":"string"},"note":{"description":"Note is why, in the operator's own words. Optional, and bounded.","type":"string"},"verdict":{"description":"Verdict is allow or deny.","type":"string"}},"type":"object"},"ReferenceOverrideIn":{"properties":{"key":{"description":"Key is the member: a domain, a CIDR or address, an issuer prefix, a\ndevice digest. It is matched the same way the baseline is, so a deny on\ntempbox.example also covers mail.tempbox.example.","type":"string"},"note":{"description":"Note is why, in your own words. Optional, bounded to 512 bytes.","type":"string"},"verdict":{"description":"Verdict is allow or deny, and nothing else. An override is a decision —\nunlike a baseline entry, which states facts and leaves the decision to your\npolicy — because your organisation is the only party entitled to say \"for\nus, this one is fine\".","type":"string"}},"type":"object"},"ReferenceReceipt":{"properties":{"asOf":{"description":"AsOf is when the load happened, RFC 3339. Absent is dated on arrival, which\ncan only make the list look older than it is.","type":"string"},"keys":{"description":"Keys is how many designations that load carried. Zero from a publisher who\ndesignates somebody is a failed load wearing a successful one's clothes,\nand belongs in Refusal instead.","type":"integer"},"refusal":{"description":"Refusal is why the load failed, when it did.","type":"string"},"source":{"description":"Source is the publisher this receipt is for.","type":"string"},"version":{"description":"Version is the digest of what that publisher supplied, so a refresh that\nchanged nothing can be told from a refresh that did not run.","type":"string"}},"type":"object"},"ReferenceSet":{"properties":{"age":{"description":"Age is how long ago that was.","type":"string"},"asOf":{"description":"AsOf is when the OLDEST contributing publisher was current, RFC 3339. The\noldest and not the newest: a set is exactly as fresh as its weakest source.","type":"string"},"keys":{"description":"Keys is how many members the baseline carries.","type":"integer"},"kind":{"description":"Kind is how the baseline comes to exist: fetch (downloaded from a\npublisher), local (computed here), attest (held by the component that\nscreens against it, freshness reported), or seam (declared and NOT held,\nbecause the source needs a licence we do not have).","type":"string"},"match":{"description":"Match is how a key is tested: exact, domain, net, digits, pattern or range.","type":"string"},"maxAge":{"description":"MaxAge is how old this set may be before it is stale.","type":"string"},"overrides":{"description":"Overrides is how many entries YOUR org has laid over this baseline.","type":"integer"},"refusal":{"description":"Refusal names why the set cannot be relied on, when it cannot: never\nloaded, held elsewhere, or a licence we do not hold. Non-empty means a\nlookup against this set will not answer, rather than answering clean.","type":"string"},"set":{"description":"Set is the name this set is addressed by.","type":"string"},"sources":{"description":"Sources is each contributing publisher, its licence and its own freshness.","items":{"$ref":"#/components/schemas/ReferenceSource"},"type":"array"},"stale":{"description":"Stale is whether it is past that bound. A stale set still answers and says\nso, because yesterday's list beats none.","type":"boolean"},"version":{"description":"Version is the exact baseline consulted — every contributing publisher and\nits content digest. A decision records this and an auditor resolves it back.","type":"string"},"what":{"description":"What the set holds, in one sentence.","type":"string"}},"type":"object"},"ReferenceSetsOut":{"properties":{"refused":{"description":"Refused names the sets that cannot be consulted at all. A key checked\nagainst one of these is UNKNOWN, not clean.","items":{"type":"string"},"type":"array"},"sets":{"description":"Sets is the whole catalog, in a stable order.","items":{"$ref":"#/components/schemas/ReferenceSet"},"type":"array"},"stale":{"description":"Stale names the sets past their freshness bound — the list to alarm on.","items":{"type":"string"},"type":"array"}},"type":"object"},"ReferenceSource":{"properties":{"asOf":{"description":"AsOf is when this publisher was current, RFC 3339.","type":"string"},"basis":{"description":"Basis is the KIND of permission this publisher's data reaches you under:\nlicence (an explicit grant), registry (the registry of record publishing for\nanyone to consult), operator (an operator's own machine-readable statement\nabout its own network, published for third parties to filter by — not a\nlicence, and not claimed as one), own (computed here), or none (nothing\nreaches you: the membership is held by the component that screens against\nit). It is on the wire so the licence position is an audit you can run.","type":"string"},"keys":{"description":"Keys is how many members this publisher contributed.","type":"integer"},"origin":{"description":"Origin is exactly where it was taken from, so it can be taken again.","type":"string"},"refusal":{"description":"Refusal is why this publisher's last take failed, if it did. The set keeps\nits previous version of this source and ages out visibly rather than\nsilently shrinking.","type":"string"},"source":{"description":"Source is the publisher.","type":"string"},"terms":{"description":"Terms is the CITATION that basis points at — the licence identifier, the\nregistry, or the operator publication. A source with no stated terms is not\nin the catalog.","type":"string"},"version":{"description":"Version is the content digest of what this publisher last supplied. Two\nrefreshes that agree on it took the same data.","type":"string"}},"type":"object"},"ReferenceTaken":{"properties":{"keys":{"description":"Keys is how many members it carries.","type":"integer"},"refusal":{"description":"Refusal is why this publisher contributed nothing, if it did not. The set\nkeeps its previous version of this source rather than shrinking.","type":"string"},"resumed":{"description":"Resumed is true when this run continued a version a previous run left\nhalf-landed.","type":"boolean"},"source":{"description":"Source is the publisher.","type":"string"},"unchanged":{"description":"Unchanged is true when the publisher's data was byte-for-byte the set we\nalready held.","type":"boolean"},"version":{"description":"Version is the content digest that landed.","type":"string"},"wrote":{"description":"Wrote is how many rows this run actually wrote. Zero with Unchanged means\nthe publisher served the same set again.","type":"integer"}},"type":"object"},"ReferenceVersion":{"properties":{"asOf":{"description":"AsOf is when the oldest of them was current, RFC 3339.","type":"string"},"refusal":{"description":"Refusal is why it could not be consulted, when it could not.","type":"string"},"set":{"description":"Set is the set.","type":"string"},"stale":{"description":"Stale is whether it is past its freshness bound.","type":"boolean"},"version":{"description":"Version is every contributing publisher and its content digest.","type":"string"}},"type":"object"},"RefreshReferenceIn":{"properties":{"force":{"description":"Force accepts a take whose size moved past the change bound. A publisher\nserving a tenth or ten times its previous list is refused by default and the\nprevious version is left standing; this is the operator saying the change is\nreal. It cannot make an empty, truncated or unparseable take land — those are\nerrors, not magnitudes.","type":"boolean"},"receipts":{"description":"Receipts are supplied by the component that holds the membership, for a set\nof kind attest. They are refused on any other kind, and a set of kind attest\nis refused without them: this plane never invents a freshness it did not\nobserve.","items":{"$ref":"#/components/schemas/ReferenceReceipt"},"type":"array"},"set":{"description":"Set is the set to refresh.","type":"string"}},"type":"object"},"RefreshReferenceOut":{"properties":{"set":{"description":"Set is the set refreshed.","type":"string"},"stale":{"description":"Stale is whether it is STILL past its freshness bound after the refresh,\nwhich is what a publisher that has stopped answering looks like.","type":"boolean"},"took":{"description":"Took is what each publisher contributed.","items":{"$ref":"#/components/schemas/ReferenceTaken"},"type":"array"},"version":{"description":"Version is the set's new composed version.","type":"string"}},"type":"object"},"Registration":{"properties":{"createdAt":{"description":"CreatedAt is the unix second the formation was opened.","type":"integer"},"name":{"description":"Name is the company name the entity is being formed under.","type":"string"},"org":{"description":"Org is the org whose formation this row projects.","type":"string"},"stage":{"description":"Stage is the formation's current state — what the platform reads to see which\nformations are stalled and where.","type":"string"},"structure":{"description":"Structure is the legal entity being formed: c-corp, llc or dao-llc.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second of the most recent write to the formation, and\nthe key the register sorts on (newest activity first).","type":"integer"}},"type":"object"},"Release":{"properties":{"app_id":{"description":"AppID scopes the release to an app build (\"hanzo\" | \"lux\" | \"zoo\").","type":"string"},"artifact_ref":{"description":"ArtifactRef is where the binary lives (object-store key / OCI ref / path).","type":"string"},"cosign_cert":{"description":"CosignCert is the cosign/Fulcio cert (keyless) or public key ref used to\nverify CosignSignature. The download response hands this to the client.","type":"string"},"cosign_signature":{"description":"CosignSignature is the base64 cosign signature over the artifact digest.","type":"string"},"created_at":{"type":"integer"},"id":{"description":"ID is the release identifier, e.g. \"engine-rocm-0.4.2-linux-amd64\". The\naccelerator belongs here — one product is built several ways — and never\nin Product below.","type":"string"},"min_features":{"description":"MinFeatures, when set, are features the license must include to download.","items":{"type":"string"},"type":"array"},"platform":{"description":"Platform is \"\u003cos\u003e/\u003carch\u003e\", e.g. \"linux/amd64\".","type":"string"},"product":{"description":"Product is the licensed product this artifact belongs to (commerce SKU):\n\"engine\" for every one of those builds.","type":"string"},"sha256":{"description":"SHA256 is the hex digest of the artifact (integrity + cosign subject).","type":"string"},"version":{"description":"Version is the semantic version of the binary.","type":"string"},"yanked":{"description":"Yanked marks a pulled release (download refused; tokens may be revoked\nrelease-scoped too).","type":"boolean"}},"type":"object"},"ReleaseList":{"properties":{"releases":{"description":"Releases is the published releases, always an array and never null.","items":{"$ref":"#/components/schemas/Release"},"type":"array"}},"type":"object"},"ReleaseState":{"properties":{"endedAt":{"type":"integer"},"error":{"description":"Error is why it stopped, when it failed.","type":"string"},"id":{"description":"ID is the build id returned by the 202.","type":"string"},"image":{"description":"Image is the tag the release publishes on success.","type":"string"},"reached":{"description":"Reached is the last pipeline step completed: built, smoked, tagged, pinned.","type":"string"},"sha":{"description":"SHA is the commit the release pinned.","type":"string"},"startedAt":{"description":"StartedAt / EndedAt are unix seconds.","type":"integer"},"status":{"description":"Status is \"releasing\", \"released\" or \"failed\".","type":"string"},"version":{"description":"Version is that tag without the leading \"v\".","type":"string"}},"type":"object"},"ReloadIn":{"properties":{"name":{"description":"Name is the app, from the path. It must be one the manifest declares.","type":"string"},"scope":{"description":"Scope \"host\" applies here only. Default \"fleet\" rolls it out one host at\na time, halting on the first host that fails to come up.","type":"string"},"sum":{"type":"string"},"url":{"description":"URL is the artifact directly, for an origin with no index. Sum is its hex\nSHA-256 and is REQUIRED with it: zip refuses an unverified download, and\nso does this.","type":"string"},"version":{"description":"Version is a release tag, resolved to a URL and digest through the\norigin's binaries.json index — the same index CI publishes, so there is\nno second table mapping versions to digests.","type":"string"}},"type":"object"},"ResearchArtifact":{"properties":{"content":{"description":"base64 bytes on write; the server hashes + stores them (never returned)","type":"string"},"git_branch":{"type":"string"},"git_dirty":{"type":"boolean"},"git_sha":{"type":"string"},"kind":{"type":"string"},"lib_versions":{},"project":{"type":"string"},"ref":{"description":"server-derived content address (sha256:\u003chash\u003e)","type":"string"},"retention_class":{"type":"string"},"run_id":{"type":"string"},"sha256":{"description":"SERVER-derived on write; the identity","type":"string"},"ts":{"type":"integer"},"visibility":{"type":"string"}},"type":"object"},"ResearchTotals":{"properties":{"attempts":{"description":"canonical","type":"integer"},"attempts_retained":{"type":"integer"},"benchmarks":{"type":"integer"},"by_kind":{"items":{"$ref":"#/components/schemas/KindTotal"},"type":"array"},"cost_usd":{"type":"number"},"experiments":{"description":"canonical","type":"integer"},"experiments_retained":{"type":"integer"},"models":{"type":"integer"},"project":{"type":"string"},"projects":{"type":"integer"}},"type":"object"},"ResolveReferenceIn":{"properties":{"keys":{"description":"Keys are the values to look up, at most 100 per call: email addresses or\ndomains, IP addresses, card prefixes, user-agent strings, autonomous system\nnumbers, device digests.","items":{"type":"string"},"type":"array"},"sets":{"description":"Sets narrows which sets to consult. Empty consults every set whose matcher\ncan read the keys given.","items":{"type":"string"},"type":"array"}},"type":"object"},"ResolveReferenceOut":{"properties":{"answers":{"description":"Answers is one entry per (set, key) consulted.","items":{"$ref":"#/components/schemas/ReferenceAnswer"},"type":"array"},"consulted":{"description":"Consulted names the version of every set that took part, so a decision can\nrecord precisely what it leaned on. Record this with the decision: it is\nwhat makes the decision reproducible a year later.","items":{"$ref":"#/components/schemas/ReferenceVersion"},"type":"array"},"refused":{"description":"Refused names the consulted sets that could not answer at all. A key that\nmissed in one of these is UNKNOWN, not clean.","items":{"type":"string"},"type":"array"},"stale":{"description":"Stale names the consulted sets past their freshness bound. Staleness is\nitself a risk signal — a decision taken against a three-week-old list is a\nweaker decision, and this is how it knows.","items":{"type":"string"},"type":"array"}},"type":"object"},"Result":{"properties":{"host":{"type":"string"},"msg":{"type":"string"},"ok":{"type":"boolean"},"version":{"type":"string"}},"type":"object"},"RevenueCustomer":{"properties":{"balanceCents":{"type":"integer"},"display":{"type":"string"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"plan":{"type":"string"},"spendCents":{"type":"integer"}},"type":"object"},"RevenueData":{"properties":{"arpuCents":{"type":"integer"},"customers":{"type":"integer"},"generatedAt":{"type":"string"},"mrrCents":{"type":"integer"},"payingCustomers":{"type":"integer"},"perCustomer":{"items":{"$ref":"#/components/schemas/RevenueCustomer"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"spendTrend":{"items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"totalBalancesCents":{"type":"integer"},"totalSpendCents":{"type":"integer"}},"type":"object"},"RevenueOut":{"properties":{"data":{"$ref":"#/components/schemas/RevenueData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"ReviewRequest":{"properties":{"glossary":{"additionalProperties":{"type":"string"},"description":"Glossary is the terminology the entry was translated under. Its VERSION — the\ndigest of the sorted terms — is part of the entry's identity, so editing a term\nyields a new entry rather than overwriting the old rendering.","type":"object"},"source":{"description":"Source is the ORIGINAL string this entry translates. Required; part of the\nentry's identity, so a different source is a different entry.","type":"string"},"state":{"description":"State is the entry's new position on the review ladder: suggested, approved or\npublished. `machine` is engine-only and is refused here — a human may not demote\na string back into the churn.","type":"string"},"target":{"description":"Target is the target language tag (BCP-47, e.g. \"es\" or \"pt-BR\"). Required;\npart of the entry's identity.","type":"string"},"text":{"description":"Text is the reviewed translation to store. A human write always wins over the\nstored value.","type":"string"},"tier":{"description":"Tier is the engine tier the entry belongs to, quality (the default) or bulk.\nPart of the entry's identity: the two tiers keep separate renderings.","type":"string"}},"type":"object"},"RevocationEntry":{"properties":{"at":{"type":"integer"},"by":{"type":"string"},"reason":{"type":"string"},"scope":{"type":"string"},"value":{"type":"string"}},"type":"object"},"RevokeRequest":{"properties":{"reason":{"description":"Reason is the operator's note, echoed back by verify so a support agent can\nexplain the refusal.","type":"string"},"scope":{"description":"Scope is what the revocation matches on: \"nonce\" kills one token,\n\"holder\" every token issued to one bearer, \"fingerprint\" every token bound\nto one device, and \"release\" every token scoped to one binary release.","type":"string"},"value":{"description":"Value is the concrete nonce, holder, fingerprint or release id to revoke.","type":"string"}},"required":["scope","value"],"type":"object"},"RevokeResponse":{"properties":{"entry":{"$ref":"#/components/schemas/RevocationEntry","description":"Entry is the stored revocation, including who recorded it and when."},"revoked":{"description":"Revoked is always true — a failure is an error status, not a false here.","type":"boolean"}},"type":"object"},"RoleAssignment":{"properties":{"role":{"description":"Role is the granted role's name.","type":"string"},"user":{"description":"User is the member the role is granted to.","type":"string"}},"type":"object"},"RoundInput":{"properties":{"name":{"description":"Name is the round's name on the cap table, e.g. \"Seed\". Required.","type":"string"},"preMoneyValuation":{"description":"PreMoneyValuation is the valuation the round prices off, before the new money.","type":"number"},"pricePerShare":{"description":"PricePerShare is the per-share price of a priced round.","type":"number"},"roundType":{"description":"RoundType is PRICED, SAFE or CONVERTIBLE_NOTE. Defaults to PRICED.","type":"string"},"shareClassId":{"description":"ShareClassID is the cap table's share class the round issues into.","type":"string"},"targetAmount":{"description":"TargetAmount is the amount the round is raising, recorded verbatim on the\ncanonical cap table's rounds.create contract.","type":"number"}},"type":"object"},"Route":{"properties":{"host":{"description":"Host is the exact hostname this route matches, lowercased with any trailing\ndot stripped. It is a GLOBALLY unique claim — one route across the whole\nedge may hold a host, so no tenant can hijack another's.","type":"string"},"id":{"description":"ID identifies the route within the org: [A-Za-z0-9-_.], at most 128 chars.\nA create that omits it gets a generated one.","type":"string"},"middlewares":{"description":"Middlewares are the ids of the edge transforms to apply, in this order,\nbefore the request reaches the service. At most 16.","items":{"type":"string"},"type":"array"},"pathPrefix":{"description":"PathPrefix narrows the match to requests under this path; it must start\nwith \"/\". Empty matches every path on the host.","type":"string"},"priority":{"description":"Priority orders routes that share a host: higher wins, and equal priorities\nfall back to the longer PathPrefix.","type":"integer"},"service":{"description":"Service is the id of the backend pool this route dispatches to. A route\nnaming a service that does not exist is skipped at compile, not served.","type":"string"},"tls":{"description":"TLS asks the edge to terminate TLS for Host with an ACME-managed certificate.","type":"boolean"}},"type":"object"},"RouteCandidate":{"properties":{"account":{"description":"Account is the provider-side account identifier.","type":"string"},"available":{"description":"Available reports whether the candidate is routable right now.","type":"boolean"},"billing":{"description":"Billing is the cost consequence of dialing this candidate: plan (the\nuser's own subscription) or commerce (the metered gateway path).","type":"string"},"headroomPct":{"description":"HeadroomPct is the remaining rate-limit capacity, 0..100. A link with no\nsnapshot counts as full headroom.","type":"number"},"host":{"description":"Host is that machine's hostname label.","type":"string"},"kind":{"description":"Kind is how the account authenticates: subscription or apikey.","type":"string"},"linkId":{"description":"LinkID is the underlying link's opaque handle.","type":"string"},"machine":{"description":"Machine is the machine the account is signed in on.","type":"string"},"plan":{"description":"Plan is the provider plan label the account is on.","type":"string"},"provider":{"description":"Provider is the AI provider the candidate account belongs to.","type":"string"},"reason":{"description":"Reason says why the candidate is not routable, when Available is false.","type":"string"}},"type":"object"},"RoutePlan":{"properties":{"candidates":{"description":"Candidates is every linked account in preference order: subscriptions\nfirst, then metered api-key accounts as the backstop.","items":{"$ref":"#/components/schemas/RouteCandidate"},"type":"array"},"generatedAt":{"description":"GeneratedAt is when the plan was computed, RFC 3339 UTC.","type":"string"},"primary":{"$ref":"#/components/schemas/RouteCandidate","description":"Primary is the first available candidate; absent when every account is\nrate-limited."}},"type":"object"},"RoutedUsage":{"properties":{"account":{"description":"Account is the provider-side account identifier.","type":"string"},"billing":{"description":"Billing is how the routed inference bills: plan or commerce.","type":"string"},"completionTokens":{"description":"CompletionTokens is the routed completion-token count.","type":"integer"},"costCents":{"description":"CostCents is the routed cost in cents.","type":"integer"},"kind":{"description":"Kind is how the account authenticates: subscription or apikey.","type":"string"},"promptTokens":{"description":"PromptTokens is the routed prompt-token count.","type":"integer"},"provider":{"description":"Provider is the AI provider the row's account belongs to.","type":"string"},"requests":{"description":"Requests is how many requests the gateway routed through this account.","type":"integer"},"totalTokens":{"description":"TotalTokens is the routed total token count.","type":"integer"}},"type":"object"},"Rule":{"properties":{"category":{"description":"Category is the COA expense account a matching bill books to. An upsert normalizes\na slug (\"cloud\") to its account number.","type":"string"},"pattern":{"description":"Pattern is the merchant substring the rule matches on, case-insensitively. It is\nalso the key an upsert writes by.","type":"string"},"priority":{"description":"Priority breaks ties: when several patterns match, the highest wins.","type":"integer"}},"type":"object"},"SaaSCategory":{"properties":{"category":{"type":"string"},"mrrCents":{"type":"integer"},"subscriptions":{"type":"integer"}},"type":"object"},"SaaSCustomer":{"properties":{"category":{"type":"string"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"plan":{"type":"string"},"seats":{"type":"integer"},"since":{"type":"string"},"status":{"type":"string"},"usageCents":{"type":"integer"}},"type":"object"},"SaaSEvent":{"properties":{"at":{"type":"string"},"category":{"type":"string"},"mrrDeltaCents":{"type":"integer"},"org":{"type":"string"},"plan":{"type":"string"},"type":{"type":"string"}},"type":"object"},"SaaSPlan":{"properties":{"active":{"type":"integer"},"category":{"type":"string"},"mrrCents":{"type":"integer"},"name":{"type":"string"},"plan":{"type":"string"},"seats":{"type":"integer"},"trialing":{"type":"integer"}},"type":"object"},"SaaSRevenue":{"properties":{"activeSubscriptions":{"type":"integer"},"arrCents":{"type":"integer"},"byCategory":{"items":{"$ref":"#/components/schemas/SaaSCategory"},"type":"array"},"churnedMrrCents":{"type":"integer"},"mrrCents":{"type":"integer"},"netNewMrrCents":{"type":"integer"},"newMrrCents":{"type":"integer"},"payingCustomers":{"type":"integer"},"trials":{"type":"integer"}},"type":"object"},"SaaSSubs":{"properties":{"byPlan":{"items":{"$ref":"#/components/schemas/SaaSPlan"},"type":"array"},"canceled":{"type":"integer"},"new":{"type":"integer"},"recent":{"items":{"$ref":"#/components/schemas/SaaSEvent"},"type":"array"},"trialsActive":{"type":"integer"}},"type":"object"},"SaaSUsage":{"properties":{"instrumented":{"type":"boolean"},"requests":{"type":"integer"},"windowUsageCents":{"type":"integer"}},"type":"object"},"SbomHealth":{"properties":{"datastore":{"description":"Datastore reports whether the shared datastore connection this subsystem reads\nand writes through is established. False means the data endpoints answer 503.","type":"boolean"},"service":{"description":"Service names the subsystem answering: always \"sbom\".","type":"string"},"status":{"description":"Status is the liveness verdict: always \"ok\" here, because the process answering\nat all IS the liveness fact.","type":"string"},"table":{"description":"Table is the fully-qualified datastore table the components live in.","type":"string"}},"type":"object"},"SbomIngest":{"properties":{"document":{"description":"Document is the raw CycloneDX bill of materials, any JSON. Its components[]\nare flattened and persisted; nothing else is read or stored."},"format":{"description":"Format names the document format; \"cyclonedx\" is the only one parsed.","type":"string"},"gitSha":{"description":"GitSha is the commit the image was built from.","type":"string"},"imageDigest":{"description":"ImageDigest is the content-addressed digest (sha256:…) the components are\nkeyed under. Required — it, not a tenant, is what an SBOM belongs to.","type":"string"},"imageRef":{"description":"ImageRef is the human-readable image reference the digest was published as.\nA resolve matches on either this or the digest.","type":"string"},"sourceRepo":{"description":"SourceRepo is the repository the image was built from.","type":"string"}},"type":"object"},"SbomIngested":{"properties":{"componentCount":{"description":"ComponentCount is how many components the CycloneDX document yielded and this\ncall persisted.","type":"integer"},"imageDigest":{"description":"ImageDigest is the content-addressed digest the components were keyed under.","type":"string"}},"type":"object"},"ScaleIn":{"properties":{"count":{"description":"Count is the node count to set.","type":"integer"},"id":{"description":"ID is the DOKS cluster id, from the path.","type":"string"},"pool":{"description":"Pool is the node pool, from the path. Its DO id or its name — both are unique\nwithin a cluster, and an operator reads the name off the board.","type":"string"}},"type":"object"},"ScanDraft":{"properties":{"balanced":{"type":"boolean"},"category":{"type":"string"},"confidence":{"type":"string"},"extracted":{"$ref":"#/components/schemas/Extracted"},"questions":{"items":{"$ref":"#/components/schemas/Question"},"type":"array"},"scanId":{"type":"string"},"vendor":{"type":"string"},"voucher":{"$ref":"#/components/schemas/Voucher"}},"type":"object"},"ScheduleInput":{"properties":{"id":{"description":"ID is the campaign id from the path.","type":"string"},"scheduledAt":{"description":"ScheduledAt is the unix send time. 0 clears the schedule.","type":"integer"}},"type":"object"},"Scope":{"properties":{"org":{"description":"Org is the IAM org slug the rows were read under: the validated principal's,\nresolved server-side.","type":"string"}},"type":"object"},"ScreenResult":{"properties":{"draftReply":{"description":"DraftReply is a suggested email reply for staff to edit and send.","type":"string"},"error":{"description":"Error says why a failed screen failed — no AI gateway configured, a gateway\nerror, or a reply that carried no parseable JSON. Absent on success.","type":"string"},"model":{"description":"Model is the LLM the screen ran on.","type":"string"},"score":{"description":"Score is the model's 0..100 fit score, clamped to that range.","type":"integer"},"screenedAt":{"description":"ScreenedAt is the unix second the screen finished (0 while pending).","type":"integer"},"status":{"description":"Status is the screen's state: pending | done | failed.","type":"string"},"suggestedCredits":{"description":"SuggestedCredits is the recommended credit grant in USD, snapped to the\nnearest allowed rung: 0 | 5000 | 25000 | 50000 | 150000.","type":"integer"},"summary":{"description":"Summary is the model's short assessment of the application.","type":"string"},"tier1Backed":{"description":"Tier1Backed is the model's read on tier-1 backing, normalized to\n\"yes\", \"no\" or \"unclear\" (anything it cannot resolve reads \"unclear\").","type":"string"}},"type":"object"},"Section":{"properties":{"detail":{"type":"string"},"enabled":{"type":"boolean"},"id":{"type":"string"},"order":{"type":"integer"},"title":{"type":"string"}},"type":"object"},"SelfRank":{"properties":{"costCents":{"description":"CostCents is the caller's own spend in whole US cents. Always populated — your\nown spend is never withheld from you — so here 0 really does mean zero.","type":"integer"},"handle":{"description":"Handle is how the caller appears on this board: their chosen handle, falling back\nto their username, on a user board; their org id on the global board. Present\neven when unlisted — this is the caller looking at themselves.","type":"string"},"listed":{"description":"Listed says whether the caller is publicly visible on this board: opted in on a\nuser board, org opted in (or the viewer is a platform admin) on the global one.\nFalse is the prompt to offer the opt-in, and explains an unranked global self.","type":"boolean"},"metric":{"description":"Metric is whichever of the three values above the board was ranked by, so a\nclient can compare the caller against the rows without re-reading the request.\nMetric \u003c= 0 is exactly the case that leaves Ranked false.","type":"integer"},"ofTotal":{"description":"OfTotal is the size of the universe Rank is out of — \"rank N of OfTotal\". On a\nuser board that is the org's users with any usage in the window; on the global\nboard it is every active org for a platform admin, and the count of opted-in\norgs for everyone else.","type":"integer"},"rank":{"description":"Rank is the caller's 1-based standing, computed as (subjects whose windowed\nmetric strictly exceeds the caller's) + 1. It is exact against the whole ranked\nuniverse, not just the returned page, so it can far exceed len(rows). Read it\nonly when Ranked.","type":"integer"},"ranked":{"description":"Ranked is false when the caller holds no position: they had no usage in the\nwindow, or (on the global board) their org has not opted into public listing and\nso is not ranked against a set it never joined. Rank is then 0 and means nothing.","type":"boolean"},"requests":{"description":"Requests is the caller's own request count in the window, 0 if they were idle.","type":"integer"},"tokens":{"description":"Tokens is the caller's own prompt+completion tokens in the window.","type":"integer"}},"type":"object"},"Sequence":{"properties":{"createdAt":{"description":"CreatedAt and UpdatedAt are unix seconds, both server-assigned.","type":"integer"},"id":{"description":"ID is the server-assigned sequence id (\"seq_\" + 128 random bits).","type":"string"},"name":{"description":"Name is the sequence's label. Required, trimmed, capped at 1024 bytes.","type":"string"},"status":{"description":"Status is the lifecycle: draft, active or archived. Empty means draft, and\nONLY an active sequence accepts enrollments.","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"SequenceList":{"properties":{"data":{"description":"Data is the page; an empty array when the org has no sequence.","items":{"$ref":"#/components/schemas/Sequence"},"type":"array"}},"type":"object"},"SequenceStatus":{"properties":{"id":{"description":"ID is the sequence id from the path.","type":"string"},"status":{"description":"Status is draft, active or archived. Required; there is no default here,\nunlike on create. Only an active sequence accepts enrollments.","type":"string"}},"type":"object"},"SequenceView":{"properties":{"sequence":{"$ref":"#/components/schemas/Sequence"},"steps":{"description":"Steps are in send order (idx ascending); empty for a sequence with no\nmessages yet, which enrolls fine and completes immediately.","items":{"$ref":"#/components/schemas/Step"},"type":"array"}},"type":"object"},"Sequences":{"properties":{"consumer_seq":{"description":"Consumer is the consumer's own sequence.","type":"integer"},"stream_seq":{"description":"Stream is the corresponding stream sequence.","type":"integer"}},"type":"object"},"SeriesPoint":{"properties":{"t":{"type":"string"},"value":{"type":"integer"}},"type":"object"},"Service":{"properties":{"backends":{"description":"Backends are the upstream servers to balance across: 1..32 of them.","items":{"$ref":"#/components/schemas/Backend"},"type":"array"},"id":{"description":"ID identifies the pool within the org: [A-Za-z0-9-_.], at most 128 chars.\nA create that omits it gets a generated one. Routes reference it by this id.","type":"string"},"passHostHeader":{"description":"PassHostHeader forwards the client's original Host header upstream instead\nof rewriting it to the backend's.","type":"boolean"}},"type":"object"},"ServiceInput":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"hosts":{"items":{"type":"string"},"type":"array"},"service":{"type":"string"},"waitlistMode":{"type":"boolean"}},"type":"object"},"ServiceView":{"properties":{"createdAt":{"type":"integer"},"description":{"type":"string"},"displayName":{"type":"string"},"hosts":{"items":{"type":"string"},"type":"array"},"service":{"type":"string"},"updatedAt":{"type":"integer"},"updatedBy":{"type":"string"},"waitlistMode":{"type":"boolean"}},"type":"object"},"SetReferenceIn":{"properties":{"entries":{"description":"Entries are the overrides to write, up to 1000 per call.","items":{"$ref":"#/components/schemas/ReferenceOverrideIn"},"type":"array"}},"type":"object"},"SetReferenceOut":{"properties":{"overrides":{"description":"Overrides is how many your org now holds in this set.","type":"integer"},"set":{"description":"Set is the set written in.","type":"string"},"written":{"description":"Written is how many entries this call wrote.","type":"integer"}},"type":"object"},"SharePolicy":{"properties":{"revenueShareBps":{"type":"integer"},"updatedAt":{"type":"integer"}},"type":"object"},"Signer":{"properties":{"email":{"description":"Email is the address the signature request is sent to.","type":"string"},"name":{"description":"Name is the recipient's name, as it appears on the signature request.","type":"string"}},"type":"object"},"Skill":{"properties":{"content":{"description":"Content is the SKILL.md body, markdown.","type":"string"},"createdAt":{"description":"CreatedAt is when the skill was last written, Unix seconds.","type":"integer"},"description":{"description":"Description is the one-line summary discovery shows for the skill.","type":"string"},"id":{"description":"ID is the skill's id within the org. It is DERIVED from Name, so writing\nthe same name again revises that skill rather than adding another.","type":"string"},"name":{"description":"Name is the skill's name: one lowercase path segment (a-z0-9, _ or -).","type":"string"},"org":{"description":"Org is the org that authored the skill — the validated caller's, never a\nvalue the body supplied.","type":"string"}},"type":"object"},"Snapshot":{"properties":{"at":{"type":"string"},"clusters":{"items":{"$ref":"#/components/schemas/Cluster"},"type":"array"},"complete":{"type":"boolean"},"cost":{"$ref":"#/components/schemas/Cost"},"findings":{"items":{"$ref":"#/components/schemas/Finding"},"type":"array"},"incompleteReason":{"type":"string"},"loadBalancers":{"items":{"$ref":"#/components/schemas/LoadBalancer"},"type":"array"},"nodes":{"items":{"$ref":"#/components/schemas/Node"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"totals":{"$ref":"#/components/schemas/Totals"},"volumes":{"items":{"$ref":"#/components/schemas/Volume"},"type":"array"}},"type":"object"},"SourceState":{"properties":{"available":{"description":"Available is whether this side's ledger answered. False means its rows are\nmissing, not that there were none.","type":"boolean"},"note":{"description":"Note is the human sentence that says what this side's numbers mean, so a\nboard cannot present a plan percentage as a Hanzo charge.","type":"string"},"scope":{"description":"Scope is whose rows this side carries: \"user\" or \"org\".","type":"string"},"source":{"description":"Source is the table of record the rows came from.","type":"string"}},"type":"object"},"SourceStatus":{"properties":{"at":{"type":"string"},"error":{"type":"string"},"name":{"type":"string"},"ok":{"type":"boolean"},"rows":{"type":"integer"}},"type":"object"},"Sources":{"properties":{"commerce":{"description":"Commerce is whether the billing ledger answered the spend block.","type":"boolean"},"warehouse":{"description":"Warehouse is whether the usage warehouse answered the LLM block.","type":"boolean"}},"type":"object"},"Span":{"properties":{"endLine":{"type":"integer"},"file":{"type":"string"},"kind":{"type":"string"},"line":{"type":"integer"},"repo":{"type":"string"},"role":{"description":"context: match | definition | caller","type":"string"},"score":{"type":"number"},"snippet":{"type":"string"},"symbol":{"type":"string"},"tier":{"type":"string"}},"type":"object"},"SpanBody":{"properties":{"duration":{"type":"integer"},"id":{"type":"string"},"kind":{"type":"string"},"parent":{"type":"string"},"status":{"type":"string"},"trace":{"type":"string"}},"type":"object"},"Spec":{"properties":{"arch":{"description":"amd64 | arm64 | ...","type":"string"},"cpus":{"description":"logical cores","type":"integer"},"gpus":{"items":{"$ref":"#/components/schemas/GPU"},"type":"array"},"memory":{"description":"total RAM, bytes","type":"integer"},"os":{"description":"linux | darwin | windows","type":"string"}},"type":"object"},"Spend":{"properties":{"available":{"description":"Available is false when the commerce ledger was unconfigured or\nunreachable. Every number below is then an honest zero, NOT a measured one.","type":"boolean"},"availableCents":{"description":"AvailableCents is what of that balance is still spendable.","type":"integer"},"balanceCents":{"description":"BalanceCents is the prepaid wallet's balance, in US cents.","type":"integer"},"byCategory":{"description":"ByCategory is the window's spend split by ledger category, largest first.","items":{"$ref":"#/components/schemas/CategorySpend"},"type":"array"},"mtdCents":{"description":"MTDCents is commerce's authoritative month-to-date consumed figure, which\nis a different period from the window and is not derived from it.","type":"integer"},"overageCents":{"description":"OverageCents is month-to-date consumption beyond the plan's allowance.","type":"integer"},"series":{"description":"Series is the window's spend over time, gap-filled at the window's\ninterval.","items":{"$ref":"#/components/schemas/SpendPoint"},"type":"array"},"source":{"description":"Source names where the roll-up came from.","type":"string"},"totalCents":{"description":"TotalCents is consumption over the requested window, in US cents. It is\nself-consistent with ByCategory and Series.","type":"integer"}},"type":"object"},"SpendPoint":{"properties":{"cents":{"description":"Cents is the consumption recorded in that bucket, in US cents.","type":"integer"},"t":{"description":"T is the bucket's start instant, RFC3339 UTC. Buckets are gap-filled, so a\nwindow with no spend still has its points.","type":"string"}},"type":"object"},"StageEvent":{"properties":{"at":{"description":"At is the unix second of the move.","type":"integer"},"by":{"description":"By is who moved it: \"system\" for intake and the AI auto-advance, else the\nvalidated staff user id.","type":"string"},"from":{"description":"From is the stage moved out of; empty on the intake event that opens the log.","type":"string"},"note":{"description":"Note is the free-text comment recorded with the move. Absent when none.","type":"string"},"to":{"description":"To is the stage moved into.","type":"string"}},"type":"object"},"StarterKit":{"properties":{"category":{"description":"groups the kit in the gallery browser (\"Portfolio\", \"SaaS\")","type":"string"},"demo":{"description":"live demo (\u003cslug\u003e.hanzo.app), when deployed","type":"string"},"description":{"description":"the browse-card blurb","type":"string"},"features":{"description":"the highlights the card lists, at most 32","items":{"type":"string"},"type":"array"},"framework":{"description":"the stack the kit is built on (\"Next.js 14.2 + TS\")","type":"string"},"org":{"description":"owner of a PRIVATE template; empty in the public catalog","type":"string"},"preview":{"description":"the still image the browse card renders","type":"string"},"rating":{"description":"Rating is public-gallery curation, on the same terms as Tier: catalog-only,\nnever accepted from a request, absent on a customer's own kit.","type":"number"},"slug":{"description":"the kit's identity — lowercase alphanumeric with dashes, max 40","type":"string"},"source":{"description":"the repository the kit is forked from","type":"string"},"tier":{"description":"Tier is public-gallery curation, carried verbatim from the embedded catalog.\nNo request can set it — neither write body has the field and neither builds a\nkit carrying one — so it is absent on every customer-published kit.","type":"integer"},"title":{"description":"display name","type":"string"},"useCase":{"description":"what the kit is for, in a phrase","type":"string"},"variants":{"description":"the shapes this template ships in","items":{"$ref":"#/components/schemas/Variant"},"type":"array"}},"type":"object"},"State":{"properties":{"bytes":{"description":"Bytes is the total stored size.","type":"integer"},"consumer_count":{"description":"Consumers is the number of consumers attached to this stream.","type":"integer"},"first_seq":{"description":"FirstSeq is the sequence of the first stored message.","type":"integer"},"first_ts":{"description":"FirstTS is the timestamp of the first stored message.","format":"date-time","type":"string"},"last_seq":{"description":"LastSeq is the sequence of the last stored message.","type":"integer"},"last_ts":{"description":"LastTS is the timestamp of the last stored message.","format":"date-time","type":"string"},"messages":{"description":"Messages is the number of messages currently stored.","type":"integer"},"num_deleted":{"description":"Deleted is the number of deleted messages (sequence gaps).","type":"integer"},"num_subjects":{"description":"Subjects is the number of distinct subjects stored.","type":"integer"}},"type":"object"},"Status":{"properties":{"addr":{"description":"Addr is the socket or address serving it.","type":"string"},"disabled":{"description":"Disabled is true when Unload stopped it deliberately, as opposed to it\nhaving crashed. Both answer 503, so without this an operator cannot tell\na maintenance window from an outage — and would page for the former.","type":"boolean"},"name":{"type":"string"},"pid":{"description":"PID is the child process, or 0 when this host did not start it.","type":"integer"},"prefix":{"description":"Prefix is the FIRST subtree this plugin answers — the one a log line\nnames it by. Prefixes is every subtree, and a plugin may own several.\nReporting only the first would understate the blast radius of taking\nthis plugin down, which is the question a fleet view exists to answer.","type":"string"},"prefixes":{"items":{"type":"string"},"type":"array"},"reloads":{"description":"Reloads counts successful swaps since Load. A climbing number on one\nhost and not its peers is the signal that a rollout is uneven.","type":"integer"},"restarts":{"description":"Restarts counts times the supervisor brought this plugin back after it\ndied on its own. Distinct from Reloads, which are deliberate: a nonzero\nRestarts is a plugin crashing, and a climbing one is a crash loop.","type":"integer"},"running":{"description":"Running is false after Unload, or after a child exited and no Reload has\nreplaced it. Its routes stay registered and answer 503, so a false here\nis the difference between \"not deployed\" and \"deployed but down\".","type":"boolean"},"since":{"description":"Since is when the CURRENT instance started — it resets on Reload, so it\nreports the age of what is running, not of the mount.","format":"date-time","type":"string"},"source":{"description":"Source is where the binary came from: \"embedded\", \"path\", \"url\", or\n\"remote\" for an instance this host did not start.","type":"string"},"usage":{"$ref":"#/components/schemas/Usage","description":"Usage is what this plugin costs right now, read from the kernel."},"version":{"description":"Version is the artifact's SHA-256 when it was installed from a URL —\nthe only version identifier that cannot drift from the bits actually\nrunning, since it IS the bits. Empty for the other sources.","type":"string"}},"type":"object"},"Step":{"properties":{"body":{"description":"Body is the message text. Required. The signed one-click unsubscribe link\nis appended to it at send time.","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds, server-assigned.","type":"integer"},"delaySeconds":{"description":"DelaySeconds is how long after the previous step this one sends (after\nenrollment, for step 0).","type":"integer"},"id":{"description":"ID is the server-assigned step id (\"step_\" + 128 random bits).","type":"string"},"idx":{"description":"Idx is the step's 0-based position, assigned by appending: a new step\nalways lands after the last one.","type":"integer"},"sequenceId":{"description":"SequenceID is the sequence this step belongs to.","type":"string"},"subject":{"description":"Subject is the email subject line, capped at 1024 bytes.","type":"string"}},"type":"object"},"StepInput":{"properties":{"body":{"description":"Body is the message text. Required.","type":"string"},"delaySeconds":{"description":"DelaySeconds is how long after the previous step this one sends (after\nenrollment, for the first step). Must be \u003e= 0.","type":"integer"},"id":{"description":"SequenceID is the sequence id from the path (the route's :id).","type":"string"},"subject":{"description":"Subject is the email subject line, capped at 1024 bytes.","type":"string"}},"type":"object"},"StepList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Step"},"type":"array"}},"type":"object"},"StepSettings":{"properties":{"actionName":{"type":"string"},"input":{"additionalProperties":{"type":"object"},"type":"object"},"pieceName":{"type":"string"},"pieceVersion":{"type":"string"},"triggerName":{"type":"string"}},"type":"object"},"StorefrontResult":{"properties":{"imageUrl":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string"},"store":{"type":"string"}},"type":"object"},"Strategy":{"properties":{"action":{"type":"string"},"blog":{"$ref":"#/components/schemas/Blog","description":"long-form explainer (nil for un-blogged tactics)"},"category":{"type":"string"},"enabled":{"type":"boolean"},"era":{"description":"modern | heritage","type":"string"},"id":{"type":"string"},"principle":{"description":"the spine slug this tactic files under","type":"string"},"source":{"description":"provenance / attribution","type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"workload":{"type":"string"}},"type":"object"},"Stream":{"properties":{"config":{"$ref":"#/components/schemas/Config","description":"Config is the stream's configuration."},"created":{"description":"Created is when the stream was created.","format":"date-time","type":"string"},"name":{"description":"Name is the stream name within the org.","type":"string"},"state":{"$ref":"#/components/schemas/State","description":"State is the stream's current state."}},"type":"object"},"Streams":{"properties":{"streams":{"description":"Streams is the page, ordered by name.","items":{"$ref":"#/components/schemas/Stream"},"type":"array"},"total":{"description":"Total is the org's stream count before paging.","type":"integer"}},"type":"object"},"Subject":{"properties":{"createdAt":{"type":"integer"},"email":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"ref":{"description":"the org's own opaque external id for this subject","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"SubscriptionRow":{"properties":{"display":{"type":"string"},"id":{"type":"string"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"plan":{"type":"string"},"renews":{"type":"string"},"started":{"type":"string"},"status":{"type":"string"},"user":{"type":"string"}},"type":"object"},"SubscriptionsOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SubscriptionRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"SubsystemsOut":{"properties":{"data":{"$ref":"#/components/schemas/subsystemBoard"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Summary":{"properties":{"active":{"type":"integer"},"budget":{"description":"Budget and Spend are the summed campaign budget and spend, in cents.","type":"integer"},"campaigns":{"description":"Campaigns is how many campaigns the org has, Active how many are running.","type":"integer"},"spend":{"type":"integer"}},"type":"object"},"Suppression":{"properties":{"address":{"description":"Address is the recipient, normalized (lower-cased, trimmed) so an opt-out\ncannot be slipped past on a case or whitespace difference. Required.","type":"string"},"channel":{"description":"Channel is the surface opted out of: email, sms, social, meta, google or\ntiktok. Empty means email. Opting out of one leaves the others reachable.","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds, server-assigned.","type":"integer"},"reason":{"description":"Reason is a free-text note, capped at 1024 bytes. The public one-click\nendpoint records \"one-click unsubscribe\".","type":"string"}},"type":"object"},"SuppressionList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Suppression"},"type":"array"}},"type":"object"},"SwitchView":{"properties":{"category":{"type":"string"},"description":{"type":"string"},"env":{"type":"string"},"key":{"type":"string"},"label":{"type":"string"},"readOnly":{"type":"boolean"},"source":{"type":"string"},"type":{"type":"string"},"value":{"type":"string"}},"type":"object"},"Symbol":{"properties":{"detail":{"type":"string"},"kind":{"type":"integer"},"name":{"type":"string"},"range":{"$ref":"#/components/schemas/Range"}},"type":"object"},"TLSConfig":{"properties":{"acmeEmail":{"description":"ACMEEmail is the ACME account email. It binds an account for the lifetime\nof an edge process, so it applies only when the edge (re)starts.","type":"string"},"extraHosts":{"description":"ExtraHosts get certificates without owning a route — at most 256. They feed\nthe ACME HostPolicy and hot-apply on the next reload.","items":{"type":"string"},"type":"array"},"staging":{"description":"Staging issues from Let's Encrypt's staging directory (untrusted certs, high\nrate limits). Like ACMEEmail it applies only when the edge (re)starts.","type":"boolean"}},"type":"object"},"Template":{"properties":{"body":{"type":"string"},"enabled":{"type":"boolean"},"id":{"type":"string"},"title":{"type":"string"}},"type":"object"},"Timeseries":{"properties":{"end":{"description":"End is the window's exclusive upper bound, RFC3339 UTC.","type":"string"},"interval":{"description":"Interval is the bucket width: hour or day.","type":"string"},"range":{"description":"Range is the window that was actually applied: 24h, 7d, 30d or custom.","type":"string"},"scope":{"$ref":"#/components/schemas/Scope","description":"Scope names the tenant these numbers belong to."},"series":{"description":"Series is one point per bucket, oldest first, with empty buckets zero-filled.","items":{"$ref":"#/components/schemas/UsagePoint"},"type":"array"},"source":{"description":"Source is the warehouse table the series read.","type":"string"},"start":{"description":"Start is the window's inclusive lower bound, RFC3339 UTC.","type":"string"}},"type":"object"},"Tool":{"properties":{"activated":{"description":"Activated is filled by the registry from the activation store for the\nrequesting (org,project); providers leave it zero. An unactivated tool is\ndiscoverable but refused 403 at dispatch.","type":"boolean"},"description":{"description":"Description is the prose a model reads to decide whether to call the tool.","type":"string"},"dispatchable":{"description":"Dispatchable is whether the tool can be CALLED. False for a listing-only\nentry: a skill is activated and attached to an agent, never called.","type":"boolean"},"inputSchema":{"description":"Schema is the JSON Schema of the call arguments — the MCP inputSchema.\nAbsent for a tool that takes none."},"name":{"description":"Name is the tool's id in the flat, fleet-wide tool namespace — the value a\ntools/call passes. Unique across sources: a collision is resolved by source\nprecedence before the caller ever sees it.","type":"string"},"price":{"$ref":"#/components/schemas/Price","description":"Price is what a call costs and who is paid, absent for a free tool.\nEnforcement is the x402 settlement seam; this is the declaration."},"source":{"description":"Source is where the tool comes from: connector, function, zap-service,\nagent, skill or mcp.","type":"string"}},"type":"object"},"Top":{"properties":{"end":{"description":"End is the window's exclusive upper bound, RFC3339 UTC.","type":"string"},"models":{"$ref":"#/components/schemas/TopModels","description":"Models ranks the window's LLM models by spend — real per-org data."},"products":{"$ref":"#/components/schemas/TopProducts","description":"Products ranks the window's products by revenue."},"range":{"description":"Range is the window that was actually applied: 24h, 7d, 30d or custom.","type":"string"},"scope":{"$ref":"#/components/schemas/Scope","description":"Scope names the tenant these rankings belong to."},"start":{"description":"Start is the window's inclusive lower bound, RFC3339 UTC.","type":"string"},"topPages":{"$ref":"#/components/schemas/Breakdown","description":"Pages ranks the paths visitors requested, by pageviews."},"topReferrers":{"$ref":"#/components/schemas/Breakdown","description":"Referrers ranks the external domains visitors arrived from, by pageviews."},"topSources":{"$ref":"#/components/schemas/Breakdown","description":"Sources ranks the utm_source campaigns visitors arrived on, by pageviews."}},"type":"object"},"TopModels":{"properties":{"available":{"description":"Available is true whenever the ledger answered, including with no rows.","type":"boolean"},"items":{"description":"Items is the ranked models, highest spend first.","items":{"$ref":"#/components/schemas/ModelRow"},"type":"array"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"}},"type":"object"},"TopProducts":{"properties":{"available":{"description":"Available is false when the product-event table could not be read.","type":"boolean"},"items":{"description":"Items is the ranked products, highest revenue first. Empty rather than absent.","items":{"$ref":"#/components/schemas/ProductRow"},"type":"array"},"reason":{"description":"Reason says why the lens is unavailable. Omitted when it is available.","type":"string"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"}},"type":"object"},"TotalView":{"properties":{"confidence":{"description":"Confidence says how much the counters mean; a percentage-only meter leaves\nthem at zero.","type":"string"},"costCents":{"description":"CostCents is the row's cost in US cents. For an \"account\" row this is the\nPROVIDER's own charge, not a Hanzo one.","type":"integer"},"provider":{"description":"Provider is the upstream the usage was measured against.","type":"string"},"requests":{"description":"Requests is how many requests the row covers.","type":"integer"},"scope":{"description":"Scope is whose row it is: \"user\" for the caller's own linked accounts,\n\"org\" for the whole tenant's Hanzo-routed usage.","type":"string"},"source":{"description":"Source is where the row came from: \"account\" is the provider's own meter\non the caller's linked account, \"hanzo\" is Hanzo-routed inference. The two\nare never summed.","type":"string"},"tokens":{"description":"Tokens is the total tokens the row covers.","type":"integer"},"usedPct":{"description":"UsedPct is how much of a plan window the row consumed, 0–100. It is a\nshare, never money.","type":"number"},"window":{"description":"Window is the meter window class the row rolls up, when it has one.","type":"string"},"windows":{"description":"Windows is how many window instances rolled up into the row.","type":"integer"}},"type":"object"},"Totals":{"properties":{"attachedGiB":{"type":"integer"},"attachedVolumes":{"type":"integer"},"clusters":{"type":"integer"},"detachedGiB":{"type":"integer"},"detachedVolumes":{"type":"integer"},"idlePVCs":{"type":"integer"},"loadBalancers":{"type":"integer"},"localDiskGiB":{"type":"integer"},"measuredGiB":{"type":"integer"},"measuredVolumes":{"description":"Fill. MeasuredVolumes/UnmeasuredVolumes are the honesty denominator: UsedGiB and\nWastedGiB describe the measured set ONLY, so a board showing waste must show how\nmuch of the fleet the figure was computed from. Unmeasured capacity contributes\nnothing to either — it is not assumed empty, and it is not assumed full.","type":"integer"},"nodes":{"type":"integer"},"unmeasuredGiB":{"type":"integer"},"unmeasuredVolumes":{"type":"integer"},"unreferencedGiB":{"type":"integer"},"unreferencedVolumes":{"type":"integer"},"usedGiB":{"type":"integer"},"volumeGiB":{"type":"integer"},"volumes":{"type":"integer"},"wastedGiB":{"type":"integer"}},"type":"object"},"TrafficCaller":{"properties":{"action":{"description":"Action is the verdict currently held against it, if any.","type":"string"},"cred":{"description":"Cred is the caller's key: a credential fingerprint (a per-process one-way\ndigest, not a key) for a validated caller, and \"ip:\u003caddr\u003e\" for one that\npresented no credential we could validate.","type":"string"},"failures":{"description":"Failures is how many ended 401 or 403.","type":"integer"},"held_until":{"description":"HeldUntil is when the held verdict lapses, unix seconds.","type":"integer"},"paths":{"description":"Paths is the approximate number of distinct paths it touched (max 64).","type":"integer"},"reason":{"description":"Reason is why that verdict was reached.","type":"string"},"requests":{"description":"Requests is its request count in the window.","type":"integer"}},"type":"object"},"TrafficView":{"properties":{"blind":{"description":"Blind is how many requests in the window carried no identity to attribute\nthem to — no validated credential and no client address. Non-zero on a\npublic plane means the client address is not reaching this process (a TCP\nload balancer with no PROXY protocol in front of it, typically), so this\nscope's callers cannot be told apart and nothing can be held against them.","type":"integer"},"callers":{"description":"Callers is the scope's busiest callers this window. A credentialed caller\nappears as a FINGERPRINT — a per-process one-way digest: enough to recognise\nthe same caller across requests, never enough to reconstruct the credential.","items":{"$ref":"#/components/schemas/TrafficCaller"},"type":"array"},"ceiling":{"description":"Ceiling is the most callers this scope may hold at once.","type":"integer"},"denied":{"description":"Denied is how many of them the gate refused.","type":"integer"},"lanes":{"additionalProperties":{"type":"integer"},"description":"Lanes is the request count per lane — agent, human, bot, unknown. This is\nthe split that separates a customer's automation from a scraper.","type":"object"},"mode":{"description":"Mode is the abuse gate's posture for this scope: \"shadow\" records the scorer's\naction without enforcing it, \"live\" enforces it.","type":"string"},"org":{"description":"Org is the scope this view was taken for — the validated principal's own,\nnever a value the caller supplied. Empty names the anonymous lane, the one\nscope that has no tenant.","type":"string"},"refused":{"description":"Refused is how many callers this scope's ceilings turned away in the window.","type":"integer"},"requests":{"description":"Requests is how many requests this scope made in the window.","type":"integer"},"screens":{"description":"Screens is how many of them were put to the scorer — the billable unit of\nthe risk product. Counted from the first request, whatever the SKU costs.","type":"integer"},"strain":{"description":"Strain is what this scope's ceilings are doing: \"clear\" below them, \"full\"\nat them, \"refuse\" once a caller has been turned away inside this window —\nwhich means that caller is UNMEASURED and the numbers here are a sample\nrather than a census. It is reported rather than logged because the\nalternative — a bound that degrades a scope silently — is the failure this\ndesign exists to rule out. No other scope can move it.","type":"string"},"tracked":{"description":"Tracked is how many callers this scope holds state for right now, and\nCeiling is the most it may hold. Tracked == Ceiling is the fact a bound\nthat binds cannot hide.","type":"integer"},"unscored":{"description":"Unscored is how many of those screens got NO answer — the scorer was absent,\nstuck, slow, erroring or silent. An unanswered screen allows ordinary\ntraffic, so this is the number that separates \"a quiet day\" from \"the judge\nstopped answering and nothing said so\".","type":"integer"},"window_sec":{"description":"WindowSec is the span the counts cover, in seconds.","type":"integer"}},"type":"object"},"TransitionResult":{"properties":{"distribution":{"$ref":"#/components/schemas/PublishResult"},"doctype":{"type":"string"},"from":{"type":"string"},"name":{"type":"string"},"storefront":{"$ref":"#/components/schemas/StorefrontResult"},"to":{"type":"string"}},"type":"object"},"TreasuryReport":{"properties":{"accruedCents":{"description":"lifetime revenue-share into the fund","type":"integer"},"byProgramCents":{"additionalProperties":{"type":"integer"},"description":"program → lifetime paid","type":"object"},"paidCents":{"description":"lifetime backed payouts out of the fund","type":"integer"},"policy":{"$ref":"#/components/schemas/SharePolicy","description":"current revenue-share policy"},"reserveCents":{"description":"fund:reserve balance (available now)","type":"integer"},"solventForPayout":{"description":"reserve \u003e 0: at least some payout is backable","type":"boolean"}},"type":"object"},"TreeEntry":{"properties":{"lang":{"type":"string"},"path":{"type":"string"},"symbols":{"type":"integer"}},"type":"object"},"TrialBalance":{"properties":{"balanced":{"type":"boolean"},"from":{"type":"string"},"rows":{"items":{"$ref":"#/components/schemas/TrialBalanceRow"},"type":"array"},"to":{"type":"string"},"totalCredit":{"type":"integer"},"totalDebit":{"type":"integer"}},"type":"object"},"TrialBalanceRow":{"properties":{"account":{"type":"string"},"closingCredit":{"type":"integer"},"closingDebit":{"type":"integer"},"credit":{"description":"period movement","type":"integer"},"debit":{"description":"period movement","type":"integer"},"name":{"type":"string"},"openingCredit":{"type":"integer"},"openingDebit":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"Txn":{"properties":{"amountCents":{"type":"integer"},"category":{"description":"COA account number of the P\u0026L line","type":"string"},"categoryName":{"type":"string"},"date":{"type":"string"},"description":{"type":"string"},"source":{"description":"source_kind: bank_txn | scan | commerce_txn","type":"string"},"vendor":{"type":"string"},"voucherId":{"type":"integer"}},"type":"object"},"UTM":{"properties":{"campaign":{"type":"string"},"content":{"type":"string"},"medium":{"type":"string"},"source":{"type":"string"},"term":{"type":"string"}},"type":"object"},"Unsubscribed":{"properties":{"address":{"type":"string"},"channel":{"type":"string"},"unsubscribed":{"type":"boolean"}},"type":"object"},"Usage":{"properties":{"cpuNs":{"description":"CPU is total user+system time this instance has consumed since it\nstarted. It only ever climbs, so a rate is the interesting derivative.","type":"integer"},"fds":{"type":"integer"},"rssBytes":{"description":"RSS is resident memory in bytes — the honest number for \"what does this\nplugin cost\", as opposed to virtual size.","type":"integer"},"threads":{"description":"Threads and FDs are the two limits a busy service hits first, and both\nare leaks when they climb without bound.","type":"integer"}},"type":"object"},"UsageFundingOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/UsageFundingRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"UsageFundingRow":{"properties":{"cost_cents":{"type":"integer"},"funding":{"description":"credit | paid | paid_only | byo","type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"requests":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"UsagePoint":{"properties":{"requests":{"description":"Requests is how many LLM calls fell in this bucket.","type":"integer"},"spendCents":{"description":"SpendCents is what they cost, in cents.","type":"integer"},"t":{"description":"T is the bucket's start, RFC3339 UTC, aligned to the interval.","type":"string"},"tokens":{"description":"Tokens is prompt plus completion tokens over those calls.","type":"integer"}},"type":"object"},"Variant":{"properties":{"framework":{"description":"only when it differs from the template's","type":"string"},"id":{"description":"selector, unique within the template (\"react\", \"grid-3-fluid\")","type":"string"},"kind":{"description":"the axis it varies: format | page | theme","type":"string"},"label":{"description":"human label for the picker","type":"string"},"source":{"description":"the repository this shape is forked from; the synthesized default shape carries the template's own","type":"string"}},"type":"object"},"Vendor":{"properties":{"amountCents":{"type":"integer"},"note":{"type":"string"},"service":{"type":"string"},"source":{"description":"\"actual\" | \"estimated\"","type":"string"},"vendor":{"type":"string"}},"type":"object"},"VendorRow":{"properties":{"aliases":{"description":"Aliases are the other spellings a receipt may print the vendor under; a scan\nmatching any of them resolves to this vendor.","items":{"type":"string"},"type":"array"},"canonical":{"description":"Canonical is the vendor's one true name, and the key an upsert writes by.","type":"string"},"defaultCategory":{"description":"DefaultCategory is the COA expense account new bills from this vendor book to.\nAn upsert normalizes a slug (\"software\") to its account number.","type":"string"}},"type":"object"},"Verdict":{"properties":{"flags":{"items":{"$ref":"#/components/schemas/DriftFlag"},"type":"array"},"severity":{"type":"string"}},"type":"object"},"VerifyOut":{"properties":{"data":{"$ref":"#/components/schemas/Integrity"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"VerifyRequest":{"properties":{"app":{"description":"App overrides the app_id the token is expected to carry. Leave it empty and\nthe token's own app_id is used — an online verify is informational, and it\nis the ENGINE that enforces the app at boot.","type":"string"},"token":{"description":"Token is the license token to check.","type":"string"}},"required":["token"],"type":"object"},"VerifyResponse":{"properties":{"app_id":{"description":"AppID is the brand the token runs under.","type":"string"},"exp":{"description":"Exp is the token's expiry, Unix seconds.","type":"integer"},"features":{"description":"Features are the capability grants the token carries.","items":{"type":"string"},"type":"array"},"fingerprint_bound":{"description":"Bound reports that the token carries a device binding.","type":"boolean"},"holder":{"description":"Holder is who the token was issued to.","type":"string"},"nonce":{"description":"Nonce uniquely identifies the token.","type":"string"},"reason":{"description":"Reason says why an invalid token was rejected. Empty when Valid.","type":"string"},"revoked":{"description":"Revoked reports that the signature was good but the token has been revoked.","type":"boolean"},"valid":{"description":"Valid is the single answer: signature, schema, expiry, app and revocation\nall passed.","type":"boolean"}},"type":"object"},"VersionMeta":{"properties":{"brand":{"type":"string"},"updatedAt":{"type":"integer"},"version":{"type":"integer"}},"type":"object"},"Volume":{"properties":{"blockedReason":{"type":"string"},"cluster":{"description":"Cluster/ClusterID are the PROVEN owner — resolved through a PV that names this\nvolume, never through the tag.","type":"string"},"clusterId":{"type":"string"},"controller":{"description":"Controller is the workload owning the pod that mounts this volume\n(\"StatefulSet/luxd\"), or \"\" when nothing mounts it. It names who has to act.","type":"string"},"createdAt":{"type":"string"},"deletable":{"type":"boolean"},"dropletIds":{"items":{"type":"integer"},"type":"array"},"expandBlockedReason":{"type":"string"},"expandable":{"description":"Expandable/ExpandBlockedReason are the GROW verdict, kept separate from Deletable\nbecause the two ask opposite questions: a volume is deletable when nothing uses it,\nand expandable when something uses it in a way this board can grow completely.","type":"boolean"},"hasUsage":{"description":"HasUsage reports whether a kubelet actually MEASURED this volume's filesystem.\n\nFalse means NOT MEASURED. It does NOT mean empty, and the three fields below are\nmeaningless — not zero — when it is false. A reading exists only while a running pod\nhas the volume mounted on a node that answered; a detached, idle or unreferenced\nvolume has none. Rendering an unmeasured volume as \"0 used / 100% wasted\" would\ninvent the single most expensive lie this board could tell, so every consumer must\nbranch on this flag and show unknown.","type":"boolean"},"id":{"type":"string"},"idle":{"type":"boolean"},"monthlyCents":{"type":"integer"},"mountedBy":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"nodeName":{"type":"string"},"pv":{"type":"string"},"pvPhase":{"type":"string"},"pvcName":{"type":"string"},"pvcNamespace":{"type":"string"},"region":{"type":"string"},"sizeGiB":{"type":"integer"},"state":{"type":"string"},"tagCluster":{"description":"TagCluster is the `k8s:\u003cuuid\u003e` tag. ADVISORY ONLY: it outlives the cluster that\nset it. Shown so the operator can see tag-vs-truth disagree, never acted on.","type":"string"},"usedBytes":{"description":"UsedBytes is the measured filesystem usage. BYTES, not GiB: the volumes this exists\nto catch hold a fraction of a GiB in 200, and rounding that to an integer GiB would\nprint the very 0 the flag above exists to prevent.","type":"integer"},"wastedGiB":{"description":"WastedGiB is provisioned minus measured, in the unit DigitalOcean BILLS: whole GiB\nof the volume's own size, never the filesystem's capacity — a 200 GiB volume carries\na 196 GiB filesystem after format overhead, and the invoice says 200.","type":"integer"},"wastedMonthlyCents":{"type":"integer"}},"type":"object"},"VolumeIn":{"properties":{"id":{"description":"ID is the DO volume id, from the path.","type":"string"},"name":{"description":"Name is the snapshot name on the snapshot action. Blank gets a deterministic\n\"\u003cvolume\u003e-predelete-\u003cunix\u003e\" so the undo is findable in the DO console.","type":"string"},"sizeGiB":{"description":"SizeGiB is the target size on the resize action. A volume only ever grows —\nExpandTo is the verdict that refuses a shrink, so this is not validated here.","type":"integer"},"snapshot":{"description":"Snapshot is the snapshot-first switch on DELETE. Anything other than the literal\n\"false\" snapshots before destroying — the snapshot IS the undo, so waiving it is\ndeliberate and explicit.","type":"string"}},"type":"object"},"VolumeSnapshotOut":{"properties":{"data":{"$ref":"#/components/schemas/digitalocean.Snapshot"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"Voucher":{"properties":{"description":{"description":"Description is the human line for the event, e.g. the vendor a bill came from.","type":"string"},"legs":{"description":"Legs are the sides of the posting. They must balance: Σdebit == Σcredit, give or\ntake the 2¢ round-off allowance.","items":{"$ref":"#/components/schemas/Leg"},"type":"array"},"postingAt":{"description":"PostingAt is the RFC3339 instant the event posts at — the time every statement\nwindow filters on.","type":"string"},"sourceId":{"description":"SourceID is the source event's own id within that namespace. Together with\nSourceKind it is the key that makes a repeat posting a no-op.","type":"string"},"sourceKind":{"description":"SourceKind is the idempotency namespace naming what booked this, e.g. \"scan\".","type":"string"}},"type":"object"},"Wallet":{"properties":{"accountId":{"type":"string"},"address":{"type":"string"},"agent":{"type":"string"},"chain":{"type":"string"},"createdAt":{"type":"integer"},"custody":{"type":"string"},"financeAccount":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"project":{"type":"string"},"tier":{"type":"string"}},"type":"object"},"WalletAccount":{"properties":{"createdAt":{"type":"integer"},"id":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"}},"type":"object"},"WebOverview":{"properties":{"available":{"description":"Available is false when the product-event table could not be read — the lens is\nreported missing rather than as zeros that look like real traffic.","type":"boolean"},"pageviews":{"description":"Pageviews is how many $pageview events landed in the window.","type":"integer"},"reason":{"description":"Reason says why the lens is unavailable. Omitted when it is available.","type":"string"},"sessions":{"description":"Sessions is how many distinct visits they span.","type":"integer"},"source":{"description":"Source is the warehouse table the lens read.","type":"string"},"visitors":{"description":"Visitors is how many distinct people those pageviews came from.","type":"integer"}},"type":"object"},"Wire":{"properties":{"action":{"type":"string"},"authMethod":{"type":"string"},"email":{"type":"string"},"hash":{"type":"string"},"home":{"description":"Home is present ONLY on a cross-org action: the org the actor came FROM,\nwhile Org is the org they acted IN. A console row carrying `home` is a\nplatform-admin impersonation and should be rendered as one.","type":"string"},"isAdmin":{"type":"boolean"},"method":{"type":"string"},"org":{"type":"string"},"path":{"type":"string"},"prevHash":{"type":"string"},"reason":{"type":"string"},"requestId":{"type":"string"},"resource":{"type":"string"},"resourceId":{"type":"string"},"result":{"type":"string"},"seq":{"type":"integer"},"sourceIp":{"type":"string"},"status":{"type":"integer"},"sub":{"type":"string"},"time":{"type":"string"},"userAgent":{"type":"string"}},"type":"object"},"WorkerScriptPut":{"properties":{"bindings":{},"compatibilityDate":{"type":"string"},"compatibilityFlags":{"items":{"type":"string"},"type":"array"},"mainModule":{"type":"string"},"script":{"type":"string"}},"type":"object"},"accList":{"properties":{"data":{"description":"Data is the org's tracked accreditation records, newest first.","items":{"$ref":"#/components/schemas/accView"},"type":"array"},"disclaimer":{"description":"Disclaimer states that statuses are tracked or provider-reported, never a\nplatform assertion of legal or regulatory compliance.","type":"string"}},"type":"object"},"accView":{"properties":{"basis":{"description":"Basis is the qualification category: income, net_worth, professional_license,\nor entity.","type":"string"},"createdAt":{"description":"CreatedAt is the unix second the record was created.","type":"integer"},"evidenceDocId":{"description":"EvidenceDocID references an evidence document in the org's sealed data room.","type":"string"},"expiresAt":{"description":"ExpiresAt is the unix second a confirmation ages out; 0 means none.","type":"integer"},"id":{"description":"ID is the accreditation record's opaque id.","type":"string"},"method":{"description":"Method is how the state was established: self_attested, third_party_letter,\nor provider_verified.","type":"string"},"note":{"description":"Note is a non-PII operator note.","type":"string"},"reviewerSub":{"description":"ReviewerSub is the org user who recorded a decision on this record.","type":"string"},"status":{"description":"Status is the tracked state: asserted, provider_verified, reviewer_confirmed,\nrejected, or expired.","type":"string"},"subjectId":{"description":"SubjectID is the opaque id of the subject the record is about.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second the record last changed.","type":"integer"}},"type":"object"},"accountFoldView":{"properties":{"account":{"$ref":"#/components/schemas/cloudAccountView","description":"Account is the account as it is now recorded."},"clusters":{"description":"Clusters is one entry per cluster discovered in the account. It is empty\nwhen discovery itself failed, which leaves the previously folded set\nuntouched rather than mass-detaching it.","items":{"$ref":"#/components/schemas/clusterResult"},"type":"array"}},"type":"object"},"accountList":{"properties":{"accounts":{"description":"Accounts are the org's accounts, newest first.","items":{"$ref":"#/components/schemas/WalletAccount"},"type":"array"}},"type":"object"},"accountView":{"properties":{"address":{"description":"Address is the ledger account address (\"org:acme:wallet\", \"fund:reserve\", …).","type":"string"},"balanceCents":{"description":"BalanceCents is that account's signed balance in minor units.","type":"integer"}},"type":"object"},"accounts":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/RoutedUsage"},"type":"array"},"scope":{"type":"string"},"source":{"type":"string"},"total":{"$ref":"#/components/schemas/AccountsTotal"}},"type":"object"},"accountsOut":{"properties":{"accounts":{"description":"Accounts are the ledger accounts in scope with their balances.","items":{"$ref":"#/components/schemas/accountView"},"type":"array"},"scope":{"description":"Scope is the scope actually served: \"org\" or \"house\".","type":"string"},"tenant":{"description":"Tenant is the org whose accounts these are (empty for the house scope's own rows).","type":"string"}},"type":"object"},"accreditationDecision":{"properties":{"id":{"description":"ID is the accreditation record to decide, from the path.","type":"string"},"status":{"description":"Status is the decision being recorded: reviewer_confirmed, provider_verified,\nrejected, or expired.","type":"string"}},"type":"object"},"accreditationReq":{"properties":{"basis":{"description":"Basis is the qualification category: income, net_worth, professional_license,\nor entity.","type":"string"},"evidenceDocId":{"description":"EvidenceDocID references an evidence document in the org's sealed data room.","type":"string"},"expiresAt":{"description":"ExpiresAt is the unix second a confirmation ages out; 0 means none.","type":"integer"},"method":{"description":"Method is how the state was established: self_attested, third_party_letter,\nor provider_verified.","type":"string"},"note":{"description":"Note is a non-PII operator note.","type":"string"},"status":{"description":"Status may only be \"asserted\" (empty reads as asserted); every confirmed,\nrejected or expired state is recorded via the decision endpoint.","type":"string"},"subjectId":{"description":"SubjectID names the subject this record is about; it must exist within the org.","type":"string"}},"type":"object"},"accruals":{"properties":{"accrued":{"description":"Accrued is how many NEW commission accruals this run created, counted across\nevery upline level. The accrual is latched at most once per (affiliate, source\norg, period), so a re-run inside the same month reports 0 having changed\nnothing — 0 means \"already accrued\", not \"failed\".","type":"integer"},"royaltiesAccrued":{"description":"RoyaltiesAccrued is how many OSS-author royalty accruals the SAME spend read\nproduced in the sibling authors program. One read drives both.","type":"integer"},"royaltyFailures":{"description":"RoyaltyFailures is reported, not swallowed: a sweep that could not reach\nthe royalty store must not read as one that found nothing owed. The count\nwas already computed and then dropped on the floor, which is the same\nsilence the typed leg was added to end.","type":"integer"},"swept":{"description":"Swept is how many source (referred) orgs the run visited, bounded at 500 per\nrun. A source with no spend this period, or one whose spend could not be read,\nstill counts as swept.","type":"integer"}},"type":"object"},"accrualsOut":{"properties":{"data":{"$ref":"#/components/schemas/accruals","description":"Data is what the run did: sources visited, new accruals, royalties alongside."},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"actionsView":{"properties":{"data":{"description":"Data is the most-recent actions first, capped at listActionsLimit.","items":{"$ref":"#/components/schemas/ActionRecord"},"type":"array"}},"type":"object"},"activationReq":{"properties":{"activate":{"description":"Activate switches these tool names on for the caller's org and project.","items":{"type":"string"},"type":"array"},"deactivate":{"description":"Deactivate switches these tool names off.","items":{"type":"string"},"type":"array"}},"type":"object"},"activationSet":{"properties":{"enabled":{"description":"Enabled is every tool name activated for the caller's org and project.","items":{"type":"string"},"type":"array"}},"type":"object"},"activityFeed":{"properties":{"activity":{"description":"Activity is the merged run/create/update events, newest first, capped at 50.","items":{"$ref":"#/components/schemas/activityView"},"type":"array"}},"type":"object"},"activityOut":{"properties":{"data":{"description":"Data is the change log newest-first: who created, updated or deleted which key, when.","items":{"$ref":"#/components/schemas/ActivityRow"},"type":"array"}},"type":"object"},"activityView":{"properties":{"agent":{"description":"agent name","type":"string"},"at":{"description":"RFC3339 UTC","type":"string"},"id":{"type":"string"},"kind":{"description":"invoked|failed|created|updated (from real events)","type":"string"},"message":{"type":"string"}},"type":"object"},"adSummary":{"properties":{"active":{"description":"Active is how many of those campaigns are in the active state.","type":"integer"},"budget":{"description":"Budget is the summed budget of every campaign in the org, in cents.","type":"integer"},"campaigns":{"description":"Campaigns is how many campaigns the org has, in every state.","type":"integer"},"spend":{"description":"Spend is the summed spend of every campaign in the org, in cents.","type":"integer"}},"type":"object"},"addDomainReq":{"properties":{"app":{"description":"App is the application's slug, from the path.","type":"string"},"host":{"description":"Host is the hostname to attach. Required, and must be a valid DNS hostname.","type":"string"},"project":{"description":"Project is the project the application lives under, from the path.","type":"string"}},"type":"object"},"adminAffiliateView":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued, in cents. It only grows — a\npayout moves paidCents, never this.","type":"integer"},"approvedAt":{"description":"ApprovedAt is when staff approved, Unix seconds UTC. 0 means never approved.","type":"integer"},"code":{"description":"Code is the minted referral code, the slug the ?aff link carries. Empty until\napproval mints it. Codes are one global namespace across all affiliates.","type":"string"},"createdAt":{"description":"CreatedAt is when the org applied, Unix seconds UTC.","type":"integer"},"id":{"description":"ID is the affiliate's server-minted handle, \"aff_\"-prefixed — the id the\napprove, suspend, rate and payout routes address.","type":"string"},"org":{"description":"Org is the partner's own org slug. It appears ONLY on this cross-tenant admin\nview; no partner-facing read ever names another org.","type":"string"},"paidCents":{"description":"PaidCents is lifetime commission already paid out, in cents — credits grants\nand record-only cash disbursements alike.","type":"integer"},"pendingCents":{"description":"PendingCents is accrued minus paid, in cents: what is still owed, and the hard\nceiling the next payout is reserved against. Never negative.","type":"integer"},"rateBps":{"description":"RateBps is this affiliate's DIRECT (level 1) commission rate in basis points\nOF Hanzo's margin (2000 = 20% of margin, never of the customer's bill). Levels\n2 and 3 are platform-wide switches and are not carried per affiliate.","type":"integer"},"referredCount":{"description":"ReferredCount is how many orgs this affiliate is the DIRECT referrer of,\ncounted from the attribution edges. It is 0 on the single-affiliate answers\n(approve, suspend, rate, payout), which do not run the count.","type":"integer"},"requestedCode":{"description":"RequestedCode is the vanity code the applicant asked for. A request, not an\nallocation: approval mints a different slug if this one was taken. Absent when\nnone was asked for.","type":"string"},"status":{"description":"Status is \"applied\", \"approved\" or \"suspended\". Only \"approved\" resolves for\nattribution and accrues; \"suspended\" stops future earning and claws nothing\nback.","type":"string"},"suspendedAt":{"description":"SuspendedAt is when staff suspended, Unix seconds UTC. 0 means never\nsuspended; it is not cleared by a later re-approval.","type":"integer"}},"type":"object"},"adminAuthorView":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime royalty accrued, in integer USD cents: the sum of\nevery latched accrual (spend × shareBps / 10000). It only ever rises — a\npayout is recorded against paidCents and never reduces this.","type":"integer"},"approvedAt":{"description":"ApprovedAt is unix seconds of the first approval, and 0 means never approved —\nwhich is also \"has never been able to accrue\". Re-approving to renegotiate the\nshare leaves it at the original date.","type":"integer"},"createdAt":{"description":"CreatedAt is unix seconds at the FIRST connect. Re-connecting re-links the\nlogin and leaves this alone, so it dates the enrolment, not the latest link.","type":"integer"},"deployCount":{"description":"DeployCount is how many attribution edges point at this author — one per\n(repository, project, deploying org), so re-deploying the same project adds\nnone. It includes self-deploys, which are recorded for provenance and excluded\nfrom accrual, so it measures reach, not the earning set.","type":"integer"},"githubLogin":{"description":"GithubLogin is the linked forge account, lowercased. It comes from IAM's\nlinked account when the connect had one — which is also what sets verified —\nand otherwise from the login the caller declared. The treasury author carries\n\"\u003cbrand\u003e-maintainers\".","type":"string"},"id":{"description":"ID is the author record's server-minted handle, \"aut_\"-prefixed. It is the id\nthe approve, suspend, payout and admin-basis routes address.","type":"string"},"org":{"description":"Org is the tenant org that owns this author record — UNIQUE, one author per\norg. It is exposed HERE and nowhere else (Author.Org is json:\"-\" on the tenant\nsurface), and it is the org excluded from this author's own accrual: deploying\nyour own repo earns you nothing.","type":"string"},"paidCents":{"description":"PaidCents is lifetime royalty RECORDED as paid, in integer USD cents. It rises\nthe moment a payout reserves against pending — recording, not settling; a human\nmoves the money out of band — and falls back only when a payout is voided.","type":"integer"},"pendingCents":{"description":"PendingCents is what a payout may still draw against — accrued − paid, floored\nat zero. It is derived for each response, never stored, and it is the exact\nfigure the atomic payout guard refuses to exceed.","type":"integer"},"repoCount":{"description":"RepoCount is how many of this author's repository claims are VERIFIED, counted\nfor this response in one GROUP BY over the whole table rather than a query per\nrow. The single-author replies from approve, suspend and payout report 0: they\ncarry the mutated row, not a re-listing.","type":"integer"},"shareBps":{"description":"ShareBps is the royalty rate accrual applies, in basis points of a deploying\norg's metered spend for the period: 2000 (the platform default) is 20%, 10000\nwould be the entire spend. The platform keeps 10000 − shareBps. Changing it\nnever rewrites history — each ledger row keeps the rate it was written with.","type":"integer"},"status":{"description":"Status is connected, approved or suspended. Only an approved author accrues;\na connected one may verify repos and collect deploy edges but earns nothing\nuntil a reviewer admits it.","type":"string"},"suspendedAt":{"description":"SuspendedAt is unix seconds of the most recent suspension. 0 means the author\nis not suspended: either never was, or was and has since been approved again,\nwhich clears this back to 0.","type":"integer"},"verified":{"description":"Verified is IDENTITY proof of the login, NOT proof of any repository: true\nwhen the connect took the login from IAM's linked forge account (and for the\nseeded treasury author), false when the caller merely declared it. A false\nhere still earns — repository ownership is proven separately, per claim.","type":"boolean"}},"type":"object"},"adminBonusDirectory":{"properties":{"referrals":{"description":"Referrals is every referral in the directory, both orgs exposed.","items":{"$ref":"#/components/schemas/adminReferralView"},"type":"array"},"summary":{"$ref":"#/components/schemas/adminSummary","description":"Summary is the fleet tally across those referrals."}},"type":"object"},"adminBonusesEnvelope":{"properties":{"data":{"$ref":"#/components/schemas/adminBonusDirectory","description":"Data is the directory itself."},"msg":{"description":"Msg is empty on success; the console surfaces it when status is not \"ok\".","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"adminBook":{"properties":{"data":{"$ref":"#/components/schemas/adminBookData","description":"Data is the book."},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is \"ok\" — the operator console's envelope discriminator.","type":"string"}},"type":"object"},"adminBookData":{"properties":{"authors":{"description":"Authors are the author records, with each one's repository and deploy counts.","items":{"$ref":"#/components/schemas/adminAuthorView"},"type":"array"},"summary":{"$ref":"#/components/schemas/authorProgramSummary","description":"Summary is the fleet roll-up: how many authors at each status and the money\naccrued, pending and paid across all of them."}},"type":"object"},"adminCatalogOut":{"properties":{"models":{"description":"Models is every model the catalog holds — disabled ones included — each\ncarrying its enablement state under \"_overlay\".","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"},"providers":{"additionalProperties":{"type":"object"},"description":"Providers is every provider the catalog holds, keyed by name, each\ncarrying its enablement state under \"_overlay\".","type":"object"},"updated":{"description":"Updated is when the catalog was last refreshed, as the pricing source\nrecorded it.","type":"object"}},"type":"object"},"adminEnablementBoard":{"properties":{"items":{"description":"Items is every item an operator has set a state on. An item nobody has\ntouched is absent: it is generally available by default.","items":{"$ref":"#/components/schemas/adminEnablementItem"},"type":"array"}},"type":"object"},"adminEnablementItem":{"properties":{"betaOrgs":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"kind":{"type":"string"},"state":{"description":"off|beta|ga","type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"adminMe":{"properties":{"displayName":{"type":"string"},"email":{"type":"string"},"isSuperAdmin":{"type":"boolean"},"isWhiteLabel":{"description":"IsWhiteLabel marks the admitted NON-super tier: an admin of an enabled\nwhite-label tenant org. Mutually exclusive with IsSuperAdmin (the gate lets\nexactly one tier through). The operator SPA reads it to render the SUBTREE\ncockpit — the fleet god-view nav (finance/revenue/metrics/o11y/providers) is\nhidden — while a super sees the whole fleet.","type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"scopeOrgs":{"description":"ScopeOrgs is the caller's visible tenant window: empty for a SuperAdmin (means\nALL orgs), or the WL tenant's own subtree (today the singleton {org}). The SPA\nthreads it through the faceting/drill-down layer so a WL tenant can never widen\na filter past their subtree.","items":{"type":"string"},"type":"array"}},"type":"object"},"adminReferralView":{"properties":{"code":{"description":"Code is the referral code the referral was recorded against.","type":"string"},"createdAt":{"description":"CreatedAt is when the referral was recorded, as a Unix timestamp.","type":"integer"},"id":{"description":"ID is the referral's handle.","type":"string"},"qualifiedAt":{"description":"QualifiedAt is when the referee first made metered spend, as a Unix\ntimestamp; 0 while still pending.","type":"integer"},"refereeOrg":{"description":"RefereeOrg is the org that signed up with it.","type":"string"},"referrerOrg":{"description":"ReferrerOrg is the org whose code was used.","type":"string"},"status":{"description":"Status is the referral's lifecycle state: \"signup\" or \"qualified\".","type":"string"}},"type":"object"},"adminReportData":{"properties":{"anchor":{"$ref":"#/components/schemas/anchorStatus","description":"Anchor is the Hanzo L1 anchoring status of the ledger root."},"journal":{"description":"Journal is the recent double-entry entries, newest first.","items":{"$ref":"#/components/schemas/JournalEntry"},"type":"array"},"report":{"$ref":"#/components/schemas/TreasuryReport","description":"Report is the reserve-fund snapshot — available, accrued, paid, per-program, policy."}},"type":"object"},"adminReportOut":{"properties":{"data":{"$ref":"#/components/schemas/adminReportData","description":"Data is the treasury board."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success; the transport maps a non-ok envelope to an error.","type":"string"}},"type":"object"},"adminSummary":{"properties":{"qualified":{"description":"Qualified is how many referees have made metered spend.","type":"integer"},"signup":{"description":"Signup is how many are recorded but not yet qualified.","type":"integer"},"total":{"description":"Total is every referral in the directory.","type":"integer"}},"type":"object"},"advanceIn":{"properties":{"to":{"description":"To is the target stage: structure, founders, payment, documents, esign,\ngenesis, import or company.","type":"string"}},"type":"object"},"affiliateBoard":{"properties":{"leaders":{"description":"Leaders are the top opt-in affiliates, by handle and aggregate figures only.","items":{"$ref":"#/components/schemas/leaderboardRow"},"type":"array"},"total":{"description":"Total is the approved population where it is known; omitted where the top\npage truncated and the caller has no rank to derive it from.","type":"integer"},"you":{"$ref":"#/components/schemas/leaderboardRow","description":"You is the caller's own row with its exact global rank; only an approved\naffiliate has one."}},"type":"object"},"affiliateData":{"properties":{"affiliate":{"$ref":"#/components/schemas/adminAffiliateView","description":"Affiliate is the row as it stands AFTER the action that returned it. Its\nreferredCount is 0 here: these single-affiliate answers do not run the count."}},"type":"object"},"affiliateEarnings":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued, in cents.","type":"integer"},"byPeriod":{"description":"ByPeriod is the per-period ledger: the margin earned against and the\ncommission taken from it.","items":{"$ref":"#/components/schemas/periodEarningView"},"type":"array"},"byReferredOrg":{"description":"ByReferredOrg is each referral's aggregate contribution — the affiliate's\nOWN share, never the referred org's spend.","items":{"$ref":"#/components/schemas/orgEarningView"},"type":"array"},"isAffiliate":{"description":"IsAffiliate says whether the caller org has an affiliate record. On false it\nis the ONLY field present — there is no ledger to report, and the zeros you\nmight expect are absent rather than reported as earnings of nothing.","type":"boolean"},"marginBps":{"description":"MarginBps is the platform gross-margin fraction commission is a rate OF.","type":"integer"},"paidCents":{"description":"PaidCents is lifetime commission already paid out, in cents.","type":"integer"},"pendingCents":{"description":"PendingCents is accrued minus paid — what the platform still owes.","type":"integer"}},"type":"object"},"affiliateLinks":{"properties":{"isAffiliate":{"description":"IsAffiliate says whether the caller org has an affiliate record. On false only\nmaxLinks comes back — there are no links, and there is no link to mint until\nthe org applies and is approved.","type":"boolean"},"links":{"description":"Links is the caller's share links, each with its URL and funnel.","items":{"$ref":"#/components/schemas/codeView"},"type":"array"},"maxLinks":{"description":"MaxLinks is how many share links one affiliate may hold.","type":"integer"},"status":{"description":"Status is the caller's affiliate status: \"applied\", \"approved\" or\n\"suspended\"; absent for a non-affiliate. Minting a link requires \"approved\",\nbecause a link that cannot accrue quietly loses the referral.","type":"string"}},"type":"object"},"affiliateOut":{"properties":{"data":{"$ref":"#/components/schemas/affiliateData","description":"Data carries the affiliate row the action just wrote."},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"affiliateSelf":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued, in cents. It only grows — a\npayout is recorded against paidCents and never reduces this.","type":"integer"},"code":{"description":"Code is the minted referral code, the slug the ?aff link carries. Absent until\nstaff approve; codes live in ONE global namespace across all affiliates.","type":"string"},"defaultRateBps":{"description":"DefaultRateBps is the direct rate a new affiliate starts at, in basis points\nof margin (2000 = 20%). Answered ONLY to a caller that has not applied, as the\nquote beside `schedule`.","type":"integer"},"downlineTotal":{"description":"DownlineTotal counts every org in the caller's downline across the levels.","type":"integer"},"handle":{"description":"Handle is the opt-in public leaderboard name. Empty means opted out: the\ncaller keeps its rank and still sees its own row, it is just not listed.","type":"string"},"id":{"description":"ID is the affiliate's server-minted handle, \"aff_\"-prefixed. Absent until the\norg applies.","type":"string"},"isAffiliate":{"description":"IsAffiliate says whether the caller org has an affiliate record. On false the\nanswer carries the rate SCHEDULE and the default rate instead of a downline,\nso the console can show what the caller would earn.","type":"boolean"},"levels":{"description":"Levels is the caller's downline per upline level, with the rate paid there.","items":{"$ref":"#/components/schemas/levelView"},"type":"array"},"link":{"description":"Link is the shareable ?aff URL built from the code. Empty until a code is\nminted, since there is nothing to share before approval.","type":"string"},"marginBps":{"description":"MarginBps is the platform gross-margin fraction, in basis points, that every\nrate here is a rate OF. Read live per request, so it is the value in force\nnow, not the one that applied to commission already accrued.","type":"integer"},"paidCents":{"description":"PaidCents is lifetime commission already paid out, in cents — credits grants\nand record-only cash disbursements alike.","type":"integer"},"payouts":{"description":"Payouts is the payout history, newest first, bounded to the last 100 rows.","items":{"$ref":"#/components/schemas/remittance"},"type":"array"},"pendingCents":{"description":"PendingCents is accrued minus paid, in cents — what the platform still owes\nand the ceiling on the next payout. Never negative.","type":"integer"},"rateBps":{"description":"RateBps is the caller's OWN direct (level 1) commission rate, in basis points\nof margin. Levels 2 and 3 are platform-wide and appear in `levels`.","type":"integer"},"schedule":{"description":"Schedule is the rate schedule quoted to a caller that has not applied.","items":{"$ref":"#/components/schemas/levelView"},"type":"array"},"status":{"description":"Status is \"applied\", \"approved\" or \"suspended\"; absent for a caller that never\napplied. Only \"approved\" mints links and accrues.","type":"string"}},"type":"object"},"affiliateStanding":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued, in cents.","type":"integer"},"code":{"description":"Code is the minted referral code; empty until staff approve.","type":"string"},"defaultRateBps":{"description":"DefaultRateBps is the direct rate a new affiliate would get, answered only\nto a caller that has not applied.","type":"integer"},"handle":{"description":"Handle is the opt-in public leaderboard name; empty means opted out.","type":"string"},"id":{"description":"ID is the affiliate's server-minted handle, \"aff_\"-prefixed — what staff\napprove, suspend, re-rate and pay against. Absent until the org applies.","type":"string"},"isAffiliate":{"description":"IsAffiliate says whether the caller org has an affiliate record at all. It is\nthe ONE field an org that never applied gets besides defaultRateBps: on false,\nread nothing else here — every other field is absent, not zero.","type":"boolean"},"link":{"description":"Link is the shareable ?aff URL; empty until a code is minted.","type":"string"},"marginBps":{"description":"MarginBps is the platform gross-margin fraction commission is a rate OF.","type":"integer"},"paidCents":{"description":"PaidCents is lifetime commission already paid out, in cents.","type":"integer"},"payouts":{"description":"Payouts is the payout history, newest rows bounded.","items":{"$ref":"#/components/schemas/remittance"},"type":"array"},"pendingCents":{"description":"PendingCents is accrued minus paid — what the platform still owes.","type":"integer"},"rateBps":{"description":"RateBps is the affiliate's own direct commission rate, in basis points.","type":"integer"},"referredCount":{"description":"ReferredCount is how many orgs this affiliate has referred.","type":"integer"},"requestedCode":{"description":"RequestedCode is the vanity code asked for at apply time — a request, not an\nallocation. Approval mints `code`, which may be a different slug if this one\nwas already taken.","type":"string"},"status":{"description":"Status is \"applied\", \"approved\" or \"suspended\". Only an approved affiliate has\na code that resolves for attribution and accrues commission; suspended keeps\nwhat it already earned but stops earning more.","type":"string"}},"type":"object"},"agentBinding":{"properties":{"agentName":{"type":"string"},"botVersion":{"type":"string"},"createdTime":{"type":"string"},"machineId":{"type":"string"},"message":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"owner":{"type":"string"},"provider":{"type":"string"},"publicIp":{"type":"string"},"status":{"type":"string"},"updatedTime":{"type":"string"}},"type":"object"},"agentDetail":{"properties":{"computeRef":{"type":"string"},"createdAt":{"type":"string"},"description":{"type":"string"},"executionMode":{"type":"string"},"id":{"type":"string"},"instructions":{"type":"string"},"model":{"type":"string"},"name":{"type":"string"},"recentRuns":{"items":{"$ref":"#/components/schemas/agentRunView"},"type":"array"},"runs":{"type":"integer"},"schedule":{"type":"string"},"serviceAccountId":{"type":"string"},"status":{"type":"string"},"tools":{"items":{"type":"string"},"type":"array"},"updatedAt":{"type":"string"}},"type":"object"},"agentList":{"properties":{"agents":{"description":"Agents is the org's agents, each carrying its recorded run count.","items":{"$ref":"#/components/schemas/agentView"},"type":"array"}},"type":"object"},"agentRunView":{"properties":{"actor":{"type":"string"},"agent":{"description":"What an operator needs to answer \"what ran, for whom, and what did it do\" —\nand, through traceId, to leave this record for the waterfall of the very\nsame run rather than a search that hopefully lands near it.\n\nAgent is on the row because the org-wide feed lists runs across agents, and\na run that cannot name its agent is an orphan in exactly the view built to\nmake sense of many of them. Every field is omitempty: a run recorded before\nthese columns existed reports absence rather than a zero it never measured.","type":"string"},"completionTokens":{"type":"integer"},"createdAt":{"type":"string"},"durationMs":{"type":"integer"},"error":{"type":"string"},"id":{"type":"string"},"input":{"type":"string"},"model":{"type":"string"},"output":{"type":"string"},"promptTokens":{"type":"integer"},"status":{"type":"string"},"toolCalls":{"type":"integer"},"traceId":{"type":"string"}},"type":"object"},"agentView":{"properties":{"computeRef":{"type":"string"},"createdAt":{"type":"string"},"description":{"type":"string"},"executionMode":{"type":"string"},"id":{"type":"string"},"model":{"type":"string"},"name":{"type":"string"},"runs":{"type":"integer"},"schedule":{"type":"string"},"serviceAccountId":{"type":"string"},"status":{"type":"string"},"tools":{"items":{"type":"string"},"type":"array"},"updatedAt":{"type":"string"}},"type":"object"},"aiMCPApp":{"properties":{"name":{"description":"Name is the subsystem, as the manifest names it.","type":"string"},"served":{"description":"Served reports that THIS process mounted it, so its tools are on this\nprocess's door rather than behind a sibling this process only knows the name\nof.","type":"boolean"}},"type":"object"},"aiMCPSurface":{"properties":{"apps":{"description":"Apps is one row per subsystem this deployment composes, in manifest order.","items":{"$ref":"#/components/schemas/aiMCPApp"},"type":"array"},"names":{"description":"Names are this process's own tool names, present only when the query asked\nfor them.","items":{"type":"string"},"type":"array"},"tools":{"description":"Tools is how many tools THIS PROCESS's door carries: its own typed-op\nregistry, projected. It is the only number a subsystem can state honestly —\nwhat the FLEET's door carries is a question only the host can ask, and it\nasks it by asking every subsystem (POST /v1/mcp, tools/list).","type":"integer"}},"type":"object"},"aiMetrics":{"properties":{"end":{"type":"string"},"evalRuns":{"description":"recent eval runs (progress)","items":{"$ref":"#/components/schemas/aimRunStat"},"type":"array"},"evals":{"$ref":"#/components/schemas/aimEvals"},"o11yAi":{"$ref":"#/components/schemas/aimO11yAI"},"o11yAiModels":{"description":"gen_ai spans per-model","items":{"$ref":"#/components/schemas/aimLfModelStat"},"type":"array"},"range":{"type":"string"},"scoreNames":{"description":"eval_scores per score-name","items":{"$ref":"#/components/schemas/aimScoreStat"},"type":"array"},"scoreSeries":{"description":"avg eval score over time (progress trend)","items":{"$ref":"#/components/schemas/aimScorePoint"},"type":"array"},"start":{"type":"string"},"topModels":{"description":"cloud_usage per-model (populated today)","items":{"$ref":"#/components/schemas/aimModelStat"},"type":"array"},"usage":{"$ref":"#/components/schemas/aimUsage"}},"type":"object"},"aimEvals":{"properties":{"avgScore":{"type":"number"},"datasets":{"type":"integer"},"latencyMsAvg":{"type":"number"},"models":{"type":"integer"},"runs":{"type":"integer"},"scoreNames":{"type":"integer"},"scores":{"type":"integer"},"traces":{"type":"integer"}},"type":"object"},"aimLfModelStat":{"properties":{"costUsd":{"type":"number"},"generations":{"type":"integer"},"model":{"type":"string"}},"type":"object"},"aimModelStat":{"properties":{"costCents":{"type":"integer"},"model":{"type":"string"},"requests":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"aimO11yAI":{"properties":{"costUsd":{"type":"number"},"generations":{"type":"integer"},"latencyMsAvg":{"type":"number"},"latencyMsP95":{"type":"number"}},"type":"object"},"aimRunStat":{"properties":{"avgValue":{"type":"number"},"dataset":{"type":"string"},"lastTs":{"type":"string"},"runName":{"type":"string"},"scores":{"type":"integer"}},"type":"object"},"aimScorePoint":{"properties":{"avgValue":{"type":"number"},"count":{"type":"integer"},"ts":{"type":"string"}},"type":"object"},"aimScoreStat":{"properties":{"avgValue":{"type":"number"},"count":{"type":"integer"},"maxValue":{"type":"number"},"minValue":{"type":"number"},"name":{"type":"string"}},"type":"object"},"aimUsage":{"properties":{"completionTokens":{"type":"integer"},"costCents":{"type":"integer"},"models":{"type":"integer"},"promptTokens":{"type":"integer"},"requests":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"aimetricsOut":{"properties":{"data":{"$ref":"#/components/schemas/aiMetrics"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"allowlistPutIn":{"properties":{"accessGroups":{"additionalProperties":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"description":"AccessGroups REPLACES the org's named access groups, as\ngroup name -\u003e channel -\u003e entries. Absent or null leaves them alone.","type":"object"},"channel":{"description":"Channel is the transport to edit: discord, slack, teams or telegram.\nRequired; an unknown value is a 404.","type":"string"},"dm":{"description":"DM REPLACES the config-managed DM allow entries. Absent or null leaves them\nalone; an empty list clears them. It never touches senders approved through\npairing — a policy edit cannot revoke an approved pairing.","items":{"type":"string"},"type":"array"},"dmPolicy":{"description":"DMPolicy sets how direct messages are admitted: \"pairing\" (a person must be\napproved first), \"allowlist\" (only listed senders) or \"open\". Empty leaves\nit unchanged.","type":"string"},"group":{"description":"Group REPLACES the config-managed group allow entries. Absent or null\nleaves them alone; an empty list clears them.","items":{"type":"string"},"type":"array"},"groupPolicy":{"description":"GroupPolicy sets how group and thread rooms are admitted: \"open\",\n\"allowlist\" or \"disabled\". Empty leaves it unchanged.","type":"string"}},"type":"object"},"allowlistView":{"properties":{"accessGroups":{"additionalProperties":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"description":"AccessGroups is the org's named sender sets, as group name -\u003e channel -\u003e\nmember entries, held once for the whole org. A DM or Group entry written\n`accessGroup:\u003cname\u003e` admits any sender listed under that name for THIS\nchannel, or under the channel `*`, which is how one set covers all four\ntransports. Replaced wholesale by the PUT.","type":"object"},"dm":{"description":"DM is the CONFIG-managed DM allow entries — the list PUT\n/v1/channels/allowlist owns and replaces wholesale. An entry matches a sender\neither EXACTLY, as the transport-native id inbox messages carry, or as\n`accessGroup:\u003cname\u003e` resolved through AccessGroups. A bare `*` admits\neveryone, but only while DMPolicy is \"open\": it is gate syntax, not an\nidentity, so under \"allowlist\" it matches nobody.","items":{"type":"string"},"type":"array"},"dmPolicy":{"description":"DMPolicy decides every inbound DIRECT message, defaulting to \"pairing\" when\nthe org has never set one. \"pairing\": a sender with no entry is sent a\npairing code and the message is DROPPED — it never reaches the inbox — and\nthey are admitted only once an admin approves. \"allowlist\": only DM admits,\nand Paired senders are suspended, since a pairing grant counts under\n\"pairing\" alone. \"open\" is not unconditional either — it still requires `*`\nor a matching entry in DM.","type":"string"},"group":{"description":"Group is the CONFIG-managed group allow entries, consulted only while\nGroupPolicy is \"allowlist\". Entries match the same two ways as DM, and here a\nbare `*` admits every sender in the room.","items":{"type":"string"},"type":"array"},"groupPolicy":{"description":"GroupPolicy decides every inbound GROUP or THREAD message — a thread is a\ngroup surface — defaulting to \"open\". \"open\" admits every sender in the room.\n\"allowlist\" admits only what Group lists, so an EMPTY Group blocks the\nchannel's group rooms outright. \"disabled\" drops all of them.","type":"string"},"paired":{"description":"Paired is the senders admitted by PAIRING — the entries POST\n/v1/channels/pairing/approve minted, DM scope only. READ-ONLY on this\nendpoint: the PUT writes config entries and can never revoke one of these\n(listing a paired sender under DM instead promotes that entry to config,\nwhich the admin then owns). They admit only while DMPolicy is \"pairing\".","items":{"type":"string"},"type":"array"}},"type":"object"},"analyticsData":{"properties":{"activeCustomers":{"description":"Active customers — from the usage ledger.","items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"arpuCents":{"type":"integer"},"churn":{"description":"Churn — logo churn (count) + rate.","items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"churnRatePct":{"type":"number"},"computed":{"additionalProperties":{"type":"boolean"},"description":"Transparency: which metrics are backed by real data vs honest-empty.","type":"object"},"cumulativeCustomers":{"items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"dau":{"type":"integer"},"generatedAt":{"type":"string"},"growthRatePct":{"type":"number"},"interval":{"type":"string"},"ltvCents":{"description":"null until churn is observed","type":"integer"},"mau":{"type":"integer"},"mrrCents":{"description":"Revenue analytics.","type":"integer"},"newCustomers":{"type":"integer"},"nrrPct":{"description":"null — needs MRR history","type":"number"},"range":{"type":"string"},"retention":{"$ref":"#/components/schemas/retentionGrid","description":"Retention triangle — signup cohort × active period."},"revenue":{"items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"signups":{"description":"Growth — from IAM createdTime (always real).","items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"topCustomers":{"items":{"$ref":"#/components/schemas/analyticsSlice"},"type":"array"},"totalCustomers":{"type":"integer"},"usage":{"description":"Usage analytics.","items":{"$ref":"#/components/schemas/SeriesPoint"},"type":"array"},"wau":{"type":"integer"}},"type":"object"},"analyticsOut":{"properties":{"data":{"$ref":"#/components/schemas/analyticsData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"analyticsSlice":{"properties":{"hint":{"type":"string"},"label":{"type":"string"},"value":{"type":"integer"}},"type":"object"},"analyticsView":{"properties":{"funnel":{"$ref":"#/components/schemas/Funnel","description":"Funnel is the org's trailing-30-day traffic → signups → orders from the\nshared analytics warehouse; available is false when it has emitted nothing."},"recommendations":{"description":"Recommendations are the next-best GTM actions derived from that funnel.","items":{"type":"string"},"type":"array"}},"type":"object"},"anchorData":{"properties":{"anchor":{"$ref":"#/components/schemas/anchorStatus","description":"Anchor is the Hanzo L1 anchoring status of the ledger root after this call."}},"type":"object"},"anchorOut":{"properties":{"data":{"$ref":"#/components/schemas/anchorData","description":"Data is the anchoring status."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success. A submit that failed still answers ok with the\nanchor's own status set to \"error\" — the attempt is the product.","type":"string"}},"type":"object"},"anchorStatus":{"properties":{"chainId":{"type":"integer"},"contract":{"type":"string"},"currentRoot":{"description":"0x… root of the journal as it stands now","type":"string"},"entryCount":{"type":"integer"},"lastAt":{"type":"integer"},"lastBlock":{"type":"integer"},"lastRoot":{"description":"The last committed on-chain anchor (nil-fields until the first successful submit).","type":"string"},"lastTxHash":{"type":"string"},"note":{"type":"string"},"rpcConfigured":{"type":"boolean"},"signerConfigured":{"type":"boolean"},"status":{"description":"pending | anchored | error","type":"string"},"synced":{"description":"true when the last anchored root == the current root","type":"boolean"}},"type":"object"},"apiKey":{"properties":{"createdAt":{"description":"CreatedAt is when the key last changed, as IAM records it.","type":"string"},"key":{"description":"Key is the FULL value, and is present for a publishable key only: it is\npublic by construction and useless to its holder if it cannot be read back.","type":"string"},"prefix":{"description":"Prefix is the recognizable, non-secret head of the key — enough to tell two\nkeys apart, never enough to use one.","type":"string"},"type":{"description":"Type is the key class: secret (sk-) or publishable (pk-).","type":"string"}},"type":"object"},"apiKeyList":{"properties":{"keys":{"description":"Keys is every key the caller holds, at most one per type.","items":{"$ref":"#/components/schemas/apiKey"},"type":"array"}},"type":"object"},"appView":{"properties":{"buildType":{"type":"string"},"createdAt":{"type":"integer"},"currentDeploymentId":{"type":"string"},"description":{"type":"string"},"dockerfile":{"type":"string"},"domains":{"items":{"type":"string"},"type":"array"},"env":{"items":{"$ref":"#/components/schemas/EnvVarJSON"},"type":"array"},"environment":{"type":"string"},"health":{"type":"string"},"id":{"type":"string"},"image":{"$ref":"#/components/schemas/imageView"},"name":{"type":"string"},"namespace":{"type":"string"},"org":{"type":"string"},"phase":{"type":"string"},"port":{"type":"integer"},"projectId":{"type":"string"},"replicas":{"type":"integer"},"repo":{"$ref":"#/components/schemas/gitSource"},"secretSync":{"description":"\"\"|pending|syncing|ready|failed (secrets.go)","type":"string"},"secretSyncDetail":{"description":"honest reason when not ready","type":"string"},"slug":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"storageGb":{"description":"GiB; absent means stateless","type":"integer"},"updatedAt":{"type":"integer"}},"type":"object"},"application":{"properties":{"code":{"description":"Code is the minted referral code. Empty on a first apply — applying does not\nmint a code, approval does; a re-apply echoes whatever the row already holds.","type":"string"},"created":{"description":"Created says whether THIS call made the row. false means the org had already\napplied and nothing changed — no second row, no reset of an existing approval.\nThe HTTP status states the same fact: 201 when true, 200 when false.","type":"boolean"},"id":{"description":"ID is the affiliate's server-minted handle, \"aff_\"-prefixed — the id staff\napprove, suspend, re-rate and pay against.","type":"string"},"rateBps":{"description":"RateBps is the direct (level 1) commission rate the row carries, in basis\npoints OF Hanzo's margin (2000 = 20% of margin, never of the customer's bill).","type":"integer"},"requestedCode":{"description":"RequestedCode echoes the vanity code asked for, normalized to lower case. It\nis a request only: approval mints a different slug if this one is taken.","type":"string"},"status":{"description":"Status is \"applied\" for a row this call created. A re-apply echoes the\nexisting row's status, which may already be \"approved\" or \"suspended\".","type":"string"}},"type":"object"},"applicationList":{"properties":{"data":{"description":"Data is the page of applications, newest first.","items":{"$ref":"#/components/schemas/ProgramApplication"},"type":"array"}},"type":"object"},"applyRequest":{"properties":{"requestedCode":{"description":"RequestedCode is the vanity code the applicant asks for; approval may mint\na different one if it is taken. Body-only: the URL cannot supply it.","type":"string"}},"type":"object"},"approval":{"properties":{"code":{"description":"Code overrides the minted code; else the requested vanity code, else a\nderived slug.","type":"string"},"id":{"description":"ID is the affiliate to approve, from the path.","type":"string"}},"type":"object"},"approvePairingIn":{"properties":{"channel":{"description":"Channel is the transport the request came in on: discord, slack, teams or telegram.","type":"string"},"code":{"description":"Code is the pairing code from GET /v1/channels/pairing. It is a capability:\nholding it is what authorises the approval, alongside org admin.","type":"string"}},"type":"object"},"approveRequest":{"properties":{"id":{"description":"ID is the author to approve, from the path.","type":"string"},"shareBps":{"description":"ShareBps overrides this author's royalty share, in basis points (0–10000).\n0 keeps the platform default. A share change never rewrites history: existing\nledger rows keep the share that was applied when they were written.","type":"integer"}},"type":"object"},"argoApp":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/argoMeta"},"spec":{"$ref":"#/components/schemas/argoSpec"},"status":{"$ref":"#/components/schemas/argoStatus"}},"type":"object"},"argoAppList":{"properties":{"apiVersion":{"type":"string"},"items":{"items":{"$ref":"#/components/schemas/argoApp"},"type":"array"},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/argoListMeta"}},"type":"object"},"argoCluster":{"properties":{"connectionState":{"$ref":"#/components/schemas/argoConnectionState"},"info":{"$ref":"#/components/schemas/argoClusterInfo"},"name":{"type":"string"},"server":{"type":"string"}},"type":"object"},"argoClusterInfo":{"properties":{"applicationsCount":{"type":"integer"},"connectionState":{"$ref":"#/components/schemas/argoConnectionState"},"serverVersion":{"type":"string"}},"type":"object"},"argoClusterList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/argoCluster"},"type":"array"},"metadata":{"$ref":"#/components/schemas/argoListMeta"}},"type":"object"},"argoConnectionState":{"properties":{"attemptedAt":{"type":"string"},"message":{"type":"string"},"status":{"type":"string"}},"type":"object"},"argoDestination":{"properties":{"name":{"description":"ArgoCD allows a destination by cluster name; omitted for the in-cluster projection.","type":"string"},"namespace":{"type":"string"},"server":{"type":"string"}},"type":"object"},"argoGroupKind":{"properties":{"group":{"type":"string"},"kind":{"type":"string"}},"type":"object"},"argoHealth":{"properties":{"message":{"type":"string"},"status":{"type":"string"}},"type":"object"},"argoInfoItem":{"properties":{"name":{"type":"string"},"value":{"type":"string"}},"type":"object"},"argoListMeta":{"properties":{"resourceVersion":{"type":"string"}},"type":"object"},"argoMeta":{"properties":{"creationTimestamp":{"type":"string"},"labels":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"namespace":{"type":"string"},"uid":{"type":"string"}},"type":"object"},"argoNode":{"properties":{"createdAt":{"type":"string"},"group":{"type":"string"},"health":{"$ref":"#/components/schemas/argoHealth"},"images":{"items":{"type":"string"},"type":"array"},"info":{"items":{"$ref":"#/components/schemas/argoInfoItem"},"type":"array"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"parentRefs":{"items":{"$ref":"#/components/schemas/argoResourceRef"},"type":"array"},"resourceVersion":{"type":"string"},"uid":{"type":"string"},"version":{"type":"string"}},"type":"object"},"argoProject":{"properties":{"apiVersion":{"type":"string"},"kind":{"type":"string"},"metadata":{"$ref":"#/components/schemas/argoMeta"},"spec":{"$ref":"#/components/schemas/argoProjectSpec"},"status":{"$ref":"#/components/schemas/argoProjectStat"}},"type":"object"},"argoProjectList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/argoProject"},"type":"array"},"metadata":{"$ref":"#/components/schemas/argoListMeta"}},"type":"object"},"argoProjectSpec":{"properties":{"clusterResourceWhitelist":{"items":{"$ref":"#/components/schemas/argoGroupKind"},"type":"array"},"description":{"type":"string"},"destinations":{"items":{"$ref":"#/components/schemas/argoDestination"},"type":"array"},"sourceRepos":{"items":{"type":"string"},"type":"array"}},"type":"object"},"argoProjectStat":{"properties":{},"type":"object"},"argoResourceRef":{"properties":{"group":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"uid":{"type":"string"},"version":{"type":"string"}},"type":"object"},"argoResourceStatus":{"properties":{"group":{"type":"string"},"health":{"$ref":"#/components/schemas/argoHealth"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"status":{"type":"string"},"version":{"type":"string"}},"type":"object"},"argoRevisionMetadata":{"properties":{"author":{"type":"string"},"date":{"type":"string"},"message":{"type":"string"},"signatureInfo":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"argoSource":{"properties":{"path":{"type":"string"},"repoURL":{"type":"string"},"targetRevision":{"type":"string"}},"type":"object"},"argoSpec":{"properties":{"destination":{"$ref":"#/components/schemas/argoDestination"},"project":{"type":"string"},"source":{"$ref":"#/components/schemas/argoSource"}},"type":"object"},"argoStatus":{"properties":{"health":{"$ref":"#/components/schemas/argoHealth"},"reconciledAt":{"type":"string"},"resources":{"items":{"$ref":"#/components/schemas/argoResourceStatus"},"type":"array"},"summary":{"$ref":"#/components/schemas/argoSummary"},"sync":{"$ref":"#/components/schemas/argoSyncStatus"}},"type":"object"},"argoSummary":{"properties":{"images":{"items":{"type":"string"},"type":"array"}},"type":"object"},"argoSyncStatus":{"properties":{"revision":{"type":"string"},"status":{"type":"string"}},"type":"object"},"argoSyncWindows":{"properties":{"activeWindows":{"items":{"type":"object"},"type":"array"},"assignedWindows":{"items":{"type":"object"},"type":"array"},"canSync":{"type":"boolean"}},"type":"object"},"argoTree":{"properties":{"hosts":{"items":{"type":"object"},"type":"array"},"nodes":{"items":{"$ref":"#/components/schemas/argoNode"},"type":"array"},"orphanedNodes":{"items":{"$ref":"#/components/schemas/argoNode"},"type":"array"}},"type":"object"},"artifactOut":{"properties":{"created":{"description":"Created is false when these exact bytes were already recorded — the write is a no-op.","type":"boolean"},"ref":{"description":"Ref is the content address, \"sha256:\u003chash\u003e\".","type":"string"},"rolled_up":{"description":"RolledUp is false when the OLAP roll-up was skipped; the SQLite write still stands.","type":"boolean"},"sha256":{"description":"SHA256 is the SERVER's hash of the bytes — the artifact's identity.","type":"string"}},"type":"object"},"artifactsOut":{"properties":{"data":{"description":"Data are the artifacts, newest first. Content bytes are never returned here.","items":{"$ref":"#/components/schemas/ResearchArtifact"},"type":"array"},"total":{"description":"Total is len(data).","type":"integer"}},"type":"object"},"askPostIn":{"properties":{"query":{"description":"Query is the question, from the BODY. Takes precedence over `?q=`.","type":"string"},"repo":{"description":"Repo is the repository narrowing, from the BODY. Takes precedence over `?repo=`.","type":"string"}},"type":"object"},"askRequest":{"properties":{"followUps":{"type":"boolean"},"language":{"type":"string"},"maxQueries":{"type":"integer"},"maxSources":{"type":"integer"},"mode":{"type":"string"},"model":{"type":"string"},"q":{"type":"string"},"question":{"type":"string"},"sources":{"items":{"type":"string"},"type":"array"},"stream":{"type":"boolean"},"system":{"type":"string"}},"type":"object"},"attributeRequest":{"properties":{"code":{"description":"Code is the affiliate code the referred org arrived with. Body-only: the\nURL cannot supply it.","type":"string"}},"type":"object"},"attribution":{"properties":{"code":{"description":"Code is the affiliate code the edge was recorded under, normalized to lower\ncase. On a re-post it is the code of the STANDING edge, which may differ from\nthe one just sent — first touch wins.","type":"string"},"created":{"description":"Created says whether THIS call made the edge. false means the caller org was\nalready attributed and nothing moved. The HTTP status says the same: 201 when\ntrue, 200 when false.","type":"boolean"},"createdAt":{"description":"CreatedAt is when the edge was FIRST recorded, Unix seconds UTC. On a re-post\nit is the original time, not now.","type":"integer"},"id":{"description":"ID is the attribution edge's server-minted handle, \"afr_\"-prefixed.","type":"string"}},"type":"object"},"auditList":{"properties":{"data":{"description":"Data is the org's compliance.* audit rows, newest first.","items":{"$ref":"#/components/schemas/Wire"},"type":"array"},"disclaimer":{"description":"Disclaimer states that statuses are provider-reported or tracked, never a\nplatform assertion of legal or regulatory compliance.","type":"string"}},"type":"object"},"authorData":{"properties":{"author":{"$ref":"#/components/schemas/adminAuthorView","description":"Author is the author record after the change. Its repository and deploy counts\nare 0 here — this is the mutated row, not a re-listing."}},"type":"object"},"authorProgramSummary":{"properties":{"accruedCents":{"description":"AccruedCents is the page's lifetime royalty accrued, in integer USD cents.","type":"integer"},"approved":{"description":"Approved is how many are admitted and accruing.","type":"integer"},"connected":{"description":"Connected is how many of those are enrolled but not yet admitted to earning.","type":"integer"},"paidCents":{"description":"PaidCents is what has been RECORDED as paid across the page, in integer USD\ncents. Recorded, not settled: the money leaves in a human's hands.","type":"integer"},"pendingCents":{"description":"PendingCents is what the platform still owes across the page, in integer USD\ncents — the sum of each author's own accrued − paid, each floored at zero.","type":"integer"},"suspended":{"description":"Suspended is how many have been stopped from accruing further. An author holds\nexactly one status, so the three buckets never overlap and connected +\napproved + suspended = total.","type":"integer"},"total":{"description":"Total is how many author records this response actually carried. The roll-up\nis folded over the SAME page as authors — newest first, bounded by limit\n(default 500, ceiling 1000) — so on a program larger than the page it\nsummarizes that page, not the fleet.","type":"integer"}},"type":"object"},"authorRepo":{"properties":{"badgeMarkdown":{"description":"BadgeMarkdown is the ready-to-paste README snippet, DERIVED for each response\nfrom this deployment's badge host and never stored: a \"Deploy on Hanzo\" image\nlinking to the one-click import of this repository. Re-hosting the builder\nchanges every badge without touching a row.","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds when the claim was first recorded. It equals\nverifiedAt on the first proof and then stays put while verifiedAt moves, so the\npair reads as \"claimed since / last proven\".","type":"integer"},"method":{"description":"Method is HOW ownership was proven: \"oauth\" — an IAM-linked forge token showed\nadmin or push on the repository; \"file\" — a hanzo.json on the default branch\ncarried this author's verify code; or \"maintainer\" — the repository sits in a\nfirst-party namespace, where ownership is intrinsic and the treasury author\nholds it with no proof step. Omitted on a row written before the method was\nrecorded.","type":"string"},"repoUrl":{"description":"RepoURL is the claim key in canonical form — lowercased \"host/owner/name\",\nno scheme, no .git, host ∈ {github.com, gitlab.com}. A deploy's source repo is\nnormalized through the same function before attribution, so the two sides can\nnever miss on a cosmetic difference. UNIQUE across every author: first proven\nclaim wins.","type":"string"},"verified":{"description":"Verified reports that ownership was proven. Only a proven claim is ever\nwritten, so it is true on every row this surface returns; the deploy path\nre-reads it regardless, because an unverified claim attributes nothing.","type":"boolean"},"verifiedAt":{"description":"VerifiedAt is unix seconds of the most recent successful proof. Re-verifying\nrefreshes it, and the method beside it, in place.","type":"integer"}},"type":"object"},"authorResult":{"properties":{"data":{"$ref":"#/components/schemas/authorData","description":"Data carries the author."},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is \"ok\".","type":"string"}},"type":"object"},"authorSweepResult":{"properties":{"data":{"$ref":"#/components/schemas/sweepCounts","description":"Data is the sweep's counts."},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is \"ok\".","type":"string"}},"type":"object"},"authoredPluginList":{"properties":{"plugins":{"description":"Plugins is every plugin this org built, newest first, each carrying the\nTypeScript as authored. The bundled artifact is never rendered.","items":{"$ref":"#/components/schemas/AuthoredPlugin"},"type":"array"}},"type":"object"},"authoredSkillList":{"properties":{"skills":{"description":"Skills is every skill this org authored, each with its SKILL.md content.","items":{"$ref":"#/components/schemas/Skill"},"type":"array"}},"type":"object"},"authorizeOut":{"properties":{"authorizeUrl":{"description":"AuthorizeURL is the provider consent (or bot deep-link) URL.","type":"string"}},"type":"object"},"autoCreate":{"properties":{"data":{"description":"Data is the flow graph — the product's nodes/edges document, verbatim:\nnodes carry a piece type (webhook, schedule, http, set, branch) and its\nconfig; edges wire them. Omit it to create an empty flow."},"name":{"description":"Name is the flow's display name.","type":"string"}},"type":"object"},"autoStart":{"properties":{"flow":{"description":"Flow is the id of the flow to run.","type":"string"},"input":{"description":"Input is the trigger payload handed to the run, verbatim JSON object.\nThe run's state starts as {\"trigger\": input}."}},"type":"object"},"autoStatus":{"properties":{"reachable":{"description":"Reachable is true when the auto service answered its health probe.","type":"boolean"}},"type":"object"},"autoUpdate":{"properties":{"data":{"description":"Data replaces the flow graph when present, verbatim."},"flow":{"description":"Flow is the flow's id, taken from the path.","type":"string"},"name":{"description":"Name renames the flow when present.","type":"string"}},"type":"object"},"backfillQuery":{"properties":{"before":{"description":"Before bounds the seed to ledger rows written before this RFC3339 instant.\nDefaults to now, and is snapped down to UTC midnight — the rollup's grain, so\nthe seeded days and the guarded days are the same set. Pass the day the\nincremental view started capturing, so seed and view never share a day.","type":"string"},"force":{"description":"Force must be exactly \"true\" to seed a rollup that already holds rows. It is\nspelled as a string, not a flag, because the guard has always compared this\nvalue literally — \"1\" and \"yes\" do NOT force.","type":"string"}},"type":"object"},"backfillResult":{"properties":{"forced":{"description":"Forced is true when the caller overrode the already-populated guard.","type":"boolean"},"seededBefore":{"description":"SeededBefore is the RFC3339 upper bound the seed actually used.","type":"string"},"status":{"description":"Status is \"ok\" — a seed that did not run answered an error instead.","type":"string"}},"type":"object"},"baseHealth":{"properties":{"service":{"description":"Service is \"base\" — which subsystem answered.","type":"string"},"status":{"description":"Status is \"ok\" when the subsystem is serving.","type":"string"}},"type":"object"},"baseInstance":{"properties":{"created":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"plan":{"type":"string"},"region":{"type":"string"},"status":{"type":"string"},"url":{"type":"string"}},"type":"object"},"basesOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/baseInstance"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"basisResult":{"properties":{"data":{"additionalProperties":{"type":"object"},"description":"Data is the royalty basis.","type":"object"},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is \"ok\".","type":"string"}},"type":"object"},"beginIn":{"properties":{"alreadyIncorporated":{"description":"AlreadyIncorporated declares an org that already has an entity, which takes\nthe import path (POST /v1/company/skip) instead of the formation path.","type":"boolean"},"jurisdiction":{"description":"Jurisdiction is the state of formation: DE or WY.","type":"string"},"name":{"description":"Name is the proposed company name.","type":"string"},"structure":{"description":"Structure is the legal entity to form: c-corp, llc or dao-llc.","type":"string"}},"type":"object"},"binarySpec":{"properties":{"image":{"type":"string"},"ldflags":{"type":"string"},"main":{"type":"string"},"name":{"type":"string"},"out":{"type":"string"},"platforms":{"items":{"type":"string"},"type":"array"},"run":{"type":"string"}},"type":"object"},"bindAgentReq":{"properties":{"agentName":{"description":"AgentName is the cloud Agent (/v1/agents) the machine will run. Required.","type":"string"},"botVersion":{"description":"BotVersion pins the @hanzo/bot runtime version; empty takes the default.","type":"string"},"id":{"description":"ID is the machine to bind, from the URL path.","type":"string"}},"type":"object"},"bindingList":{"properties":{"agentBindings":{"description":"AgentBindings is one row per bound machine, emitted verbatim as vm reports\nit.","items":{"$ref":"#/components/schemas/agentBinding"},"type":"array"}},"type":"object"},"blobJSON":{"properties":{"binary":{"description":"Binary marks content git could not treat as text; it comes back base64.","type":"boolean"},"content":{"description":"Content is the file's bytes, empty when Truncated.","type":"string"},"encoding":{"description":"Encoding is how Content is carried: \"utf8\" verbatim, or \"base64\".","type":"string"},"path":{"description":"Path is the file's repo-relative path.","type":"string"},"size":{"description":"Size is the file's byte length in the repo, whatever was returned below.","type":"integer"},"truncated":{"description":"Truncated marks a file past the 1 MiB view cap. No content is sent —\nclone the repo for it.","type":"boolean"}},"type":"object"},"blueprintCounts":{"properties":{"principles":{"type":"integer"},"sections":{"type":"integer"},"steps":{"type":"integer"},"strategies":{"type":"integer"},"templates":{"type":"integer"}},"type":"object"},"blueprintHealth":{"properties":{"blueprints":{"description":"Blueprints is how many blueprints this build has embedded and priced.","type":"integer"},"rateCard":{"$ref":"#/components/schemas/RateCard","description":"RateCard is the rate card actually in force after the operator env overlay\n(CLOUD_BLUEPRINT_UCPU_HR / CLOUD_BLUEPRINT_UGB_HR), not the shipped default."},"service":{"description":"Service names the subsystem answering — always \"blueprint\".","type":"string"},"status":{"description":"Status is \"ok\"; the route answers 200 whenever the subsystem is mounted.","type":"string"}},"type":"object"},"blueprintIndex":{"properties":{"data":{"description":"Data is one row per embedded blueprint, sorted by template id.","items":{"$ref":"#/components/schemas/blueprintRow"},"type":"array"}},"type":"object"},"blueprintRow":{"properties":{"estCentsPerMonth":{"description":"CentsPerMonth is the estimated compute cost of running the whole stack for\none month, in USD cents, from the rate card GET /v1/blueprint/health echoes.","type":"integer"},"services":{"description":"Services is how many compose services the stack runs.","type":"integer"},"templateId":{"description":"TemplateID is the blueprint slug — the id GET /v1/blueprint/sbom takes as\n?template= and the path under templates.hanzo.ai/blueprints/\u003cid\u003e/.","type":"string"}},"type":"object"},"blueprintVersionsView":{"properties":{"brand":{"description":"Brand is the blueprint key the history belongs to — this deployment's brand,\nor \"\" (the base blueprint) when the brand has no row of its own.","type":"string"},"versions":{"description":"Versions are the stored versions, newest first: metadata only, never the\ndocuments.","items":{"$ref":"#/components/schemas/VersionMeta"},"type":"array"}},"type":"object"},"blueprintView":{"properties":{"blueprint":{"$ref":"#/components/schemas/Blueprint","description":"Blueprint is the whole authored document, including items disabled for the\norg-facing reads, with every enabled flag written out explicitly."},"brand":{"description":"Brand is the key this blueprint is stored under — the deployment's brand, or\n\"\" for the shared base blueprint it falls back to.","type":"string"},"counts":{"$ref":"#/components/schemas/blueprintCounts","description":"Counts summarises how many items each collection holds."},"version":{"description":"Version is the active stored version number (1 is the seed). Each edit\nappends a new one; nothing is ever overwritten.","type":"integer"}},"type":"object"},"boardItem":{"properties":{"doctype":{"type":"string"},"name":{"type":"string"},"project":{"type":"string"},"status":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"boardPage":{"properties":{"count":{"description":"Count is the number of rows in THIS page — never the org's total.","type":"integer"},"data":{"description":"Data is the matching items, most recently updated first.","items":{"$ref":"#/components/schemas/boardItem"},"type":"array"}},"type":"object"},"boardResp":{"properties":{"account":{"description":"Account is the account the series narrows to, when one was named.","type":"string"},"available":{"description":"Available reports whether the warehouse answered; false is an honest \"we\nhave no data\", NOT zero usage.","type":"boolean"},"current":{"description":"Current is the live state of each lane — the dash headline.","items":{"$ref":"#/components/schemas/readingView"},"type":"array"},"from":{"description":"From and To are the resolved [from, to) window, RFC 3339 UTC.","type":"string"},"provider":{"description":"Provider is the provider whose meter answered.","type":"string"},"range":{"description":"Range is the resolved period label.","type":"string"},"scope":{"description":"Scope is always \"user\": the caller's own linked accounts.","type":"string"},"source":{"description":"Source is always \"account\": the provider's own meter, not a Hanzo charge.","type":"string"},"to":{"type":"string"},"windows":{"description":"Windows is every window instance in range, newest first.","items":{"$ref":"#/components/schemas/readingView"},"type":"array"}},"type":"object"},"botList":{"properties":{"bots":{"description":"Bots is one row per kind=bot machine, each joined with its agent binding\nwhen it has one.","items":{"$ref":"#/components/schemas/botView"},"type":"array"}},"type":"object"},"botMember":{"properties":{"active":{"description":"Active is whether the agent projects as a LIVE workspace member, derived\nfrom its registry status: empty, \"active\" and \"ready\" are live, anything\nelse (archived/retired) is not. An inactive bot drops out of the Team list\nwhile its past authorship survives.","type":"boolean"},"id":{"description":"the agent id","type":"string"},"name":{"description":"display name","type":"string"},"personRef":{"description":"the projected Person _id","type":"string"},"userId":{"description":"derived member account uuid (personUuid)","type":"string"}},"type":"object"},"botRoster":{"properties":{"bots":{"description":"Bots is every agent of the caller's org, projected as a workspace member.","items":{"$ref":"#/components/schemas/botMember"},"type":"array"}},"type":"object"},"botSync":{"properties":{"projected":{"description":"Projected is how many roster entries the reconcile touched.","type":"integer"},"synced":{"description":"Synced is true when the reconcile ran.","type":"boolean"}},"type":"object"},"botView":{"properties":{"agent":{"type":"string"},"binding":{"$ref":"#/components/schemas/agentBinding"},"createdTime":{"type":"string"},"gpu":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"mem":{"type":"string"},"name":{"type":"string"},"os":{"type":"string"},"privateIp":{"type":"string"},"provider":{"type":"string"},"publicIp":{"type":"string"},"region":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"vcpu":{"type":"integer"}},"type":"object"},"bucketCreateIn":{"properties":{"name":{"description":"Name is the bucket name to create.","type":"string"}},"type":"object"},"bucketRecord":{"properties":{"bucket":{"description":"Bucket is the bucket's name within the org.","type":"string"},"history":{"description":"History is how many revisions each key keeps.","type":"integer"},"ttl":{"description":"TTL is the entry expiry in seconds; 0 means none.","type":"integer"},"values":{"description":"Values is how many values the bucket holds right now.","type":"integer"}},"type":"object"},"bucketWrite":{"properties":{"bucket":{"description":"Bucket is the bucket's name within the org, from the path: 1–64 of\n[A-Za-z0-9_], no dash.","type":"string"},"history":{"description":"History is how many revisions each key keeps, 1–64. 0 means 1.","type":"integer"},"maxValue":{"description":"MaxValue caps one value's size in bytes. 0 or less means the server's\nceiling.","type":"integer"},"ttl":{"description":"TTL expires entries after this many SECONDS. 0 means no expiry.","type":"integer"}},"type":"object"},"buildBoard":{"properties":{"builds":{"description":"Builds are the org's real BuildKit build records, newest first.","items":{"$ref":"#/components/schemas/buildRow"},"type":"array"}},"type":"object"},"buildList":{"properties":{"builds":{"description":"Builds is every published build, most recently updated first.","items":{"$ref":"#/components/schemas/buildSummary"},"type":"array"}},"type":"object"},"buildOut":{"properties":{"bytes":{"type":"integer"},"generated":{"type":"boolean"},"plugin":{"$ref":"#/components/schemas/AuthoredPlugin"}},"type":"object"},"buildRequest":{"properties":{"name":{"type":"string"},"provider":{"type":"string"},"source":{"type":"string"},"spec":{"type":"string"}},"type":"object"},"buildRow":{"properties":{"commit":{"description":"Commit is the short git ref the build pinned.","type":"string"},"duration":{"description":"Duration is the wall time of a TERMINAL build; empty while it still runs.","type":"string"},"id":{"description":"ID is the build record's id.","type":"string"},"repo":{"description":"Repo is the repo the build built, or the image it produced.","type":"string"},"startedAt":{"description":"StartedAt is when the build was recorded, RFC3339 UTC.","type":"string"},"status":{"description":"Status is the build's real state: queued, building, succeeded or failed.","type":"string"}},"type":"object"},"buildSummary":{"properties":{"agent":{"type":"string"},"endedAt":{"type":"string"},"org":{"type":"string"},"project":{"type":"string"},"repo":{"type":"string"},"session":{"type":"string"},"startedAt":{"type":"string"},"status":{"type":"string"},"title":{"type":"string"},"turns":{"type":"integer"}},"type":"object"},"buildTurn":{"properties":{"actor":{"type":"string"},"at":{"type":"string"},"body":{"type":"string"},"commit":{"type":"string"},"kind":{"type":"string"},"subject":{"type":"string"},"turn":{"type":"integer"}},"type":"object"},"buildView":{"properties":{"agent":{"type":"string"},"endedAt":{"type":"string"},"model":{"type":"string"},"org":{"type":"string"},"project":{"type":"string"},"repo":{"type":"string"},"session":{"type":"string"},"startedAt":{"type":"string"},"status":{"type":"string"},"title":{"type":"string"},"turns":{"items":{"$ref":"#/components/schemas/buildTurn"},"type":"array"},"verify":{"description":"Verify is the exact command that re-derives every commit binding below\nstraight from git, so nothing here has to be taken on trust.","type":"string"}},"type":"object"},"busAck":{"properties":{"duplicate":{"description":"Duplicate is true when JetStream deduplicated the message by its\nNats-Msg-Id instead of storing it again.","type":"boolean"},"ok":{"description":"OK is true when the bus accepted the message.","type":"boolean"},"seq":{"description":"Seq is the message's sequence in that stream.","type":"integer"},"stream":{"description":"Stream is the stream that stored the message — absent when no stream\ncaptures the subject and the message went out core (fire-and-forget).","type":"string"}},"type":"object"},"busMessage":{"properties":{"data":{"description":"Data is the payload as UTF-8 text.","type":"string"},"headers":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Headers are the message's headers, when it carries any.","type":"object"},"seq":{"description":"Seq is the message's stream sequence — fetched messages only.","type":"integer"},"subject":{"description":"Subject is the message's subject in the org's own namespace.","type":"string"},"time":{"description":"Time is when the stream stored the message, RFC3339 — fetched messages\nonly.","type":"string"}},"type":"object"},"busPublish":{"properties":{"data":{"description":"Data is the payload, carried verbatim as UTF-8 text (typically JSON).\nBinary payloads belong on the NATS port.","type":"string"},"headers":{"additionalProperties":{"type":"string"},"description":"Headers are optional message headers, one value per name. A Nats-Msg-Id\nheader is JetStream's deduplication key: a repeat within the stream's\ndedup window is acknowledged as duplicate rather than stored twice.","type":"object"},"subject":{"description":"Subject is the subject to publish to, in the org's own namespace — e.g.\norders.created. No wildcards.","type":"string"}},"type":"object"},"busRequest":{"properties":{"data":{"description":"Data is the request payload, carried verbatim as UTF-8 text.","type":"string"},"headers":{"additionalProperties":{"type":"string"},"description":"Headers are optional request headers, one value per name.","type":"object"},"subject":{"description":"Subject is the subject a responder listens on, in the org's namespace.","type":"string"},"timeoutMs":{"description":"TimeoutMs bounds the wait for a reply. 0 or less means the default of\n5000; anything above 30000 is clamped to 30000.","type":"integer"}},"type":"object"},"byoGPU":{"properties":{"arch":{"description":"native target, e.g. \"gfx1151\"","type":"string"},"memoryTotal":{"description":"VRAM (or unified pool), e.g. \"122880 MiB\"","type":"string"},"name":{"type":"string"},"unified":{"description":"unified CPU/GPU memory pool (APU / SoC)","type":"boolean"}},"type":"object"},"byoWorker":{"properties":{"arch":{"description":"Arch/CPUs/Memory are the connecting host's static CPU spec, mirrored from the\nregistration: Arch is runtime.GOARCH (amd64 | arm64), Memory is total RAM in\nBYTES — the same fields a code-linked run-target carries, so the /v1/fleet\nboard renders a linked node's arch + cores + RAM like any other unit.","type":"string"},"capabilities":{"description":"Capabilities the worker advertises (\"studio.render\", \"engine.serve\"); Engine\nis present when it runs a hanzo-engine model server. Both additive + omitempty.","items":{"type":"string"},"type":"array"},"cpuModel":{"type":"string"},"cpus":{"type":"integer"},"cuda":{"type":"string"},"driver":{"type":"string"},"engine":{"$ref":"#/components/schemas/engineAdvertisement"},"firstSeen":{"type":"string"},"gpus":{"items":{"$ref":"#/components/schemas/byoGPU"},"type":"array"},"hip":{"type":"string"},"hostname":{"type":"string"},"id":{"type":"string"},"jobQueue":{"type":"string"},"lastHeartbeat":{"type":"string"},"location":{"description":"\"on-prem\" (BYO has no cloud region)","type":"string"},"memory":{"type":"integer"},"os":{"type":"string"},"provider":{"description":"always \"byo\"","type":"string"},"rocm":{"type":"string"},"status":{"description":"online | offline","type":"string"},"version":{"type":"string"}},"type":"object"},"campaignInput":{"properties":{"account":{"description":"Account is the provider ad-account this campaign runs on (Meta act_\u003cid\u003e). Optional.","type":"string"},"budget":{"description":"Budget is the campaign budget in MINOR units (cents). Negative values clamp to 0.","type":"integer"},"name":{"description":"Name is the campaign's display label. Required; trimmed and bounded to 1024 bytes.","type":"string"},"objective":{"description":"Objective is the campaign goal as the provider names it. Optional, bounded to 1024 bytes.","type":"string"},"platform":{"description":"Platform is the ad network: meta, google, tiktok or x. Empty defaults to meta.","type":"string"},"spend":{"description":"Spend is the amount spent so far in MINOR units (cents). Negative values clamp to 0.","type":"integer"},"status":{"description":"Status is the lifecycle state: draft, active, paused or completed. Empty defaults to draft.","type":"string"}},"type":"object"},"campaignList":{"properties":{"data":{"description":"Data is the matching campaigns, newest-updated first.","items":{"$ref":"#/components/schemas/AdCampaign"},"type":"array"}},"type":"object"},"campaignPage":{"properties":{"data":{"description":"Data are the campaigns on this page.","items":{"$ref":"#/components/schemas/campaignRecord"},"type":"array"}},"type":"object"},"campaignRecord":{"properties":{"audience":{"description":"Audience is an opaque reference to the segment this campaign targets. It is\nstored and echoed but not yet handed to the executors — a channel targets\nthrough the provider account it runs under — so it is documentation for now.\nAbsent when never set.","type":"string"},"budget":{"description":"Budget is the campaign's total budget in CENTS, handed to each executor as the\nbudget for its channel. 0 means none was set.","type":"integer"},"channels":{"description":"Channels are the fan-out targets, at most one per kind and at most 12, each\ncarrying its own post-launch state. Empty means nothing to launch, which is\nwhat makes a launch of this campaign a 400.","items":{"$ref":"#/components/schemas/ChannelSpec"},"type":"array"},"content":{"description":"Content is the ordered creative set, at most 32, empty entries dropped.\nContent[0] is the creative that runs; the rest are A/B variants a wired\nexperiment can assign per launch.","items":{"type":"string"},"type":"array"},"createdAt":{"description":"CreatedAt is when the campaign was created, in unix seconds. Server-set.","type":"integer"},"id":{"description":"ID is the campaign's server-minted handle — \"cmp_\" and 128 random bits — and\nthe id every other campaign call is addressed by. Never read off the wire: a\ncreate that sends one has it ignored.","type":"string"},"name":{"description":"Name is the campaign's display name. Required on write, trimmed, and capped at\n2048 characters.","type":"string"},"scheduleAt":{"description":"ScheduleAt is when the campaign should run, in unix seconds. 0 (absent) means\nlaunch immediately. It is passed to each executor; nothing in this service\nwakes up to launch it for you.","type":"integer"},"status":{"description":"Status is the lifecycle state, server-owned and never accepted from a caller.\nFour values actually occur: draft (inert and fully mutable — nothing is sent\nand no budget is committed), live, paused and failed. After a fan-out live\nmeans AT LEAST ONE channel launched — read the channel rows for the rest —\nand failed means none did.","type":"string"},"updatedAt":{"description":"UpdatedAt is the last write in unix seconds — an edit, a launch or a pause.\nServer-set on every save.","type":"integer"}},"type":"object"},"campaignResults":{"properties":{"abTest":{"description":"ABTest is the creative A/B analysis from the experiments primitive\n(experiments.Analyze, pull-model), present only when the campaign runs\nmore than one creative and an experiment is wired. Opaque JSON — campaign\nstays decoupled from the experiments analysis type."},"available":{"description":"Available is false when the analytics warehouse is not connected or the query\nfailed: the funnel below is then zero because nothing could be read, not\nbecause nothing happened. Spend and Channels are still real — they come from\nthe connectors, not the warehouse.","type":"boolean"},"cac":{"description":"CAC is customer acquisition cost: spend DOLLARS per conversion, rounded to\ncents. 0 when nothing converted — that is \"not yet computable\", not \"free\".","type":"number"},"campaignId":{"description":"CampaignID is the campaign these results are for, echoed from the request.","type":"string"},"channels":{"description":"Channels is the per-channel spend breakdown that SpendCents sums, one row per\nchannel on the campaign including the ones that never launched.","items":{"$ref":"#/components/schemas/ChannelMetric"},"type":"array"},"clicks":{"description":"Clicks is the campaign's click events over the window.","type":"integer"},"conversions":{"description":"Conversions is the terminal funnel events attributed to the campaign — orders\ncompleted, signups completed, explicit conversion events.","type":"integer"},"ctr":{"description":"CTR is clicks per impression, a fraction rounded to 4 places (0.0123 = 1.23%),\nnot a percentage. 0 when there were no impressions to divide by.","type":"number"},"cvr":{"description":"CVR is conversions per click, a fraction rounded to 4 places. 0 when there\nwere no clicks.","type":"number"},"end":{"description":"End is the window's end, RFC3339 UTC — the read's own clock unless an explicit\npair was given. The window is a LOOKBACK, not the campaign's own lifetime.","type":"string"},"impressions":{"description":"Impressions is how many times the campaign's creatives were shown, counted\nfrom its utm_campaign-tagged impression events.","type":"integer"},"name":{"description":"Name is the campaign's display name at read time, so a result can be labelled\nwithout a second fetch.","type":"string"},"range":{"description":"Range is the window actually used: 24h, 7d, 30d, 90d, or \"custom\" when an\nexplicit start/end pair was honored. An unparseable or absent range reads 30d,\nso this is the value to trust, not the one that was sent.","type":"string"},"revenue":{"description":"Revenue is the summed revenue attribute of the campaign's events, in whole\nCURRENCY UNITS (dollars) — the one money value here that is not in cents.","type":"number"},"roas":{"description":"ROAS is return on ad spend: revenue per spend DOLLAR, rounded to 2 places\n(2.5 = $2.50 back per $1). 0 when nothing was spent.","type":"number"},"source":{"description":"Source names the analytics table the funnel was read from, so an operator can\nsee exactly what was counted. Set even when Available is false.","type":"string"},"spendCents":{"description":"SpendCents is the campaign's total spend in CENTS: the sum of what each live\nchannel's provider reports. A channel whose spend could not be read\ncontributes 0 and says so on its own row.","type":"integer"},"start":{"description":"Start is the window's inclusive start, RFC3339 UTC.","type":"string"},"status":{"description":"Status is the campaign's lifecycle state at read time — draft, live, paused,\ncompleted or failed. A draft has never run, so its funnel is legitimately zero.","type":"string"},"visitors":{"description":"Visitors is how many distinct people the campaign reached, counted by event\nidentity across ALL its events in the window — not a subset of Impressions, so\nit can exceed them for a campaign whose provider reports clicks but not views.","type":"integer"}},"type":"object"},"campaignSummary":{"properties":{"budget":{"description":"Budget is the sum of every campaign's budget, in CENTS.","type":"integer"},"campaigns":{"description":"Campaigns is how many campaigns the org has, in any state.","type":"integer"},"channels":{"description":"Channels are the channel kinds this deployment has an executor wired for.\nA kind absent here is a kind a launch will honestly record as unavailable.","items":{"type":"string"},"type":"array"},"live":{"description":"Live is how many of them are currently live.","type":"integer"}},"type":"object"},"campaignUpdate":{"properties":{"audience":{"type":"string"},"budget":{"type":"integer"},"channels":{"items":{"$ref":"#/components/schemas/ChannelSpec"},"type":"array"},"content":{"items":{"type":"string"},"type":"array"},"id":{"description":"ID is the campaign to update, from the path.","type":"string"},"name":{"type":"string"},"scheduleAt":{"type":"integer"}},"type":"object"},"campaignWrite":{"properties":{"audience":{"description":"Audience is the segment or audience selector this campaign targets.","type":"string"},"budget":{"description":"Budget is the campaign's total budget in CENTS. Negative reads as 0.","type":"integer"},"channels":{"description":"Channels are the fan-out targets, at most one per kind (paid, organic,\nemail) and at most 12. A channel's status and provider id are server-owned:\nwhatever the caller sends for them is replaced with \"pending\".","items":{"$ref":"#/components/schemas/ChannelSpec"},"type":"array"},"content":{"description":"Content is the ordered creative set. Content[0] is the active creative and\nthe rest are A/B variants; at most 32, empty entries dropped.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the campaign's display name. Required; trimmed and capped at 2048\ncharacters.","type":"string"},"scheduleAt":{"description":"ScheduleAt is when the campaign should run, in unix seconds. Negative reads\nas 0 (immediately).","type":"integer"}},"type":"object"},"capIn":{"properties":{"id":{"description":"ID is the cap to edit or remove, from the path. Unused by the list and create ops.","type":"string"},"org":{"description":"Org is the tenant to act on. Required for a SuperAdmin — they must name their\ntarget; ignored for a white-label admin, who always acts on their own org.","type":"string"}},"type":"object"},"capabilities":{"properties":{"actions":{"description":"Actions is whether the transport renders an INTERACTIVE control natively, and\nit is the flag to read before composing one. The vocabulary is a closed\nkind-tagged union (envelope.go), exactly four kinds, each carrying only its\nown field plus an optional label:\n\n\tcommand  — a bot command to run (`command`), rendered as a button that\n\t           invokes it.\n\turl      — an external link (`url`), rendered as a link button.\n\tselect   — a menu (`options`, each a label and the value choosing it\n\t           returns), rendered as a picker.\n\tapproval — a reference to an approval request (`approval.id`), rendered as\n\t           approve/deny controls bound to that id.\n\nFalse on all four transports this pass, and nothing refuses a send for it:\nactions are accepted, validated per kind, and flattened by renderText to one\nline each after the text — `[label] command`, `[label] url`,\n`[label] opt | opt`, `[label] approval requested: \u003cid\u003e`. So a caller that\nneeds a real control must read this flag and degrade itself; a caller that\nonly needs the choice communicated can send actions and take the text form.","type":"boolean"},"dm":{"description":"DM is whether the transport carries a DIRECT message at all. True for slack,\nteams and telegram. False for discord, honestly: that ingress is guild-scoped\nslash commands — an interaction without a guild id is refused at the door —\nso nothing ever arrives classified as a DM, no reply route is ever learned\nfor one, and a send addressed at a Discord DM is refused 409.","type":"boolean"},"group":{"description":"Group is whether the transport carries multi-person rooms — a Discord guild\nchannel, a Slack channel, a Teams channel or group chat, a Telegram group or\nsupergroup. True on all four.","type":"boolean"},"media":{"description":"Media is whether the transport renders an ATTACHMENT natively. False on all\nfour this pass, and a send is not refused for it: renderText flattens each\nattachment to one `kind: url (mime)` line after the text rather than dropping\nit.","type":"boolean"},"thread":{"description":"Thread is whether a reply can be threaded UNDER a specific message. True for\nslack alone: it is the only transport whose ingress reports a thread\n(thread_ts, published as the envelope's replyTo) and whose door posts back\ninto it. Discord's replyTo makes an inline reply rather than a thread,\nTelegram's answers one message id, and Teams carries no reply target at all —\na replyTo sent to it is ignored.","type":"boolean"}},"type":"object"},"captableClassHolding":{"properties":{"authorized":{"description":"Authorized is how many shares of the class are authorized.","type":"integer"},"classType":{"description":"ClassType is COMMON or PREFERRED.","type":"string"},"issued":{"description":"Issued is how many shares of the class have been issued.","type":"integer"},"name":{"description":"Name is the class name.","type":"string"},"shareClassId":{"description":"ShareClassID is the share class.","type":"string"}},"type":"object"},"captableCompany":{"properties":{"createdAt":{"description":"CreatedAt is when the company row was seeded, in unix milliseconds.","type":"integer"},"id":{"description":"ID is the company id, which is the tenant's own org id.","type":"string"},"incorporationCountry":{"description":"IncorporationCountry is the ISO country the entity is incorporated in.","type":"string"},"incorporationState":{"description":"IncorporationState is the state or province of incorporation.","type":"string"},"incorporationType":{"description":"IncorporationType is the entity kind, e.g. LLC or C_CORP.","type":"string"},"name":{"description":"Name is the company's legal name.","type":"string"},"publicId":{"description":"PublicID is the company's shareable public identifier.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the company row last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"captableCompanyUpdate":{"properties":{"incorporationCountry":{"description":"IncorporationCountry is the ISO country the entity is incorporated in.\nOptional; omitted, null or empty clears it. Any JSON scalar is accepted\nand stored as its text."},"incorporationState":{"description":"IncorporationState is the state or province of incorporation. Optional;\nomitted, null or empty clears it. Any JSON scalar is accepted and stored\nas its text."},"incorporationType":{"description":"IncorporationType is the entity kind, e.g. LLC or C_CORP. Optional;\nomitted, null or empty clears it. Any JSON scalar is accepted and stored\nas its text."},"name":{"description":"Name is the company's legal name. Required, and it must be a non-empty\nstring — anything else is refused with the cap table's own validation\nerror."}},"type":"object"},"captableConvertibles":{"properties":{"notes":{"$ref":"#/components/schemas/captableInstrumentTotal","description":"Notes is the convertible notes total."},"safes":{"$ref":"#/components/schemas/captableInstrumentTotal","description":"Safes is the SAFEs total."}},"type":"object"},"captableDeleted":{"properties":{"success":{"description":"Success is true when the row was removed.","type":"boolean"}},"type":"object"},"captableEquityPlan":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is the ISO date the board approved the plan.","type":"string"},"comments":{"description":"Comments is free-form notes on the plan.","type":"string"},"createdAt":{"description":"CreatedAt is when the plan was recorded, in unix milliseconds.","type":"integer"},"defaultCancellatonBehavior":{"description":"DefaultCancellatonBehavior is what happens to cancelled grants, RETIRE or\nRETURN_TO_POOL. The key is spelled as the cap-table wire spells it.","type":"string"},"id":{"description":"ID is the equity plan id.","type":"string"},"initialSharesReserved":{"description":"InitialSharesReserved is how many shares the plan reserves.","type":"integer"},"name":{"description":"Name is the plan name, e.g. \"2026 Stock Option Plan\".","type":"string"},"planEffectiveDate":{"description":"PlanEffectiveDate is the ISO date the plan takes effect.","type":"string"},"shareClassId":{"description":"ShareClassID is the class the reserved shares come from.","type":"string"}},"type":"object"},"captableEquityPlans":{"properties":{"data":{"description":"Data is every equity plan on the caller org's cap table, newest first.","items":{"$ref":"#/components/schemas/captableEquityPlan"},"type":"array"}},"type":"object"},"captableHolding":{"properties":{"fullyDiluted":{"description":"FullyDiluted is shares plus options.","type":"integer"},"name":{"description":"Name is the stakeholder's name.","type":"string"},"options":{"description":"Options is the shares under this stakeholder's non-terminal option grants.","type":"integer"},"ownershipPct":{"description":"OwnershipPct is fullyDiluted as a percentage of the company's\nfullyDilutedShares, rounded to two decimals; 0 when nothing is issued.","type":"number"},"shares":{"description":"Shares is the shares this stakeholder holds by certificate.","type":"integer"},"stakeholderId":{"description":"StakeholderID is the stakeholder.","type":"string"}},"type":"object"},"captableInstrumentTotal":{"properties":{"capital":{"description":"Capital is the total capital across those instruments.","type":"number"},"count":{"description":"Count is how many instruments there are.","type":"integer"}},"type":"object"},"captableInvestment":{"properties":{"amount":{"description":"Amount is the cash invested.","type":"number"},"date":{"description":"Date is the ISO date of the investment.","type":"string"},"id":{"description":"ID is the investment id.","type":"string"},"roundId":{"description":"RoundID is the round the cheque went into.","type":"string"},"shareClassId":{"description":"ShareClassID is the class shares were issued in, for a priced round.","type":"string"},"shares":{"description":"Shares is how many shares the investment bought; 0 when the round issues\nno equity at the time of investment.","type":"integer"},"stakeholderId":{"description":"StakeholderID is the investor.","type":"string"},"stakeholderName":{"description":"StakeholderName is that investor's name.","type":"string"}},"type":"object"},"captableInvestments":{"properties":{"data":{"description":"Data is every investment across every round, newest first.","items":{"$ref":"#/components/schemas/captableInvestment"},"type":"array"}},"type":"object"},"captableNote":{"properties":{"capital":{"description":"Capital is the principal the investor lent.","type":"number"},"conversionCap":{"description":"ConversionCap is the valuation cap on conversion, if any.","type":"number"},"discountRate":{"description":"DiscountRate is the discount to the next round's price, if any.","type":"number"},"id":{"description":"ID is the note id.","type":"string"},"interestRate":{"description":"InterestRate is the annual interest rate, if any.","type":"number"},"issueDate":{"description":"IssueDate is the ISO date the note was signed.","type":"string"},"publicId":{"description":"PublicID is the note's shareable identifier, unique within the company.","type":"string"},"stakeholderId":{"description":"StakeholderID is the investor.","type":"string"},"stakeholderName":{"description":"StakeholderName is that investor's name.","type":"string"},"status":{"description":"Status is the note's state, e.g. DRAFT or ACTIVE.","type":"string"},"type":{"description":"Type is the instrument kind, e.g. NOTE.","type":"string"}},"type":"object"},"captableNotes":{"properties":{"data":{"description":"Data is every convertible note, newest first.","items":{"$ref":"#/components/schemas/captableNote"},"type":"array"}},"type":"object"},"captableOption":{"properties":{"cliffYears":{"description":"CliffYears is how many years before any of the grant vests.","type":"integer"},"equityPlanId":{"description":"EquityPlanID is the plan the grant draws from.","type":"string"},"equityPlanName":{"description":"EquityPlanName is that plan's name.","type":"string"},"exercisePrice":{"description":"ExercisePrice is the strike price per share.","type":"number"},"expirationDate":{"description":"ExpirationDate is the ISO date the grant expires.","type":"string"},"grantId":{"description":"GrantID is the grant number, unique within the company.","type":"string"},"id":{"description":"ID is the option id.","type":"string"},"issueDate":{"description":"IssueDate is the ISO date the grant was issued.","type":"string"},"quantity":{"description":"Quantity is how many shares the grant covers.","type":"integer"},"stakeholderId":{"description":"StakeholderID is the grantee.","type":"string"},"stakeholderName":{"description":"StakeholderName is that grantee's name.","type":"string"},"status":{"description":"Status is the grant's state, e.g. DRAFT, ACTIVE, EXERCISED, EXPIRED or\nCANCELLED. Only non-terminal grants dilute the cap table.","type":"string"},"type":{"description":"Type is the grant kind, ISO or NSO.","type":"string"},"vestingYears":{"description":"VestingYears is the total vesting period in years.","type":"integer"}},"type":"object"},"captableOptions":{"properties":{"data":{"description":"Data is every option grant, newest first.","items":{"$ref":"#/components/schemas/captableOption"},"type":"array"}},"type":"object"},"captableRound":{"properties":{"closeDate":{"description":"CloseDate is the ISO date the round closed, once it has.","type":"string"},"createdAt":{"description":"CreatedAt is when the round was recorded, in unix milliseconds.","type":"integer"},"id":{"description":"ID is the round id.","type":"string"},"name":{"description":"Name is the round name, e.g. \"Series A\".","type":"string"},"preMoneyValuation":{"description":"PreMoneyValuation is the pre-money valuation, for a priced round.","type":"number"},"pricePerShare":{"description":"PricePerShare is the price per share, for a priced round.","type":"number"},"raisedAmount":{"description":"RaisedAmount is how much has been invested so far.","type":"number"},"roundType":{"description":"RoundType is PRICED, SAFE or CONVERTIBLE_NOTE.","type":"string"},"shareClassId":{"description":"ShareClassID is the class a priced round issues into.","type":"string"},"status":{"description":"Status is OPEN or CLOSED.","type":"string"},"targetAmount":{"description":"TargetAmount is how much the round set out to raise.","type":"number"}},"type":"object"},"captableRoundCloseRequest":{"properties":{"closeDate":{"description":"CloseDate is the date to record the round as closed on. Optional: omitted,\nnull or empty records TODAY. Any JSON scalar is accepted and stored as its\ntext, and the text is stored unparsed, so a caller that wants an ISO date\nsends one."},"id":{"description":"ID is the round to close. It is the path segment: the URL is the\naddressing authority, and the org it is resolved in comes from the\ncaller's principal, so an id from another tenant is simply not found.","type":"string"}},"type":"object"},"captableRoundDetail":{"properties":{"investments":{"description":"Investments is every investment into this round, oldest first.","items":{"$ref":"#/components/schemas/captableRoundInvestment"},"type":"array"},"round":{"$ref":"#/components/schemas/captableRound","description":"Round is the round itself."}},"type":"object"},"captableRoundInvestment":{"properties":{"amount":{"description":"Amount is the cash invested.","type":"number"},"comments":{"description":"Comments is the note recorded with the cheque, if any.","type":"string"},"date":{"description":"Date is the ISO date of the investment.","type":"string"},"id":{"description":"ID is the investment id.","type":"string"},"shares":{"description":"Shares is how many shares the investment bought; 0 when the round issues no\nequity at the time of investment.","type":"integer"},"stakeholderId":{"description":"StakeholderID is the investor.","type":"string"},"stakeholderName":{"description":"StakeholderName is that investor's name.","type":"string"}},"type":"object"},"captableRoundTotals":{"properties":{"count":{"description":"Count is how many rounds the company has recorded.","type":"integer"},"totalRaised":{"description":"TotalRaised is the sum of every round's raised amount.","type":"number"}},"type":"object"},"captableRounds":{"properties":{"data":{"description":"Data is every round, newest first.","items":{"$ref":"#/components/schemas/captableRound"},"type":"array"}},"type":"object"},"captableSafe":{"properties":{"capital":{"description":"Capital is the cash the investor put in.","type":"number"},"discountRate":{"description":"DiscountRate is the discount to the next round's price, if any.","type":"number"},"id":{"description":"ID is the SAFE id.","type":"string"},"issueDate":{"description":"IssueDate is the ISO date the SAFE was signed.","type":"string"},"mfn":{"description":"MFN is true when the SAFE carries a most-favoured-nation clause.","type":"boolean"},"proRata":{"description":"ProRata is true when the SAFE carries pro-rata rights.","type":"boolean"},"publicId":{"description":"PublicID is the SAFE's shareable identifier, unique within the company.","type":"string"},"stakeholderId":{"description":"StakeholderID is the investor.","type":"string"},"stakeholderName":{"description":"StakeholderName is that investor's name.","type":"string"},"status":{"description":"Status is the SAFE's state, e.g. DRAFT or ACTIVE.","type":"string"},"type":{"description":"Type is POST_MONEY or PRE_MONEY.","type":"string"},"valuationCap":{"description":"ValuationCap is the valuation cap, if any.","type":"number"}},"type":"object"},"captableSafes":{"properties":{"data":{"description":"Data is every SAFE, newest first.","items":{"$ref":"#/components/schemas/captableSafe"},"type":"array"}},"type":"object"},"captableShare":{"properties":{"capitalContribution":{"description":"CapitalContribution is the cash paid for the certificate, if recorded.","type":"number"},"certificateId":{"description":"CertificateID is the certificate number, unique within the company.","type":"string"},"companyLegends":{"description":"CompanyLegends are the restrictive legends printed on the certificate.","items":{"type":"string"},"type":"array"},"id":{"description":"ID is the share id.","type":"string"},"issueDate":{"description":"IssueDate is the ISO date the certificate was issued.","type":"string"},"pricePerShare":{"description":"PricePerShare is the price paid per share, if recorded.","type":"number"},"quantity":{"description":"Quantity is how many shares the certificate covers.","type":"integer"},"shareClassId":{"description":"ShareClassID is the class the shares belong to.","type":"string"},"shareClassName":{"description":"ShareClassName is that class's name.","type":"string"},"shareClassType":{"description":"ShareClassType is that class's type, COMMON or PREFERRED.","type":"string"},"stakeholderId":{"description":"StakeholderID is the holder of the certificate.","type":"string"},"stakeholderName":{"description":"StakeholderName is that holder's name.","type":"string"},"status":{"description":"Status is ACTIVE or DRAFT.","type":"string"}},"type":"object"},"captableShareClass":{"properties":{"classType":{"description":"ClassType is COMMON or PREFERRED.","type":"string"},"companyName":{"description":"CompanyName is the name of the company whose cap table this is.","type":"string"},"conversionRights":{"description":"ConversionRights describes what the class converts into, e.g.\nCONVERTS_TO_FUTURE_ROUND.","type":"string"},"id":{"description":"ID is the share class id.","type":"string"},"idx":{"description":"Idx is the class's 1-based position within the company, in creation order.","type":"integer"},"initialSharesAuthorized":{"description":"InitialSharesAuthorized is how many shares of this class are authorized.","type":"integer"},"liquidationPreferenceMultiple":{"description":"LiquidationPreferenceMultiple is the preference multiple on liquidation.","type":"number"},"name":{"description":"Name is the class name, e.g. \"Common\" or \"Series A Preferred\".","type":"string"},"parValue":{"description":"ParValue is the par value per share.","type":"number"},"participationCapMultiple":{"description":"ParticipationCapMultiple caps participation on liquidation; 0 is uncapped.","type":"number"},"prefix":{"description":"Prefix is the certificate prefix, CS for common and PS for preferred.","type":"string"},"pricePerShare":{"description":"PricePerShare is the issue price per share.","type":"number"},"seniority":{"description":"Seniority orders classes in a liquidation waterfall; higher is more senior.","type":"integer"},"votesPerShare":{"description":"VotesPerShare is how many votes one share of this class carries.","type":"integer"}},"type":"object"},"captableShares":{"properties":{"data":{"description":"Data is every issued share certificate, newest first.","items":{"$ref":"#/components/schemas/captableShare"},"type":"array"}},"type":"object"},"captableStakeholder":{"properties":{"city":{"description":"City is the stakeholder's city, if recorded.","type":"string"},"companyName":{"description":"CompanyName is the name of the company whose cap table this is.","type":"string"},"country":{"description":"Country is the stakeholder's two-letter country code.","type":"string"},"createdAt":{"description":"CreatedAt is when the stakeholder was added, in unix milliseconds.","type":"integer"},"currentRelationship":{"description":"CurrentRelationship is how the stakeholder relates to the company, e.g.\nFOUNDER, INVESTOR or EMPLOYEE.","type":"string"},"email":{"description":"Email is the stakeholder's email, unique within the company.","type":"string"},"id":{"description":"ID is the stakeholder id.","type":"string"},"institutionName":{"description":"InstitutionName names the institution, when the stakeholder is one.","type":"string"},"name":{"description":"Name is the stakeholder's full name.","type":"string"},"stakeholderType":{"description":"StakeholderType is INDIVIDUAL or INSTITUTION.","type":"string"},"state":{"description":"State is the stakeholder's state or province, if recorded.","type":"string"},"streetAddress":{"description":"StreetAddress is the stakeholder's street address, if recorded.","type":"string"},"taxId":{"description":"TaxID is the stakeholder's tax identifier, if recorded.","type":"string"},"zipcode":{"description":"Zipcode is the stakeholder's postal code, if recorded.","type":"string"}},"type":"object"},"captableStakeholderPatch":{"properties":{"city":{"description":"City is the stakeholder's city."},"currentRelationship":{"description":"CurrentRelationship is how the stakeholder relates to the company, e.g.\nFOUNDER, INVESTOR or EMPLOYEE. This route stores it as sent — unlike\nadding a stakeholder, it is not checked against the vocabulary."},"email":{"description":"Email is the stakeholder's email. This route stores it as sent — unlike\nadding a stakeholder, it is not checked for shape or uniqueness."},"id":{"description":"ID is the stakeholder to update. It is the path segment: the URL is the\naddressing authority, and the org it is resolved in comes from the\ncaller's principal, so an id from another tenant is simply not found.","type":"string"},"institutionName":{"description":"InstitutionName names the institution, when the stakeholder is one."},"name":{"description":"Name is the stakeholder's full name."},"stakeholderType":{"description":"StakeholderType is INDIVIDUAL or INSTITUTION. This route stores it as\nsent — unlike adding a stakeholder, it is not checked against the\nvocabulary."},"state":{"description":"State is the stakeholder's state or province."},"streetAddress":{"description":"StreetAddress is the stakeholder's street address."},"taxId":{"description":"TaxID is the stakeholder's tax identifier."},"zipcode":{"description":"Zipcode is the stakeholder's postal code."}},"type":"object"},"captableSummary":{"properties":{"byShareClass":{"description":"ByShareClass is each share class's authorized-versus-issued position, in\nclass creation order.","items":{"$ref":"#/components/schemas/captableClassHolding"},"type":"array"},"byStakeholder":{"description":"ByStakeholder is each stakeholder's position, largest holding first.","items":{"$ref":"#/components/schemas/captableHolding"},"type":"array"},"company":{"$ref":"#/components/schemas/captableSummaryCompany","description":"Company names the company the cap table is computed for."},"convertibles":{"$ref":"#/components/schemas/captableConvertibles","description":"Convertibles is the capital on SAFEs and notes that have not converted."},"rounds":{"$ref":"#/components/schemas/captableRoundTotals","description":"Rounds is the fundraising rollup."},"totals":{"$ref":"#/components/schemas/captableTotals","description":"Totals is the company-wide share count."}},"type":"object"},"captableSummaryCompany":{"properties":{"id":{"description":"ID is the company id, which is the tenant's own org id.","type":"string"},"name":{"description":"Name is the company's legal name.","type":"string"}},"type":"object"},"captableTotals":{"properties":{"fullyDilutedShares":{"description":"FullyDilutedShares is outstandingShares plus grantedOptions.","type":"integer"},"grantedOptions":{"description":"GrantedOptions is the shares under non-terminal option grants — grants that\nare EXERCISED, EXPIRED or CANCELLED are excluded, so nothing double-counts.","type":"integer"},"outstandingShares":{"description":"OutstandingShares is the sum of every issued share certificate.","type":"integer"},"shareClasses":{"description":"ShareClasses is how many share classes the company has authorized.","type":"integer"},"stakeholders":{"description":"Stakeholders is how many stakeholders the company has.","type":"integer"}},"type":"object"},"captableUpdated":{"properties":{"message":{"description":"Message is the human sentence the cap table wrote, e.g. \"Company updated\".","type":"string"},"success":{"description":"Success is true when the update was applied.","type":"boolean"}},"type":"object"},"capturedError":{"properties":{"distinctId":{"description":"DistinctID is the person/visitor the error is attributed to. Omitted when the\nrow carries none.","type":"string"},"event":{"description":"Event is the event name the error was stored under, e.g. $error.","type":"string"},"exception":{"description":"Exception is the captured error itself — the {type, message, stack, handled}\nobject, already redacted at the fold point. Omitted when the row has none."},"id":{"description":"ID is the row's stable event id — the client's own idempotency id when it sent\none, else the server-minted one.","type":"string"},"library":{"description":"Library is the client SDK that reported the error. Omitted when absent.","type":"string"},"libraryVersion":{"description":"LibraryVer is that SDK's version. Omitted when absent.","type":"string"},"path":{"description":"Path is the URL's path component. Omitted when absent.","type":"string"},"product":{"description":"Product is the surface that emitted the error. Omitted when absent.","type":"string"},"properties":{"description":"Properties is the row's whole property bag, returned verbatim as stored — any\nJSON object, $exception included. Omitted when the row carries none or it did\nnot parse."},"sessionId":{"description":"SessionID groups the events of one visit. Omitted when the client sent none.","type":"string"},"timestamp":{"description":"Timestamp is when the error was captured, RFC3339 UTC.","type":"string"},"url":{"description":"URL is the full page address the error fired on. Omitted when absent.","type":"string"}},"type":"object"},"catalogEntry":{"properties":{"configured":{"type":"boolean"},"description":{"type":"string"},"displayName":{"type":"string"},"kind":{"description":"\"native\" | \"piece\"","type":"string"},"provider":{"type":"string"}},"type":"object"},"catalogList":{"properties":{"data":{"description":"Data is every starter prompt, each importable as-is with POST /v1/prompts.","items":{"$ref":"#/components/schemas/CatalogEntry"},"type":"array"}},"type":"object"},"catalogOut":{"properties":{"connectors":{"description":"Connectors is every connectable source, sorted by provider.","items":{"$ref":"#/components/schemas/catalogEntry"},"type":"array"}},"type":"object"},"catalogPage":{"properties":{"data":{"description":"Data is the page of matching entries, most recently updated first.","items":{"$ref":"#/components/schemas/Entry"},"type":"array"},"facets":{"additionalProperties":{"additionalProperties":{"type":"integer"},"type":"object"},"description":"Facets counts the whole matching set along every browse axis, so a rail a\nclient renders is a rail that has results behind it. Keyed axis → value → count.","type":"object"},"total":{"description":"Total is how many entries matched BEFORE paging — what a pager sizes itself on.","type":"integer"}},"type":"object"},"challengeView":{"properties":{"expiresAt":{"description":"ExpiresAt is when the nonce stops being redeemable, as a Unix timestamp.","type":"integer"},"message":{"description":"Message is the EXACT text to personal_sign. It is reconstructed server-side\nfrom the validated org, the slot and the nonce at redemption, so signing\nanything else cannot claim the slot.","type":"string"},"nonce":{"description":"Nonce is the single-use, org-bound challenge value to send back with the\nsignature.","type":"string"},"tokenId":{"description":"TokenID is the slot the challenge was issued for.","type":"integer"},"ttlSeconds":{"description":"TTLSeconds is the challenge lifetime in seconds.","type":"integer"}},"type":"object"},"channelAdd":{"properties":{"account":{"description":"Account is the provider account this channel runs under: an ad-account, a\npage, or a mailing-list id.","type":"string"},"id":{"description":"ID is the campaign to add the channel to, from the path.","type":"string"},"kind":{"description":"Kind is the channel kind and the identity a campaign holds at most one of:\npaid, organic or email.","type":"string"},"platform":{"description":"Platform is the provider within the kind — meta, google, x, instagram, or\nthe email provider.","type":"string"}},"type":"object"},"channelList":{"properties":{"data":{"description":"Data is every social channel the caller's org has connected, disabled ones\nincluded (Disabled says which).","items":{"$ref":"#/components/schemas/Channel"},"type":"array"}},"type":"object"},"channelView":{"properties":{"account":{"description":"Account is the id-shaped fact about that connection: the lowercased external\nid integrations custodies for it — a Discord guild id, a Slack team\n(workspace) id, a Teams AAD tenant id, or the Telegram chat the org bound.\nEmpty when not connected. Informational: the access policy keys on\n(org, channel), so exactly one account is representable per pair.","type":"string"},"accountLabel":{"description":"AccountLabel is the human label of that same account — the Discord guild\nname, the Slack team name, the Teams tenant name (falling back to the tenant\nid), the Telegram chat title. DISPLAY ONLY: never a key, and never swapped\nwith Account, on any surface.","type":"string"},"capabilities":{"$ref":"#/components/schemas/capabilities","description":"Capabilities is what this transport renders natively — read it before\ncomposing a message that needs threading, media or interactive actions."},"connected":{"description":"Connected is whether integrations holds a connection for (this org, this\ntransport) — whether someone finished its connect flow. False leaves Account\nand AccountLabel empty, and a send is then refused downstream rather than\nhere: by the transport's own binding check (403 for a Telegram chat this org\nhas not bound, 409 for a Discord or Teams room with no inbound-learned\nroute), or on Slack by the absent per-org bot token, which surfaces as 502.","type":"boolean"},"dmPolicy":{"description":"DMPolicy is how this org admits direct messages here: \"pairing\", \"allowlist\"\nor \"open\", defaulting to \"pairing\" when the org has never set one.","type":"string"},"groupPolicy":{"description":"GroupPolicy is how this org admits group and thread rooms here: \"open\",\n\"allowlist\" or \"disabled\", defaulting to \"open\". Both policy fields come\nback EMPTY — rather than the listing failing — when the policy cannot be\nread; GET /v1/channels/allowlist carries the same two with the entries they\nconsult.","type":"string"},"id":{"description":"ID is the fixed transport identifier — discord, slack, teams or telegram —\nand the value every route on this surface names a channel by, including the\n`:channel` segment of the send path. The listing is always in that order.","type":"string"},"pendingPairing":{"description":"PendingPairing counts the org's UNEXPIRED pairing requests on this channel:\nexactly the rows GET /v1/channels/pairing returns for it, one per person\nwaiting on an admin. It never exceeds three — the pending cap per\n(org, channel) — and expired requests are not counted.","type":"integer"}},"type":"object"},"chatChannels":{"properties":{"channels":{"description":"Channels is every chat transport this deployment supports, in a fixed\norder, each carrying whether the org has connected it, the account behind\nthe connection, what the transport can do, the org's DM/group access\npolicies for it, and how many pairing requests are waiting.","items":{"$ref":"#/components/schemas/channelView"},"type":"array"}},"type":"object"},"chatRequest":{"properties":{"message":{"description":"Message is the founder's question for the Business AI. Required; trimmed,\nand clipped to 4 KiB so a caller cannot amplify the AI prompt.","type":"string"}},"type":"object"},"chatResponse":{"properties":{"funnel":{"$ref":"#/components/schemas/Funnel","description":"Funnel is the org's trailing-window traffic → signups → orders."},"reply":{"description":"Reply is the coach's answer, grounded only in the quests and funnel below.\nWhen no AI plane is reachable it is the deterministic reply naming the top\nreal quest — never silence, never invention.","type":"string"},"suggestions":{"description":"Suggestions are the current candidate quests, ranked best-first.","items":{"$ref":"#/components/schemas/suggestion"},"type":"array"}},"type":"object"},"checkList":{"properties":{"data":{"description":"Data is the org's verifications, newest first, without subject PII.","items":{"$ref":"#/components/schemas/checkView"},"type":"array"},"disclaimer":{"description":"Disclaimer states that statuses are provider-reported, never a platform\nassertion of legal or regulatory compliance.","type":"string"}},"type":"object"},"checkView":{"properties":{"createdAt":{"description":"CreatedAt is the unix second the verification was started.","type":"integer"},"decidedAt":{"description":"DecidedAt is the unix second a terminal status was recorded.","type":"integer"},"decidedBy":{"description":"DecidedBy records who settled a terminal status: the provider name, or a\nreviewer's user id for a recorded manual decision.","type":"string"},"id":{"description":"ID is the verification's opaque id.","type":"string"},"kind":{"description":"Kind is the party type: \"individual\" (KYC) or \"business\" (KYB).","type":"string"},"provider":{"description":"Provider is the verification provider this check runs through.","type":"string"},"status":{"description":"Status is the check's state: pending, provider_verified, provider_rejected,\nmanual_review, or expired (provider-reported), or reviewer_confirmed — the\none value a privileged human reviewer records, never a provider.","type":"string"},"subjectId":{"description":"SubjectID is the opaque id of the subject under verification.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second the verification last changed.","type":"integer"},"verifyUrl":{"description":"VerifyURL is the provider's hosted verification flow for the subject, when one exists.","type":"string"}},"type":"object"},"claim":{"properties":{"created":{"description":"Created reports whether this call recorded a new claim (201) or found an\nexisting one (200).","type":"boolean"},"org":{"$ref":"#/components/schemas/orgView","description":"Org is the verified owner-wide claim, present when an owner was claimed. It\ncovers every repository the author publishes under that owner."},"repo":{"$ref":"#/components/schemas/authorRepo","description":"Repo is the verified repository claim, present when a repository was claimed."}},"type":"object"},"claimKeyOut":{"properties":{"claimKey":{"description":"ClaimKey is the capability itself. It is returned ONCE and never again — only\nits SHA-256 hash is stored — so a daemon that loses it mints a new one.","type":"string"},"targetId":{"description":"TargetID is the machine the key authenticates.","type":"string"}},"type":"object"},"claimRequest":{"properties":{"code":{"description":"Code is the referrer's referral code, as it appeared in their ?ref= link.\nCase and surrounding whitespace do not matter.","type":"string"}},"type":"object"},"claimView":{"properties":{"code":{"description":"Code is the referral code the referral was recorded against.","type":"string"},"created":{"description":"Created is true when this call recorded the referral and false when it found\none already recorded for this referee — the idempotent replay.","type":"boolean"},"createdAt":{"description":"CreatedAt is when the referral was first recorded, as a Unix timestamp.","type":"integer"},"id":{"description":"ID is the referral's handle.","type":"string"},"status":{"description":"Status is the referral's lifecycle state: \"signup\" until the referee\nmakes metered spend, then \"qualified\", then \"credited\".","type":"string"}},"type":"object"},"clickCount":{"properties":{"counted":{"description":"Counted says the in-memory buffer took the ping. It does NOT say the code\nexists — this is deliberately not a code-existence oracle, and an unknown code\nsimply no-ops at flush time. false means the buffer was full and the ping was\ndropped, which is harmless: clicks are vanity and move no money.","type":"boolean"}},"type":"object"},"clickRequest":{"properties":{"code":{"description":"Code is the share-link code that was clicked. Body-only: the URL cannot\nsupply it.","type":"string"}},"type":"object"},"cloudAccountView":{"properties":{"account":{"description":"Account is the provider's human label for it, e.g. the DigitalOcean team\nemail.","type":"string"},"clusters":{"description":"Clusters is the fleet names this account currently owns. Unlinking detaches\nexactly these and nothing else.","items":{"type":"string"},"type":"array"},"externalId":{"description":"ExternalID is the provider's own identifier for the account — a\nDigitalOcean account uuid, an AWS account id, a GCP project.","type":"string"},"label":{"description":"Label is the org-chosen name for this account within the provider, which is\nhow a second account at the same provider is addressed. Defaults to\n\"default\".","type":"string"},"linkedAt":{"description":"LinkedAt is when the account was first linked, RFC3339 UTC. Re-linking the\nsame label keeps the original.","type":"string"},"project":{"description":"Project is the fleet shard the account's clusters were folded into,\nrecorded at link time so a later sync or unlink acts on the same shard.","type":"string"},"provider":{"description":"Provider is the cloud the account belongs to: digitalocean, aws, gcp or\nazure.","type":"string"},"syncedAt":{"description":"SyncedAt is when it was last discovered, RFC3339 UTC.","type":"string"}},"type":"object"},"cloudAccountsView":{"properties":{"accounts":{"description":"Accounts is every account this org has linked, across all providers. Empty\nwhen it has linked none.","items":{"$ref":"#/components/schemas/cloudAccountView"},"type":"array"}},"type":"object"},"clusterAttach":{"properties":{"default":{"description":"Default marks this the org's default cluster for scheduling.","type":"boolean"},"kubeconfig":{"description":"Kubeconfig is the cluster's kubeconfig, verbatim. Required — a body without\none is not an attach.","type":"string"},"name":{"description":"Name is the fleet-local name for the cluster; lower-cased, and the key the\ndetach route addresses it by. Required.","type":"string"},"provider":{"description":"Provider is a free-form label for where the cluster runs (\"gke\", \"on-prem\");\nit is display only, not a routing key.","type":"string"}},"type":"object"},"clusterDetached":{"properties":{"detached":{"description":"Detached is the lower-cased fleet name that was removed.","type":"string"}},"type":"object"},"clusterDetailView":{"properties":{"amdGpu":{"type":"integer"},"createdAt":{"type":"string"},"doClusterId":{"type":"string"},"doksClusterId":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"nodeCount":{"type":"integer"},"nodePools":{"items":{"$ref":"#/components/schemas/nodePoolView"},"type":"array"},"nodeSize":{"type":"string"},"nodes":{"items":{"$ref":"#/components/schemas/machineView"},"type":"array"},"nvidiaGpu":{"type":"integer"},"region":{"type":"string"},"status":{"type":"string"}},"type":"object"},"clusterList":{"properties":{"clusters":{"description":"Clusters is the merged fleet — kind \"managed\" for Visor-provisioned, \"byo\"\nfor an attached kubeconfig.","items":{"$ref":"#/components/schemas/clusterView"},"type":"array"},"degraded":{"description":"Degraded names any source that did not answer, so an empty Clusters means\n\"you have none\" only when this is absent. Omitted when everything answered,\nso a healthy response is unchanged. See degraded.go.","items":{"$ref":"#/components/schemas/sourceFailure"},"type":"array"}},"type":"object"},"clusterResult":{"properties":{"amdGpu":{"description":"AmdGPU is how many AMD GPUs those nodes advertise.","type":"integer"},"cluster":{"description":"Cluster is the stable fleet name the cluster was folded under — the name\n/v1/clusters shows and a workload targets.","type":"string"},"error":{"description":"Error is why this cluster did not fold — a billing denial, an unsafe\nkubeconfig, or an unreachable apiserver. It never contains credential\nmaterial.","type":"string"},"folded":{"description":"Folded is whether it reached the fleet. False means Error says why, and\nthis cluster alone was skipped.","type":"boolean"},"nodes":{"description":"Nodes is how many nodes the fleet counted in it.","type":"integer"},"nvidiaGpu":{"description":"NvidiaGPU is how many NVIDIA GPUs those nodes advertise.","type":"integer"},"region":{"description":"Region is the provider region it runs in.","type":"string"},"source":{"description":"Source is the cluster's own name at the provider.","type":"string"}},"type":"object"},"clusterView":{"properties":{"amdGpu":{"type":"integer"},"createdAt":{"type":"string"},"doClusterId":{"type":"string"},"doksClusterId":{"type":"string"},"kind":{"description":"Fleet fields (additive): \"managed\" (Visor-provisioned) vs \"byo\" (attached\nkubeconfig), and the live GPU inventory a BYO cluster reports.","type":"string"},"name":{"type":"string"},"nodeCount":{"type":"integer"},"nodePools":{"items":{"$ref":"#/components/schemas/nodePoolView"},"type":"array"},"nodeSize":{"type":"string"},"nvidiaGpu":{"type":"integer"},"region":{"type":"string"},"status":{"type":"string"}},"type":"object"},"codeView":{"properties":{"clicks":{"description":"Clicks is how many pings this code has taken. The one STORED counter here and\npure vanity: no accrual or payout reads it, pings are coalesced in memory and\nflushed in batches, and a dropped tally is accepted rather than contending\nwith the money write path. Do not reconcile it against anything.","type":"integer"},"code":{"description":"Code is the link's slug — 3–32 chars of a–z, 0–9 and hyphen — unique across\nthe WHOLE directory, so any affiliate's code resolves an attribution.","type":"string"},"conversions":{"description":"Conversions is how many of those signups have actually produced positive\ncommission for the caller. Also derived, from the accrual rows, so it is\n≤ signups and lags a referral until the first sweep after it spends.","type":"integer"},"createdAt":{"description":"CreatedAt is when the link was minted, Unix seconds UTC.","type":"integer"},"label":{"description":"Label is the caller's own note for the link (\"twitter\", \"newsletter\").\nCosmetic: trimmed, stripped of control characters, capped at 48 bytes, and\nnever part of the code. \"primary\" on the link mirrored at approval.","type":"string"},"signups":{"description":"Signups is how many orgs were attributed with this code — DERIVED by counting\nattribution edges, never stored, so it cannot drift from the ledger.","type":"integer"},"url":{"description":"URL is the full shareable link, the brand host plus ?aff=\u003ccode\u003e. The host is\nthe deployment's own brand, so a Lux or Zoo install never mints a hanzo.ai\nlink.","type":"string"}},"type":"object"},"collabPayload":{"properties":{"content":{"additionalProperties":{"type":"string"},"description":"Content maps a document field to its ProseMirror markup JSON.","type":"object"},"source":{"description":"Source is the blob ref a getContent reads the snapshot from. Absent means\nthere is no snapshot to read, which answers empty content.","type":"string"},"updates":{"additionalProperties":{"type":"string"},"description":"Updates carries, per field, a base64 Y.js state update encoding the SAME\nmarkup — the front computes it (markupToYDoc → encodeStateAsUpdate) so a\ncreateContent seeds the live-editing lane's update log, not just the\nsnapshot blob. Without it a dialog-created description is invisible in the\ncollaborative editor, which replays the ydoc log, never the snapshot.","type":"object"}},"type":"object"},"collabRequest":{"properties":{"documentId":{"description":"DocumentID addresses the document field, as\n\"\u003cworkspaceUuid\u003e|\u003cobjectClass\u003e|\u003cobjectId\u003e|\u003cobjectAttr\u003e\" — the\ncollaborator-client encodeDocumentId shape, from the path.","type":"string"},"method":{"description":"Method is the verb: createContent, updateContent or getContent.","type":"string"},"payload":{"$ref":"#/components/schemas/collabPayload","description":"Payload is the verb's argument."}},"type":"object"},"collabResult":{"properties":{"content":{"additionalProperties":{"type":"string"},"description":"Content maps each document field to its value for the verb: the new blob\nref after a createContent, the stored markup after a getContent.","type":"object"},"error":{"description":"Error carries a SEMANTIC refusal, which this RPC reports under 200 because\nthe client throws on result.error — auth and tenancy failures are HTTP\nstatuses instead.","type":"string"}},"type":"object"},"commitJSON":{"properties":{"authorEmail":{"description":"AuthorEmail is the commit author's email.","type":"string"},"authorName":{"description":"AuthorName is the commit author's name.","type":"string"},"date":{"description":"Date is the author date, RFC 3339 UTC.","type":"string"},"message":{"description":"Message is the commit's SUBJECT — its first line only.","type":"string"},"sha":{"description":"SHA is the full commit hash.","type":"string"},"shortSha":{"description":"ShortSHA is the abbreviated hash a UI displays.","type":"string"}},"type":"object"},"commitsJSON":{"properties":{"commits":{"description":"Commits are newest first.","items":{"$ref":"#/components/schemas/commitJSON"},"type":"array"}},"type":"object"},"companyList":{"properties":{"data":{"description":"Data is the page of companies, most recently updated first.","items":{"$ref":"#/components/schemas/Company"},"type":"array"}},"type":"object"},"companyReq":{"properties":{"arr":{"description":"ARR is annual recurring revenue in minor units (cents) of Currency.","type":"integer"},"city":{"description":"City is the head-office city.","type":"string"},"country":{"description":"Country is the head-office country.","type":"string"},"currency":{"description":"Currency is the ISO code ARR is denominated in; empty defaults to USD.","type":"string"},"domainName":{"description":"DomainName is the company's primary domain, e.g. \"acme.com\".","type":"string"},"employees":{"description":"Employees is the headcount.","type":"integer"},"id":{"description":"ID names the company to update and comes from the path. A create ignores\nit: the server mints the id.","type":"string"},"idealCustomerProfile":{"description":"ICP marks the company as an ideal-customer-profile fit.","type":"boolean"},"linkedinLink":{"description":"Linkedin is the company's LinkedIn URL.","type":"string"},"name":{"description":"Name is the company name. Required.","type":"string"},"xLink":{"description":"XLink is the company's X (Twitter) URL.","type":"string"}},"type":"object"},"computeLeaf":{"properties":{"active":{"type":"integer"},"app":{"type":"string"},"kind":{"type":"string"},"lastTs":{"type":"string"},"machines":{"type":"integer"},"org":{"type":"string"},"project":{"type":"string"},"spendCents":{"type":"integer"}},"type":"object"},"computeOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/computeLeaf"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"connView":{"properties":{"account":{"description":"Account is the provider's label for the connected account.","type":"string"},"connectedAt":{"description":"ConnectedAt is when the connector was last (re)established, RFC 3339 UTC.","type":"string"},"expiresAt":{"description":"ExpiresAt is when the access token expires, RFC 3339 UTC; empty for a\nnon-expiring credential. Reading the token auto-rotates inside the window.","type":"string"},"externalId":{"description":"ExternalID is the provider's own id for that account.","type":"string"},"id":{"description":"ID is provider + \":\" + label — what every other connector route addresses.","type":"string"},"label":{"description":"Label is the caller's name for this connection (\"default\", \"work\").","type":"string"},"provider":{"description":"Provider is the user-scoped provider's registry id.","type":"string"},"scopes":{"description":"Scopes are the permissions the credential carries. Never null; [] when none.","items":{"type":"string"},"type":"array"}},"type":"object"},"connectIn":{"properties":{"accountId":{"description":"AccountID is the provider account the credential should be scoped to, for the\nproviders whose Verify needs one (Cloudflare). Ignored by the OAuth path.","type":"string"},"provider":{"description":"Provider is the connector's registry id, from the :provider path segment.","type":"string"},"token":{"description":"Token is the customer's provider credential. Its PRESENCE — not its value —\nis what selects the apikey seal over the OAuth flow for a provider that\noffers both: {\"token\":\"…\"}, even empty, is an apikey attempt (→ verify, which\nanswers the \"token required\" 400 on an empty value), while a body with no\ntoken key (the console Connect button, `hanzo connector add` with no --token)\nstarts OAuth. Read on STDIN by the CLI, never argv; never logged or echoed.","type":"string"}},"type":"object"},"connectOut":{"properties":{"account":{"description":"Account is the account label the provider reported for the credential.\napikey path only; a pointer because \"\" is a real answer the provider gave.","type":"string"},"authorizeUrl":{"description":"AuthorizeURL is the provider consent URL to send the user to. OAuth path only.","type":"string"},"connected":{"description":"Connected is true on the apikey path once the credential verified and sealed.","type":"boolean"},"externalId":{"description":"ExternalID is the provider's account id for the credential. apikey path only.","type":"string"},"provider":{"description":"Provider is the connector's registry id. apikey path only.","type":"string"},"scopes":{"description":"Scopes are the permissions the credential carries. apikey path only; never\nnull on that path ([] when the provider reported none).","items":{"type":"string"},"type":"array"}},"type":"object"},"connectRequest":{"properties":{"githubLogin":{"description":"GithubLogin is the account to link. Used only when IAM holds no linked\naccount for the provider — a linked account is stronger proof and always wins.","type":"string"},"login":{"description":"Login is the provider-neutral alias for GithubLogin, preferred when both are\nsent.","type":"string"},"provider":{"description":"Provider is the forge to enrol with: github (the default) or gitlab.","type":"string"}},"type":"object"},"connectionOut":{"properties":{"account":{"description":"Account names the connected external account, when the provider reports one.","type":"string"},"provider":{"description":"Provider is the connector this answer is about.","type":"string"},"status":{"description":"Status is the connection state: connected or disconnected.","type":"string"}},"type":"object"},"connectionView":{"properties":{"account":{"description":"Account is the human label of the connected third-party account (the Slack\nteam name, the GitHub org login). Provider-supplied and sanitized on ingest.","type":"string"},"connectedAt":{"description":"ConnectedAt is when the connection was last (re)established, RFC 3339 UTC.","type":"string"},"externalId":{"description":"ExternalID is the provider's own id for the account (Slack team.id, GitHub\ninstallation_id) — the value inbound webhooks are mapped back to this org by.","type":"string"},"scopes":{"description":"Scopes are the permissions the provider granted. Never null; [] when none.","items":{"type":"string"},"type":"array"}},"type":"object"},"connectorProviderView":{"properties":{"category":{"description":"Category groups the card.","type":"string"},"description":{"description":"Description is the one-line pitch the console card shows.","type":"string"},"id":{"description":"ID is the provider's registry id and the :provider path segment.","type":"string"},"methods":{"description":"Methods are the intake paths this provider supports, derived from its\ncapabilities: \"device\", \"oauth\" (adopt an externally obtained bundle) and\n\"token\" (a customer-held credential). At least one, always.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the provider's display name.","type":"string"},"scopes":{"description":"Scopes are the permissions a connection will ask for. Never null.","items":{"type":"string"},"type":"array"}},"type":"object"},"connectorProvidersOut":{"properties":{"providers":{"description":"Providers is every USER-scoped provider, sorted by id. Never null.","items":{"$ref":"#/components/schemas/connectorProviderView"},"type":"array"}},"type":"object"},"connectorTokenOut":{"properties":{"expiresAt":{"description":"ExpiresAt is when this token expires, RFC 3339 UTC; empty if non-expiring.","type":"string"},"label":{"description":"Label is the connector's label.","type":"string"},"provider":{"description":"Provider is the connector's provider id.","type":"string"},"token":{"description":"Token is the access token, rotated first if it was within the refresh window.","type":"string"}},"type":"object"},"connectorView":{"properties":{"account":{"description":"Account names the connected external account. Absent until the org connects.","type":"string"},"configured":{"description":"Configured is true when this deployment holds OAuth credentials for the provider.","type":"boolean"},"docCount":{"description":"DocCount is the live count of this provider's documents in the org's store.","type":"integer"},"error":{"description":"Error is the last sync failure, if any. Absent until the org connects.","type":"string"},"kind":{"description":"Kind is \"native\" for a first-party Go connector, \"piece\" for a long-tail one.","type":"string"},"lastSync":{"description":"LastSync is when the last pull finished. Absent until the org connects.","type":"string"},"provider":{"description":"Provider is the connector's id.","type":"string"},"status":{"description":"Status is connected, disconnected, syncing or error.","type":"string"}},"type":"object"},"connectorsOut":{"properties":{"connectors":{"description":"Connectors is the caller's own set. Never null; [] when they have none.","items":{"$ref":"#/components/schemas/connView"},"type":"array"}},"type":"object"},"consoleSettings":{"properties":{"appsInAnyNamespaceEnabled":{"description":"AppsInAnyNamespaceEnabled is false: applications are projected from operator\nApp CRs in the platform namespaces, never declared in an arbitrary one.","type":"boolean"},"dexConfig":{"description":"DexConfig carries no connectors. This console does not run Dex; its sign-in is\nGET /v1/deploy/login, which is an IAM authorization-code round trip.","properties":{"connectors":{"items":{"$ref":"#/components/schemas/deployEmpty"},"type":"array"}},"type":"object"},"execEnabled":{"description":"ExecEnabled is false: this plane serves no container terminal.","type":"boolean"},"googleAnalytics":{"description":"GoogleAnalytics carries no tracking id and anonymizes users — this console\nreports no analytics.","properties":{"anonymizeUsers":{"type":"boolean"},"trackingID":{"type":"string"}},"type":"object"},"help":{"description":"Help carries no chat link and no binary download URLs.","properties":{"binaryUrls":{"additionalProperties":{"type":"string"},"type":"object"},"chatText":{"type":"string"},"chatUrl":{"type":"string"}},"type":"object"},"hydratorEnabled":{"description":"HydratorEnabled is false: there is no manifest hydrator on this plane.","type":"boolean"},"kustomizeVersions":{"description":"KustomizeVersions is always empty: an App CR is an image pin, not a kustomize\nbuild.","items":{"type":"string"},"type":"array"},"oidcConfig":{"$ref":"#/components/schemas/deployEmpty","description":"OidcConfig is always null. The SPA's own OIDC flow is deliberately not\nconfigured — identity is owned by Hanzo IAM at the edge and minted for this\nhost by GET /v1/deploy/login."},"plugins":{"description":"Plugins is always empty: this plane loads no argocd config-management plugins.","items":{"$ref":"#/components/schemas/deployEmpty"},"type":"array"},"statusBadgeEnabled":{"description":"StatusBadgeEnabled is false: no badge endpoint is served.","type":"boolean"},"statusBadgeRootUrl":{"description":"StatusBadgeRootUrl is always empty, for the same reason.","type":"string"},"syncWithReplaceAllowed":{"description":"SyncWithReplaceAllowed is false: a sync here asks the operator to reconcile an\nApp CR, and never replaces an object.","type":"boolean"},"uiBannerContent":{"description":"UiBannerContent is always empty: this console shows no banner.","type":"string"},"uiCssURL":{"description":"UiCssURL is always empty: no stylesheet is injected.","type":"string"},"url":{"description":"Url is the console's public origin, https://cd.hanzo.ai.","type":"string"},"userLoginsDisabled":{"description":"UserLoginsDisabled is true: the SPA must not render its own username/password\nform. Signing in goes through IAM, at GET /v1/deploy/login.","type":"boolean"}},"type":"object"},"consumerPage":{"properties":{"data":{"description":"Data are the stream's consumers.","items":{"$ref":"#/components/schemas/consumerRecord"},"type":"array"}},"type":"object"},"consumerRecord":{"properties":{"ack":{"description":"Ack is the acknowledgement discipline: explicit, none or all.","type":"string"},"ackWait":{"description":"AckWait is the redelivery timeout in seconds.","type":"integer"},"acked":{"description":"Acked is the stream sequence acknowledged furthest.","type":"integer"},"deliver":{"description":"Deliver is the starting point: all, last, new or lastPerSubject.","type":"string"},"delivered":{"description":"Delivered is the stream sequence delivered furthest.","type":"integer"},"filter":{"description":"Filter is the subject filter, in the org's namespace; empty means all.","type":"string"},"maxDeliver":{"description":"MaxDeliver is the delivery-attempt cap; -1 means unlimited.","type":"integer"},"name":{"description":"Name is the durable consumer name.","type":"string"},"pending":{"description":"Pending is how many messages await delivery.","type":"integer"},"redelivered":{"description":"Redelivered is how many messages are being redelivered.","type":"integer"},"stream":{"description":"Stream is the stream it consumes, in the org's view.","type":"string"}},"type":"object"},"consumerWrite":{"properties":{"ack":{"type":"string"},"ackWait":{"type":"integer"},"deliver":{"type":"string"},"filter":{"type":"string"},"maxDeliver":{"type":"integer"},"name":{"description":"Name is the durable consumer name: 1–64 of [A-Za-z0-9_-].","type":"string"},"stream":{"description":"Stream is the stream to consume, from the path.","type":"string"}},"type":"object"},"contactList":{"properties":{"data":{"description":"Data is the page of contacts, most recently updated first.","items":{"$ref":"#/components/schemas/Contact"},"type":"array"}},"type":"object"},"contactReq":{"properties":{"city":{"description":"City is where the person is based.","type":"string"},"companyId":{"description":"CompanyID links the contact to one of the org's companies.","type":"string"},"email":{"description":"Email is the person's email address.","type":"string"},"firstName":{"description":"FirstName is the person's given name.","type":"string"},"id":{"description":"ID names the contact to update and comes from the path. A create ignores\nit: the server mints the id.","type":"string"},"jobTitle":{"description":"JobTitle is the person's role at their company.","type":"string"},"lastName":{"description":"LastName is the person's family name.","type":"string"},"linkedinLink":{"description":"Linkedin is the person's LinkedIn URL.","type":"string"},"phone":{"description":"Phone is the person's phone number.","type":"string"},"xLink":{"description":"XLink is the person's X (Twitter) URL.","type":"string"}},"type":"object"},"contextIn":{"properties":{"budgetTokens":{"description":"BudgetTokens caps the bundle's size. Clamped to [256, 32000]; 0 or absent\nuses 4000.","type":"integer"},"query":{"description":"Query is what to retrieve context for. Required, max 4000 bytes.","type":"string"},"repo":{"description":"Repo narrows retrieval to one repository. Empty searches every repo the org\nhas indexed.","type":"string"}},"type":"object"},"controlCommandView":{"properties":{"command":{"type":"string"},"message":{"type":"string"},"payload":{},"seq":{"type":"integer"}},"type":"object"},"controlDrain":{"properties":{"commands":{"description":"Commands is the session's control commands newer than the cursor, oldest first.","items":{"$ref":"#/components/schemas/controlCommandView"},"type":"array"},"cursor":{"description":"Cursor is the seq to send as `after` on the next poll — the highest seq in\nthis page, or the cursor sent in when the page is empty.","type":"integer"}},"type":"object"},"cookieAck":{"properties":{"result":{"description":"Result is true when the cookie was written or cleared.","type":"boolean"}},"type":"object"},"corpusView":{"properties":{"count":{"description":"Count is how many tactics survived every filter.","type":"integer"},"stage":{"description":"Stage is the growth stage the tag join ran at — the org's observed stage, or\nthe one ?stage= previewed.","type":"string"},"strategies":{"description":"Strategies are the surviving tactics, in corpus authoring order.","items":{"$ref":"#/components/schemas/strategyView"},"type":"array"}},"type":"object"},"crawlDocument":{"properties":{"markdown":{"type":"string"},"metadata":{"additionalProperties":{},"type":"object"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"crawlRequest":{"properties":{"url":{"type":"string"}},"type":"object"},"crawlResult":{"properties":{"data":{"$ref":"#/components/schemas/crawlDocument"},"error":{"type":"string"},"success":{"type":"boolean"}},"type":"object"},"createAccountIn":{"properties":{"name":{"description":"Name is the account's label. Required, trimmed; it groups wallets and is\nnot itself a key.","type":"string"}},"type":"object"},"createAgentIn":{"properties":{"computeRef":{"type":"string"},"description":{"type":"string"},"executionMode":{"type":"string"},"instructions":{"type":"string"},"model":{"type":"string"},"name":{"type":"string"},"schedule":{"type":"string"},"serviceAccountId":{"type":"string"},"tools":{"items":{"type":"string"},"type":"array"}},"type":"object"},"createAppReq":{"properties":{"buildType":{"description":"BuildType is `pack` — the zero-config default that detects any project —\nor `dockerfile`, the explicit escape hatch. An image app never builds.","type":"string"},"description":{"description":"Description is free text about what the application is.","type":"string"},"dockerfile":{"description":"Dockerfile is the path to build from, for buildType `dockerfile`.","type":"string"},"domains":{"description":"Domains are extra ingress hosts. The canonical default host is always\nattached; a bare custom host is refused here and must go through\nadd-domain → verify first.","items":{"type":"string"},"type":"array"},"env":{"description":"Env is the application's environment. Keys must match\n`^[A-Za-z_][A-Za-z0-9_]*$`; a variable marked `secret: true` is sealed into\nKMS and its plaintext is never written to the database.","items":{"$ref":"#/components/schemas/EnvVarJSON"},"type":"array"},"environment":{"description":"Environment is the deploy target this app names (\"production\" by default).","type":"string"},"image":{"$ref":"#/components/schemas/imageOrigin","description":"Image is the container image to run, for source `image`."},"name":{"description":"Name is the application's display name. Required; the slug is derived from\nit when none is given.","type":"string"},"port":{"description":"Port is the container port the app listens on.","type":"integer"},"project":{"description":"Project is the project to create the application under, from the path.","type":"string"},"replicas":{"description":"Replicas is how many copies to run; clamped to the deployment's limit\nrather than refused.","type":"integer"},"repo":{"$ref":"#/components/schemas/gitOrigin","description":"Repo is the git source to build from, for source `git`."},"slug":{"description":"Slug is the app's identity in the cluster — its CR name and part of its\nhost. Given or derived from Name, it must match\n`^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`, and one already used in this\nproject is 409.","type":"string"},"source":{"description":"Source is `git`, which requires repo.url, or `image`, which requires\nimage.repository. Anything else is 400.","type":"string"},"storageGb":{"description":"StorageGB is the persistent volume size in GiB; absent means stateless.\nClamped to the deployment's limit rather than refused.","type":"integer"}},"type":"object"},"createClusterReq":{"properties":{"name":{"description":"Name is the cluster's name. Required.","type":"string"},"nodePool":{"properties":{"count":{"type":"integer"},"name":{"type":"string"},"size":{"type":"string"}},"type":"object"},"region":{"description":"Region is the provider region slug (e.g. \"nyc3\"). Required.","type":"string"},"version":{"description":"Version is the Kubernetes version slug; empty takes the provider default.","type":"string"}},"type":"object"},"createEndpointIn":{"properties":{"description":{"description":"Description is a free-text label for the console. Optional, clipped to 1024 bytes.","type":"string"},"events":{"description":"Events are NATS subject patterns to subscribe to (e.g. \"commerce.order.\u003e\").\nAn empty or omitted list means EVERY event on the platform bus. Max 64\npatterns, each max 256 bytes.","items":{"type":"string"},"type":"array"},"status":{"description":"Status is \"active\" or \"disabled\". Empty defaults to active. A disabled\nendpoint receives no bus deliveries, but can still be exercised with\nPOST /v1/webhooks/{id}/test.","type":"string"},"url":{"description":"URL is the https:// address each matching event is POSTed to. Required,\nmax 2048 bytes; http:// and every other scheme is refused, because a\nwebhook carries signed event data and must not travel in the clear.","type":"string"}},"type":"object"},"createFlowReq":{"properties":{"displayName":{"description":"DisplayName names the flow's initial draft version.","type":"string"},"externalId":{"description":"ExternalID is the caller's own id for this flow. Optional.","type":"string"},"folderId":{"description":"FolderID groups the flow in the builder's tree. Optional.","type":"string"},"trigger":{"$ref":"#/components/schemas/FlowTrigger","description":"Trigger is the root of the step tree — how the flow starts, and the action\nchain that follows. Optional: a flow may be created empty and edited later."}},"type":"object"},"createLBReq":{"properties":{"forwarding_rules":{"description":"ForwardingRules are the listen→backend port mappings. Empty defaults to\nplain HTTP 80→80, the same default DigitalOcean's own console applies.","items":{"$ref":"#/components/schemas/fwdRule"},"type":"array"},"name":{"description":"Name is the FRIENDLY name, a DNS-safe slug of at most 40 characters. The\nphysical DigitalOcean name is derived from it and the caller's org.","type":"string"},"region":{"description":"Region is the DigitalOcean region slug (nyc3, sfo3, …). Required.","type":"string"},"size":{"description":"Size is the DigitalOcean size slug. Empty takes DO's default.","type":"string"},"type":{"description":"Type is the DigitalOcean load-balancer type. Empty takes DO's default (REGIONAL).","type":"string"}},"type":"object"},"createLinkRequest":{"properties":{"code":{"description":"Code is an optional vanity code; it must be free across the whole\ndirectory, and omitting it mints a random one. Body-only.","type":"string"},"label":{"description":"Label is cosmetic — trimmed, stripped of control characters, capped — and\nnever part of a code. Body-only: the URL cannot supply it.","type":"string"}},"type":"object"},"createReq":{"properties":{"description":{"description":"Description is a free-form blurb, max 4KiB.","type":"string"},"name":{"description":"Name is the repo's handle, unique within the scope, and the last segment of\nboth clone URLs. Must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$; a trailing\n\".git\" is stripped first. Required.","type":"string"},"project":{"description":"Project narrows the repo to a sub-scope of the org. Omit it to use the\ncaller's own X-Project-Id scope; it can never widen past the caller's org.","type":"string"},"public":{"description":"Public grants ANONYMOUS read (fetch) only; push and the whole control plane\nstay org-authed. Defaults to false.","type":"boolean"}},"type":"object"},"createServerReq":{"properties":{"authHeader":{"description":"AuthHeader is the request header the credential is injected into, e.g.\n\"Authorization\". Empty means the server needs no credential.","type":"string"},"listing":{"description":"Listing enables a CATALOG entry instead — the id from GET /v1/tools/catalog.\nThe endpoint is the listing's own streamable-http remote, so a listing that\nonly ships a stdio package is refused: there is nothing to reach yet.","type":"string"},"name":{"description":"Name labels the server for the org. Required with URL; with Listing it\ndefaults to the listing's own title.","type":"string"},"secret":{"description":"Secret is the credential VALUE. It is sealed into KMS under a per-org ref\nand never stored in SQLite, never listed, and never returned.","type":"string"},"url":{"description":"URL is the server's JSON-RPC endpoint. It must be an http(s) URL naming a\nPUBLIC host: loopback, link-local, private and cloud-metadata addresses are\nrefused here and again when the dialer connects.","type":"string"}},"type":"object"},"createVPCReq":{"properties":{"ip_range":{"description":"IPRange is the VPC's private CIDR. Empty lets DigitalOcean assign one.","type":"string"},"name":{"description":"Name is the FRIENDLY name, a DNS-safe slug of at most 40 characters. The\nphysical DigitalOcean name is derived from it and the caller's org.","type":"string"},"region":{"description":"Region is the DigitalOcean region slug (nyc3, sfo3, …). Required.","type":"string"}},"type":"object"},"createVersionIn":{"properties":{"displayName":{"description":"DisplayName names the new version.","type":"string"},"id":{"description":"ID is the flow to add a version to, from the path.","type":"string"},"trigger":{"$ref":"#/components/schemas/FlowTrigger","description":"Trigger is the root of the version's step tree. Optional: a version with no\ntrigger is created invalid, and cannot run until one is set."}},"type":"object"},"createWalletIn":{"properties":{"accountId":{"description":"AccountID is the account this wallet belongs to. Required, and it must be\nan account of the caller's own org — an unknown one is a 404.","type":"string"},"agent":{"description":"Agent optionally narrows the wallet to one agent within the org. It becomes\na segment of the key ref, so it must be a url-safe segment with no slash.","type":"string"},"chain":{"description":"Chain is the EVM chain this wallet is for, as \"eip155:\u003cn\u003e\" or a bare\ndecimal chain id. Optional; a Safe defaults to the Hanzo L1 (36963).","type":"string"},"custody":{"description":"Custody selects the signing backend: \"kms\" (in-process, always available),\n\"mpc\" or \"treasury\" (the deployed MPC ring), or \"safe\" (a Safe smart wallet\nowned by an MPC key). Empty uses the deployment's default. A backend that is\nnot configured fails CLOSED with 503 rather than fabricating a signature.","type":"string"},"name":{"description":"Name is the wallet's display label. Optional.","type":"string"},"tier":{"description":"Tier is the MPC wallet tier: hot, warm, cold, gas, bridge, contract_admin,\nvalidator, quarantine or disaster_recovery. Empty defaults to hot.","type":"string"}},"type":"object"},"credentialIn":{"properties":{"accountId":{"description":"AccountID scopes the credential where the provider's Verify needs one.","type":"string"},"label":{"description":"Label names this connection; empty means \"default\".","type":"string"},"oauth":{"$ref":"#/components/schemas/oauthBundleIn","description":"OAuth is a bundle the CLI already obtained through its own local PKCE flow.\nPresent ⇒ the Adopt path; absent ⇒ the Token path."},"provider":{"description":"Provider is the user-scoped provider's registry id, from the path.","type":"string"},"token":{"description":"Token is the customer-held credential for the Verify path. Read on STDIN by\nthe CLI, never argv; never logged, echoed, or stored outside KMS.","type":"string"}},"type":"object"},"credentialOut":{"properties":{"connected":{"description":"Connected is always true — a failed verification is a 400 and stores nothing.","type":"boolean"},"connector":{"$ref":"#/components/schemas/connView","description":"Connector is the connector as it now stands."}},"type":"object"},"crmSummary":{"properties":{"companies":{"description":"Companies is how many companies the org has.","type":"integer"},"contacts":{"description":"Contacts is how many contacts the org has.","type":"integer"},"opportunities":{"description":"Opportunities is how many opportunities the org has.","type":"integer"}},"type":"object"},"csrfResp":{"properties":{"csrfToken":{"description":"Token is the value to send back in the X-CSRF-Token header. It is bound to the\ncaller's identity, so it authorizes writes as them and as nobody else.","type":"string"},"expiresIn":{"description":"ExpiresIn is the token's lifetime in seconds. Fetch a new one when it lapses;\na write with an expired token is refused.","type":"integer"}},"type":"object"},"curateReq":{"properties":{"featured":{"description":"Featured puts the listing on the front of the shelf, or takes it off.","type":"boolean"},"hidden":{"description":"Hidden takes the listing off the org-visible shelf, or puts it back.","type":"boolean"},"id":{"description":"ID is the listing to curate, from the path.","type":"string"},"logo":{"description":"Logo is the brand mark to render, an https URL. Empty clears ours and lets\nthe next sync adopt the publisher's own icon again.","type":"string"},"official":{"description":"Official overrides the derivation: setting it makes this answer FINAL, so\nno later sync re-derives over it. That is the difference between a default\nand a decision — the derivation can only tell that a domain-verified\npublisher serves the endpoint, not that the product is theirs.","type":"boolean"}},"type":"object"},"curriculumView":{"properties":{"curriculum":{"$ref":"#/components/schemas/Curriculum","description":"Curriculum is the enabled journey: its version, title and ordered steps."},"custom":{"description":"Custom is true when the org's OWN curriculum override is active; false when\nthe journey comes from the brand blueprint or the embedded fixture.","type":"boolean"}},"type":"object"},"dashResp":{"properties":{"account":{"description":"Account is the linked account that was asked about, when one was named.","type":"string"},"available":{"description":"Available is false when the warehouse could not be read. That means \"no\nanswer\", NOT \"no usage\" — the two lists below are then empty for a reason.","type":"boolean"},"current":{"description":"Current is the newest window instance of each lane — the dash headline.","items":{"$ref":"#/components/schemas/usageWindowView"},"type":"array"},"from":{"description":"From is the inclusive start of that window, RFC3339 UTC.","type":"string"},"provider":{"description":"Provider is the upstream that was asked about, echoed back.","type":"string"},"range":{"description":"Range is the window that was served: 1h, 24h, 7d or 30d.","type":"string"},"scope":{"description":"Scope says whose rows these are: the caller's own linked accounts.","type":"string"},"source":{"description":"Source names the meter of record — the provider's own login, not Hanzo.","type":"string"},"to":{"description":"To is the exclusive end of that window, RFC3339 UTC.","type":"string"},"windows":{"description":"Windows is every instance in range, newest first — the history behind it.","items":{"$ref":"#/components/schemas/usageWindowView"},"type":"array"}},"type":"object"},"databaseCreateIn":{"properties":{"name":{"description":"Name is the database name to create.","type":"string"}},"type":"object"},"dataroomAddDocument":{"properties":{"documentId":{"description":"DocumentId is the document to attach. Required, and it must already exist\nin the caller's own store — this route attaches, it never uploads."},"id":{"description":"ID is the room to add to. It is the path segment: the URL is the addressing\nauthority, and the org it is resolved in comes from the caller's principal,\nso an id from another tenant is simply not found.","type":"string"},"orderIndex":{"description":"OrderIndex fixes the document's place in the viewer's list. Optional; a\nnumber or a numeric string is accepted, and anything that is not a number\nleaves the document unordered, which sorts it last."}},"type":"object"},"dataroomCreate":{"properties":{"description":{"description":"Description is the room's description. Optional; any JSON scalar is\naccepted and stored as its text, and omitting it leaves the room with none."},"name":{"description":"Name is the room's display name. Required, and a room without one is\nrefused with the data room's own validation error."}},"type":"object"},"dataroomDocument":{"properties":{"contentType":{"description":"ContentType is the mime type recorded at upload, null when none was sent.","type":"string"},"createdAt":{"description":"CreatedAt is when the document was uploaded, in unix milliseconds.","type":"integer"},"fileKey":{"description":"FileKey is the opaque object-storage key the bytes are stored under. It is\nscoped to the tenant's own key prefix and is not a URL.","type":"string"},"fileSize":{"description":"FileSize is the stored byte count, null when it was not recorded.","type":"integer"},"id":{"description":"ID is the document id.","type":"string"},"name":{"description":"Name is the document's display name.","type":"string"},"numPages":{"description":"NumPages is the page count, null when it was not supplied at upload.","type":"integer"},"type":{"description":"Type is the document's kind, null when it was not recorded.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the document row last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"dataroomDocumentOne":{"properties":{"document":{"$ref":"#/components/schemas/dataroomDocument","description":"Document is the document itself."}},"type":"object"},"dataroomDocuments":{"properties":{"documents":{"description":"Documents is every document in the caller's own store, newest first.","items":{"$ref":"#/components/schemas/dataroomDocument"},"type":"array"}},"type":"object"},"dataroomLink":{"properties":{"allowDownload":{"description":"AllowDownload is whether a visitor may download, rather than only view.","type":"boolean"},"allowList":{"description":"AllowList narrows which addresses pass the email gate. An entry may be a\nfull address, an \"@domain.com\" suffix, or a bare \"domain.com\". An EMPTY\nlist admits everyone.","items":{"type":"string"},"type":"array"},"createdAt":{"description":"CreatedAt is when the link was minted, in unix milliseconds.","type":"integer"},"dataroomId":{"description":"DataroomId is the room the link opens, null for a single-document link.","type":"string"},"denyList":{"description":"DenyList rejects addresses, in the same three forms as the allow list, and\nis checked BEFORE it — so deny always wins.","items":{"type":"string"},"type":"array"},"documentId":{"description":"DocumentId is the document the link opens, null for a room link.","type":"string"},"emailProtected":{"description":"EmailProtected is whether a visitor must state an address to enter.","type":"boolean"},"expiresAt":{"description":"ExpiresAt is when the link closes, in unix milliseconds; null never expires.","type":"integer"},"hasPassword":{"description":"HasPassword reports THAT a password is set. The stored form is a bcrypt\nhash and no route returns it.","type":"boolean"},"id":{"description":"ID is the link id — the public token a visitor opens the room with.","type":"string"},"isArchived":{"description":"IsArchived is whether the link has been retired.","type":"boolean"},"linkType":{"description":"LinkType is DATAROOM_LINK or DOCUMENT_LINK.","type":"string"},"name":{"description":"Name is the link's label, null when none was given.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the link last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"dataroomLinkCreate":{"properties":{"allowDownload":{"description":"AllowDownload permits downloading rather than viewing only. Optional and\nOFF by default; any truthy JSON value turns it on."},"allowList":{"description":"AllowList narrows which addresses pass the email gate. Optional; an entry\nmay be a full address (\"ada@example.com\"), an \"@domain.com\" suffix, or a\nbare \"domain.com\". An omitted or EMPTY list admits everyone, so a link with\nno list enforces the email gate alone.","items":{},"type":"array"},"dataroomId":{"description":"DataroomId is the room to share. One of dataroomId or documentId is\nrequired, and the target must exist in the caller's own store."},"denyList":{"description":"DenyList rejects addresses, in the same three forms as the allow list.\nOptional. It is checked BEFORE the allow list, so deny always wins.","items":{},"type":"array"},"documentId":{"description":"DocumentId shares a SINGLE document instead of a room. One of dataroomId or\ndocumentId is required."},"emailProtected":{"description":"EmailProtected makes a visitor state an address before entering. Optional\nand ON by default: it is disabled only by the JSON literal false, so any\nother value — including the string \"false\" — leaves the gate on."},"expiresAt":{"description":"ExpiresAt closes the link, in unix milliseconds. Optional; a number or a\nnumeric string is accepted, and omitting it means the link never expires."},"name":{"description":"Name labels the link. Optional; any JSON scalar is stored as its text."},"password":{"description":"Password gates the link. Optional; it is hashed with bcrypt before storage\nand is never readable back through any route."}},"type":"object"},"dataroomLinkOne":{"properties":{"link":{"$ref":"#/components/schemas/dataroomLink","description":"Link is the link itself, including the id a visitor opens it with."}},"type":"object"},"dataroomLinkStats":{"properties":{"linkId":{"description":"LinkId is the link these counts are for.","type":"string"},"pages":{"description":"Pages is the per-page breakdown, in page order.","items":{"$ref":"#/components/schemas/dataroomPageStat"},"type":"array"},"totalPageViews":{"description":"TotalPageViews is how many page views the link received.","type":"integer"},"totalViews":{"description":"TotalViews is how many viewing sessions the link opened.","type":"integer"}},"type":"object"},"dataroomLinks":{"properties":{"links":{"description":"Links is every non-archived link, newest first.","items":{"$ref":"#/components/schemas/dataroomLink"},"type":"array"}},"type":"object"},"dataroomMember":{"properties":{"contentType":{"description":"ContentType is the mime type recorded at upload, null when none was sent.","type":"string"},"createdAt":{"description":"CreatedAt is when the document was uploaded, in unix milliseconds.","type":"integer"},"dataroomDocumentId":{"description":"DataroomDocumentId is the membership id — this document's place in THIS\nroom, distinct from the document id.","type":"string"},"fileKey":{"description":"FileKey is the opaque object-storage key the bytes are stored under.","type":"string"},"fileSize":{"description":"FileSize is the stored byte count, null when it was not recorded.","type":"integer"},"id":{"description":"ID is the document id.","type":"string"},"name":{"description":"Name is the document's display name.","type":"string"},"numPages":{"description":"NumPages is the page count, null when it was not supplied at upload.","type":"integer"},"orderIndex":{"description":"OrderIndex is the document's place in the viewer's list, null when it was\nadded without one. Unordered documents sort last.","type":"integer"},"type":{"description":"Type is the document's kind, null when it was not recorded.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the document row last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"dataroomMembership":{"properties":{"dataroomDocumentId":{"description":"DataroomDocumentId is the new membership id.","type":"string"},"dataroomId":{"description":"DataroomId is the room the document was added to.","type":"string"},"documentId":{"description":"DocumentId is the document that was added.","type":"string"}},"type":"object"},"dataroomPageStat":{"properties":{"avgDuration":{"description":"AvgDuration is totalDuration divided by views, rounded; 0 when unviewed.","type":"integer"},"pageNumber":{"description":"PageNumber is the page these counts are for.","type":"integer"},"totalDuration":{"description":"TotalDuration is the summed dwell measure reported for the page.","type":"integer"},"views":{"description":"Views is how many times the page was viewed.","type":"integer"}},"type":"object"},"dataroomRoom":{"properties":{"createdAt":{"description":"CreatedAt is when the room was created, in unix milliseconds.","type":"integer"},"description":{"description":"Description is the room's description, null when none was given.","type":"string"},"id":{"description":"ID is the room id, which is what other dataroom calls address it by.","type":"string"},"name":{"description":"Name is the room's display name.","type":"string"},"pId":{"description":"PId is the room's short public identifier, unique within the tenant.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the room last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"dataroomRoomDetail":{"properties":{"createdAt":{"description":"CreatedAt is when the room was created, in unix milliseconds.","type":"integer"},"description":{"description":"Description is the room's description, null when none was given.","type":"string"},"documents":{"description":"Documents is every document in the room, in the order a visitor sees them.","items":{"$ref":"#/components/schemas/dataroomMember"},"type":"array"},"id":{"description":"ID is the room id.","type":"string"},"name":{"description":"Name is the room's display name.","type":"string"},"pId":{"description":"PId is the room's short public identifier.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the room last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"dataroomRoomDetailOne":{"properties":{"dataroom":{"$ref":"#/components/schemas/dataroomRoomDetail","description":"Dataroom is the room and its contents."}},"type":"object"},"dataroomRoomOne":{"properties":{"dataroom":{"$ref":"#/components/schemas/dataroomRoom","description":"Dataroom is the room itself."}},"type":"object"},"dataroomRooms":{"properties":{"datarooms":{"description":"Datarooms is every data room in the caller's own store, newest first.","items":{"$ref":"#/components/schemas/dataroomRoom"},"type":"array"}},"type":"object"},"dataroomStats":{"properties":{"dataroomId":{"description":"DataroomId is the room these counts are for.","type":"string"},"links":{"description":"Links is the same per-page breakdown for each link into the room.","items":{"$ref":"#/components/schemas/dataroomLinkStats"},"type":"array"},"totalPageViews":{"description":"TotalPageViews is the room's page views across every link.","type":"integer"},"totalViews":{"description":"TotalViews is the room's viewing sessions across every link.","type":"integer"}},"type":"object"},"datastoreVolume":{"properties":{"mount":{"type":"string"},"name":{"type":"string"},"pct":{"type":"number"},"sizeGiB":{"type":"integer"},"usedGiB":{"type":"number"}},"type":"object"},"dayCount":{"properties":{"count":{"description":"Count is that day's total.","type":"integer"},"date":{"description":"Date is the day, YYYY-MM-DD.","type":"string"}},"type":"object"},"decisionIn":{"properties":{"email":{"description":"Email identifies the founder on the formation.","type":"string"},"status":{"description":"Status is the decision: reviewer_confirmed or failed. Nothing else is accepted.","type":"string"}},"type":"object"},"deckOut":{"properties":{"documentId":{"type":"string"}},"type":"object"},"defsOut":{"properties":{"data":{"description":"Data is every definition in the caller's (org, project) store, by key.","items":{"$ref":"#/components/schemas/DefRow"},"type":"array"}},"type":"object"},"deletedOut":{"properties":{"deleted":{"description":"Deleted is the key that no longer exists.","type":"string"}},"type":"object"},"deliveryList":{"properties":{"data":{"description":"Data is the matching attempts, newest first.","items":{"$ref":"#/components/schemas/DeliveryRow"},"type":"array"}},"type":"object"},"deployEmpty":{"properties":{},"type":"object"},"deployLogs":{"properties":{"deploymentId":{"description":"DeploymentID is the deployment these logs belong to.","type":"string"},"logs":{"description":"Logs is the recorded status timeline followed by the streamed pod output,\nnewline-separated.","type":"string"},"source":{"description":"Source says which pod the log body carries — `build`, `app`, or `none` when\nneither pod was reachable — so a console can label the pane honestly.","type":"string"}},"type":"object"},"deployRecord":{"properties":{"created":{"description":"Created reports whether this call recorded a new attribution edge (201) or\nfound an existing one (200). Absent when nothing was recorded.","type":"boolean"},"createdAt":{"description":"CreatedAt is when the edge was first recorded, in unix seconds. Absent when\nnothing was recorded.","type":"integer"},"deployId":{"description":"DeployID is the attribution edge's handle. Absent when nothing was recorded.","type":"string"},"reason":{"description":"Reason says why nothing was attributed. Present only when recorded is false.","type":"string"},"recorded":{"description":"Recorded reports whether the deploy was attributed to an author at all. False\nis the ordinary answer for a project built from no repository, or from one no\nauthor has verified — never an error, so a deploy path can fire this\nunconditionally.","type":"boolean"},"self":{"description":"Self reports that the deploying org IS the author's org. Such a deploy is\nrecorded for provenance but excluded from accrual. Absent when nothing was\nrecorded.","type":"boolean"}},"type":"object"},"deployReq":{"properties":{"app":{"description":"App is the application's slug, from the path.","type":"string"},"commit":{"description":"Commit is the git commit or ref to build, for a git-source app. Defaults to\nthe app's branch.","type":"string"},"project":{"description":"Project is the project the application lives under, from the path.","type":"string"},"tag":{"description":"Tag is the image tag to deploy, for an image-source app. Defaults to the\napp's tag, then `latest`.","type":"string"}},"type":"object"},"deployRequest":{"properties":{"project":{"description":"Project is the deployed project's id. Required.","type":"string"},"repoUrl":{"description":"RepoURL is the source repository the project was built from. Empty means a\nhand-built project with nothing to attribute — an honest no-op, not an error.","type":"string"}},"type":"object"},"deploymentView":{"properties":{"applicationId":{"type":"string"},"buildId":{"type":"string"},"commit":{"type":"string"},"createdAt":{"type":"integer"},"id":{"type":"string"},"image":{"type":"string"},"message":{"type":"string"},"org":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"integer"},"version":{"type":"integer"}},"type":"object"},"destinationDisconnected":{"properties":{"disconnected":{"description":"Disconnected is true when the credentials and the row are gone.","type":"boolean"}},"type":"object"},"destinationList":{"properties":{"destinations":{"description":"Destinations is one card per registered platform, in slug order.","items":{"$ref":"#/components/schemas/DestinationStatus"},"type":"array"}},"type":"object"},"destinationTest":{"properties":{"error":{"description":"Error is the platform's rejection, present only on a failed send.","type":"string"},"message":{"description":"Message is the platform's own note about the send, present only on success.","type":"string"},"ok":{"description":"OK is true when the platform accepted the synthetic event.","type":"boolean"},"sent":{"description":"Sent is how many events the platform accepted, present only on success.","type":"integer"}},"type":"object"},"devicePollOut":{"properties":{"connector":{"$ref":"#/components/schemas/connView","description":"Connector is the connected connector. Present only on \"connected\"."},"interval":{"description":"Interval is the seconds to wait before the next poll. Present only while\npending, and it may rise when the provider asks the client to slow down.","type":"integer"},"status":{"description":"Status is the flow's state. \"pending\" means poll again after Interval.","type":"string"}},"type":"object"},"deviceStartIn":{"properties":{"label":{"description":"Label names this connection so one user can hold several per provider\n(\"work\", \"personal\"). Empty means \"default\". 1-64 of [A-Za-z0-9._-].","type":"string"},"provider":{"description":"Provider is the user-scoped provider's registry id, from the path.","type":"string"}},"type":"object"},"deviceStartOut":{"properties":{"expiresAt":{"description":"ExpiresAt is when the flow dies, RFC 3339 UTC.","type":"string"},"flow":{"description":"Flow is the id to poll with.","type":"string"},"interval":{"description":"Interval is the seconds to wait between polls.","type":"integer"},"userCode":{"description":"UserCode is the short code the user types at VerifyURL.","type":"string"},"verifyUrl":{"description":"VerifyURL is the page the user opens to enter UserCode.","type":"string"}},"type":"object"},"deviceView":{"properties":{"accounts":{"description":"Accounts is every account the caller has signed in on this machine.","items":{"$ref":"#/components/schemas/linkView"},"type":"array"},"activeSessions":{"description":"ActiveSessions is how many agent sessions the caller currently has running\non this machine; 0 where the agent plane is not mounted.","type":"integer"},"host":{"description":"Host is the machine's hostname label, from its most-recently-seen account.","type":"string"},"lastSeen":{"description":"LastSeen is when any account on this machine last reported, RFC 3339 UTC.","type":"string"},"machine":{"description":"Machine is the stable machine identifier.","type":"string"},"os":{"description":"OS is the machine's operating system label.","type":"string"}},"type":"object"},"digitalocean.Snapshot":{"properties":{"ID":{"type":"string"},"Name":{"type":"string"},"SizeGiB":{"type":"integer"}},"type":"object"},"directoryData":{"properties":{"affiliates":{"description":"Affiliates is one row per affiliate across the whole fleet, ORG EXPOSED,\noldest first and bounded by the request's limit.","items":{"$ref":"#/components/schemas/adminAffiliateView"},"type":"array"},"summary":{"$ref":"#/components/schemas/totals","description":"Summary tallies exactly the rows above — not the whole table — so a limit that\ntruncates the page truncates the tally with it."}},"type":"object"},"directoryOut":{"properties":{"data":{"$ref":"#/components/schemas/directoryData","description":"Data is the affiliate directory and its tally."},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"disbursal":{"properties":{"amountCents":{"description":"AmountCents is the payout, integer cents; it must be positive and can\nnever exceed the affiliate's pending commission. Body-only (`url:\"-\"`,\nlike every money field here): a payout must never ride the URL into\naccess logs, and the raw handler read only the body.","type":"integer"},"id":{"description":"ID is the affiliate to pay, from the path.","type":"string"},"method":{"description":"Method decides whether money moves: `credits` issues a commerce grant,\nevery other method (wire, paypal, …) is record-only.","type":"string"},"reference":{"description":"Reference is the operator's settlement note (a bank id, a ledger ref).","type":"string"}},"type":"object"},"disconnectOut":{"properties":{"disconnected":{"description":"Disconnected is always true — the org's secrets and connection row are gone.","type":"boolean"}},"type":"object"},"docTypeList":{"properties":{"data":{"description":"Data is every DocType defined in the caller's org.","items":{"$ref":"#/components/schemas/DocType"},"type":"array"}},"type":"object"},"documentList":{"properties":{"data":{"description":"Data is the matching documents, newest-updated first unless order_by said\notherwise, each projected to the requested fields plus the envelope keys.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"documentPage":{"properties":{"data":{"description":"Data are the documents, WITHOUT their rendered content — fetch one to read it.","items":{"$ref":"#/components/schemas/documentSummary"},"type":"array"},"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire.","type":"string"}},"type":"object"},"documentReply":{"properties":{"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire.","type":"string"},"document":{"$ref":"#/components/schemas/documentView","description":"Document is the document, rendered content included."}},"type":"object"},"documentSummary":{"properties":{"category":{"description":"Category is the template's category: formation, equity, ops or sales.","type":"string"},"createdAt":{"description":"CreatedAt is when the document was generated, in unix seconds.","type":"integer"},"esignProvider":{"description":"EsignProvider names the e-signature provider handling it, absent until a\nsignature has been requested.","type":"string"},"id":{"description":"ID is the document's server-minted handle, \"doc_\"-prefixed.","type":"string"},"signedAt":{"description":"SignedAt is when the provider reported completion, in unix seconds. Absent\nuntil then.","type":"integer"},"status":{"description":"Status is the lifecycle state: draft, out_for_signature, signed or voided.\nThere is deliberately no \"legally valid\" state — that is counsel's\ndetermination, not the platform's.","type":"string"},"templateId":{"description":"TemplateID is the template it was rendered from.","type":"string"},"templateVersion":{"description":"TemplateVersion is WHICH version of that template rendered it, so the\ndocument is reproducible and auditable.","type":"integer"},"title":{"description":"Title is the document's title, inherited from the template.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed, in unix seconds.","type":"integer"}},"type":"object"},"documentView":{"properties":{"body":{"description":"Body is the rendered document. It is sealed at rest and returned only to the\nowning org. When the template is counsel-review it opens with the counsel\nnotice, which the engine prepends and no caller can suppress.","type":"string"},"category":{"type":"string"},"contentType":{"description":"ContentType is the rendered body's media type — text/markdown.","type":"string"},"createdAt":{"type":"integer"},"esignProvider":{"type":"string"},"id":{"type":"string"},"signedAt":{"type":"integer"},"status":{"type":"string"},"templateId":{"type":"string"},"templateVersion":{"type":"integer"},"title":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"domainAddIn":{"properties":{"name":{"description":"Name is the custom domain to attach, e.g. \"www.acme.com\".","type":"string"},"project":{"description":"Project is the Pages project name, from the path.","type":"string"}},"type":"object"},"domainView":{"properties":{"createdAt":{"description":"CreatedAt is the unix second the custom claim was made.","type":"integer"},"detail":{"description":"Detail says why a claim is still pending, in the resolver's own words.","type":"string"},"host":{"description":"Host is the hostname itself.","type":"string"},"kind":{"description":"Kind is `default`, `subtree` or `custom` — how the org came to own it.","type":"string"},"primary":{"description":"Primary marks the app's permanent default host.","type":"boolean"},"records":{"description":"Records are the DNS records to publish while a custom claim is pending.","items":{"$ref":"#/components/schemas/Record"},"type":"array"},"status":{"description":"Status is `live`, `provisioning`, `pending_deploy` or `pending`, derived\nfrom the operator CR and never fabricated.","type":"string"},"url":{"description":"URL is the host as an HTTPS address.","type":"string"},"verified":{"description":"Verified is whether ownership is settled — always true for a host the org\nstructurally owns.","type":"boolean"}},"type":"object"},"driftBoard":{"properties":{"apps":{"description":"Apps are the service rows, ordered by org, then app, then env.","items":{"$ref":"#/components/schemas/AppView"},"type":"array"},"summary":{"$ref":"#/components/schemas/fleetSummary","description":"Summary counts the board by drift severity."}},"type":"object"},"driftTally":{"properties":{"ok":{"description":"OK is how many rows run what they declare.","type":"integer"},"red":{"description":"Red is how many have drifted badly.","type":"integer"},"yellow":{"description":"Yellow is how many have drifted within tolerance.","type":"integer"}},"type":"object"},"embedStatusResp":{"properties":{"app":{"description":"App is the app this verdict is about.","type":"string"},"embedUrl":{"description":"EmbedURL is the in-app landing URL to frame. Empty when the caller is not\nentitled — a non-entitled caller never receives it.","type":"string"},"entitled":{"description":"Entitled is whether the caller's org may frame this brand-owned app.","type":"boolean"},"origin":{"description":"Origin is the app's origin on this deployment's own brand domain.","type":"string"},"phase":{"description":"Phase is the verdict in one word: not-entitled, not-provisioned or ready.","type":"string"},"reachable":{"description":"Reachable is whether the app answered the liveness probe.","type":"boolean"}},"type":"object"},"enableResp":{"properties":{"accountToken":{"description":"AccountToken is the org's own tunnel-account credential. Treat it as a secret:\nit is what the CLI enables an environment with.","type":"string"},"controller":{"description":"Controller is the public controller endpoint the CLI enables against.","type":"string"},"namespace":{"description":"Namespace is the public frontend a share is published into, when the\ndeployment names one.","type":"string"},"urlTemplate":{"description":"URLTemplate is the shape a share token expands to, so the CLI can print the\nresulting URL without asking again.","type":"string"}},"type":"object"},"enablementBoard":{"properties":{"betas":{"description":"Betas are the subset of Items the caller's org may still opt into.","items":{"$ref":"#/components/schemas/userEnablementItem"},"type":"array"},"items":{"description":"Items is every managed item, each resolved for the caller's org.","items":{"$ref":"#/components/schemas/userEnablementItem"},"type":"array"},"org":{"description":"Org is the org this view was resolved for; empty for a caller with no\nvalidated principal, who sees only the generally-available items.","type":"string"}},"type":"object"},"enablementOptRef":{"properties":{"id":{"description":"ID is the item within that namespace.","type":"string"},"kind":{"description":"Kind is the item's namespace: \"model\", \"provider\" or \"feature\".","type":"string"}},"type":"object"},"endpointList":{"properties":{"data":{"description":"Data is the org's endpoints, newest first, each with its signing secret\nREDACTED — the secret leaves the server only on create and on rotate.","items":{"$ref":"#/components/schemas/Endpoint"},"type":"array"}},"type":"object"},"endpointReq":{"properties":{"connector":{"description":"Connector names the stored credential to reach this endpoint with.","type":"string"},"locator":{"description":"Locator addresses the resource. For a git source it is the https clone URL on\nthe provider's own host, with no embedded credentials; for a native target it\nis the repository name.","type":"string"},"provider":{"description":"Provider is the platform: github or gitlab for a source; a target defaults to\nthe native Hanzo Git plane.","type":"string"}},"type":"object"},"endpointView":{"properties":{"connector":{"type":"string"},"locator":{"type":"string"},"provider":{"type":"string"}},"type":"object"},"engineAdvertisement":{"properties":{"apis":{"description":"[\"openai\",\"anthropic\"]","items":{"type":"string"},"type":"array"},"models":{"description":"ids from the node's GET /v1/models","items":{"type":"string"},"type":"array"},"status":{"description":"\"ready\" | \"unreachable\"","type":"string"},"url":{"type":"string"}},"type":"object"},"engineStatus":{"properties":{"reachable":{"description":"Reachable is true when the engine answered its health probe.","type":"boolean"},"revision":{"description":"Revision is the engine build's git revision, present only when\nreachable (the server's own build identity — it publishes no semver).","type":"string"}},"type":"object"},"enrollReq":{"properties":{"account":{"description":"Account is the provider-side account identifier.","type":"string"},"host":{"description":"Host is the machine's human hostname label.","type":"string"},"kind":{"description":"Kind decides how the account's inference BILLS and defaults to\nsubscription: a subscription account bills the user's own monthly plan and\nis metered here for visibility only, while an apikey account bills through\ncommerce on the gateway path.","type":"string"},"machine":{"description":"Machine is the stable machine identifier. Required, length-bounded.","type":"string"},"os":{"description":"OS is the machine's operating system label.","type":"string"},"plan":{"description":"Plan is the provider plan label (e.g. \"Claude Max\").","type":"string"},"provider":{"description":"Provider is the AI provider the account belongs to. Required, length-bounded.","type":"string"},"usage":{"description":"Usage is an optional usage snapshot, clamped and re-serialized to known\nfields; omitting it keeps the last good one."}},"type":"object"},"enrolment":{"properties":{"created":{"description":"Created reports whether this call enrolled the org (201) or found an existing\nenrolment (200).","type":"boolean"},"githubLogin":{"description":"GithubLogin is the linked forge account.","type":"string"},"id":{"description":"ID is the author record's server-minted handle, \"aut_\"-prefixed.","type":"string"},"shareBps":{"description":"ShareBps is this author's royalty share in basis points of the spend their\ndeployed work generates.","type":"integer"},"status":{"description":"Status is connected, approved or suspended. Only an approved author earns.","type":"string"},"verified":{"description":"Verified reports whether any repository or owner claim has been proven yet.","type":"boolean"},"verifyCode":{"description":"VerifyCode is this author's stable proof token — the value a repository's\nverify file must carry.","type":"string"},"verifyFile":{"description":"VerifyFile is the repo-root file the file method reads, on the default branch.","type":"string"},"verifySnippet":{"description":"VerifySnippet is that file's exact contents, ready to commit.","type":"string"}},"type":"object"},"entitlementsView":{"properties":{"enabled":{"description":"Enabled is the org's turned-on product ids, sorted. Always an array, never null.","items":{"type":"string"},"type":"array"}},"type":"object"},"environmentBoard":{"properties":{"environments":{"description":"Environments are the org's deploy targets, in first-seen order.","items":{"$ref":"#/components/schemas/environmentRow"},"type":"array"}},"type":"object"},"environmentRow":{"properties":{"id":{"description":"ID is the environment's name, which is also its identity — an environment\nis derived from the apps that target it, so it has no id of its own.","type":"string"},"name":{"description":"Name is the environment's name as an app declared it.","type":"string"},"services":{"description":"Services are the apps that target this environment, by name.","items":{"type":"string"},"type":"array"},"status":{"description":"Status rolls up the real states of this environment's apps: degraded,\nactive, idle or empty.","type":"string"},"type":{"description":"Type buckets the name for display: production, staging, development or\ncustom.","type":"string"},"updatedAt":{"description":"UpdatedAt is when any of them last changed, RFC3339 UTC; empty when unset.","type":"string"}},"type":"object"},"errorList":{"properties":{"data":{"description":"Data is the errors, newest first. Empty rather than absent when there are none.","items":{"$ref":"#/components/schemas/capturedError"},"type":"array"}},"type":"object"},"esignCompleteIn":{"properties":{"signed":{"description":"Signed, when present, overrides what the provider reports — the manual path\nfor a provider whose webhook is not wired. Omit it to take the provider's answer.","type":"boolean"}},"type":"object"},"esignOut":{"properties":{"esignRef":{"description":"EsignRef is the provider's reference for the signature request.","type":"string"},"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's incorporation record with the reference recorded on it."},"provider":{"description":"Provider is the wired e-signature provider's name.","type":"string"}},"type":"object"},"evaluateIn":{"properties":{"distinct_id":{"description":"DistinctID is the identity the flags are evaluated for. Required.","type":"string"},"groups":{"description":"Groups are the group-level properties, keyed by group type index."},"person_properties":{"description":"PersonProperties are the person-level properties conditions match against."}},"type":"object"},"eventList":{"properties":{"data":{"description":"Data is the events, newest first. Empty rather than absent when there are none.","items":{"$ref":"#/components/schemas/productEvent"},"type":"array"}},"type":"object"},"eventView":{"properties":{"actor":{"type":"string"},"createdAt":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"payload":{},"seq":{"type":"integer"},"sessionId":{"type":"string"}},"type":"object"},"experimentsOut":{"properties":{"data":{"description":"Data are the canonical experiment versions.","items":{"$ref":"#/components/schemas/Experiment"},"type":"array"},"total":{"description":"Total is len(data) — the rows in this answer, not the store's history.","type":"integer"}},"type":"object"},"fetchQuery":{"properties":{"batch":{"description":"Batch is the most messages to return. 0 or less means 1; anything above\n100 is clamped to 100.","type":"integer"},"name":{"description":"Name is the consumer, from the path.","type":"string"},"stream":{"description":"Stream is the stream, from the path.","type":"string"},"waitMs":{"description":"WaitMs is how long to wait for the batch to fill before answering with\nwhat arrived. 0 or less means the default of 5000; clamped to 30000.","type":"integer"}},"type":"object"},"fileContent":{"properties":{"content":{"description":"Content is the file's text as the index stored it. It is NOT guaranteed\nbyte-verbatim — the git object plane is the source of record for exact\nbytes, history and blame.","type":"string"},"lang":{"description":"Lang is the detected language.","type":"string"},"path":{"description":"Path echoes the file that was read.","type":"string"},"repo":{"description":"Repo echoes the repository it came from.","type":"string"}},"type":"object"},"fileInput":{"properties":{"content":{"description":"Content is the file's full text. Max 1 MiB per file; binary files should\nsimply be omitted rather than sent.","type":"string"},"path":{"description":"Path is the file's repo-relative path, e.g. \"internal/store/db.go\".","type":"string"}},"type":"object"},"fileJSON":{"properties":{"content":{"description":"Content is the file's bytes, empty when Truncated.","type":"string"},"encoding":{"description":"Encoding is how Content is carried: \"utf8\" verbatim, or \"base64\".","type":"string"},"path":{"description":"Path is the file's repo-relative path.","type":"string"},"size":{"description":"Size is the file's byte length in the repo.","type":"integer"},"truncated":{"description":"Truncated marks a file past the read cap; no content is sent. A caller\nassembling a desired set must treat this as INCOMPLETE, never as empty.","type":"boolean"}},"type":"object"},"filesJSON":{"properties":{"files":{"description":"Files are the selected files, sorted by path. Directories are never\nreturned.","items":{"$ref":"#/components/schemas/fileJSON"},"type":"array"},"rev":{"description":"Rev is the full revision the ref resolved to — pin follow-up reads to it.","type":"string"}},"type":"object"},"filingPage":{"properties":{"data":{"description":"Data are the filing records.","items":{"$ref":"#/components/schemas/legalFiling"},"type":"array"},"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire.","type":"string"}},"type":"object"},"filingReply":{"properties":{"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire.","type":"string"},"filing":{"$ref":"#/components/schemas/legalFiling","description":"Filing is the tracking record."}},"type":"object"},"filingRequest":{"properties":{"documentIds":{"description":"DocumentIDs are the documents to file. At least one is required, and every\none must belong to the caller's org — a filing can never reach across orgs.","items":{"type":"string"},"type":"array"},"jurisdiction":{"description":"Jurisdiction is the state or agency the filing is for, e.g. \"DE\".","type":"string"}},"type":"object"},"financeBalanceView":{"properties":{"asOf":{"type":"string"},"availableCents":{"type":"integer"},"currency":{"type":"string"},"dueCents":{"type":"integer"},"pendingCents":{"type":"integer"}},"type":"object"},"financeCredit":{"properties":{"cents":{"type":"integer"},"expiresAt":{"type":"string"},"grantedAt":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"remainingCents":{"type":"integer"}},"type":"object"},"financeInvoice":{"properties":{"cents":{"type":"integer"},"currency":{"type":"string"},"date":{"type":"string"},"dueDate":{"type":"string"},"id":{"type":"string"},"number":{"type":"string"},"status":{"type":"string"},"url":{"type":"string"}},"type":"object"},"financeLedgerEntry":{"properties":{"account":{"type":"string"},"balanceCents":{"type":"integer"},"cents":{"type":"integer"},"currency":{"type":"string"},"date":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"}},"type":"object"},"financePaymentMethod":{"properties":{"brand":{"type":"string"},"expMonth":{"type":"integer"},"expYear":{"type":"integer"},"id":{"type":"string"},"isDefault":{"type":"boolean"},"last4":{"type":"string"},"type":{"type":"string"}},"type":"object"},"financeUsageView":{"properties":{"currency":{"type":"string"},"end":{"type":"string"},"lines":{"items":{"$ref":"#/components/schemas/usageLine"},"type":"array"},"series":{"items":{"$ref":"#/components/schemas/sample"},"type":"array"},"start":{"type":"string"},"totalCents":{"type":"integer"}},"type":"object"},"flagsOut":{"properties":{"data":{"$ref":"#/components/schemas/BoardView"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"fleetBoard":{"properties":{"units":{"description":"Units is the union across sources — agent run-targets, BYO workers, BYO\nclusters and Visor machines — each row naming the source it came from.","items":{"$ref":"#/components/schemas/fleetUnit"},"type":"array"}},"type":"object"},"fleetMetrics":{"properties":{"at":{"type":"string"},"gpuUtil":{"type":"number"},"load1":{"type":"number"},"memFree":{"type":"integer"},"memUsed":{"type":"integer"}},"type":"object"},"fleetSpec":{"properties":{"arch":{"type":"string"},"cpus":{"type":"integer"},"gpuModel":{"type":"string"},"gpus":{"type":"integer"},"memory":{"type":"integer"},"os":{"type":"string"}},"type":"object"},"fleetSummary":{"properties":{"byDrift":{"$ref":"#/components/schemas/driftTally","description":"ByDrift counts those rows green, yellow and red."},"total":{"description":"Total is how many rows the board returned, after filtering.","type":"integer"}},"type":"object"},"fleetUnit":{"properties":{"host":{"type":"string"},"kind":{"type":"string"},"label":{"type":"string"},"metrics":{"$ref":"#/components/schemas/fleetMetrics"},"queued":{"type":"integer"},"running":{"description":"Running is what the unit is actively executing: agent sessions for a run-target,\nin-flight renders for a BYO GPU. Queued is the gpu-jobs backlog on this GPU's\nlane (BYO units only; an agent unit does not queue). Both come from the org's\ngpu-jobs queue for BYO units, overlaid in listFleet.","type":"integer"},"sessions":{"type":"integer"},"source":{"type":"string"},"spec":{"$ref":"#/components/schemas/fleetSpec"},"status":{"type":"string"},"unit":{"type":"string"}},"type":"object"},"flowCreate":{"properties":{"data":{"description":"Data is the workflow graph (the product's nodes/edges document),\nverbatim. Omit it to create an empty workflow."},"description":{"description":"Description says what the workflow does.","type":"string"},"name":{"description":"Name is the workflow's display name, unique within the org's project\n(the product de-duplicates by suffixing).","type":"string"}},"type":"object"},"flowPage":{"properties":{"data":{"description":"Data is the page of flows, newest-updated first.","items":{"$ref":"#/components/schemas/Flow"},"type":"array"}},"type":"object"},"flowRun":{"properties":{"input":{"description":"Input is the run's chat input value, handed to the graph's input node.","type":"string"},"session":{"description":"Session groups runs into one conversation; the product mints one when\nabsent and returns it in the response.","type":"string"},"tweaks":{"description":"Tweaks override component fields for this run only (the product's\ntweaks document), verbatim."},"workflow":{"description":"Workflow is the UUID of the workflow to run.","type":"string"}},"type":"object"},"flowStatus":{"properties":{"reachable":{"description":"Reachable is true when the flow service answered its health probe.","type":"boolean"},"version":{"description":"Version is the flow service's own version, present only when reachable.","type":"string"}},"type":"object"},"flowUpdate":{"properties":{"data":{"description":"Data replaces the workflow graph when present, verbatim."},"description":{"description":"Description replaces the description when present.","type":"string"},"locked":{"description":"Locked freezes or unfreezes the workflow against edits when present.","type":"boolean"},"name":{"description":"Name renames the workflow when present.","type":"string"},"workflow":{"description":"Workflow is the workflow's UUID, taken from the path.","type":"string"}},"type":"object"},"formationView":{"properties":{"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's one incorporation record."},"nextStages":{"description":"NextStages are the stages reachable from the formation's current stage,\nwhether or not their guards are satisfied yet.","items":{"type":"string"},"type":"array"}},"type":"object"},"foundersIn":{"properties":{"founders":{"description":"Founders is every founding stakeholder. Each needs a name and an email, and\nequityBps between 0 and 10000 (1% == 100 bps).","items":{"$ref":"#/components/schemas/Founder"},"type":"array"}},"type":"object"},"funnel":{"properties":{"convertedOrgs":{"description":"ConvertedOrgs is how many distinct referred orgs have produced positive\ncommission at least once — a referral that actually spent.","type":"integer"},"ratePct":{"description":"RatePct is convertedOrgs over referredOrgs as a PERCENTAGE, 0–100, and the one\nnon-integer figure on this board. It is 0 when nothing has been referred yet,\nnot undefined.","type":"number"},"referredOrgs":{"description":"ReferredOrgs is how many attribution edges exist fleet-wide — one per referred\norg, first-touch, so it is also the count of distinct referred orgs.","type":"integer"}},"type":"object"},"fwdRule":{"properties":{"entry_port":{"description":"EntryPort is the port the load balancer listens on.","type":"integer"},"entry_protocol":{"description":"EntryProtocol is the protocol the load balancer listens with (http, https, tcp).","type":"string"},"target_port":{"description":"TargetPort is the backend port traffic is forwarded to.","type":"integer"},"target_protocol":{"description":"TargetProtocol is the protocol used to reach the backend droplets.","type":"string"}},"type":"object"},"gcOut":{"properties":{"maintained":{"description":"Maintained is always true; the call fails rather than reporting false.","type":"boolean"},"repo":{"description":"Repo is the repo that was repacked.","type":"string"},"sizeBytes":{"description":"SizeBytes is the size measured AFTER the repack — usually smaller, since\nrepacking drops the packs it supersedes.","type":"integer"}},"type":"object"},"generateRequest":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Data supplies every merge field the template declares, keyed by field key.\nEvery declared field is REQUIRED: a missing one is refused with 400 rather\nthan rendered as a blank into a contract.","type":"object"},"templateId":{"description":"TemplateID is the template to render. Required; resolved for the caller's\norg, so an override wins over the built-in.","type":"string"}},"type":"object"},"gitOrigin":{"properties":{"branch":{"description":"Branch is the branch to build; defaults to `main` for a git source.","type":"string"},"url":{"description":"URL is the repository clone URL. Required for source `git`, and validated\nagainst the same allowlist the privileged build enforces.","type":"string"}},"type":"object"},"gitSource":{"properties":{"branch":{"type":"string"},"provider":{"type":"string"},"url":{"type":"string"}},"type":"object"},"githubBackfillIn":{"properties":{"state":{"description":"State is the GitHub issue state to walk: \"open\" (the default), \"closed\" or\n\"all\". Anything else is a 400.","type":"string"}},"type":"object"},"githubBackfillResult":{"properties":{"created":{"description":"Created is how many native issues this pass created.","type":"integer"},"failed":{"description":"Failed is how many repos or issues errored; the pass continues past each.","type":"integer"},"issues":{"description":"Issues is how many upstream issues were seen.","type":"integer"},"repos":{"description":"Repos is how many granted repos were walked (archived/disabled are skipped).","type":"integer"},"truncated":{"description":"Truncated is set when the time budget or the issue cap stopped the pass early.\nRe-run to continue — the mirror is idempotent by ExtRef, so nothing duplicates.","type":"boolean"},"updated":{"description":"Updated is how many existing native issues this pass refreshed.","type":"integer"}},"type":"object"},"githubImportIn":{"properties":{"all":{"description":"All imports every repository the installation grants, instead of naming\nthem. Archived and disabled repositories are skipped either way — they\ncannot be fetched.","type":"boolean"},"repos":{"description":"Repos names the repositories to import, either owner-qualified\n(\"hanzo-apps/ai\") or as a bare name (\"ai\"); a trailing \".git\" is stripped.\nA bare name that matches more than one granted repository is an error\nrather than a guess, because one Hanzo org may hold several GitHub\ninstallations and a name is only unique within an owner.\nIgnored when all is true.","items":{"type":"string"},"type":"array"}},"type":"object"},"githubImportOut":{"properties":{"queued":{"description":"Queued is how many repositories were handed to the background importer.","type":"integer"},"repos":{"description":"Repos names those repositories, in the installation's listing order.","items":{"type":"string"},"type":"array"}},"type":"object"},"githubPagesBuildOut":{"properties":{"repo":{"description":"Repo is the repository the build was queued for.","type":"string"},"status":{"description":"Status is GitHub's build state at the moment it was queued (\"queued\").","type":"string"},"url":{"description":"URL is GitHub's API URL for the build, for polling it there.","type":"string"}},"type":"object"},"githubPagesDisabledOut":{"properties":{"disabled":{"description":"Disabled is always true — a failure is an HTTP error, never this shape.","type":"boolean"},"repo":{"description":"Repo is the repository whose site was deleted.","type":"string"}},"type":"object"},"githubPagesEnableReq":{"properties":{"branch":{"description":"Branch is the legacy source branch; empty defaults to the repo's own default\nbranch. Ignored when buildType is \"workflow\".","type":"string"},"buildType":{"description":"BuildType selects the builder: \"workflow\" builds via GitHub Actions, anything\nelse builds from the branch source above.","type":"string"},"path":{"description":"Path is the source directory within the branch: \"/\" (the default) or \"/docs\".\nGitHub allows no others.","type":"string"},"repo":{"description":"Repo is the repository, from the :repo path segment.","type":"string"}},"type":"object"},"githubPagesSource":{"properties":{"branch":{"description":"Branch is the branch the site builds from.","type":"string"},"path":{"description":"Path is the directory within that branch: \"/\" or \"/docs\".","type":"string"}},"type":"object"},"githubPagesUpdateReq":{"properties":{"branch":{"description":"Branch switches the legacy source branch. Empty leaves the source alone.","type":"string"},"buildType":{"description":"BuildType switches the builder: \"legacy\" or \"workflow\". Empty leaves it.","type":"string"},"cname":{"description":"CNAME is the custom domain. Omit to leave it alone, \"\" to clear it, or a\nvalid FQDN to set it.","type":"string"},"httpsEnforced":{"description":"HTTPSEnforced toggles GitHub's enforce-HTTPS bit. Omit to leave it alone.","type":"boolean"},"path":{"description":"Path is the source directory to pair with Branch: \"/\" (the default) or\n\"/docs\". Read only when Branch is given.","type":"string"},"repo":{"description":"Repo is the repository, from the :repo path segment.","type":"string"}},"type":"object"},"githubPagesUpdatedOut":{"properties":{"repo":{"description":"Repo is the repository that was updated.","type":"string"},"updated":{"description":"Updated is always true — a failure is an HTTP error, never this shape.","type":"boolean"}},"type":"object"},"githubPagesView":{"properties":{"buildType":{"description":"BuildType is the builder in use: \"legacy\" (branch source) or \"workflow\".","type":"string"},"cname":{"description":"CNAME is the custom domain, absent when none is set.","type":"string"},"custom404":{"description":"Custom404 is whether the repo ships its own 404 page.","type":"boolean"},"httpsEnforced":{"description":"HTTPSEnforced is GitHub's enforce-HTTPS bit.","type":"boolean"},"repo":{"description":"Repo is the repository the site belongs to.","type":"string"},"source":{"$ref":"#/components/schemas/githubPagesSource","description":"Source is the branch + path the site builds from. Absent under \"workflow\"."},"status":{"description":"Status is GitHub's build state: \"built\", \"building\" or \"errored\". Absent\nbefore the first build.","type":"string"},"url":{"description":"URL is the live site (GitHub's html_url).","type":"string"}},"type":"object"},"githubRepoView":{"properties":{"defaultBranch":{"description":"DefaultBranch is the repo's default branch at GitHub.","type":"string"},"fullName":{"description":"FullName is GitHub's owner/name.","type":"string"},"htmlUrl":{"description":"HTMLURL is the repo's page at GitHub.","type":"string"},"imported":{"description":"Imported is whether this repo has been mirrored into git.hanzo.ai.","type":"boolean"},"lastSyncedAt":{"description":"LastSyncedAt is the last successful mirror, RFC 3339 UTC. Absent if never.","type":"string"},"name":{"description":"Name is the repository's short name within the installation.","type":"string"},"private":{"description":"Private is GitHub's visibility bit for the repo.","type":"boolean"},"syncStatus":{"description":"SyncStatus is \"synced\", \"conflict\", or \"\" when the repo is not imported.","type":"string"}},"type":"object"},"githubReposOut":{"properties":{"repos":{"description":"Repos is every repo the installation grants. Never null; [] when none.","items":{"$ref":"#/components/schemas/githubRepoView"},"type":"array"}},"type":"object"},"gpuAlertList":{"properties":{"alerts":{"description":"Alerts is always empty, and typed as a raw list because Visor exposes no\nalert inventory for this surface to shape: there is nothing to describe\nuntil there is something to return.","items":{"type":"object"},"type":"array"}},"type":"object"},"gpuJob":{"properties":{"attempt":{"type":"integer"},"closeTime":{"type":"string"},"failureCause":{"type":"string"},"gpu":{"type":"string"},"id":{"type":"string"},"label":{"type":"string"},"lastHeartbeat":{"type":"string"},"leaseExpiry":{"type":"string"},"runId":{"type":"string"},"startTime":{"type":"string"},"status":{"description":"queued|running|completed|failed|canceled","type":"string"},"type":{"type":"string"},"worker":{"type":"string"}},"type":"object"},"gpuList":{"properties":{"gpus":{"description":"GPUs is every accelerator the org has, from Visor GPU droplets and from BYO\nworkers alike.","items":{"$ref":"#/components/schemas/gpuView"},"type":"array"}},"type":"object"},"gpuView":{"properties":{"id":{"type":"string"},"location":{"type":"string"},"machine":{"type":"string"},"memory":{"type":"string"},"model":{"type":"string"},"name":{"type":"string"},"provider":{"description":"Provider distinguishes a BYO accelerator (\"byo\") from a Visor-provisioned\none (the machine's real provider). Memory is VRAM when known (BYO reports it\nfrom nvidia-smi; Visor's machine object carries none, so it stays empty and\nthe UI renders \"—\"). Both are additive + omitempty: existing rows are\nunaffected and the console normalizer ignores fields it does not read.","type":"string"},"region":{"type":"string"},"status":{"type":"string"}},"type":"object"},"grantOut":{"properties":{"updated":{"description":"Updated is the number of records the grant applied to. Zero is a 404, never a silent no-op.","type":"integer"}},"type":"object"},"graphEdge":{"properties":{"from":{"type":"string"},"kind":{"description":"parent | link | provenance","type":"string"},"to":{"type":"string"}},"type":"object"},"graphNode":{"properties":{"id":{"description":"\"\u003cdoctype\u003e:\u003cname\u003e\" — globally unique, click-to-open key","type":"string"},"name":{"description":"the document name (empty for synthetic nodes)","type":"string"},"project":{"type":"string"},"title":{"description":"display label","type":"string"},"type":{"description":"kb-page | kb-memory | kb-source | kb-connector | unresolved","type":"string"}},"type":"object"},"graphOut":{"properties":{"degraded":{"description":"Degraded is true when the store was unreachable and this graph is honestly\nempty rather than wrong. Absent on a normal answer.","type":"boolean"},"edges":{"description":"Edges are the parent tree, the resolved wikilinks and the connector provenance.","items":{"$ref":"#/components/schemas/graphEdge"},"type":"array"},"nodes":{"description":"Nodes are the pages, memories, sources, connectors and unresolved link targets.","items":{"$ref":"#/components/schemas/graphNode"},"type":"array"}},"type":"object"},"handleRequest":{"properties":{"handle":{"description":"Handle is the public leaderboard display name; empty opts out. Body-only:\nthe URL cannot supply it.","type":"string"}},"type":"object"},"handleSet":{"properties":{"handle":{"description":"Handle is the display name as STORED, echoed back after trimming. Empty means\nthe caller opted out: it keeps its rank and still sees its own row, it is just\nno longer listed to anyone else.","type":"string"}},"type":"object"},"healthLens":{"properties":{"available":{"description":"Available reports whether that table exists in the warehouse right now.","type":"boolean"},"table":{"description":"Table is the fully-qualified warehouse table the lens reads.","type":"string"}},"type":"object"},"healthLenses":{"properties":{"events":{"$ref":"#/components/schemas/healthLens","description":"Events is the web/commerce lens (event.fact, signal='act'), honest-empty until the\ncollector emits."},"llm":{"$ref":"#/components/schemas/healthLens","description":"LLM is the live per-org usage ledger lens (hanzo.cloud_usage)."}},"type":"object"},"healthOut":{"properties":{"engine":{"description":"Engine names the evaluator this deployment runs.","type":"string"},"ok":{"description":"OK is true whenever the flag engine is serving.","type":"boolean"}},"type":"object"},"healthPlane":{"properties":{"bus":{"description":"Bus is the address this process reaches the plane at.","type":"string"},"ready":{"description":"Ready reports whether an ingest would succeed right now. False is a 503.","type":"boolean"},"reason":{"description":"Reason is the plane's own failure text, present only when Ready is false.","type":"string"},"stream":{"description":"Stream is the JetStream stream every signal lands on.","type":"string"}},"type":"object"},"healthReport":{"properties":{"datastore":{"description":"Datastore reports whether the shared warehouse client has a live connection.\nIt is load-bearing for the READ path: false is one of the two ways this\nanswers 503.","type":"boolean"},"lenses":{"$ref":"#/components/schemas/healthLenses","description":"Lenses is per-lens table availability, probed only when connected — so it is\nabsent from a degraded report, which has nothing to say about tables it could\nnot reach."},"lost":{"$ref":"#/components/schemas/loss","description":"Lost is the count of facts the sink irrecoverably dropped since boot\n(warehouse.go). It is reported on the DEGRADED report too, and deliberately: a\nwarehouse that is unreachable is exactly when facts start failing their\ndeliveries, so suppressing the number here would hide it precisely when it\nmoves. ANY NON-ZERO VALUE IS AN ALARM — it counts data the door already\nanswered 200 for."},"plane":{"$ref":"#/components/schemas/healthPlane","description":"Plane reports the event plane — the bus and the stream every accepted event is\npublished to BEFORE any of it reaches the warehouse. It is load-bearing for the\nWRITE path, and it is here because its absence was a real outage: this endpoint\nanswered 200/ok on warehouse connectivity alone while every POST /v1/event 503'd\non a stream that could not bind, so 100% ingest loss was invisible to monitoring.\nA probe that cannot see the write path cannot report the write path."},"reason":{"description":"Reason is the human-readable cause, present only on a degraded report.","type":"string"},"service":{"description":"Service names the subsystem answering, so a probe aggregating several health\nendpoints can attribute a degraded one.","type":"string"},"status":{"description":"Status is ok or degraded. Degraded is the 503 and means EITHER load-bearing\ndependency is down — the warehouse this subsystem reads, or the event plane it\nwrites. It is not moved by a missing lens table, which is honest-empty.","type":"string"},"warehouse":{"description":"Warehouse names the datastore database every lens reads.","type":"string"}},"type":"object"},"healthView":{"properties":{"provider":{"description":"Provider is the wired verification provider's name (\"manual\" by default).","type":"string"},"status":{"description":"Status is \"ok\" when the subsystem is live.","type":"string"}},"type":"object"},"helpArticle":{"properties":{"body":{"description":"Body is the article's rich-text content as the author saved it.","type":"string"},"category":{"description":"Category is the name of the knowledge-base section the article sits in, or\nempty when it is filed under none.","type":"string"},"excerpt":{"description":"Excerpt is the short summary the author wrote for listings, or empty.","type":"string"},"slug":{"description":"Slug is the article's stable public identifier — the path segment it was\naddressed by.","type":"string"},"title":{"description":"Title is the article's headline.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second the article was last written.","type":"integer"}},"type":"object"},"helpArticleCard":{"properties":{"category":{"description":"Category is the name of the knowledge-base section the article sits in, or\nempty when it is filed under none.","type":"string"},"excerpt":{"description":"Excerpt is the short summary the author wrote for listings, or empty.","type":"string"},"slug":{"description":"Slug is the article's stable public identifier and the path segment\n/v1/help/articles/{slug} addresses it by.","type":"string"},"title":{"description":"Title is the article's headline.","type":"string"},"updatedAt":{"description":"UpdatedAt is the unix second the article was last written, in the help\ncenter's own store.","type":"integer"}},"type":"object"},"helpArticleList":{"properties":{"data":{"description":"Data is the matching Published, public articles, newest write order last —\nthe store's order, not a ranking. Empty when the center has none.","items":{"$ref":"#/components/schemas/helpArticleCard"},"type":"array"}},"type":"object"},"helpCategory":{"properties":{"description":{"description":"Description is the section's blurb, or empty.","type":"string"},"name":{"description":"Name is the section's name, and the value an article's category matches.","type":"string"}},"type":"object"},"helpCategoryList":{"properties":{"data":{"description":"Data is the sections that front at least one public article. Empty when the\ncenter publishes none.","items":{"$ref":"#/components/schemas/helpCategory"},"type":"array"}},"type":"object"},"helpTicketFiled":{"properties":{"status":{"description":"Status is the lifecycle state the ticket was filed in — always \"Open\".","type":"string"},"ticket":{"description":"Ticket is the opaque, random customer-facing reference (\"tkt_\" + 24 hex\ncharacters). It is NOT the ticket's internal name: that name is sequential,\nand handing it out would disclose the center's ticket volume.","type":"string"}},"type":"object"},"helpTicketIntake":{"properties":{"description":{"description":"Description is the customer's message. Optional; it becomes the ticket's\ndescription AND the opening entry of its conversation thread. Clipped at\n16 KiB.","type":"string"},"email":{"description":"Email is how the support team replies. Required; clipped at 320 characters\n(the RFC 5321 maximum). It is recorded as the ticket's customer, and it is\nnot verified.","type":"string"},"priority":{"description":"Priority is Low, Medium, High or Urgent, case-insensitively. Anything else —\nincluding omitting it — is recorded as Medium rather than refused.","type":"string"},"subject":{"description":"Subject is the one-line summary of the problem. Required; longer than 300\ncharacters is clipped rather than refused.","type":"string"}},"type":"object"},"hit":{"properties":{"doctype":{"type":"string"},"name":{"type":"string"},"project":{"type":"string"},"provider":{"type":"string"},"score":{"type":"number"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"iam.AccountItem":{"properties":{"modifyRule":{"type":"string"},"name":{"type":"string"},"regex":{"type":"string"},"tab":{"type":"string"},"viewRule":{"type":"string"},"visible":{"type":"boolean"}},"type":"object"},"iam.Address":{"properties":{"city":{"type":"string"},"line1":{"type":"string"},"line2":{"type":"string"},"region":{"type":"string"},"state":{"type":"string"},"tag":{"type":"string"},"zipCode":{"type":"string"}},"type":"object"},"iam.Answer":{"properties":{"code":{"type":"string"},"data":{"type":"object"},"data2":{"type":"object"},"data3":{"type":"object"},"msg":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"sub":{"type":"string"}},"type":"object"},"iam.Application":{"properties":{"affiliationUrl":{"type":"string"},"category":{"type":"string"},"cert":{"type":"string"},"certObj":{"$ref":"#/components/schemas/iam.Cert"},"certPublicKey":{"type":"string"},"clientCert":{"type":"string"},"clientId":{"description":"ClientId is the OAuth2/OIDC client identifier and the GLOBAL key every\nconfidential-client resolver authenticates against (store.GetApplicationByClientId,\nthe mint gates, Basic auth). It MUST be globally unique across ALL owners — a\ncollision would let one app shadow another at that key. This store persists each\nentity as a JSON document in a shared table, so there is no per-field column to\ncarry a DB UNIQUE index; uniqueness is enforced at the write in\napplications.Create/Update (ensureClientIdUnique), exactly as the (owner,name)\nnatural key is, and store.GetApplicationByClientId resolves admin-preferring as\ndefense-in-depth.","type":"string"},"clientSecret":{"type":"string"},"codeResendTimeout":{"type":"integer"},"cookieExpireInHours":{"type":"integer"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"customScopes":{"items":{"$ref":"#/components/schemas/iam.ScopeDescription"},"type":"array"},"defaultGroup":{"type":"string"},"deleted":{"type":"boolean"},"description":{"type":"string"},"disableSamlAttributes":{"type":"boolean"},"disableSignin":{"type":"boolean"},"displayName":{"type":"string"},"domain":{"type":"string"},"enableAutoSignin":{"type":"boolean"},"enableCodeSignin":{"type":"boolean"},"enableExclusiveSignin":{"type":"boolean"},"enableLinkWithEmail":{"type":"boolean"},"enablePassword":{"type":"boolean"},"enableSamlAssertionSignature":{"type":"boolean"},"enableSamlC14n10":{"type":"boolean"},"enableSamlCompress":{"type":"boolean"},"enableSamlPostBinding":{"type":"boolean"},"enableSignUp":{"type":"boolean"},"enableSigninSession":{"type":"boolean"},"enableWebAuthn":{"type":"boolean"},"environment":{"type":"string"},"expireInHours":{"type":"number"},"failedSigninFrozenTime":{"type":"integer"},"failedSigninLimit":{"type":"integer"},"favicon":{"type":"string"},"footerHtml":{"type":"string"},"forcedRedirectOrigin":{"type":"string"},"forgetUrl":{"type":"string"},"formBackgroundUrl":{"type":"string"},"formBackgroundUrlMobile":{"type":"string"},"formCss":{"type":"string"},"formCssMobile":{"type":"string"},"formOffset":{"type":"integer"},"formSideHtml":{"type":"string"},"grantTypes":{"items":{"type":"string"},"type":"array"},"headerHtml":{"type":"string"},"homepageUrl":{"type":"string"},"id":{"type":"string"},"ipRestriction":{"type":"string"},"ipWhitelist":{"type":"string"},"isShared":{"type":"boolean"},"logo":{"type":"string"},"name":{"type":"string"},"order":{"type":"integer"},"orgChoiceMode":{"type":"string"},"organization":{"type":"string"},"organizationObj":{"$ref":"#/components/schemas/iam.Organization"},"otherDomains":{"items":{"type":"string"},"type":"array"},"owner":{"type":"string"},"project":{"type":"string"},"providers":{"items":{"$ref":"#/components/schemas/iam.ProviderItem"},"type":"array"},"redirectUris":{"items":{"type":"string"},"type":"array"},"refreshExpireInHours":{"type":"number"},"samlAttributes":{"items":{"$ref":"#/components/schemas/iam.SamlItem"},"type":"array"},"samlHashAlgorithm":{"type":"string"},"samlReplyUrl":{"type":"string"},"scopes":{"items":{"$ref":"#/components/schemas/iam.ScopeItem"},"type":"array"},"signinHtml":{"type":"string"},"signinItems":{"items":{"$ref":"#/components/schemas/iam.SigninItem"},"type":"array"},"signinMethods":{"items":{"$ref":"#/components/schemas/iam.SigninMethod"},"type":"array"},"signinUrl":{"type":"string"},"signupHtml":{"type":"string"},"signupItems":{"items":{"$ref":"#/components/schemas/iam.SignupItem"},"type":"array"},"signupUrl":{"type":"string"},"sslCert":{"type":"string"},"sslMode":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"termsOfUse":{"type":"string"},"themeData":{"$ref":"#/components/schemas/iam.ThemeData"},"title":{"type":"string"},"tokenAttributes":{"items":{"$ref":"#/components/schemas/iam.JwtItem"},"type":"array"},"tokenFields":{"items":{"type":"string"},"type":"array"},"tokenFormat":{"type":"string"},"tokenSigningMethod":{"type":"string"},"type":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"upstreamHost":{"type":"string"},"useEmailAsSamlNameId":{"type":"boolean"}},"type":"object"},"iam.ApplicationListResult":{"properties":{"applications":{"items":{"$ref":"#/components/schemas/iam.Application"},"type":"array"}},"type":"object"},"iam.ApplicationRef":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name"],"type":"object"},"iam.AuditLog":{"properties":{"action":{"type":"string"},"clientIp":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"id":{"type":"string"},"isTriggered":{"type":"boolean"},"language":{"type":"string"},"method":{"type":"string"},"name":{"type":"string"},"object":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"requestUri":{"type":"string"},"response":{"type":"string"},"statusCode":{"type":"integer"},"updatedAt":{"format":"date-time","type":"string"},"user":{"type":"string"}},"type":"object"},"iam.CartItem":{"properties":{"displayName":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"price":{"type":"number"},"quantity":{"type":"integer"}},"type":"object"},"iam.Cert":{"properties":{"accessKey":{"type":"string"},"accessSecret":{"type":"string"},"account":{"type":"string"},"bitSize":{"type":"integer"},"certificate":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"cryptoAlgorithm":{"type":"string"},"deleted":{"type":"boolean"},"displayName":{"type":"string"},"domainExpireTime":{"type":"string"},"expireInYears":{"type":"integer"},"expireTime":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"privateKey":{"type":"string"},"provider":{"type":"string"},"scope":{"type":"string"},"type":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"iam.ConsentRecord":{"properties":{"application":{"type":"string"},"grantedScopes":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.CreateInput":{"properties":{"password":{"type":"string"},"user":{"$ref":"#/components/schemas/iam.User"}},"type":"object"},"iam.CreateOrganizationInput":{"properties":{"accountItems":{"items":{"$ref":"#/components/schemas/iam.AccountItem"},"type":"array"},"accountMenu":{"type":"string"},"balanceCredit":{"type":"number"},"balanceCurrency":{"type":"string"},"countryCodes":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"dcrPolicy":{"type":"string"},"defaultApplication":{"type":"string"},"defaultAvatar":{"type":"string"},"defaultPassword":{"type":"string"},"deleted":{"type":"boolean"},"disableSignin":{"type":"boolean"},"displayName":{"type":"string"},"enableSoftDeletion":{"type":"boolean"},"enableTour":{"type":"boolean"},"failedSigninFrozenTime":{"type":"integer"},"failedSigninLimit":{"type":"integer"},"favicon":{"type":"string"},"founder":{"type":"string"},"hasPrivilegeConsent":{"type":"boolean"},"id":{"type":"string"},"initScore":{"type":"integer"},"ipRestriction":{"type":"string"},"ipWhitelist":{"type":"string"},"isPersonal":{"type":"boolean"},"isProfilePublic":{"type":"boolean"},"kerberosKdcHost":{"type":"string"},"kerberosKeytab":{"type":"string"},"kerberosRealm":{"type":"string"},"kerberosServiceName":{"type":"string"},"languages":{"items":{"type":"string"},"type":"array"},"ldapAttributes":{"items":{"type":"string"},"type":"array"},"logo":{"type":"string"},"logoDark":{"type":"string"},"masterPassword":{"type":"string"},"masterVerificationCode":{"type":"string"},"mfaItems":{"items":{"$ref":"#/components/schemas/iam.MfaItem"},"type":"array"},"mfaRememberInHours":{"type":"integer"},"name":{"type":"string"},"navItems":{"items":{"type":"string"},"type":"array"},"orgBalance":{"type":"number"},"owner":{"type":"string"},"passwordExpireDays":{"type":"integer"},"passwordObfuscatorKey":{"type":"string"},"passwordObfuscatorType":{"type":"string"},"passwordOptions":{"items":{"type":"string"},"type":"array"},"passwordSalt":{"type":"string"},"passwordType":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"themeData":{"$ref":"#/components/schemas/iam.ThemeData"},"updatedAt":{"format":"date-time","type":"string"},"useEmailAsUsername":{"type":"boolean"},"usePermanentAvatar":{"type":"boolean"},"userBalance":{"type":"number"},"userNavItems":{"items":{"type":"string"},"type":"array"},"userTypes":{"items":{"type":"string"},"type":"array"},"websiteUrl":{"type":"string"},"widgetItems":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.CreateSessionIn":{"properties":{"application":{"type":"string"},"exclusiveSignin":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"sessionId":{"items":{"type":"string"},"type":"array"}},"required":["owner","name","application"],"type":"object"},"iam.DeleteOrganizationInput":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.DeleteOrganizationOutput":{"properties":{"affected":{"type":"boolean"}},"type":"object"},"iam.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.DeleteResponse":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.DeleteResult":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.DeleteSessionOut":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.FaceId":{"properties":{"faceIdData":{"items":{"type":"number"},"type":"array"},"imageUrl":{"type":"string"},"name":{"type":"string"}},"type":"object"},"iam.Input":{"properties":{"createdTime":{"type":"string"},"description":{"type":"string"},"displayName":{"type":"string"},"isDefault":{"type":"boolean"},"metadata":{"type":"string"},"name":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"workspace":{"type":"string"}},"type":"object"},"iam.Invitation":{"properties":{"application":{"type":"string"},"code":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"defaultCode":{"type":"string"},"deleted":{"type":"boolean"},"displayName":{"type":"string"},"email":{"type":"string"},"id":{"type":"string"},"isRegexp":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"phone":{"type":"string"},"quota":{"type":"integer"},"signupGroup":{"type":"string"},"state":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedTime":{"type":"string"},"usedCount":{"type":"integer"},"username":{"type":"string"}},"type":"object"},"iam.JwtItem":{"properties":{"category":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"value":{"type":"string"}},"type":"object"},"iam.Key":{"properties":{"accessKey":{"description":"AccessKey (pk-*) is the publishable identifier and lookup index;\nAccessSecret (sk-*) is the confidential secret.","type":"string"},"accessSecret":{"type":"string"},"application":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"description":"CreatedTime and UpdatedTime are RFC3339 audit stamps carried as strings\nfor byte-parity with the v1 row (orm.Model separately tracks CreatedAt /\nUpdatedAt as time.Time for the store's own lifecycle).","type":"string"},"deleted":{"type":"boolean"},"displayName":{"description":"DisplayName is the human-facing label.","type":"string"},"expireTime":{"description":"ExpireTime is when the key stops being honored (empty = never). State is\nthe lifecycle flag (\"Active\", \"test\", …); \"test\" mints test-env\ncredentials instead of live ones.","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organization":{"type":"string"},"owner":{"description":"Owner is the tenant that holds the key; Name is unique within Owner.","type":"string"},"scope":{"description":"Scope is the key's ACCESS CLASS, orthogonal to Type (which names the bound\nprincipal). Empty (the default, \"secret\") is a full key: a pk- publishable\nhalf AND a confidential sk- half, the sk- authenticating a server-side reader.\nKeyScopePublish is a WRITE-ONLY publishable key — a pk- half only, no secret —\nthat resolves to just an ORG (never a principal) at the ingest door and is safe\nto ship in client JS. A missing value on an existing row reads as the default,\nso every pre-Scope key is a secret key unchanged.","type":"string"},"state":{"type":"string"},"type":{"description":"Type is the scope the key is bound to — \"Organization\", \"Application\",\n\"User\", or \"General\" — and Organization / Application / User name the\nconcrete principal for whichever scope Type selects.","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedTime":{"type":"string"},"user":{"type":"string"}},"type":"object"},"iam.ListOrganizationsOutput":{"properties":{"count":{"type":"integer"},"organizations":{"items":{"$ref":"#/components/schemas/iam.Organization"},"type":"array"}},"type":"object"},"iam.ListOutput":{"properties":{"auditLogs":{"items":{"$ref":"#/components/schemas/iam.AuditLog"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"iam.ListResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/iam.Key"},"type":"array"}},"type":"object"},"iam.ListSessionsIn":{"properties":{"application":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner"],"type":"object"},"iam.ListSessionsOut":{"properties":{"sessions":{"items":{"$ref":"#/components/schemas/iam.Session"},"type":"array"}},"type":"object"},"iam.ManagedAccount":{"properties":{"application":{"type":"string"},"signinUrl":{"type":"string"},"username":{"type":"string"}},"type":"object"},"iam.MfaAccount":{"properties":{"accountName":{"type":"string"},"issuer":{"type":"string"},"origin":{"type":"string"}},"type":"object"},"iam.MfaItem":{"properties":{"name":{"type":"string"},"rule":{"type":"string"}},"type":"object"},"iam.MfaProps":{"properties":{"countryCode":{"type":"string"},"enabled":{"type":"boolean"},"isPreferred":{"type":"boolean"},"mfaRememberInHours":{"type":"integer"},"mfaType":{"type":"string"},"url":{"type":"string"}},"type":"object"},"iam.Organization":{"properties":{"accountItems":{"items":{"$ref":"#/components/schemas/iam.AccountItem"},"type":"array"},"accountMenu":{"type":"string"},"balanceCredit":{"type":"number"},"balanceCurrency":{"type":"string"},"countryCodes":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"dcrPolicy":{"type":"string"},"defaultApplication":{"type":"string"},"defaultAvatar":{"type":"string"},"defaultPassword":{"type":"string"},"deleted":{"type":"boolean"},"disableSignin":{"type":"boolean"},"displayName":{"type":"string"},"enableSoftDeletion":{"type":"boolean"},"enableTour":{"type":"boolean"},"failedSigninFrozenTime":{"type":"integer"},"failedSigninLimit":{"description":"Per-organization signin throttle. Zero means \"inherit the application\ndefault\"; a non-zero value overrides it. Safe bounds are clamped by the\nresource service before persistence.","type":"integer"},"favicon":{"type":"string"},"founder":{"description":"Founder is the stable storage id of the identity that provisioned this org\n(self-service onboarding). It is the resume token that makes provisioning\nconverge on a backend where each write autocommits independently (no\ntransaction rollback): after a partial failure that created the org but did\nnot move the founder in, a retry recognises the org as the founder's own and\ncompletes it, instead of refusing it as \"already taken\". It also fences the\norg to ONE tenant — a different identity can never complete or join it.","type":"string"},"hasPrivilegeConsent":{"type":"boolean"},"id":{"type":"string"},"initScore":{"type":"integer"},"ipRestriction":{"type":"string"},"ipWhitelist":{"type":"string"},"isPersonal":{"type":"boolean"},"isProfilePublic":{"type":"boolean"},"kerberosKdcHost":{"type":"string"},"kerberosKeytab":{"type":"string"},"kerberosRealm":{"type":"string"},"kerberosServiceName":{"type":"string"},"languages":{"items":{"type":"string"},"type":"array"},"ldapAttributes":{"items":{"type":"string"},"type":"array"},"logo":{"type":"string"},"logoDark":{"type":"string"},"masterPassword":{"type":"string"},"masterVerificationCode":{"type":"string"},"mfaItems":{"items":{"$ref":"#/components/schemas/iam.MfaItem"},"type":"array"},"mfaRememberInHours":{"type":"integer"},"name":{"type":"string"},"navItems":{"items":{"type":"string"},"type":"array"},"orgBalance":{"description":"Balance fields are read-only mirrors; authoritative balances live in\nCommerce (billing.hanzo.ai). Carried for field-complete v1 parity.","type":"number"},"owner":{"type":"string"},"passwordExpireDays":{"type":"integer"},"passwordObfuscatorKey":{"type":"string"},"passwordObfuscatorType":{"type":"string"},"passwordOptions":{"items":{"type":"string"},"type":"array"},"passwordSalt":{"type":"string"},"passwordType":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"themeData":{"$ref":"#/components/schemas/iam.ThemeData"},"updatedAt":{"format":"date-time","type":"string"},"useEmailAsUsername":{"type":"boolean"},"usePermanentAvatar":{"type":"boolean"},"userBalance":{"type":"number"},"userNavItems":{"items":{"type":"string"},"type":"array"},"userTypes":{"items":{"type":"string"},"type":"array"},"websiteUrl":{"type":"string"},"widgetItems":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.Permission":{"properties":{"actions":{"items":{"type":"string"},"type":"array"},"adapter":{"type":"string"},"approveTime":{"type":"string"},"approver":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"description":"Descriptive metadata.","type":"string"},"deleted":{"type":"boolean"},"description":{"type":"string"},"displayName":{"type":"string"},"domains":{"items":{"type":"string"},"type":"array"},"effect":{"type":"string"},"groups":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"model":{"description":"Authorization model, targets, and decision. AuthzModel carries the v1\n`model` column (the named authz model); it is not the Go identifier\n`Model` because that name is taken by the embedded orm.Model[Permission]\nmixin. The HTTP contract is unchanged — json:\"model\".","type":"string"},"name":{"type":"string"},"owner":{"description":"Identity — the (owner, name) natural key.","type":"string"},"resourceType":{"type":"string"},"resources":{"items":{"type":"string"},"type":"array"},"roles":{"items":{"type":"string"},"type":"array"},"state":{"type":"string"},"submitter":{"description":"Submission / approval workflow.","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"users":{"description":"Subjects the grant is evaluated for.","items":{"type":"string"},"type":"array"}},"type":"object"},"iam.Project":{"properties":{"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"description":{"type":"string"},"displayName":{"type":"string"},"id":{"type":"string"},"isDefault":{"type":"boolean"},"metadata":{"type":"string"},"name":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"workspace":{"type":"string"}},"type":"object"},"iam.Provider":{"properties":{"appId":{"type":"string"},"bucket":{"type":"string"},"category":{"type":"string"},"cert":{"type":"string"},"clientId":{"type":"string"},"clientId2":{"type":"string"},"clientSecret":{"type":"string"},"clientSecret2":{"type":"string"},"content":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"customAuthUrl":{"type":"string"},"customLogo":{"type":"string"},"customTokenUrl":{"type":"string"},"customUserInfoUrl":{"type":"string"},"deleted":{"type":"boolean"},"disableSsl":{"type":"boolean"},"displayName":{"type":"string"},"domain":{"type":"string"},"emailRegex":{"type":"string"},"enablePkce":{"type":"boolean"},"enableProxy":{"type":"boolean"},"enableSignAuthnRequest":{"type":"boolean"},"endpoint":{"type":"string"},"host":{"type":"string"},"httpHeaders":{"additionalProperties":{"type":"string"},"type":"object"},"id":{"type":"string"},"idP":{"type":"string"},"intranetEndpoint":{"type":"string"},"issuerUrl":{"type":"string"},"metadata":{"type":"string"},"method":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"pathPrefix":{"type":"string"},"port":{"type":"integer"},"providerUrl":{"type":"string"},"receiver":{"type":"string"},"regionId":{"type":"string"},"scopes":{"type":"string"},"signName":{"type":"string"},"sslMode":{"type":"string"},"subType":{"type":"string"},"templateCode":{"type":"string"},"title":{"type":"string"},"type":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"userMapping":{"additionalProperties":{"type":"string"},"type":"object"}},"type":"object"},"iam.ProviderItem":{"properties":{"bindingRule":{"items":{"type":"string"},"type":"array"},"canSignIn":{"type":"boolean"},"canSignUp":{"type":"boolean"},"canUnlink":{"type":"boolean"},"countryCodes":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"owner":{"type":"string"},"prompted":{"type":"boolean"},"provider":{"$ref":"#/components/schemas/iam.Provider"},"rule":{"type":"string"},"signupGroup":{"type":"string"}},"type":"object"},"iam.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.Response":{"properties":{"code":{"description":"Code is a STABLE machine-readable reason, where the human `msg` is\ndeliberately generic. `msg` is prose for a person and several distinct causes\nlegitimately share one sentence; a caller that must BRANCH on the cause — or\ntell its own user which of them happened — cannot parse prose. Optional, so\nevery existing envelope is byte-identical and no SDK changes.","type":"string"},"data":{"type":"object"},"data2":{"type":"object"},"data3":{"type":"object"},"msg":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"sub":{"type":"string"}},"type":"object"},"iam.Role":{"properties":{"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"description":{"type":"string"},"displayName":{"type":"string"},"domains":{"items":{"type":"string"},"type":"array"},"groups":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"isEnabled":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"users":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.SamlItem":{"properties":{"name":{"type":"string"},"nameFormat":{"type":"string"},"value":{"type":"string"}},"type":"object"},"iam.ScopeDescription":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"iam.ScopeItem":{"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"name":{"type":"string"},"tools":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.Session":{"properties":{"application":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"exclusiveSignin":{"description":"ExclusiveSignin is a transient control flag (v1 xorm:\"-\"): a caller sets\nit on a create to collapse SessionId down to the single incoming cookie\ninstead of appending. It is never stored — a persisted session always\ncarries it false, so orm:\"-\" keeps it off the column backends and\nomitempty keeps it out of the SQLite JSON blob.","type":"boolean"},"id":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"sessionId":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"iam.SessionRef":{"properties":{"application":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name","application"],"type":"object"},"iam.SigninItem":{"properties":{"customCss":{"type":"string"},"isCustom":{"type":"boolean"},"label":{"type":"string"},"name":{"type":"string"},"placeholder":{"type":"string"},"rule":{"type":"string"},"visible":{"type":"boolean"}},"type":"object"},"iam.SigninMethod":{"properties":{"displayName":{"type":"string"},"name":{"type":"string"},"rule":{"type":"string"}},"type":"object"},"iam.SignupItem":{"properties":{"customCss":{"type":"string"},"label":{"type":"string"},"name":{"type":"string"},"options":{"items":{"type":"string"},"type":"array"},"placeholder":{"type":"string"},"prompted":{"type":"boolean"},"regex":{"type":"string"},"required":{"type":"boolean"},"rule":{"type":"string"},"type":{"type":"string"},"visible":{"type":"boolean"}},"type":"object"},"iam.ThemeData":{"properties":{"borderRadius":{"type":"integer"},"colorPrimary":{"type":"string"},"isCompact":{"type":"boolean"},"isEnabled":{"type":"boolean"},"themeType":{"type":"string"}},"type":"object"},"iam.Token":{"properties":{"accessToken":{"type":"string"},"accessTokenHash":{"type":"string"},"application":{"type":"string"},"code":{"type":"string"},"codeChallenge":{"type":"string"},"codeChallengeMethod":{"type":"string"},"codeExpireIn":{"type":"integer"},"codeIsUsed":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"expiresIn":{"type":"integer"},"id":{"type":"string"},"name":{"type":"string"},"nonce":{"description":"Nonce is the OIDC authorize nonce, stored on the code and echoed into the\nid_token minted at the exchange (OIDC Core §3.1.3.6) so a relying party\nbinds the id_token to its own request and detects replay.","type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"publicGrant":{"description":"PublicGrant records that this grant was established WITHOUT client\nauthentication — a PKCE code exchange from a client that presented no\nsecret. Whether a client is confidential is a property of the GRANT, not\nonly of the registration: `hanzo-cli` and every @hanzo/iam SPA keep a\nregistered secret for a BACKEND path while the surface that actually signs\nin is a public PKCE client that cannot hold one. authorizationCodeGrant\nalready makes exactly that bounded relaxation; this is the same fact,\nrecorded so refreshTokenGrant can honour it instead of demanding a secret\nthe client never had (which 401s invalid_client and kills the session at\nthe access token's expiry). Carried across rotation, so the second refresh\nbehaves like the first.","type":"boolean"},"redirectUri":{"description":"RedirectUri binds the authorization code to the exact redirect URI of the\nauthorize request (RFC 6749 §4.1.3): the token endpoint refuses a code\nredeemed with a different redirect_uri, closing code-injection across a\nclient's registered URIs.","type":"string"},"refreshConsumed":{"type":"boolean"},"refreshExpireIn":{"type":"integer"},"refreshFamily":{"description":"Refresh-token rotation state (v2). Each refresh belongs to a family (the\ngrant); rotation mints a new row in the same family and marks the prior\none consumed. Presenting a consumed refresh is reuse — the whole family is\nrevoked (RFC 9700 §4.14.2). RefreshExpireIn is the refresh token's own\nabsolute expiry (unix), independent of the access token's shorter life.","type":"string"},"refreshToken":{"type":"string"},"refreshTokenHash":{"type":"string"},"resource":{"description":"RFC 8707 resource indicator","type":"string"},"scope":{"type":"string"},"tokenType":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"user":{"type":"string"},"userCode":{"type":"string"}},"type":"object"},"iam.UpdateInput":{"properties":{"password":{"type":"string"},"user":{"$ref":"#/components/schemas/iam.User"}},"type":"object"},"iam.UpdateOrganizationInput":{"properties":{"accountItems":{"items":{"$ref":"#/components/schemas/iam.AccountItem"},"type":"array"},"accountMenu":{"type":"string"},"balanceCredit":{"type":"number"},"balanceCurrency":{"type":"string"},"countryCodes":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"dcrPolicy":{"type":"string"},"defaultApplication":{"type":"string"},"defaultAvatar":{"type":"string"},"defaultPassword":{"type":"string"},"deleted":{"type":"boolean"},"disableSignin":{"type":"boolean"},"displayName":{"type":"string"},"enableSoftDeletion":{"type":"boolean"},"enableTour":{"type":"boolean"},"failedSigninFrozenTime":{"type":"integer"},"failedSigninLimit":{"type":"integer"},"favicon":{"type":"string"},"founder":{"type":"string"},"hasPrivilegeConsent":{"type":"boolean"},"id":{"type":"string"},"initScore":{"type":"integer"},"ipRestriction":{"type":"string"},"ipWhitelist":{"type":"string"},"isPersonal":{"type":"boolean"},"isProfilePublic":{"type":"boolean"},"kerberosKdcHost":{"type":"string"},"kerberosKeytab":{"type":"string"},"kerberosRealm":{"type":"string"},"kerberosServiceName":{"type":"string"},"languages":{"items":{"type":"string"},"type":"array"},"ldapAttributes":{"items":{"type":"string"},"type":"array"},"logo":{"type":"string"},"logoDark":{"type":"string"},"masterPassword":{"type":"string"},"masterVerificationCode":{"type":"string"},"mfaItems":{"items":{"$ref":"#/components/schemas/iam.MfaItem"},"type":"array"},"mfaRememberInHours":{"type":"integer"},"name":{"type":"string"},"navItems":{"items":{"type":"string"},"type":"array"},"orgBalance":{"type":"number"},"owner":{"type":"string"},"passwordExpireDays":{"type":"integer"},"passwordObfuscatorKey":{"type":"string"},"passwordObfuscatorType":{"type":"string"},"passwordOptions":{"items":{"type":"string"},"type":"array"},"passwordSalt":{"type":"string"},"passwordType":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"themeData":{"$ref":"#/components/schemas/iam.ThemeData"},"updatedAt":{"format":"date-time","type":"string"},"useEmailAsUsername":{"type":"boolean"},"usePermanentAvatar":{"type":"boolean"},"userBalance":{"type":"number"},"userNavItems":{"items":{"type":"string"},"type":"array"},"userTypes":{"items":{"type":"string"},"type":"array"},"websiteUrl":{"type":"string"},"widgetItems":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.UpdateSessionIn":{"properties":{"application":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"sessionId":{"items":{"type":"string"},"type":"array"}},"required":["owner","name","application"],"type":"object"},"iam.User":{"properties":{"accessKey":{"description":"API credentials. AccessSecret / AccessSecretHash / the OAuth tokens are\nbearer material. AccessSecretHash MUST persist (orm stores via JSON; a\njson:\"-\" field is never saved), so it carries a real json tag and the\nhandler's redact() strips it (and AccessSecret + the token fields) before\nresponding.","type":"string"},"accessSecret":{"type":"string"},"accessSecretHash":{"type":"string"},"accessToken":{"type":"string"},"address":{"items":{"type":"string"},"type":"array"},"addresses":{"items":{"$ref":"#/components/schemas/iam.Address"},"type":"array"},"adfs":{"type":"string"},"affiliation":{"type":"string"},"alipay":{"type":"string"},"amazon":{"type":"string"},"apple":{"type":"string"},"applicationScopes":{"items":{"$ref":"#/components/schemas/iam.ConsentRecord"},"type":"array"},"auth0":{"type":"string"},"avatar":{"type":"string"},"avatarType":{"type":"string"},"azuread":{"type":"string"},"azureadb2c":{"type":"string"},"baidu":{"type":"string"},"balance":{"description":"Balance mirrors v1 for lossless migration but is authoritative in\nCommerce (billing.hanzo.ai), not here — do not write it from IAM.","type":"number"},"balanceCredit":{"type":"number"},"balanceCurrency":{"type":"string"},"battlenet":{"type":"string"},"bilibili":{"type":"string"},"bio":{"type":"string"},"birthday":{"type":"string"},"bitbucket":{"type":"string"},"box":{"type":"string"},"cart":{"items":{"$ref":"#/components/schemas/iam.CartItem"},"type":"array"},"cloudfoundry":{"type":"string"},"countryCode":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdIp":{"description":"Sign-in provenance.","type":"string"},"createdTime":{"type":"string"},"currency":{"type":"string"},"custom":{"type":"string"},"custom10":{"type":"string"},"custom2":{"type":"string"},"custom3":{"type":"string"},"custom4":{"type":"string"},"custom5":{"type":"string"},"custom6":{"type":"string"},"custom7":{"type":"string"},"custom8":{"type":"string"},"custom9":{"type":"string"},"dailymotion":{"type":"string"},"deezer":{"type":"string"},"deleted":{"type":"boolean"},"deletedTime":{"type":"string"},"digitalocean":{"type":"string"},"dingtalk":{"type":"string"},"discord":{"type":"string"},"displayName":{"description":"Profile.","type":"string"},"douyin":{"type":"string"},"dropbox":{"type":"string"},"education":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"eveonline":{"type":"string"},"externalId":{"type":"string"},"faceIds":{"items":{"$ref":"#/components/schemas/iam.FaceId"},"type":"array"},"facebook":{"type":"string"},"firstName":{"type":"string"},"fitbit":{"type":"string"},"gender":{"type":"string"},"gitea":{"type":"string"},"gitee":{"type":"string"},"github":{"description":"Linked federated-identity subjects, one column per connector (v1 parity).","type":"string"},"gitlab":{"type":"string"},"google":{"type":"string"},"groups":{"items":{"type":"string"},"type":"array"},"hash":{"type":"string"},"heroku":{"type":"string"},"homepage":{"type":"string"},"iam":{"type":"string"},"id":{"description":"Id is the user's STABLE OPAQUE identifier — the value the OIDC `sub` claim\ncarries. It is the v1 the legacy surface per-row UUID (e.g.\n\"e7d7fda0-4c53-4508-9d35-7ec892b7e5d7\"), migrated verbatim so a user's `sub`\nis byte-identical across the cutover: every live session, external reference,\nand the downstream money-path principal keyed on `sub` survive unchanged. A\nuser minted natively in v2 is assigned a fresh UUID here on create, so the\n`sub` is ALWAYS a stable opaque id going forward — never the (Owner, Name)\npair, which is mutable (a rename would otherwise silently reissue identity).\n\nIt is distinct from the embedded orm.Model STORAGE KEY — the value the datastore\nlocks and looks a row up by — which is NOT (Owner, Name) for every row: a MIGRATED\nlegacy row is stamped \"owner/name\" (SetId in the migrator), but a v2-native\nusers.Create'd row is NOT — Create allocates rather than pinning a key, so its\nstorage key is a store-assigned surrogate id (a decimal string like\n\"17847909129933610000001\"). (Owner, Name) is therefore the natural/QUERY key\n(unique, indexed), not necessarily the storage key: resolve a row for a locked\nwrite by its REAL key (store.GetUserByName(...).Key().Encode(), which stamps both\nshapes — see internal/oidc updateUser), never by assuming \"owner/name\". This Id is\na first-class, indexed DOMAIN field; its json tag \"id\" dominates the promoted\norm.Model `Id_` (also \"id\") by shallower depth, so the persisted record's \"id\" is\nthis UUID — exactly the v1 shape. A row that carries no Id (a not-yet-assigned\npre-cutover user) falls back to the (Owner, Name) subject at mint; every other\npath resolves `sub`→user by Id.","type":"string"},"idCard":{"type":"string"},"idCardType":{"type":"string"},"influxcloud":{"type":"string"},"infoflow":{"type":"string"},"instagram":{"type":"string"},"intercom":{"type":"string"},"invitation":{"type":"string"},"invitationCode":{"type":"string"},"ipWhitelist":{"type":"string"},"isAdmin":{"type":"boolean"},"isDefaultAvatar":{"description":"State flags.","type":"boolean"},"isDeleted":{"type":"boolean"},"isForbidden":{"type":"boolean"},"isOnline":{"type":"boolean"},"isVerified":{"type":"boolean"},"kakao":{"type":"string"},"karma":{"type":"integer"},"kwai":{"type":"string"},"language":{"type":"string"},"lark":{"type":"string"},"lastChangePasswordTime":{"type":"string"},"lastName":{"type":"string"},"lastSigninIp":{"type":"string"},"lastSigninTime":{"type":"string"},"lastSigninWrongTime":{"type":"string"},"lastfm":{"type":"string"},"ldap":{"type":"string"},"line":{"type":"string"},"linkedin":{"type":"string"},"location":{"type":"string"},"mailru":{"type":"string"},"managedAccounts":{"items":{"$ref":"#/components/schemas/iam.ManagedAccount"},"type":"array"},"meetup":{"type":"string"},"mfaAccounts":{"items":{"$ref":"#/components/schemas/iam.MfaAccount"},"type":"array"},"mfaEmailEnabled":{"type":"boolean"},"mfaItems":{"items":{"$ref":"#/components/schemas/iam.MfaItem"},"type":"array"},"mfaPhoneEnabled":{"type":"boolean"},"mfaPushEnabled":{"type":"boolean"},"mfaPushProvider":{"type":"string"},"mfaPushReceiver":{"type":"string"},"mfaRadiusEnabled":{"type":"boolean"},"mfaRadiusProvider":{"type":"string"},"mfaRadiusUsername":{"type":"string"},"mfaRememberDeadline":{"type":"string"},"microsoftonline":{"type":"string"},"multiFactorAuths":{"items":{"$ref":"#/components/schemas/iam.MfaProps"},"type":"array"},"name":{"type":"string"},"naver":{"type":"string"},"needUpdatePassword":{"type":"boolean"},"nextcloud":{"type":"string"},"okta":{"type":"string"},"onedrive":{"type":"string"},"originalRefreshToken":{"type":"string"},"originalToken":{"type":"string"},"oura":{"type":"string"},"owner":{"description":"Identity / tenancy. (Owner, Name) is the natural key.","type":"string"},"passwordHash":{"description":"Credential material. PasswordHash is a one-way bcrypt digest and is\nverify-only. It MUST be persisted (orm serializes the entity to its JSON\ndata column, so a json:\"-\" field would never be stored — that silently\nbroke login), so it carries a real json tag; the users API redact() strips\nit (and every other secret) from every response. PasswordType and\nPasswordSalt describe the digest scheme so rows hashed under the legacy\nargon2id scheme can still be verified and lazily re-hashed to bcrypt.","type":"string"},"passwordSalt":{"type":"string"},"passwordType":{"type":"string"},"patreon":{"type":"string"},"paypal":{"type":"string"},"permanentAvatar":{"type":"string"},"permissions":{"items":{"$ref":"#/components/schemas/iam.Permission"},"type":"array"},"phone":{"type":"string"},"preHash":{"type":"string"},"preferredMfaType":{"type":"string"},"properties":{"additionalProperties":{"type":"string"},"type":"object"},"qq":{"type":"string"},"ranking":{"type":"integer"},"realName":{"type":"string"},"recoveryCodes":{"items":{"type":"string"},"type":"array"},"region":{"type":"string"},"registerSource":{"type":"string"},"registerType":{"type":"string"},"roles":{"description":"Authorization attachments. Roles and Permissions are computed on read\nfrom the authz store and carried here for API parity with v1.","items":{"$ref":"#/components/schemas/iam.Role"},"type":"array"},"salesforce":{"type":"string"},"score":{"type":"integer"},"shopify":{"type":"string"},"signinWrongTimes":{"type":"integer"},"signupApplication":{"type":"string"},"slack":{"type":"string"},"soundcloud":{"type":"string"},"spotify":{"type":"string"},"steam":{"type":"string"},"strava":{"type":"string"},"stripe":{"type":"string"},"tag":{"type":"string"},"telegram":{"type":"string"},"tiktok":{"type":"string"},"title":{"type":"string"},"totpSecret":{"type":"string"},"tumblr":{"type":"string"},"twitch":{"type":"string"},"twitter":{"type":"string"},"type":{"type":"string"},"typetalk":{"type":"string"},"uber":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedTime":{"type":"string"},"verificationCode":{"type":"string"},"vk":{"type":"string"},"webauthnCredentials":{"description":"Multi-factor authentication. TotpSecret and RecoveryCodes are secret\nverify-only material — the handler strips them from every response.\nWebauthnCredentials is carried as raw JSON here for lossless migration;\nthe typed passkey model is the sibling WebauthnCredential entity.","items":{},"type":"array"},"wechat":{"type":"string"},"wecom":{"type":"string"},"weibo":{"type":"string"},"wepay":{"type":"string"},"xero":{"type":"string"},"yahoo":{"type":"string"},"yammer":{"type":"string"},"yandex":{"type":"string"},"zoom":{"type":"string"}},"type":"object"},"iam.WebauthnCredential":{"properties":{"aaguid":{"contentEncoding":"base64","type":"string"},"attachment":{"type":"string"},"attestationType":{"type":"string"},"backupEligible":{"type":"boolean"},"backupState":{"type":"boolean"},"cloneWarning":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"credentialId":{"contentEncoding":"base64","type":"string"},"deleted":{"type":"boolean"},"id":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"publicKey":{"contentEncoding":"base64","type":"string"},"signCount":{"type":"integer"},"transport":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"user":{"type":"string"},"userPresent":{"type":"boolean"},"userVerified":{"type":"boolean"}},"type":"object"},"iam.Workspace":{"properties":{"bucket":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdTime":{"type":"string"},"deleted":{"type":"boolean"},"description":{"type":"string"},"displayName":{"type":"string"},"id":{"type":"string"},"isDefault":{"type":"boolean"},"metadata":{"type":"string"},"name":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"iam.auditlogs.Input":{"properties":{"action":{"type":"string"},"clientIp":{"type":"string"},"createdTime":{"type":"string"},"isTriggered":{"type":"boolean"},"language":{"type":"string"},"method":{"type":"string"},"name":{"type":"string"},"object":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"requestUri":{"type":"string"},"response":{"type":"string"},"statusCode":{"type":"integer"},"user":{"type":"string"}},"type":"object"},"iam.bulk":{"properties":{"maxOperations":{"type":"integer"},"maxPayloadSize":{"type":"integer"},"supported":{"type":"boolean"}},"type":"object"},"iam.certs.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.certs.ListOutput":{"properties":{"certs":{"items":{"$ref":"#/components/schemas/iam.Cert"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"iam.certs.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.config":{"properties":{"authenticationSchemes":{"items":{"$ref":"#/components/schemas/iam.scheme"},"type":"array"},"bulk":{"$ref":"#/components/schemas/iam.bulk"},"changePassword":{"$ref":"#/components/schemas/iam.toggle"},"documentationUri":{"type":"string"},"etag":{"$ref":"#/components/schemas/iam.toggle"},"filter":{"$ref":"#/components/schemas/iam.filter"},"patch":{"$ref":"#/components/schemas/iam.toggle"},"schemas":{"items":{"type":"string"},"type":"array"},"sort":{"$ref":"#/components/schemas/iam.toggle"}},"type":"object"},"iam.filter":{"properties":{"maxResults":{"type":"integer"},"supported":{"type":"boolean"}},"type":"object"},"iam.invitations.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.invitations.Input":{"properties":{"application":{"type":"string"},"code":{"type":"string"},"createdTime":{"type":"string"},"defaultCode":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"isRegexp":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"phone":{"type":"string"},"quota":{"type":"integer"},"signupGroup":{"type":"string"},"state":{"type":"string"},"updatedTime":{"type":"string"},"usedCount":{"type":"integer"},"username":{"type":"string"}},"type":"object"},"iam.invitations.ListOutput":{"properties":{"invitations":{"items":{"$ref":"#/components/schemas/iam.Invitation"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"iam.invitations.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.keys.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.listProvidersOut":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/iam.Provider"},"type":"array"}},"type":"object"},"iam.listResponse":{"properties":{"Resources":{"items":{"type":"object"},"type":"array"},"itemsPerPage":{"type":"integer"},"schemas":{"items":{"type":"string"},"type":"array"},"startIndex":{"type":"integer"},"totalResults":{"type":"integer"}},"type":"object"},"iam.listTokensOut":{"properties":{"tokens":{"items":{"$ref":"#/components/schemas/iam.Token"},"type":"array"}},"type":"object"},"iam.listWebauthnCredentialsOut":{"properties":{"webauthnCredentials":{"items":{"$ref":"#/components/schemas/iam.WebauthnCredential"},"type":"array"}},"type":"object"},"iam.mutationResult":{"properties":{"affected":{"type":"boolean"},"provider":{"$ref":"#/components/schemas/iam.Provider"}},"type":"object"},"iam.permission.DeleteResponse":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.permission.ListResponse":{"properties":{"permissions":{"items":{"$ref":"#/components/schemas/iam.Permission"},"type":"array"}},"type":"object"},"iam.permission.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.person":{"properties":{"displayName":{"type":"string"},"email":{"type":"string"},"isAdmin":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"password":{"type":"string"},"passwordType":{"type":"string"},"phone":{"type":"string"}},"type":"object"},"iam.projects.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.projects.ListOutput":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/iam.Project"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"iam.projects.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.providerKey":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name"],"type":"object"},"iam.providerResult":{"properties":{"provider":{"$ref":"#/components/schemas/iam.Provider"}},"type":"object"},"iam.registration":{"properties":{"cert":{"type":"string"},"clientId":{"type":"string"},"clientSecret":{"type":"string"},"displayName":{"type":"string"},"expireInHours":{"description":"ExpireInHours and RefreshExpireInHours are the application's token\nlifetimes. They are the ONLY declarative way to say that a refresh token\nmust OUTLIVE its access token: with neither stated, oidc.refreshTTL clamps\nthe refresh lifetime to the access lifetime, so the refresh_token grant the\nregistration advertises expires at the same instant as the token it was\nmeant to renew and can never be exercised. `hanzo-cli` sat in exactly that\nstate — a browser re-login every hour, and a live refresh returning 401.\n\nPOINTERS, for the same reason as IsShared: a plain float would read as 0 on\nevery reconcile that says nothing and reset a deliberate lifetime back to\nthe default. Nil means \"not stated, leave it\".","type":"number"},"grantTypes":{"items":{"type":"string"},"type":"array"},"isShared":{"description":"IsShared declares that this application serves EVERY organization, not only\nthe one named in Organization. It is the honest description of a brand app —\nhanzo-id, hanzo-chat, a brand console — whose customers each live in their own\ntenant: self-service onboarding moves a founder OUT of the brand org, so\n`user.Owner != app.Organization` is the steady state and the app really does\nserve every org. Application.ServesOrg reads it as one of the three ways to\nsay yes.\n\nA POINTER because omission must PRESERVE. This upsert is the operator's\nsteady-state reconcile and most callers say nothing about sharing; a plain\nbool would read as false on every one of them and silently un-share an app —\nthe same shape of accident that de-secreted apps through update-application.\nNil means \"not stated, leave it\"; only an explicit true or false moves it.","type":"boolean"},"name":{"type":"string"},"organization":{"type":"string"},"public":{"description":"Public declares a client that CANNOT hold a credential — a browser SPA,\na CLI, a desktop app. It proves itself with PKCE instead, and the token\nendpoint treats \"no stored secret\" as exactly that (token.go: a secret is\nverified only when one is stored). Without this flag every upsert minted\na secret, so a public client could never be registered at all and its\nbrowser code-\u003etoken exchange 401'd `invalid_client` forever.","type":"boolean"},"redirectUris":{"items":{"type":"string"},"type":"array"},"refreshExpireInHours":{"type":"number"}},"type":"object"},"iam.reply":{"properties":{"action":{"type":"string"},"data":{"type":"object"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"iam.roles.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.roles.Input":{"properties":{"createdTime":{"type":"string"},"description":{"type":"string"},"displayName":{"type":"string"},"domains":{"items":{"type":"string"},"type":"array"},"groups":{"items":{"type":"string"},"type":"array"},"isEnabled":{"type":"boolean"},"name":{"type":"string"},"owner":{"type":"string"},"roles":{"items":{"type":"string"},"type":"array"},"users":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.roles.ListOutput":{"properties":{"roles":{"items":{"$ref":"#/components/schemas/iam.Role"},"type":"array"},"total":{"type":"integer"}},"type":"object"},"iam.roles.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iam.scheme":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"primary":{"type":"boolean"},"type":{"type":"string"}},"type":"object"},"iam.toggle":{"properties":{"supported":{"type":"boolean"}},"type":"object"},"iam.tokenKey":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name"],"type":"object"},"iam.tokenMutation":{"properties":{"affected":{"type":"boolean"},"token":{"$ref":"#/components/schemas/iam.Token"}},"type":"object"},"iam.tokenResult":{"properties":{"token":{"$ref":"#/components/schemas/iam.Token"}},"type":"object"},"iam.userBody":{"properties":{"accessKey":{"type":"string"},"accessSecret":{"type":"string"},"accessSecretHash":{"type":"string"},"accessToken":{"type":"string"},"address":{"items":{"type":"string"},"type":"array"},"addresses":{"items":{"$ref":"#/components/schemas/iam.Address"},"type":"array"},"adfs":{"type":"string"},"affiliation":{"type":"string"},"alipay":{"type":"string"},"amazon":{"type":"string"},"apple":{"type":"string"},"applicationScopes":{"items":{"$ref":"#/components/schemas/iam.ConsentRecord"},"type":"array"},"auth0":{"type":"string"},"avatar":{"type":"string"},"avatarType":{"type":"string"},"azuread":{"type":"string"},"azureadb2c":{"type":"string"},"baidu":{"type":"string"},"balance":{"type":"number"},"balanceCredit":{"type":"number"},"balanceCurrency":{"type":"string"},"battlenet":{"type":"string"},"bilibili":{"type":"string"},"bio":{"type":"string"},"birthday":{"type":"string"},"bitbucket":{"type":"string"},"box":{"type":"string"},"cart":{"items":{"$ref":"#/components/schemas/iam.CartItem"},"type":"array"},"cloudfoundry":{"type":"string"},"countryCode":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"createdIp":{"type":"string"},"createdTime":{"type":"string"},"currency":{"type":"string"},"custom":{"type":"string"},"custom10":{"type":"string"},"custom2":{"type":"string"},"custom3":{"type":"string"},"custom4":{"type":"string"},"custom5":{"type":"string"},"custom6":{"type":"string"},"custom7":{"type":"string"},"custom8":{"type":"string"},"custom9":{"type":"string"},"dailymotion":{"type":"string"},"deezer":{"type":"string"},"deleted":{"type":"boolean"},"deletedTime":{"type":"string"},"digitalocean":{"type":"string"},"dingtalk":{"type":"string"},"discord":{"type":"string"},"displayName":{"type":"string"},"douyin":{"type":"string"},"dropbox":{"type":"string"},"education":{"type":"string"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"eveonline":{"type":"string"},"externalId":{"type":"string"},"faceIds":{"items":{"$ref":"#/components/schemas/iam.FaceId"},"type":"array"},"facebook":{"type":"string"},"firstName":{"type":"string"},"fitbit":{"type":"string"},"gender":{"type":"string"},"gitea":{"type":"string"},"gitee":{"type":"string"},"github":{"type":"string"},"gitlab":{"type":"string"},"google":{"type":"string"},"groups":{"items":{"type":"string"},"type":"array"},"hash":{"type":"string"},"heroku":{"type":"string"},"homepage":{"type":"string"},"iam":{"type":"string"},"id":{"type":"string"},"idCard":{"type":"string"},"idCardType":{"type":"string"},"influxcloud":{"type":"string"},"infoflow":{"type":"string"},"instagram":{"type":"string"},"intercom":{"type":"string"},"invitation":{"type":"string"},"invitationCode":{"type":"string"},"ipWhitelist":{"type":"string"},"isAdmin":{"type":"boolean"},"isDefaultAvatar":{"type":"boolean"},"isDeleted":{"type":"boolean"},"isForbidden":{"type":"boolean"},"isOnline":{"type":"boolean"},"isVerified":{"type":"boolean"},"kakao":{"type":"string"},"karma":{"type":"integer"},"kwai":{"type":"string"},"language":{"type":"string"},"lark":{"type":"string"},"lastChangePasswordTime":{"type":"string"},"lastName":{"type":"string"},"lastSigninIp":{"type":"string"},"lastSigninTime":{"type":"string"},"lastSigninWrongTime":{"type":"string"},"lastfm":{"type":"string"},"ldap":{"type":"string"},"line":{"type":"string"},"linkedin":{"type":"string"},"location":{"type":"string"},"mailru":{"type":"string"},"managedAccounts":{"items":{"$ref":"#/components/schemas/iam.ManagedAccount"},"type":"array"},"meetup":{"type":"string"},"mfaAccounts":{"items":{"$ref":"#/components/schemas/iam.MfaAccount"},"type":"array"},"mfaEmailEnabled":{"type":"boolean"},"mfaItems":{"items":{"$ref":"#/components/schemas/iam.MfaItem"},"type":"array"},"mfaPhoneEnabled":{"type":"boolean"},"mfaPushEnabled":{"type":"boolean"},"mfaPushProvider":{"type":"string"},"mfaPushReceiver":{"type":"string"},"mfaRadiusEnabled":{"type":"boolean"},"mfaRadiusProvider":{"type":"string"},"mfaRadiusUsername":{"type":"string"},"mfaRememberDeadline":{"type":"string"},"microsoftonline":{"type":"string"},"multiFactorAuths":{"items":{"$ref":"#/components/schemas/iam.MfaProps"},"type":"array"},"name":{"type":"string"},"naver":{"type":"string"},"needUpdatePassword":{"type":"boolean"},"nextcloud":{"type":"string"},"okta":{"type":"string"},"onedrive":{"type":"string"},"originalRefreshToken":{"type":"string"},"originalToken":{"type":"string"},"oura":{"type":"string"},"owner":{"type":"string"},"password":{"type":"string"},"passwordHash":{"type":"string"},"passwordSalt":{"type":"string"},"passwordType":{"type":"string"},"patreon":{"type":"string"},"paypal":{"type":"string"},"permanentAvatar":{"type":"string"},"permissions":{"items":{"$ref":"#/components/schemas/iam.Permission"},"type":"array"},"phone":{"type":"string"},"preHash":{"type":"string"},"preferredMfaType":{"type":"string"},"properties":{"additionalProperties":{"type":"string"},"type":"object"},"qq":{"type":"string"},"ranking":{"type":"integer"},"realName":{"type":"string"},"recoveryCodes":{"items":{"type":"string"},"type":"array"},"region":{"type":"string"},"registerSource":{"type":"string"},"registerType":{"type":"string"},"roles":{"items":{"$ref":"#/components/schemas/iam.Role"},"type":"array"},"salesforce":{"type":"string"},"score":{"type":"integer"},"shopify":{"type":"string"},"signinWrongTimes":{"type":"integer"},"signupApplication":{"type":"string"},"slack":{"type":"string"},"soundcloud":{"type":"string"},"spotify":{"type":"string"},"steam":{"type":"string"},"strava":{"type":"string"},"stripe":{"type":"string"},"tag":{"type":"string"},"telegram":{"type":"string"},"tiktok":{"type":"string"},"title":{"type":"string"},"totpSecret":{"type":"string"},"tumblr":{"type":"string"},"twitch":{"type":"string"},"twitter":{"type":"string"},"type":{"type":"string"},"typetalk":{"type":"string"},"uber":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedTime":{"type":"string"},"verificationCode":{"type":"string"},"vk":{"type":"string"},"webauthnCredentials":{"items":{},"type":"array"},"wechat":{"type":"string"},"wecom":{"type":"string"},"weibo":{"type":"string"},"wepay":{"type":"string"},"xero":{"type":"string"},"yahoo":{"type":"string"},"yammer":{"type":"string"},"yandex":{"type":"string"},"zoom":{"type":"string"}},"type":"object"},"iam.users.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.users.ListOutput":{"properties":{"total":{"type":"integer"},"users":{"items":{"$ref":"#/components/schemas/iam.User"},"type":"array"}},"type":"object"},"iam.users.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name"],"type":"object"},"iam.webauthnCredentialKey":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"required":["owner","name"],"type":"object"},"iam.webauthnCredentialMutationResult":{"properties":{"affected":{"type":"boolean"},"webauthnCredential":{"$ref":"#/components/schemas/iam.WebauthnCredential"}},"type":"object"},"iam.webauthnCredentialResult":{"properties":{"webauthnCredential":{"$ref":"#/components/schemas/iam.WebauthnCredential"}},"type":"object"},"iam.workspaces.DeleteOutput":{"properties":{"deleted":{"type":"boolean"}},"type":"object"},"iam.workspaces.Input":{"properties":{"bucket":{"type":"string"},"createdTime":{"type":"string"},"description":{"type":"string"},"displayName":{"type":"string"},"isDefault":{"type":"boolean"},"metadata":{"type":"string"},"name":{"type":"string"},"organization":{"type":"string"},"owner":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"iam.workspaces.ListOutput":{"properties":{"total":{"type":"integer"},"workspaces":{"items":{"$ref":"#/components/schemas/iam.Workspace"},"type":"array"}},"type":"object"},"iam.workspaces.Ref":{"properties":{"name":{"type":"string"},"owner":{"type":"string"}},"type":"object"},"iamRowsOut":{"properties":{"data":{"type":"object"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"imageOrigin":{"properties":{"repository":{"description":"Repository is the image repository. Required for source `image`.","type":"string"},"tag":{"description":"Tag is the image tag to deploy; `latest` when omitted.","type":"string"}},"type":"object"},"imageView":{"properties":{"repository":{"type":"string"},"tag":{"type":"string"}},"type":"object"},"importCapTableIn":{"properties":{"range":{"description":"Range is an optional A1 range within the sheet; empty reads the default range.","type":"string"},"spreadsheetId":{"description":"SpreadsheetID is a Google Sheets id. Required.","type":"string"}},"type":"object"},"importCapTableOut":{"properties":{"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's incorporation record, now marked cap-table-imported."},"rows":{"description":"Rows is how many rows were read from the sheet, header included.","type":"integer"},"stakeholdersImported":{"description":"StakeholdersImported is how many stakeholders the cap table accepted.","type":"integer"}},"type":"object"},"importDocumentsIn":{"properties":{"folderId":{"description":"FolderID is a Google Drive folder id. Required.","type":"string"}},"type":"object"},"importDocumentsOut":{"properties":{"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's incorporation record with the imported document ids."},"ingested":{"description":"Ingested is how many files this call put in the data room.","type":"integer"}},"type":"object"},"inboxOut":{"properties":{"items":{"description":"Items is every document still unsorted or in draft, newest first.","items":{"$ref":"#/components/schemas/InboxItem"},"type":"array"}},"type":"object"},"inboxPage":{"properties":{"cursor":{"description":"Cursor is the row id to pass back as `since` for the next page. It is the\nlast message's id, or the requested cursor when the page is empty.","type":"integer"},"messages":{"description":"Messages are the inbound messages, oldest first.","items":{"$ref":"#/components/schemas/inboxView"},"type":"array"}},"type":"object"},"inboxView":{"properties":{"account":{"description":"Account is the lowercased external id of the org's connected account on that\ntransport: the Discord guild id, the Slack team id, the Teams AAD tenant id,\nor the bound Telegram chat id. Informational only — the gate keys on\n(org, channel), never on the account.","type":"string"},"channel":{"description":"Channel is the transport this message arrived on — discord, slack, teams or\ntelegram — and the `:channel` segment to reply through.","type":"string"},"createdAt":{"description":"CreatedAt is Unix SECONDS, stamped by the ingest goroutine when the message\nwas accepted — not the transport's own send time. Rows are dropped 30 days\nafter it.","type":"integer"},"id":{"description":"ID is the store's row id, assigned on insert — SERVER-SET, and the cursor:\npass a page's last id back as `since`. It rises with arrival order but is\nnot contiguous, because one sequence is shared by every org in the store and\na caller reads only its own rows.","type":"integer"},"replyTo":{"description":"ReplyTo is the transport's reply target for this message: Slack's thread_ts,\nor the Telegram message id it arrived as. Send it back as the body's\n`replyTo` to answer in the SAME thread. Empty means the transport reported\nnone — a top-level Slack message, and every Discord and Teams message, since\nneither carries one — and a reply then lands at the top level of the room.","type":"string"},"roomId":{"description":"RoomID is the conversation on the ORIGINATING transport, and the value to\nsend back as `room.id`: a Discord channel snowflake, a Slack conversation id\n(D… IM, C… public channel, G… private or mpim), a Teams conversation id\n(19:…@thread.… for a channel or group chat, a:… for a personal chat), or a\nTelegram chat id in decimal (negative for a group, positive for a DM). It is\nstable for the life of the room, so every message from one conversation\ncarries the same value.","type":"string"},"roomKind":{"description":"RoomKind is how ingest classified the room: \"dm\", \"group\" or \"thread\". It\ndecides which policy gated the message — dmPolicy for \"dm\", groupPolicy for\nBOTH \"group\" and \"thread\". Only Slack ever reports \"thread\"; Telegram's\nreply-to id becomes ReplyTo instead, and Discord's ingress is guild-scoped\nso its rooms are always \"group\".","type":"string"},"sender":{"description":"Sender is the TRANSPORT-NATIVE user id of whoever wrote the message — a\nDiscord member.user.id, a Slack U… user id, a Teams aadObjectId (falling\nback to from.id), a Telegram from.id in decimal. Stable per person per\ntransport, and the identity the gate keys on: an allow entry, an access-group\nmember and a pairing approval all name exactly this value.","type":"string"},"senderUser":{"description":"SenderUser is the HANZO account subject that chat identity is linked to,\nresolved at ingest through the org's user link. Best-effort and omitted when\nabsent: a person who never linked their chat account — or a link store that\ncould not be read — leaves it empty and is never blocked for it.","type":"string"},"text":{"description":"Text is the body as the transport delivered it, with the bot mention already\nstripped by the ingress adapter (on Discord it is the /hanzo prompt argument,\nsince that ingress is slash commands only), truncated to 8 KiB on store.\nInbound attachments are not stored — this is the whole of what was said.","type":"string"}},"type":"object"},"indexIn":{"properties":{"files":{"description":"Files is the full set of files to index. Required and non-empty; max 20000\nfiles, 1 MiB per file and 1 GiB in total. Unchanged files are skipped by\ncontent hash, so re-sending the whole tree is cheap.","items":{"$ref":"#/components/schemas/fileInput"},"type":"array"},"prune":{"description":"Prune deletes indexed files that are NOT in this request — which makes the\ncall a full sync of the repo rather than an upsert. Only pass it when Files\nis the complete tree.","type":"boolean"},"repo":{"description":"Repo is the repository label to index under. Required, max 200 bytes. It is\na stored column value, not a filesystem path.","type":"string"}},"type":"object"},"indexResult":{"properties":{"chunks":{"description":"Chunks is how many AST-boundary chunks the repo holds after this pass.","type":"integer"},"files":{"description":"Files is how many files the repo holds after this pass.","type":"integer"},"indexed":{"description":"Indexed is how many files were parsed and written on this pass.","type":"integer"},"pruned":{"description":"Pruned is how many stored files were deleted because prune was set and they\nwere absent from the request.","type":"integer"},"repo":{"description":"Repo is the repository that was indexed.","type":"string"},"semantic":{"description":"Semantic reports whether the semantic tier was available for this pass. When\nfalse the index is lexical + symbolic only and hybrid search still works.","type":"boolean"},"skipped":{"description":"Skipped is how many files were unchanged by content hash and left alone.","type":"integer"},"symbols":{"description":"Symbols is how many symbol definitions the repo holds after this pass.","type":"integer"},"vectors":{"description":"Vectors is how many of those chunks carry an embedding.","type":"integer"}},"type":"object"},"indexerView":{"properties":{"chain":{"description":"Chain is the chain this indexer indexes, as the indexer names it.","type":"string"},"height":{"description":"Height is the latest INDEXED block height, as a decimal string. Absent when\nnothing has been indexed yet.","type":"string"},"id":{"description":"ID identifies the indexer: its chain name, else its chain id, else the brand.","type":"string"},"lag":{"description":"Lag is how far behind the chain HEAD this indexer is. The indexer REST does not\nexpose the head, so it is always absent rather than a fabricated zero.","type":"string"},"network":{"description":"Network is the deployment's network tier: mainnet, testnet or devnet.","type":"string"},"status":{"description":"Status is \"degraded\" when /health explicitly reports unhealthy, else \"active\".","type":"string"},"updatedAt":{"description":"UpdatedAt is the latest indexed block's timestamp, RFC 3339 UTC.","type":"string"}},"type":"object"},"indexersOut":{"properties":{"indexers":{"description":"Indexers is one row per reachable chain indexer, or an empty list when the\nindexer is unreachable — never a fabricated row.","items":{"$ref":"#/components/schemas/indexerView"},"type":"array"}},"type":"object"},"infoOut":{"properties":{"jetstream":{"description":"JetStream is true when durable streams are enabled.","type":"boolean"},"max_payload":{"description":"MaxPayload is the broker's message-size ceiling in bytes.","type":"integer"},"server_id":{"description":"Server is the broker's server id.","type":"string"},"server_name":{"description":"Name is the broker's server name.","type":"string"},"streams":{"description":"Streams is the org's stream count.","type":"integer"},"version":{"description":"Version is the broker's server version.","type":"string"}},"type":"object"},"ingestOut":{"properties":{"attempts_ingested":{"description":"AttemptsIngested is how many attempt versions this call appended.","type":"integer"},"attempts_retained":{"description":"AttemptsRetained is the full versioned attempt history the store now holds.","type":"integer"},"canonical_attempts":{"description":"CanonicalAttempts is the deduped attempt count the store now holds.","type":"integer"},"canonical_experiments":{"description":"CanonicalExperiments is the deduped experiment count the store now holds.","type":"integer"},"experiments_ingested":{"description":"ExperimentsIngested is how many experiment versions this call appended.","type":"integer"},"experiments_retained":{"description":"ExperimentsRetained is the full versioned experiment history the store now holds.","type":"integer"},"project":{"description":"Project is the project the batch was filed under — the SERVER's value, never the body's.","type":"string"},"rolled_up":{"description":"RolledUp is false when the OLAP roll-up was skipped; the SQLite write still stands.","type":"boolean"}},"type":"object"},"ingestReq":{"properties":{"account":{"type":"string"},"cachedInputTokens":{"type":"integer"},"confidence":{"type":"string"},"costCents":{"type":"integer"},"costLimitCents":{"type":"integer"},"currency":{"type":"string"},"inputTokens":{"type":"integer"},"kind":{"type":"string"},"lane":{"type":"string"},"machine":{"type":"string"},"outputTokens":{"type":"integer"},"plan":{"type":"string"},"provider":{"type":"string"},"requests":{"type":"integer"},"resetsAt":{"type":"string"},"samples":{"description":"Samples is the batch form, up to 256 samples; leave it empty to send one\nsample inline on the same fields.","items":{"$ref":"#/components/schemas/readingReq"},"type":"array"},"synthetic":{"type":"boolean"},"totalTokens":{"type":"integer"},"usedPct":{"type":"number"},"window":{"type":"string"},"windowMinutes":{"type":"integer"},"windowStart":{"type":"string"}},"type":"object"},"ingestResp":{"properties":{"accepted":{"description":"Accepted is how many samples this report landed.","type":"integer"},"links":{"description":"Links is the link row each distinct (machine, provider, account) in the\nbatch refreshed.","items":{"$ref":"#/components/schemas/linkView"},"type":"array"},"stored":{"description":"Stored reports whether history was durably written; false means the\nwarehouse was unavailable and only the link rows were refreshed.","type":"boolean"}},"type":"object"},"ingressMiddlewares":{"properties":{"middlewares":{"description":"Middlewares is the org's middlewares, ordered by id.","items":{"$ref":"#/components/schemas/Middleware"},"type":"array"}},"type":"object"},"ingressRoutes":{"properties":{"routes":{"description":"Routes is the org's routes, ordered by id.","items":{"$ref":"#/components/schemas/Route"},"type":"array"}},"type":"object"},"ingressServices":{"properties":{"services":{"description":"Services is the org's services, ordered by id.","items":{"$ref":"#/components/schemas/Service"},"type":"array"}},"type":"object"},"ingressStatus":{"properties":{"acmeCacheDir":{"description":"ACMECacheDir is where autocert persists accounts and certificates.","type":"string"},"acmeStaging":{"description":"ACMEStaging is true when certificates are issued from Let's Encrypt staging.","type":"boolean"},"edgeEnabled":{"description":"EdgeEnabled is true when the edge listeners are actually bound.","type":"boolean"},"httpAddr":{"description":"HTTPAddr is the address the ACME HTTP-01 + HTTP router listens on.","type":"string"},"httpsAddr":{"description":"HTTPSAddr is the address the SNI TLS terminator listens on.","type":"string"},"liveHosts":{"description":"LiveHosts is how many hosts the compiled table routes.","type":"integer"},"proxy":{"description":"Proxy names the reverse-proxy implementation behind every route.","type":"string"},"role":{"description":"Role is \"edge\" when CLOUD_INGRESS_EDGE_ENABLED is set, else \"app\".","type":"string"},"tlsHosts":{"description":"TLSHosts is how many hosts the ACME HostPolicy will issue a certificate\nfor. NOT a subset of LiveHosts: an extraHost owns no route, and a TLS route\nnaming a missing service is skipped while its host still wants a cert.","type":"integer"}},"type":"object"},"ingressTLS":{"properties":{"acmeDirectory":{"description":"ACMEDirectory is the ACME endpoint in use: the staging URL, or\n\"letsencrypt-production\".","type":"string"},"acmeEmail":{"description":"ACMEEmail is the account email the PROCESS was started with\n(CLOUD_INGRESS_ACME_EMAIL), not the stored config's.","type":"string"},"config":{"$ref":"#/components/schemas/TLSConfig","description":"Config is the caller org's stored ACME intent."},"edgeEnabled":{"description":"EdgeEnabled is true when the edge listeners are actually bound.","type":"boolean"},"managedHosts":{"description":"ManagedHosts is every host the ACME HostPolicy will issue a certificate for\n— the union across ALL orgs of TLS-marked routes and configured extraHosts,\nbecause one process holds one certificate cache.","items":{"type":"string"},"type":"array"},"note":{"description":"Note states which fields hot-apply and which need an edge restart.","type":"string"},"role":{"description":"Role is \"edge\" when this instance binds the listeners, else \"app\".","type":"string"}},"type":"object"},"insightsBody":{"properties":{"batch":{"items":{"$ref":"#/components/schemas/insightsEvent"},"type":"array"},"distinct_id":{"type":"string"},"event":{"type":"string"},"properties":{"additionalProperties":{},"type":"object"},"timestamp":{"type":"string"},"uuid":{"type":"string"}},"type":"object"},"insightsEvent":{"properties":{"distinct_id":{"type":"string"},"event":{"type":"string"},"properties":{"additionalProperties":{},"type":"object"},"timestamp":{"type":"string"},"uuid":{"type":"string"}},"type":"object"},"insightsStatus":{"properties":{"engine":{"description":"Engine names the engine serving the surface: hanzo-analytics.","type":"string"},"ok":{"description":"OK is always true — reaching this route is the liveness fact it reports.","type":"boolean"},"surface":{"description":"Surface is the path prefix this status covers: /v1/insights.","type":"string"}},"type":"object"},"installReq":{"properties":{"tool":{"description":"Tool is the registry name of the capability to activate (or deactivate) for\nthe caller's own org and project. Required.","type":"string"}},"type":"object"},"installState":{"properties":{"installed":{"description":"Installed is its activation after the write.","type":"boolean"},"tool":{"description":"Tool is the capability the write applied to.","type":"string"}},"type":"object"},"issuePatch":{"properties":{"assignee":{"description":"Assignee is who owns the issue, at most 256 characters. Empty unassigns it.","type":"string"},"description":{"description":"Description is the issue body, at most 32768 characters.","type":"string"},"dueAt":{"description":"DueAt is when the work is due, in unix seconds — the right edge of its\nbar, or the milestone marker when there is no start. 0 clears it. It may\nnot fall before startAt.","type":"integer"},"key":{"description":"Key is the issue's project, from the path.","type":"string"},"labels":{"description":"Labels REPLACES the issue's labels with exactly this set. Each label is at\nmost 48 characters and may not contain a comma (the storage separator);\nempty entries are dropped.","items":{"type":"string"},"type":"array"},"num":{"description":"Num is the issue's number within that project, from the path.","type":"integer"},"priority":{"description":"Priority is none, urgent, high, medium or low. Empty resets it to none.","type":"string"},"startAt":{"description":"StartAt is when the work starts, in unix seconds — the left edge of its\nbar on the timeline. 0 clears it.","type":"integer"},"status":{"description":"Status moves the issue between board columns: backlog, todo, in_progress,\ndone or canceled. Empty resets it to backlog.","type":"string"},"title":{"description":"Title is the issue's one-line summary. Non-empty, at most 512 characters.","type":"string"}},"type":"object"},"issueView":{"properties":{"assignee":{"type":"string"},"createdAt":{"type":"integer"},"description":{"type":"string"},"dueAt":{"description":"unix seconds; absent = no due date","type":"integer"},"extRef":{"description":"external anchor","type":"string"},"id":{"type":"string"},"identifier":{"description":"KEY-\u003cnumber\u003e, the human handle","type":"string"},"kind":{"description":"issue | pr | epic","type":"string"},"labels":{"items":{"type":"string"},"type":"array"},"number":{"type":"integer"},"priority":{"type":"string"},"projectKey":{"type":"string"},"repo":{"description":"git repo binding","type":"string"},"source":{"description":"team | git | crm | helpdesk | cms | agent","type":"string"},"startAt":{"description":"unix seconds; absent = unscheduled","type":"integer"},"status":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"jobCancel":{"properties":{"id":{"description":"ID is the job (activity) id, from the URL path.","type":"string"},"reason":{"description":"Reason is recorded on the cancellation; empty records \"canceled from console\".","type":"string"},"run":{"description":"Run is the run id; empty defaults to the job id, which is what the dispatcher\nsets (runId == activityId == prompt_id), so the common case sends no body.","type":"string"}},"type":"object"},"jobCanceled":{"properties":{"canceled":{"description":"Canceled is the job id that was canceled.","type":"string"},"run":{"description":"Run is the run id the cancel was applied to.","type":"string"}},"type":"object"},"jobList":{"properties":{"jobs":{"description":"Jobs is the queue, most-recent-first. Every LIVE job is present; terminal\nhistory is capped, so a busy org's running work is never crowded out.","items":{"$ref":"#/components/schemas/gpuJob"},"type":"array"}},"type":"object"},"kbAuthorizeOut":{"properties":{"authorizeUrl":{"description":"AuthorizeURL is the provider's authorize endpoint with an org-bound signed state.","type":"string"}},"type":"object"},"kbConnectorsOut":{"properties":{"connectors":{"description":"Connectors is every supported provider with this org's connection state.","items":{"$ref":"#/components/schemas/connectorView"},"type":"array"}},"type":"object"},"kbSyncOut":{"properties":{"ingested":{"description":"Ingested is how many documents landed in the org's knowledge store.","type":"integer"},"provider":{"description":"Provider is the connector that was pulled.","type":"string"}},"type":"object"},"keyList":{"properties":{"data":{"description":"Data holds the org's keys.","items":{"$ref":"#/components/schemas/keyView"},"type":"array"}},"type":"object"},"keyTypeIn":{"properties":{"type":{"description":"Type is the key class to act on: \"secret\" (sk-, session-equivalent, belongs\non a server) or \"publishable\" (pk-, org-identifying, safe in a browser\nbundle). Omitted means secret, which is what every existing caller means.","type":"string"}},"type":"object"},"keyView":{"properties":{"createdAt":{"description":"CreatedAt is RFC 3339 UTC.","type":"string"},"fingerprint":{"description":"Fingerprint is the key's SHA256 fingerprint (\"SHA256:…\"), globally unique\nand the handle SSH auth resolves a presented key by.","type":"string"},"id":{"description":"ID is the key's identifier (\"gitkey_…\"), the handle to delete it by.","type":"string"},"publicKey":{"description":"PublicKey is the canonical OpenSSH authorized-key line as stored.","type":"string"},"title":{"description":"Title is the key's label — the caller's, or the comment on the key line.","type":"string"}},"type":"object"},"kitList":{"properties":{"data":{"description":"Data is the public catalog followed by the caller org's own kits.","items":{"$ref":"#/components/schemas/StarterKit"},"type":"array"}},"type":"object"},"kvAck":{"properties":{"revision":{"description":"Revision is the revision the write created.","type":"integer"}},"type":"object"},"kvEntry":{"properties":{"created":{"description":"Created is when this revision was written, RFC3339.","type":"string"},"key":{"description":"Key is the entry's key.","type":"string"},"operation":{"description":"Operation is what wrote the revision: put, del or purge.","type":"string"},"revision":{"description":"Revision is the entry's revision in the bucket.","type":"integer"},"value":{"description":"Value is the value as UTF-8 text; empty for delete and purge markers.","type":"string"}},"type":"object"},"kvPage":{"properties":{"data":{"description":"Data are the key's retained revisions.","items":{"$ref":"#/components/schemas/kvEntry"},"type":"array"}},"type":"object"},"kvWrite":{"properties":{"bucket":{"description":"Bucket is the bucket, from the path.","type":"string"},"key":{"description":"Key is the key, from the path.","type":"string"},"value":{"description":"Value is the value, carried verbatim as UTF-8 text (typically JSON).","type":"string"}},"type":"object"},"kycRefreshOut":{"properties":{"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's incorporation record with each founder's reconciled status."},"provider":{"description":"Provider is the identity-verification provider that was consulted.","type":"string"}},"type":"object"},"kycSession":{"properties":{"email":{"description":"Email is the founder the session belongs to.","type":"string"},"ref":{"description":"Ref is the provider's reference for the session.","type":"string"},"status":{"description":"Status is the session's status at start, which is always pending.","type":"string"},"verifyUrl":{"description":"VerifyURL is the hosted flow the founder visits; empty for the manual provider.","type":"string"}},"type":"object"},"kycStartOut":{"properties":{"formation":{"$ref":"#/components/schemas/Formation","description":"Formation is the org's incorporation record, with each founder's session\nreference and status recorded on it."},"provider":{"description":"Provider is the wired identity-verification provider's name.","type":"string"},"sessions":{"description":"Sessions is one entry per founder, in the order the founders are recorded.","items":{"$ref":"#/components/schemas/kycSession"},"type":"array"}},"type":"object"},"lastEventView":{"properties":{"actor":{"type":"string"},"at":{"type":"string"},"kind":{"type":"string"},"preview":{"type":"string"},"seq":{"type":"integer"}},"type":"object"},"lbList":{"properties":{"loadBalancers":{"description":"LoadBalancers are the caller org's load balancers under their friendly names.","items":{"$ref":"#/components/schemas/lbView"},"type":"array"}},"type":"object"},"lbView":{"properties":{"id":{"type":"string"},"ip":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"targets":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"leaderboardRow":{"properties":{"accruedCents":{"description":"AccruedCents is that affiliate's lifetime commission accrued, in cents, and\nwhat the board is ordered by. An aggregate: no per-customer figure is exposed.","type":"integer"},"handle":{"description":"Handle is the affiliate's self-chosen display name — the only identity the\nboard ever carries. The org behind it is never disclosed.","type":"string"},"isYou":{"description":"IsYou marks the caller's own row, so a client can highlight it without\nmatching on a handle. Absent on every other row.","type":"boolean"},"rank":{"description":"Rank is the position in the GLOBAL approved set ordered by lifetime accrued\ncommission, 1-based. Affiliates that set no handle still occupy their rank and\nare simply not listed, so the visible ranks have gaps and the board is not a\ncomplete roster. On the caller's own row the rank is computed over the whole\nset, so it is exact well outside the top page.","type":"integer"},"referredCount":{"description":"ReferredCount is how many orgs that affiliate directly referred — a count\nonly, never which orgs.","type":"integer"}},"type":"object"},"legalFiling":{"properties":{"createdAt":{"type":"integer"},"documentIds":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"jurisdiction":{"type":"string"},"note":{"type":"string"},"org":{"type":"string"},"provider":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"legalHealth":{"properties":{"status":{"description":"Status is \"ok\" when the subsystem is serving.","type":"string"},"templates":{"description":"Templates is how many built-in templates the catalog carries.","type":"integer"}},"type":"object"},"legalSigner":{"properties":{"email":{"type":"string"},"name":{"type":"string"}},"type":"object"},"legalTemplate":{"properties":{"body":{"type":"string"},"category":{"type":"string"},"counselReview":{"type":"boolean"},"fields":{"items":{"$ref":"#/components/schemas/Field"},"type":"array"},"id":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"},"version":{"type":"integer"}},"type":"object"},"levelSplit":{"properties":{"l1Cents":{"description":"L1Cents is lifetime commission accrued to DIRECT referrers, in cents.","type":"integer"},"l2Cents":{"description":"L2Cents is lifetime commission accrued one step above the direct referrer, in\ncents, at the platform-wide level-2 rate.","type":"integer"},"l3Cents":{"description":"L3Cents is lifetime commission accrued two steps above, in cents. Nothing\naccrues past level 3, so l1+l2+l3 is the whole accrual.","type":"integer"}},"type":"object"},"levelView":{"properties":{"downlineCount":{"description":"DownlineCount is how many orgs sit exactly this many hops below the caller. It\nis 0 in the schedule quoted to a caller that has not applied, which has no\ndownline to count.","type":"integer"},"level":{"description":"Level is the upline distance from the org whose spend is being shared: 1 is\nthe direct referrer, 2 and 3 the referrers above it. Nothing accrues past 3.","type":"integer"},"rateBps":{"description":"RateBps is the commission paid at this level, in basis points OF Hanzo's\nmargin (2000 = 20% of margin, never of the customer's bill). Level 1 is the\naffiliate's own negotiated rate; 2 and 3 are platform switches read live, so\nthis is the schedule actually in force, not one compiled in.","type":"integer"}},"type":"object"},"limitsBlock":{"properties":{"apiRateLimit":{"description":"APIRateLimit is requests per minute allowed against the REST /v1/world\nsurface. -1 means unlimited.","type":"integer"},"maxAlerts":{"description":"MaxAlerts is how many saved OSINT alert rules the plan allows. -1 means\nunlimited.","type":"integer"},"mcpRateLimit":{"description":"MCPRateLimit is requests per minute allowed against the MCP surface. -1\nmeans unlimited.","type":"integer"},"modelApi":{"description":"ModelAPI is whether the plan reaches the World model endpoint and the SSE\nstream. The free floor is false, and that is what a catalog outage\nresolves to.","type":"boolean"}},"type":"object"},"limitsView":{"properties":{"limits":{"$ref":"#/components/schemas/limitsBlock","description":"Limits is the plan's decision."},"plan":{"description":"Plan echoes the plan id the limits were resolved for, after the empty-means-\nworld-free default.","type":"string"},"unit":{"description":"Unit names what the two rate numbers are counted in: requests/minute.","type":"string"}},"type":"object"},"linkList":{"properties":{"devices":{"description":"Devices is the same rows folded per machine — the cross-machine \"AI\nProviders / Accounts\" view.","items":{"$ref":"#/components/schemas/deviceView"},"type":"array"},"links":{"description":"Links is every link the caller registered, newest first. Revoked links are\nINCLUDED rather than dropped, because a logged-out account keeps its usage\nhistory and audit trail.","items":{"$ref":"#/components/schemas/linkView"},"type":"array"}},"type":"object"},"linkMint":{"properties":{"link":{"$ref":"#/components/schemas/codeView","description":"Link is the link just minted, with its full shareable URL. Its funnel counters\nall start at zero — nothing has clicked or signed up through it yet."}},"type":"object"},"linkView":{"properties":{"account":{"description":"Account is the provider-side account identifier, when the collector knows it.","type":"string"},"billing":{"description":"Billing is how this account's inference bills — plan (the user's own\nsubscription, metered here for visibility only) or commerce (the gateway\npath). Derived from Kind, never stored.","type":"string"},"createdAt":{"description":"CreatedAt is when the link was first registered, RFC 3339 UTC.","type":"string"},"host":{"description":"Host is the machine's human hostname label, from its most recent report.","type":"string"},"id":{"description":"ID is the link's opaque handle (\"link_\" + 32 hex chars).","type":"string"},"kind":{"description":"Kind is how the account authenticates: subscription or apikey.","type":"string"},"lastSeen":{"description":"LastSeen is when the account last reported, RFC 3339 UTC.","type":"string"},"machine":{"description":"Machine is the stable machine identifier the collector reports.","type":"string"},"os":{"description":"OS is the machine's operating system label.","type":"string"},"plan":{"description":"Plan is the provider plan label (e.g. \"Claude Max\").","type":"string"},"provider":{"description":"Provider is the AI provider this account belongs to (claude, openai, hanzo…).","type":"string"},"status":{"description":"Status is linked or revoked. Revoked rows are retained for history.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the link was last refreshed, RFC 3339 UTC.","type":"string"},"usage":{"description":"Usage is the last good usage snapshot, clamped and re-serialized to known\nfields at ingest."},"user":{"description":"User is the owning subject — the validated caller who registered the link.","type":"string"}},"type":"object"},"listOut":{"properties":{"providers":{"description":"Providers is the whole catalog. Never null; [] when nothing is registered.","items":{"$ref":"#/components/schemas/providerView"},"type":"array"}},"type":"object"},"listingPage":{"properties":{"listings":{"description":"Listings is every listing this org has published, private ones included\n(Public says which are discoverable by others).","items":{"$ref":"#/components/schemas/Listing"},"type":"array"}},"type":"object"},"liveness":{"properties":{"service":{"description":"Service names the answering subsystem; it is always commerce.","type":"string"},"status":{"description":"Status is always ok: mounted is the only state that can answer.","type":"string"}},"type":"object"},"loss":{"properties":{"exhausted":{"description":"Exhausted counts facts the bus abandoned after maxDeliver failed inserts.","type":"integer"},"undecodable":{"description":"Undecodable counts messages acked without landing because they did not parse.","type":"integer"}},"type":"object"},"machineList":{"properties":{"machines":{"description":"Machines is every machine the org has: Visor-provisioned and BYO together.","items":{"$ref":"#/components/schemas/machineView"},"type":"array"}},"type":"object"},"machineView":{"properties":{"createdTime":{"type":"string"},"gpu":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"mem":{"type":"string"},"name":{"type":"string"},"os":{"type":"string"},"privateIp":{"type":"string"},"provider":{"type":"string"},"publicIp":{"type":"string"},"region":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"vcpu":{"type":"integer"}},"type":"object"},"makeIn":{"properties":{"ack_policy":{"type":"string"},"ack_wait":{"type":"string"},"deliver_policy":{"type":"string"},"description":{"type":"string"},"durable_name":{"type":"string"},"filter_subject":{"type":"string"},"max_ack_pending":{"type":"integer"},"max_deliver":{"type":"integer"},"opt_start_seq":{"type":"integer"},"opt_start_time":{"format":"date-time","type":"string"},"replay_policy":{"type":"string"},"stream":{"description":"Stream is the stream name, from the path.","type":"string"}},"type":"object"},"marketCatalog":{"properties":{"items":{"description":"Items is every capability the caller can see in their own (org, project),\neach carrying any public listing's shop metadata and whether it is installed.","items":{"$ref":"#/components/schemas/marketItem"},"type":"array"}},"type":"object"},"marketItem":{"properties":{"activated":{"type":"boolean"},"category":{"type":"string"},"description":{"type":"string"},"dispatchable":{"type":"boolean"},"inputSchema":{},"installed":{"type":"boolean"},"name":{"type":"string"},"price":{"$ref":"#/components/schemas/Price"},"source":{"type":"string"},"title":{"type":"string"}},"type":"object"},"mcpCatalog":{"properties":{"catalog":{"description":"Catalog is this page of listings, featured first, then by name.","items":{"$ref":"#/components/schemas/MCPListing"},"type":"array"},"limit":{"description":"Limit is the page size that was actually applied — the default or the clamp,\nwhen the request asked for neither or for too much.","type":"integer"},"offset":{"description":"Offset is where this page started, so a caller pages from what the server\ndid rather than from what it asked for.","type":"integer"},"total":{"description":"Total is how many listings the filter matched, which is more than this page\nholds whenever there is a next one.","type":"integer"}},"type":"object"},"mcpCatalogSync":{"properties":{"added":{"description":"Added is how many listings the catalog did not have before.","type":"integer"},"registry":{"description":"Registry is the upstream this pass read.","type":"string"},"total":{"description":"Total is how many listings the catalog holds now.","type":"integer"},"updated":{"description":"Updated is how many the publisher has changed since we last looked.","type":"integer"}},"type":"object"},"mcpServerList":{"properties":{"servers":{"description":"Servers is every external MCP server this org has registered. No secret\nVALUE is ever included — only whether one is set.","items":{"$ref":"#/components/schemas/MCPServer"},"type":"array"}},"type":"object"},"meOut":{"properties":{"data":{"$ref":"#/components/schemas/adminMe"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"meetHealth":{"properties":{"ready":{"description":"Ready reports whether this deployment can mint join tokens. False is the 503.","type":"boolean"},"service":{"description":"Service names the subsystem answering — always \"meet\".","type":"string"},"status":{"description":"Status is \"ok\" when tokens can be minted and \"degraded\" when they cannot.","type":"string"}},"type":"object"},"meshServiceList":{"properties":{"services":{"description":"Services is one row per ZT edge service tagged with the caller's org role.","items":{"$ref":"#/components/schemas/meshView"},"type":"array"}},"type":"object"},"meshView":{"properties":{"id":{"description":"ID is the ZT edge service's id.","type":"string"},"mtls":{"description":"Mtls is \"required\" when the service mandates end-to-end encryption, else\n\"enabled\" — the fabric mutually authenticates every link, so it is never\ntruly off.","type":"string"},"service":{"description":"Service is the edge service's name.","type":"string"},"status":{"description":"Status is \"active\": a listed service is a configured, dialable mesh entry.","type":"string"}},"type":"object"},"messagePage":{"properties":{"data":{"description":"Data are the messages in this batch — empty when nothing was pending\nwithin the wait.","items":{"$ref":"#/components/schemas/busMessage"},"type":"array"}},"type":"object"},"metricList":{"properties":{"data":{"description":"Data is one row per prompt the org owns.","items":{"$ref":"#/components/schemas/metricRow"},"type":"array"}},"type":"object"},"metricRow":{"properties":{"createdAt":{"description":"CreatedAt is when version 1 was written, RFC 3339 UTC.","type":"string"},"currentVersion":{"description":"CurrentVer is the version number served as current. It always equals\n`versions`: numbering is dense from 1, and deleting a prompt takes its whole\nhistory with it rather than leaving a gap.","type":"integer"},"lastUpdatedAt":{"description":"LastUpdatedAt is when the newest version was appended, RFC 3339 UTC — the age\nof the template you would get today.","type":"string"},"name":{"description":"Name is the prompt this row is about — its org-unique handle.","type":"string"},"type":{"description":"Type is the current version's kind.","type":"string"},"versions":{"description":"Versions is how many revisions the prompt has, COUNTED in the store and\nuncapped — so it can exceed the 100 entries a list row or a detail response\ncarries. Note the type: here `versions` is a number, while on a list row it is\nthe list of version numbers.","type":"integer"}},"type":"object"},"metricsView":{"properties":{"range":{"description":"echoes the requested window (24H|7D|30D)","type":"string"},"resource":{"$ref":"#/components/schemas/resourceUsage"},"series":{"description":"per-agent invocation histogram (real)","items":{"$ref":"#/components/schemas/seriesLine"},"type":"array"}},"type":"object"},"mintedKey":{"properties":{"accessKey":{"description":"AccessKey is the same value under its predecessor name, carried so callers\nwritten against the older field keep working. One value, two names.","type":"string"},"key":{"description":"Key is the credential, returned ONCE — a secret key is unreadable afterwards.","type":"string"},"type":{"description":"Type is the class of key that was minted.","type":"string"}},"type":"object"},"mirrorList":{"properties":{"data":{"description":"Data holds the repo's outbound mirror targets.","items":{"$ref":"#/components/schemas/mirrorTargetView"},"type":"array"}},"type":"object"},"mirrorReq":{"properties":{"name":{"description":"Name is the local repo to mirror into, from the :name path segment. It is\nCREATED on first use.","type":"string"},"project":{"description":"Project is the sub-scope to land the repo in; empty uses the caller's own,\nexactly as a create would.","type":"string"},"source":{"description":"Source is the http(s) git URL to fetch from. The host is SSRF-guarded and\nthe shared mirror credential is only sent to allowlisted hosts.","type":"string"}},"type":"object"},"mirrorTargetReq":{"properties":{"host":{"description":"Host is an optional assertion of the target's hostname. The authoritative\nhost is the one in URL; a value that disagrees with it is refused.","type":"string"},"name":{"description":"Name is the repo whose advanced refs are pushed downstream, from the :name\npath segment.","type":"string"},"url":{"description":"URL is the downstream https git remote. Must be https to an allowlisted\nhost (github.com / gitlab.com); any embedded credentials are stripped.\nRequired.","type":"string"}},"type":"object"},"mirrorTargetView":{"properties":{"createdAt":{"description":"CreatedAt is RFC 3339 UTC.","type":"string"},"host":{"description":"Host is the target's lowercased hostname, taken from URL and never the body.","type":"string"},"id":{"description":"ID is the target's identifier (\"mir_…\"), the handle to remove it by.","type":"string"},"repo":{"description":"Repo is the repo whose advanced refs are pushed downstream.","type":"string"},"url":{"description":"URL is the canonical https remote, with any embedded credentials stripped.","type":"string"}},"type":"object"},"mlResource":{"properties":{"createdAt":{"description":"CreatedAt is when Kubernetes admitted the object, RFC 3339 in UTC.","type":"string"},"name":{"description":"Name is the object's metadata.name, unique within the caller's namespace.","type":"string"},"spec":{"additionalProperties":{"type":"object"},"description":"Spec is the resource spec, verbatim as Kubernetes stores it. Present on a\nsingle-object read, absent from a list.","type":"object"},"status":{"additionalProperties":{"type":"object"},"description":"Status is the live status kserve owns, verbatim. Absent until kserve has\nwritten one.","type":"object"}},"type":"object"},"mlResourceList":{"properties":{"items":{"description":"Items is one entry per object, newest LAST (the Kubernetes list order).","items":{"$ref":"#/components/schemas/mlResource"},"type":"array"}},"type":"object"},"moduleList":{"properties":{"data":{"description":"Data is every module compiled into this binary, with the DocTypes it installs.","items":{"$ref":"#/components/schemas/ModuleInfo"},"type":"array"}},"type":"object"},"moneyBoard":{"properties":{"byOrg":{"items":{"$ref":"#/components/schemas/moneyOrgRow"},"type":"array"},"credits":{"$ref":"#/components/schemas/moneyCredits"},"generatedAt":{"type":"string"},"infrastructure":{"$ref":"#/components/schemas/moneyInfra"},"margin":{"$ref":"#/components/schemas/moneyMargin"},"revenue":{"$ref":"#/components/schemas/moneyRevenue"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"}},"type":"object"},"moneyCredits":{"properties":{"consumedCents":{"type":"integer"},"grantedCents":{"type":"integer"},"grantedPrepaidCents":{"description":"real money added","type":"integer"},"grantedTrialCents":{"description":"non-cash comps/promos","type":"integer"},"grants":{"type":"integer"},"outstandingCents":{"type":"integer"}},"type":"object"},"moneyInfra":{"properties":{"doAvgDailyBurnCents":{"type":"integer"},"doCreditRemainingCents":{"type":"integer"},"doMonthToDateCents":{"type":"integer"},"period":{"type":"string"},"treasuryReserveCents":{"type":"integer"},"vendorCogsCents":{"type":"integer"},"vendors":{"items":{"$ref":"#/components/schemas/Vendor"},"type":"array"}},"type":"object"},"moneyMargin":{"properties":{"grossCents":{"type":"integer"},"grossPct":{"type":"number"},"profitable":{"type":"boolean"},"runwayDays":{"type":"number"}},"type":"object"},"moneyOrgRow":{"properties":{"balanceCents":{"type":"integer"},"display":{"type":"string"},"grantedCents":{"type":"integer"},"grants":{"type":"integer"},"mrrCents":{"type":"integer"},"org":{"type":"string"},"plan":{"type":"string"},"spendCents":{"type":"integer"}},"type":"object"},"moneyRevenue":{"properties":{"arpuCents":{"type":"integer"},"arrCents":{"type":"integer"},"customers":{"type":"integer"},"mrrCents":{"type":"integer"},"paying":{"type":"integer"},"realizedCents":{"description":"consumed spend, fleet-wide","type":"integer"}},"type":"object"},"mutateReq":{"properties":{"add":{"description":"Add is the product ids to turn ON. Each must already be an ACTIVE entitlement\nof the org's plan, unless the caller is a platform super admin.","items":{"type":"string"},"type":"array"},"remove":{"description":"Remove is the product ids to turn OFF. Disabling is never gated.","items":{"type":"string"},"type":"array"}},"type":"object"},"myReferralView":{"properties":{"createdAt":{"description":"CreatedAt is when the referral was recorded, as a Unix timestamp.","type":"integer"},"id":{"description":"ID is the referral's handle.","type":"string"},"qualifiedAt":{"description":"QualifiedAt is when the referee first made metered spend, as a Unix\ntimestamp; 0 while the referral is still pending.","type":"integer"},"referee":{"description":"Referee is the org that signed up with my code.","type":"string"},"status":{"description":"Status is the referral's lifecycle state: \"signup\" until the referee makes\nmetered spend, then \"qualified\".","type":"string"}},"type":"object"},"myReferrals":{"properties":{"code":{"description":"Code is the org's STABLE referral code — a deterministic function of the org\nid, so it never changes and never has to be stored to be reproduced.","type":"string"},"counts":{"$ref":"#/components/schemas/statusCounts","description":"Counts tallies this org's referrals by status."},"link":{"description":"Link is the shareable signup link carrying the code, on the brand's own host.","type":"string"},"referrals":{"description":"Referrals is one row per org that signed up with this code.","items":{"$ref":"#/components/schemas/myReferralView"},"type":"array"}},"type":"object"},"namespaceCreateIn":{"properties":{"title":{"description":"Title is the namespace's display title. Cloudflare mints the id.","type":"string"}},"type":"object"},"networkList":{"properties":{"networks":{"description":"Networks holds the org's overlay network, or is empty when the org has no\nedge-routers on the fabric (no nodes → no network, never a fabricated one).","items":{"$ref":"#/components/schemas/networkView"},"type":"array"}},"type":"object"},"networkView":{"properties":{"id":{"description":"ID is the org-derived id of the overlay network — the key\nGET /v1/networks/{id} addresses.","type":"string"},"name":{"description":"Name is the org the overlay belongs to.","type":"string"},"nodes":{"description":"Nodes is how many edge-routers the org has on the fabric.","type":"integer"},"status":{"description":"Status is \"connected\" once at least one of the org's edge-routers is\nonline, else \"provisioning\" (routers exist but none has dialed home).","type":"string"}},"type":"object"},"newsResponse":{"properties":{"items":{"description":"Items is the merged, filtered, deduped feed, freshest first and capped at\n50. A source that failed is skipped rather than failing the read, so this\ncan be shorter than the pipeline's reach — it is never an error.","items":{"$ref":"#/components/schemas/NewsItem"},"type":"array"}},"type":"object"},"nextIn":{"properties":{"batch":{"description":"Batch is how many messages to pull (1–1000, default 1).","type":"integer"},"expires":{"description":"Expires is how long to wait for messages, e.g. \"5s\" (default \"30s\", max \"60s\").","type":"string"},"name":{"description":"Name is the consumer name, from the path.","type":"string"},"no_wait":{"description":"NoWait answers immediately with whatever is available instead of waiting.","type":"boolean"},"stream":{"description":"Stream is the stream name, from the path.","type":"string"}},"type":"object"},"nodeList":{"properties":{"nodes":{"description":"Nodes is one row per worker node, in the SAME machineView shape the machines\nsurface emits — a node IS a machine.","items":{"$ref":"#/components/schemas/machineView"},"type":"array"}},"type":"object"},"nodePoolView":{"properties":{"autoScale":{"type":"boolean"},"count":{"type":"integer"},"maxNodes":{"type":"integer"},"minNodes":{"type":"integer"},"name":{"type":"string"},"poolId":{"type":"string"},"size":{"type":"string"}},"type":"object"},"nodeView":{"properties":{"caps":{"description":"Caps is the capability list the node reported. It is a self-report, useful\nto SHOW and never load-bearing: what a node may actually be asked to do is\ndecided at the socket by the deployment's allowlist.","items":{"type":"string"},"type":"array"},"commands":{"description":"Commands is the command list the node reported. Same standing as Caps: a\nself-report, checked again at the socket before anything runs.","items":{"type":"string"},"type":"array"},"connectedAt":{"description":"ConnectedAt is when this node's socket was established, RFC3339 UTC.","type":"string"},"displayName":{"description":"DisplayName is the human name the node reported for itself.","type":"string"},"id":{"description":"ID is the node's own identifier within the org — the value\nPOST /v1/bot/nodes/{id}/invoke addresses it by.","type":"string"},"platform":{"description":"Platform is the operating system and architecture the node reported.","type":"string"},"version":{"description":"Version is the node agent's own version string.","type":"string"}},"type":"object"},"nodesView":{"properties":{"nodes":{"description":"Nodes is every node of the caller's org with a live socket to THIS replica,\nordered by id. A node connected to a different replica is not in it.","items":{"$ref":"#/components/schemas/nodeView"},"type":"array"}},"type":"object"},"notifyHealth":{"properties":{"service":{"description":"Service names the subsystem answering — always \"notify\".","type":"string"},"status":{"description":"Status is \"ok\"; the route answers 200 whenever the subsystem is mounted.","type":"string"}},"type":"object"},"notifySend":{"properties":{"body":{"description":"Body is the message text, sent verbatim when present — the no-template path.","type":"string"},"channel":{"description":"Channel selects the delivery channel, sms or email. The per-channel routes\n(/send/sms, /send/email) pin it, overriding whatever the body names; on the\ngeneric route it is required.","type":"string"},"event":{"description":"Event is the event name, which doubles as the template id when TemplateID is\nempty — the IAM OTP path sends event=iam.otp_sent and nothing else.","type":"string"},"provider":{"description":"Provider pins a provider service name (twilio, plivo, twilio_email, mail).\nEmpty picks the one whose org credentials are actually configured in KMS.","type":"string"},"subject":{"description":"Subject is the message subject, carried on the email channel only.","type":"string"},"sync":{"description":"Sync must be exactly \"true\": delivery here is synchronous, and anything else\nanswers 503 because the queue plane that would run an async dispatch is owned\nelsewhere. Over REST it rides as ?sync=true (the URL binds over the body); a\nby-name call states it in its arguments.","type":"string"},"template_id":{"description":"TemplateID selects a built-in template when Body is empty.","type":"string"},"template_vars":{"description":"TemplateVars carries the values the selected template renders against, as\na raw JSON object. Raw on purpose: the by-name call plane computes an\ninput's layout before reading any payload and refuses a map field\noutright, which would make every typed call to these ops fail — and that\ncall is the reason they are typed at all (IAM's OTP sender). deliver\ndecodes it right before the template renders, so REST bodies decode\nbyte-identically to the map this replaced."},"to":{"description":"To is the destination address per recipient — a phone number for sms, an\nemail address for email. Several recipients fan out into one provider call\neach, and the response shape follows the count (see the items field).","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.AWSAccountConfig":{"properties":{"regions":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.AWSCloudWatchLogsSubscription":{"properties":{"filterPattern":{"description":"https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html\n\"\" implies no filtering is required","type":"string"},"logGroupNamePrefix":{"description":"subscribe to all logs groups with specified prefix.\neg: `/aws/rds/`","type":"string"}},"type":"object"},"o11y.AWSCloudWatchMetricStreamFilter":{"properties":{"metricNames":{"items":{"type":"string"},"type":"array"},"namespace":{"description":"https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudwatch-metricstream-metricstreamfilter.html","type":"string"}},"type":"object"},"o11y.AWSConnectionArtifact":{"properties":{"connectionUrl":{"type":"string"}},"type":"object"},"o11y.AWSIntegrationConfig":{"properties":{"enabledRegions":{"items":{"type":"string"},"type":"array"},"telemetryCollectionStrategy":{"$ref":"#/components/schemas/o11y.AWSTelemetryCollectionStrategy"}},"type":"object"},"o11y.AWSLogsCollectionStrategy":{"properties":{"subscriptions":{"items":{"$ref":"#/components/schemas/o11y.AWSCloudWatchLogsSubscription"},"type":"array"}},"type":"object"},"o11y.AWSMetricsCollectionStrategy":{"properties":{"streamFilters":{"description":"to be used as https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudwatch-metricstream.html#cfn-cloudwatch-metricstream-includefilters","items":{"$ref":"#/components/schemas/o11y.AWSCloudWatchMetricStreamFilter"},"type":"array"}},"type":"object"},"o11y.AWSPostableAccountConfig":{"properties":{"deploymentRegion":{"type":"string"},"regions":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.AWSServiceConfig":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.AWSServiceLogsConfig"},"metrics":{"$ref":"#/components/schemas/o11y.AWSServiceMetricsConfig"}},"type":"object"},"o11y.AWSServiceLogsConfig":{"properties":{"enabled":{"type":"boolean"},"s3Buckets":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}},"type":"object"},"o11y.AWSServiceMetricsConfig":{"properties":{"enabled":{"type":"boolean"}},"type":"object"},"o11y.AWSTelemetryCollectionStrategy":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.AWSLogsCollectionStrategy"},"metrics":{"$ref":"#/components/schemas/o11y.AWSMetricsCollectionStrategy"},"s3Buckets":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Only available in S3 Sync Service Type in AWS","type":"object"}},"type":"object"},"o11y.Account":{"properties":{"agentReport":{"$ref":"#/components/schemas/o11y.AgentReport"},"config":{"$ref":"#/components/schemas/o11y.AccountConfig"},"createdAt":{"format":"date-time","type":"string"},"id":{},"orgId":{},"provider":{},"providerAccountId":{"type":"string"},"removedAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.AccountConfig":{"properties":{"aws":{"$ref":"#/components/schemas/o11y.AWSAccountConfig"},"azure":{"$ref":"#/components/schemas/o11y.AzureAccountConfig"},"gcp":{"$ref":"#/components/schemas/o11y.GCPAccountConfig"}},"type":"object"},"o11y.AgentReport":{"properties":{"data":{"additionalProperties":{"type":"object"},"type":"object"},"timestampMillis":{"type":"integer"}},"type":"object"},"o11y.AggregateAttributeResponse":{"properties":{"attributeKeys":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"}},"type":"object"},"o11y.Alert":{"properties":{"annotations":{"additionalProperties":{"type":"string"},"type":"object"},"endsAt":{"format":"date-time","type":"string"},"generatorURL":{"type":"string"},"labels":{"additionalProperties":{"type":"string"},"type":"object"},"startsAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.AlertStatus":{"properties":{"inhibitedBy":{"items":{"type":"string"},"type":"array"},"silencedBy":{"items":{"type":"string"},"type":"array"},"state":{"type":"string"}},"type":"object"},"o11y.AssociatedComponent":{"properties":{"name":{"type":"string"},"type":{}},"type":"object"},"o11y.AttributeKey":{"properties":{"dataType":{"type":"string"},"isColumn":{"type":"boolean"},"isJSON":{"type":"boolean"},"key":{"type":"string"},"type":{"type":"string"}},"type":"object"},"o11y.AttributesComponentEntry":{"properties":{"associatedComponent":{"$ref":"#/components/schemas/o11y.AssociatedComponent"},"attributes":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.Authorization":{"properties":{"credentials":{},"credentials_file":{"type":"string"},"credentials_ref":{"description":"CredentialsRef is the name of the secret within the secret manager to use as credentials.","type":"string"},"type":{"type":"string"}},"type":"object"},"o11y.AzureAccountConfig":{"properties":{"deploymentRegion":{"type":"string"},"resourceGroups":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.AzureConnectionArtifact":{"properties":{"cliCommand":{"type":"string"},"cloudPowerShellCommand":{"type":"string"}},"type":"object"},"o11y.AzureIntegrationConfig":{"properties":{"deploymentRegion":{"type":"string"},"resourceGroups":{"items":{"type":"string"},"type":"array"},"telemetryCollectionStrategy":{"items":{"$ref":"#/components/schemas/o11y.AzureTelemetryCollectionStrategy"},"type":"array"}},"type":"object"},"o11y.AzureLogsCollectionStrategy":{"properties":{"categoryGroups":{"description":"List of categories to enable for diagnostic settings, to start with it will have 'allLogs' and no filtering.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.AzureMetricsCollectionStrategy":{"properties":{},"type":"object"},"o11y.AzureServiceConfig":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.AzureServiceLogsConfig"},"metrics":{"$ref":"#/components/schemas/o11y.AzureServiceMetricsConfig"}},"type":"object"},"o11y.AzureServiceLogsConfig":{"properties":{"enabled":{"type":"boolean"}},"type":"object"},"o11y.AzureServiceMetricsConfig":{"properties":{"enabled":{"type":"boolean"}},"type":"object"},"o11y.AzureTelemetryCollectionStrategy":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.AzureLogsCollectionStrategy"},"metrics":{"$ref":"#/components/schemas/o11y.AzureMetricsCollectionStrategy"},"resourceProvider":{"description":"https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-providers-and-types","type":"string"},"resourceType":{"type":"string"}},"type":"object"},"o11y.BasicAuth":{"properties":{"password":{},"password_file":{"type":"string"},"password_ref":{"description":"PasswordRef is the name of the secret within the secret manager to use as the password.","type":"string"},"username":{"type":"string"},"username_file":{"type":"string"},"username_ref":{"description":"UsernameRef is the name of the secret within the secret manager to use as the username.","type":"string"}},"type":"object"},"o11y.BuilderQuery":{"properties":{"IsAnomaly":{"type":"boolean"},"QueriesUsedInFormula":{"items":{"type":"string"},"type":"array"},"ShiftBy":{"type":"integer"},"aggregateAttribute":{"$ref":"#/components/schemas/o11y.AttributeKey"},"aggregateOperator":{"type":"string"},"dataSource":{"type":"string"},"disabled":{"type":"boolean"},"expression":{"type":"string"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"functions":{"items":{"$ref":"#/components/schemas/o11y.Function"},"type":"array"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"having":{"items":{"$ref":"#/components/schemas/o11y.Having"},"type":"array"},"legend":{"type":"string"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"items":{"$ref":"#/components/schemas/o11y.OrderBy"},"type":"array"},"pageSize":{"type":"integer"},"queryName":{"type":"string"},"reduceTo":{"type":"string"},"selectColumns":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"seriesAggregation":{"type":"string"},"spaceAggregation":{"type":"string"},"stepInterval":{"type":"integer"},"temporality":{"type":"string"},"timeAggregation":{"type":"string"}},"type":"object"},"o11y.Channel":{"properties":{"createdAt":{"format":"date-time","type":"string"},"data":{"type":"string"},"id":{},"name":{"type":"string"},"orgId":{"type":"string"},"type":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.Checks":{"properties":{"missingDefaultEnabledMetrics":{"items":{"$ref":"#/components/schemas/o11y.MissingMetricsComponentEntry"},"type":"array"},"missingOptionalMetrics":{"items":{"$ref":"#/components/schemas/o11y.MissingMetricsComponentEntry"},"type":"array"},"missingRequiredAttributes":{"items":{"$ref":"#/components/schemas/o11y.MissingAttributesComponentEntry"},"type":"array"},"presentDefaultEnabledMetrics":{"items":{"$ref":"#/components/schemas/o11y.MetricsComponentEntry"},"type":"array"},"presentOptionalMetrics":{"items":{"$ref":"#/components/schemas/o11y.MetricsComponentEntry"},"type":"array"},"presentRequiredAttributes":{"items":{"$ref":"#/components/schemas/o11y.AttributesComponentEntry"},"type":"array"},"ready":{"type":"boolean"},"type":{}},"type":"object"},"o11y.CloudIntegrationService":{"properties":{"cloudIntegrationId":{},"config":{"$ref":"#/components/schemas/o11y.ServiceConfig"},"createdAt":{"format":"date-time","type":"string"},"id":{},"type":{},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.ClusterListRecord":{"properties":{"clusterUID":{"type":"string"},"cpuAllocatable":{"type":"number"},"cpuUsage":{"type":"number"},"memoryAllocatable":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"}},"type":"object"},"o11y.ClusterListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.ClusterListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.ClusterListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.ClusterRecord":{"properties":{"clusterCPU":{"type":"number"},"clusterCPUAllocatable":{"type":"number"},"clusterMemory":{"type":"number"},"clusterMemoryAllocatable":{"type":"number"},"clusterName":{"description":"TODO(nikhilmantri0902): once the underlying attr key is migrated to\nk8s.cluster.uid (see ClusterNameAttrKey), surface ClusterUID alongside\n(or replace) ClusterName.","type":"string"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"nodeCountsByReadiness":{"$ref":"#/components/schemas/o11y.NodeCountsByReadiness"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"}},"type":"object"},"o11y.Clusters":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.ClusterRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.CollectedLogAttribute":{"properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"type":"string"}},"type":"object"},"o11y.CollectedMetric":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"unit":{"type":"string"}},"type":"object"},"o11y.CompositeQuery":{"properties":{"builderQueries":{"additionalProperties":{"$ref":"#/components/schemas/o11y.BuilderQuery"},"type":"object"},"chQueries":{"additionalProperties":{"$ref":"#/components/schemas/o11y.DatastoreQuery"},"type":"object"},"fillGaps":{"description":"FillGaps is used to fill the gaps in the time series data","type":"boolean"},"panelType":{"type":"string"},"promQueries":{"additionalProperties":{"$ref":"#/components/schemas/o11y.PromQuery"},"type":"object"},"queries":{"items":{"$ref":"#/components/schemas/o11y.QueryEnvelope"},"type":"array"},"queryType":{"type":"string"},"unit":{"description":"Unit for the time series data shown in the graph\nThis is used in alerts to format the value and threshold","type":"string"}},"type":"object"},"o11y.ConnectionArtifact":{"properties":{"aws":{"$ref":"#/components/schemas/o11y.AWSConnectionArtifact","description":"required till new providers are added"},"azure":{"$ref":"#/components/schemas/o11y.AzureConnectionArtifact"},"gcp":{"$ref":"#/components/schemas/o11y.GCPConnectionArtifact"}},"type":"object"},"o11y.Credentials":{"properties":{"ingestionKey":{"type":"string"},"ingestionUrl":{"type":"string"},"o11yApiKey":{"description":"PAT","type":"string"},"o11yApiUrl":{"type":"string"}},"type":"object"},"o11y.DaemonSetListRecord":{"properties":{"availableNodes":{"type":"integer"},"cpuLimit":{"type":"number"},"cpuRequest":{"type":"number"},"cpuUsage":{"type":"number"},"daemonSetName":{"type":"string"},"desiredNodes":{"type":"integer"},"memoryLimit":{"type":"number"},"memoryRequest":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"restarts":{"type":"integer"}},"type":"object"},"o11y.DaemonSetListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.DaemonSetListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.DaemonSetListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.DaemonSetRecord":{"properties":{"currentNodes":{"type":"integer"},"daemonSetCPU":{"type":"number"},"daemonSetCPULimit":{"type":"number"},"daemonSetCPURequest":{"type":"number"},"daemonSetMemory":{"type":"number"},"daemonSetMemoryLimit":{"type":"number"},"daemonSetMemoryRequest":{"type":"number"},"daemonSetName":{"type":"string"},"desiredNodes":{"type":"integer"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"}},"type":"object"},"o11y.DaemonSets":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.DaemonSetRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.DataCollected":{"properties":{"logs":{"items":{"$ref":"#/components/schemas/o11y.CollectedLogAttribute"},"type":"array"},"metrics":{"items":{"$ref":"#/components/schemas/o11y.CollectedMetric"},"type":"array"}},"type":"object"},"o11y.DataCollectedForIntegration":{"properties":{"logs":{"items":{"$ref":"#/components/schemas/o11y.integrations.CollectedLogAttribute"},"type":"array"},"metrics":{"items":{"$ref":"#/components/schemas/o11y.integrations.CollectedMetric"},"type":"array"}},"type":"object"},"o11y.DatastoreQuery":{"properties":{"disabled":{"type":"boolean"},"legend":{"type":"string"},"query":{"type":"string"}},"type":"object"},"o11y.DeploymentListRecord":{"properties":{"availablePods":{"type":"integer"},"cpuLimit":{"type":"number"},"cpuRequest":{"type":"number"},"cpuUsage":{"type":"number"},"deploymentName":{"type":"string"},"desiredPods":{"type":"integer"},"memoryLimit":{"type":"number"},"memoryRequest":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"restarts":{"type":"integer"}},"type":"object"},"o11y.DeploymentListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.DeploymentListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.DeploymentListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.DeploymentRecord":{"properties":{"availablePods":{"type":"integer"},"deploymentCPU":{"type":"number"},"deploymentCPULimit":{"type":"number"},"deploymentCPURequest":{"type":"number"},"deploymentMemory":{"type":"number"},"deploymentMemoryLimit":{"type":"number"},"deploymentMemoryRequest":{"type":"number"},"deploymentName":{"type":"string"},"desiredPods":{"type":"integer"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"}},"type":"object"},"o11y.Deployments":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.DeploymentRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.DeprecatedGettableAlert":{"properties":{"Alert":{"$ref":"#/components/schemas/o11y.Alert"},"fingerprint":{"type":"string"},"receivers":{"items":{"type":"string"},"type":"array"},"status":{"$ref":"#/components/schemas/o11y.AlertStatus"}},"type":"object"},"o11y.DiscordConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"avatar_url":{"type":"string"},"content":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"title":{"type":"string"},"username":{"type":"string"},"webhook_url":{},"webhook_url_file":{"type":"string"}},"type":"object"},"o11y.EmailConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"auth_identity":{"type":"string"},"auth_password":{},"auth_password_file":{"type":"string"},"auth_secret":{},"auth_secret_file":{"type":"string"},"auth_username":{"type":"string"},"force_implicit_tls":{"description":"ForceImplicitTLS controls whether to use implicit TLS (direct TLS connection).\ntrue: force use of implicit TLS (direct TLS connection)\nfalse: force disable implicit TLS (use explicit TLS/STARTTLS if required)\nnil (default): auto-detect based on port (465=implicit, other=explicit) for backward compatibility","type":"boolean"},"from":{"type":"string"},"headers":{"additionalProperties":{"type":"string"},"type":"object"},"hello":{"type":"string"},"html":{"type":"string"},"require_tls":{"type":"boolean"},"smarthost":{},"text":{"type":"string"},"threading":{"$ref":"#/components/schemas/o11y.ThreadingConfig"},"tls_config":{"$ref":"#/components/schemas/o11y.TLSConfig"},"to":{"description":"Email address to notify.","type":"string"}},"type":"object"},"o11y.Event":{"properties":{"attributeMap":{"additionalProperties":{"type":"object"},"type":"object"},"isError":{"type":"boolean"},"name":{"type":"string"},"timeUnixNano":{"type":"integer"}},"type":"object"},"o11y.Filter":{"properties":{"expression":{"description":"expression to filter by following the filter syntax","type":"string"}},"type":"object"},"o11y.FilterAttributeKeyResponse":{"properties":{"attributeKeys":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"}},"type":"object"},"o11y.FilterAttributeValueRequest":{"properties":{"aggregateAttribute":{"type":"string"},"aggregateOperator":{"type":"string"},"dataSource":{"type":"string"},"endTimeMillis":{"type":"integer"},"existingFilterItems":{"items":{"$ref":"#/components/schemas/o11y.FilterItem"},"type":"array"},"filterAttributeKey":{"type":"string"},"filterAttributeKeyDataType":{"type":"string"},"includeRelated":{"type":"boolean"},"limit":{"type":"integer"},"metricNames":{"items":{"type":"string"},"type":"array"},"searchText":{"type":"string"},"startTimeMillis":{"type":"integer"},"tagType":{"type":"string"}},"type":"object"},"o11y.FilterAttributeValueResponse":{"properties":{"boolAttributeValues":{"items":{"type":"boolean"},"type":"array"},"numberAttributeValues":{"items":{"type":"object"},"type":"array"},"relatedValues":{"$ref":"#/components/schemas/o11y.FilterAttributeValueResponse"},"stringAttributeValues":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.FilterItem":{"properties":{"key":{"$ref":"#/components/schemas/o11y.AttributeKey"},"op":{"type":"string"},"value":{"type":"object"}},"type":"object"},"o11y.FilterSet":{"properties":{"items":{"items":{"$ref":"#/components/schemas/o11y.FilterItem"},"type":"array"},"op":{"type":"string"}},"type":"object"},"o11y.FlamegraphSpan":{"properties":{"attributes":{"additionalProperties":{"type":"object"},"type":"object"},"durationNano":{"type":"integer"},"event":{"items":{"$ref":"#/components/schemas/o11y.Event"},"type":"array"},"hasError":{"type":"boolean"},"level":{"type":"integer"},"name":{"type":"string"},"parentSpanId":{"type":"string"},"resource":{"additionalProperties":{"type":"string"},"type":"object"},"spanId":{"type":"string"},"timestamp":{"type":"integer"}},"type":"object"},"o11y.FormatOptions":{"properties":{"fillGaps":{"type":"boolean"},"formatTableResultForUI":{"type":"boolean"}},"type":"object"},"o11y.Function":{"properties":{"args":{"items":{"type":"object"},"type":"array"},"name":{"type":"string"},"namedArgs":{"additionalProperties":{"type":"object"},"type":"object"}},"type":"object"},"o11y.FunnelStep":{"properties":{"description":{"description":"step description","type":"string"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"has_errors":{"type":"boolean"},"id":{},"latency_pointer":{"type":"string"},"latency_type":{"type":"string"},"name":{"description":"step name","type":"string"},"service_name":{"type":"string"},"span_name":{"type":"string"},"step_order":{"type":"integer"}},"type":"object"},"o11y.GCPAccountConfig":{"properties":{"deploymentProjectId":{"description":"Project ID where central pub/sub for logs exist","type":"string"},"deploymentRegion":{"description":"Project ID where otel collector will be deployed","type":"string"},"projectIds":{"description":"List of project IDs to monitor","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.GCPConnectionArtifact":{"properties":{},"type":"object"},"o11y.GCPIntegrationConfig":{"properties":{},"type":"object"},"o11y.GCPServiceConfig":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.GCPServiceLogsConfig"},"metrics":{"$ref":"#/components/schemas/o11y.GCPServiceMetricsConfig"}},"type":"object"},"o11y.GCPServiceLogsConfig":{"properties":{"enabled":{"type":"boolean"}},"type":"object"},"o11y.GCPServiceMetricsConfig":{"properties":{"enabled":{"type":"boolean"}},"type":"object"},"o11y.GettableAccountWithConnectionArtifact":{"properties":{"connectionArtifact":{"$ref":"#/components/schemas/o11y.ConnectionArtifact"},"id":{}},"type":"object"},"o11y.GettableAccounts":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/o11y.Account"},"type":"array"}},"type":"object"},"o11y.GettableAgentCheckIn":{"properties":{"account_id":{"description":"Older fields for backward compatibility with existing AWS agents","type":"string"},"cloudIntegrationId":{"type":"string"},"cloud_account_id":{"type":"string"},"integrationConfig":{"$ref":"#/components/schemas/o11y.ProviderIntegrationConfig"},"integration_config":{"$ref":"#/components/schemas/o11y.IntegrationConfig"},"providerAccountId":{"type":"string"},"removedAt":{"format":"date-time","type":"string"},"removed_at":{"format":"date-time","type":"string"}},"type":"object"},"o11y.GettableCreatedIngestionKey":{"properties":{"id":{"type":"string"},"value":{"type":"string"}},"type":"object"},"o11y.GettableCreatedIngestionKeyLimit":{"properties":{"id":{"type":"string"}},"type":"object"},"o11y.GettableFieldKeys":{"properties":{"complete":{"type":"boolean"},"keys":{"additionalProperties":{"items":{"$ref":"#/components/schemas/o11y.TelemetryFieldKey"},"type":"array"},"type":"object"}},"type":"object"},"o11y.GettableFieldValues":{"properties":{"complete":{"type":"boolean"},"values":{"$ref":"#/components/schemas/o11y.TelemetryFieldValues"}},"type":"object"},"o11y.GettableFlamegraphTrace":{"properties":{"endTimestampMillis":{"type":"integer"},"hasMore":{"type":"boolean"},"spans":{"items":{"items":{"$ref":"#/components/schemas/o11y.FlamegraphSpan"},"type":"array"},"type":"array"},"startTimestampMillis":{"type":"integer"}},"type":"object"},"o11y.GettableFunnel":{"properties":{"created_at":{"type":"integer"},"created_by":{"type":"string"},"description":{"type":"string"},"funnel":{"$ref":"#/components/schemas/o11y.StorableFunnel"},"funnel_id":{"type":"string"},"funnel_name":{"type":"string"},"org_id":{"type":"string"},"steps":{"items":{"$ref":"#/components/schemas/o11y.FunnelStep"},"type":"array"},"updated_at":{"type":"integer"},"updated_by":{"type":"string"},"user_email":{"type":"string"}},"type":"object"},"o11y.GettableHost":{"properties":{"hosts":{"items":{"$ref":"#/components/schemas/o11y.Host"},"type":"array"},"name":{"type":"string"},"state":{"type":"string"},"tier":{"type":"string"}},"type":"object"},"o11y.GettableIngestionKeys":{"properties":{"_pagination":{"$ref":"#/components/schemas/o11y.Pagination"},"keys":{"items":{"$ref":"#/components/schemas/o11y.IngestionKey"},"type":"array"}},"type":"object"},"o11y.GettableRoutePolicy":{"properties":{"channels":{"items":{"type":"string"},"type":"array"},"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"description":{"type":"string"},"expression":{"type":"string"},"id":{"type":"string"},"kind":{},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"}},"type":"object"},"o11y.GettableRuleStateHistory":{"properties":{"fingerprint":{"type":"integer"},"labels":{"items":{"$ref":"#/components/schemas/o11y.Label"},"type":"array"},"overallState":{},"overallStateChanged":{"type":"boolean"},"ruleId":{"type":"string"},"ruleName":{"type":"string"},"state":{},"stateChanged":{"type":"boolean"},"unixMilli":{"type":"integer"},"value":{"type":"number"}},"type":"object"},"o11y.GettableRuleStateHistoryContributor":{"properties":{"count":{"type":"integer"},"fingerprint":{"type":"integer"},"labels":{"items":{"$ref":"#/components/schemas/o11y.Label"},"type":"array"},"relatedLogsLink":{"type":"string"},"relatedTracesLink":{"type":"string"}},"type":"object"},"o11y.GettableRuleStateHistoryStats":{"properties":{"currentAvgResolutionTime":{"type":"number"},"currentAvgResolutionTimeSeries":{"$ref":"#/components/schemas/o11y.TimeSeries"},"currentTriggersSeries":{"$ref":"#/components/schemas/o11y.TimeSeries"},"pastAvgResolutionTime":{"type":"number"},"pastAvgResolutionTimeSeries":{"$ref":"#/components/schemas/o11y.TimeSeries"},"pastTriggersSeries":{"$ref":"#/components/schemas/o11y.TimeSeries"},"totalCurrentTriggers":{"type":"integer"},"totalPastTriggers":{"type":"integer"}},"type":"object"},"o11y.GettableRuleStateTimeline":{"properties":{"items":{"items":{"$ref":"#/components/schemas/o11y.GettableRuleStateHistory"},"type":"array"},"nextCursor":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"o11y.GettableRuleStateWindow":{"properties":{"end":{"type":"integer"},"start":{"type":"integer"},"state":{}},"type":"object"},"o11y.GettableServicesMetadata":{"properties":{"services":{"items":{"$ref":"#/components/schemas/o11y.ServiceMetadata"},"type":"array"}},"type":"object"},"o11y.GettableSpanMapperGroups":{"properties":{"items":{"items":{"$ref":"#/components/schemas/o11y.SpanMapperGroup"},"type":"array"}},"type":"object"},"o11y.GettableSpanMappers":{"properties":{"items":{"items":{"$ref":"#/components/schemas/o11y.SpanMapper"},"type":"array"}},"type":"object"},"o11y.GettableTestRule":{"properties":{"alertCount":{"type":"integer"},"message":{"type":"string"}},"type":"object"},"o11y.GettableTraceAggregations":{"properties":{"aggregations":{"items":{"$ref":"#/components/schemas/o11y.SpanAggregationResult"},"type":"array"}},"type":"object"},"o11y.GettableWaterfallTrace":{"properties":{"endTimestampMillis":{"type":"integer"},"hasMissingSpans":{"type":"boolean"},"hasMore":{"type":"boolean"},"rootServiceEntryPoint":{"type":"string"},"rootServiceName":{"type":"string"},"spans":{"items":{"$ref":"#/components/schemas/o11y.WaterfallSpan"},"type":"array"},"startTimestampMillis":{"type":"integer"},"totalErrorSpansCount":{"type":"integer"},"totalSpansCount":{"type":"integer"},"uncollapsedSpans":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.GoogleChatReceiverConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"text":{"type":"string"},"title":{"type":"string"},"webhook_url":{}},"type":"object"},"o11y.GroupByKey":{"properties":{"description":{"type":"string"},"fieldContext":{},"fieldDataType":{},"name":{"type":"string"},"signal":{},"unit":{"type":"string"}},"required":["name"],"type":"object"},"o11y.HTTPClientConfig":{"properties":{"authorization":{"$ref":"#/components/schemas/o11y.Authorization","description":"The HTTP authorization credentials for the targets."},"basic_auth":{"$ref":"#/components/schemas/o11y.BasicAuth","description":"The HTTP basic authentication credentials for the targets."},"bearer_token":{"description":"The bearer token for the targets. Deprecated in favour of\nAuthorization.Credentials."},"bearer_token_file":{"description":"The bearer token file for the targets. Deprecated in favour of\nAuthorization.CredentialsFile.","type":"string"},"enable_http2":{"description":"EnableHTTP2 specifies whether the client should configure HTTP2.\nThe omitempty flag is not set, because it would be hidden from the\nmarshalled configuration when set to false.","type":"boolean"},"follow_redirects":{"description":"FollowRedirects specifies whether the client should follow HTTP 3xx redirects.\nThe omitempty flag is not set, because it would be hidden from the\nmarshalled configuration when set to false.","type":"boolean"},"http_headers":{"description":"HTTPHeaders specify headers to inject in the requests. Those headers\ncould be marshalled back to the users."},"no_proxy":{"type":"string"},"oauth2":{"$ref":"#/components/schemas/o11y.OAuth2","description":"The OAuth2 client credentials used to fetch a token for the targets."},"proxy_connect_header":{"additionalProperties":{"items":{},"type":"array"},"type":"object"},"proxy_from_environment":{"type":"boolean"},"proxy_url":{},"tls_config":{"$ref":"#/components/schemas/o11y.TLSConfig","description":"TLSConfig to use to connect to the targets."}},"type":"object"},"o11y.Having":{"properties":{"columnName":{"type":"string"},"op":{"type":"string"},"value":{"type":"object"}},"type":"object"},"o11y.Host":{"properties":{"is_default":{"type":"boolean"},"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"o11y.HostFilter":{"properties":{"Filter":{"$ref":"#/components/schemas/o11y.Filter"},"filterByStatus":{}},"type":"object"},"o11y.HostListRecord":{"properties":{"active":{"type":"boolean"},"cpu":{"type":"number"},"hostName":{"type":"string"},"load15":{"type":"number"},"memory":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"os":{"type":"string"},"wait":{"type":"number"}},"type":"object"},"o11y.HostListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.HostListResponse":{"properties":{"clusterNames":{"items":{"type":"string"},"type":"array"},"endTimeBeforeRetention":{"type":"boolean"},"isSendingK8SAgentMetrics":{"type":"boolean"},"nodeNames":{"items":{"type":"string"},"type":"array"},"records":{"items":{"$ref":"#/components/schemas/o11y.HostListRecord"},"type":"array"},"sentAnyHostMetricsData":{"type":"boolean"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.HostRecord":{"properties":{"activeHostCount":{"type":"integer"},"cpu":{"type":"number"},"diskUsage":{"type":"number"},"hostName":{"type":"string"},"inactiveHostCount":{"type":"integer"},"load15":{"type":"number"},"memory":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"status":{},"wait":{"type":"number"}},"type":"object"},"o11y.Hosts":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.HostRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.IncidentioConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"alert_source_token":{"description":"AlertSourceToken is the key used to authenticate with the alert source in incident.io."},"alert_source_token_file":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"max_alerts":{"description":"MaxAlerts is the maximum number of alerts to be sent per incident.io message.\nAlerts exceeding this threshold will be truncated. Setting this to 0\nallows an unlimited number of alerts. Note that if the payload exceeds\nincident.io's size limits, you will receive a 429 response and alerts\nwill not be ingested.","type":"integer"},"timeout":{"description":"Timeout is the maximum time allowed to invoke incident.io. Setting this to 0\ndoes not impose a timeout.","type":"integer"},"url":{"description":"URL to send POST request to."},"url_file":{"type":"string"}},"type":"object"},"o11y.IngestionKey":{"properties":{"created_at":{"format":"date-time","type":"string"},"expires_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"limits":{"items":{"$ref":"#/components/schemas/o11y.Limit"},"type":"array"},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updated_at":{"format":"date-time","type":"string"},"value":{"type":"string"},"workspace_id":{"type":"string"}},"type":"object"},"o11y.InstallIntegrationRequest":{"properties":{"config":{"additionalProperties":{"type":"object"},"type":"object"},"integration_id":{"type":"string"}},"type":"object"},"o11y.InstalledIntegration":{"properties":{"config":{"additionalProperties":{"type":"object"},"type":"object"},"id":{},"installed_at":{"format":"date-time","type":"string"},"org_id":{"type":"string"},"type":{"type":"string"}},"type":"object"},"o11y.Integration":{"properties":{"assets":{"$ref":"#/components/schemas/o11y.IntegrationAssets"},"author":{"$ref":"#/components/schemas/o11y.IntegrationAuthor"},"categories":{"items":{"type":"string"},"type":"array"},"configuration":{"items":{"$ref":"#/components/schemas/o11y.IntegrationConfigStep"},"type":"array"},"connection_tests":{"$ref":"#/components/schemas/o11y.IntegrationConnectionTests"},"data_collected":{"$ref":"#/components/schemas/o11y.DataCollectedForIntegration"},"description":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"installation":{"$ref":"#/components/schemas/o11y.InstalledIntegration"},"overview":{"type":"string"},"title":{"type":"string"}},"type":"object"},"o11y.IntegrationAssets":{"properties":{"alerts":{"items":{},"type":"array"},"dashboards":{"items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"},"logs":{"$ref":"#/components/schemas/o11y.LogsAssets"}},"type":"object"},"o11y.IntegrationAuthor":{"properties":{"email":{"type":"string"},"homepage":{"type":"string"},"name":{"type":"string"}},"type":"object"},"o11y.IntegrationConfig":{"properties":{"enabled_regions":{"description":"backward compatible","items":{"type":"string"},"type":"array"},"telemetry":{"$ref":"#/components/schemas/o11y.OldAWSCollectionStrategy","description":"backward compatible"}},"type":"object"},"o11y.IntegrationConfigStep":{"properties":{"instructions":{"type":"string"},"title":{"type":"string"}},"type":"object"},"o11y.IntegrationConnectionStatus":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.SignalConnectionStatus"},"metrics":{"$ref":"#/components/schemas/o11y.SignalConnectionStatus"}},"type":"object"},"o11y.IntegrationConnectionTests":{"properties":{"logs":{"$ref":"#/components/schemas/o11y.LogsConnectionTest"},"metrics":{"description":"Metric names expected to have been received for the integration.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.IntegrationsListItem":{"properties":{"author":{"$ref":"#/components/schemas/o11y.IntegrationAuthor"},"description":{"type":"string"},"icon":{"type":"string"},"id":{"type":"string"},"is_installed":{"type":"boolean"},"title":{"type":"string"}},"type":"object"},"o11y.IntegrationsListResponse":{"properties":{"integrations":{"items":{"$ref":"#/components/schemas/o11y.IntegrationsListItem"},"type":"array"}},"type":"object"},"o11y.JiraConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"api_type":{"type":"string"},"api_url":{},"custom_fields":{"additionalProperties":{"type":"object"},"type":"object"},"description":{"$ref":"#/components/schemas/o11y.JiraFieldConfig"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"issue_type":{"type":"string"},"labels":{"items":{"type":"string"},"type":"array"},"priority":{"type":"string"},"project":{"type":"string"},"reopen_duration":{},"reopen_transition":{"type":"string"},"resolve_transition":{"type":"string"},"summary":{"$ref":"#/components/schemas/o11y.JiraFieldConfig"},"wont_fix_resolution":{"type":"string"}},"type":"object"},"o11y.JiraFieldConfig":{"properties":{"enable_update":{"description":"EnableUpdate indicates whether this field should be omitted when updating an existing issue.","type":"boolean"},"template":{"description":"Template is the template string used to render the field.","type":"string"}},"type":"object"},"o11y.JobListRecord":{"properties":{"activePods":{"type":"integer"},"cpuLimit":{"type":"number"},"cpuRequest":{"type":"number"},"cpuUsage":{"type":"number"},"desiredSuccessfulPods":{"type":"integer"},"failedPods":{"type":"integer"},"jobName":{"type":"string"},"memoryLimit":{"type":"number"},"memoryRequest":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"restarts":{"type":"integer"},"successfulPods":{"type":"integer"}},"type":"object"},"o11y.JobListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.JobListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.JobListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.JobRecord":{"properties":{"activePods":{"type":"integer"},"desiredSuccessfulPods":{"type":"integer"},"failedPods":{"type":"integer"},"jobCPU":{"type":"number"},"jobCPULimit":{"type":"number"},"jobCPURequest":{"type":"number"},"jobMemory":{"type":"number"},"jobMemoryLimit":{"type":"number"},"jobMemoryRequest":{"type":"number"},"jobName":{"type":"string"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"},"successfulPods":{"type":"integer"}},"type":"object"},"o11y.Jobs":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.JobRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.Label":{"properties":{"key":{"$ref":"#/components/schemas/o11y.TelemetryFieldKey"},"value":{"type":"object"}},"type":"object"},"o11y.Limit":{"properties":{"config":{"$ref":"#/components/schemas/o11y.LimitConfig"},"created_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"key_id":{"type":"string"},"metric":{"$ref":"#/components/schemas/o11y.LimitMetric"},"signal":{"description":"\"logs\", \"traces\", \"metrics\"","type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updated_at":{"format":"date-time","type":"string"}},"type":"object"},"o11y.LimitConfig":{"properties":{"day":{"$ref":"#/components/schemas/o11y.LimitValue"},"second":{"$ref":"#/components/schemas/o11y.LimitValue"}},"type":"object"},"o11y.LimitMetric":{"properties":{"day":{"$ref":"#/components/schemas/o11y.LimitMetricValue"},"second":{"$ref":"#/components/schemas/o11y.LimitMetricValue"}},"type":"object"},"o11y.LimitMetricValue":{"properties":{"count":{"type":"integer"},"size":{"type":"integer"}},"type":"object"},"o11y.LimitValue":{"properties":{"count":{"type":"integer"},"size":{"type":"integer"}},"type":"object"},"o11y.LogsAssets":{"properties":{"pipelines":{"items":{"$ref":"#/components/schemas/o11y.PostablePipeline"},"type":"array"}},"type":"object"},"o11y.LogsConnectionTest":{"properties":{"attribute_key":{"type":"string"},"attribute_value":{"type":"string"}},"type":"object"},"o11y.MSTeamsConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"summary":{"type":"string"},"text":{"type":"string"},"title":{"type":"string"},"webhook_url":{},"webhook_url_file":{"type":"string"}},"type":"object"},"o11y.MSTeamsV2Config":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"text":{"type":"string"},"title":{"type":"string"},"webhook_url":{},"webhook_url_file":{"type":"string"}},"type":"object"},"o11y.MattermostAttachment":{"properties":{"author_icon":{"type":"string"},"author_link":{"type":"string"},"author_name":{"type":"string"},"color":{"type":"string"},"fallback":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/o11y.MattermostField"},"type":"array"},"footer":{"type":"string"},"footer_icon":{"type":"string"},"image_url":{"type":"string"},"pretext":{"type":"string"},"text":{"type":"string"},"thumb_url":{"type":"string"},"title":{"type":"string"},"title_link":{"type":"string"}},"type":"object"},"o11y.MattermostConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"attachments":{"items":{"$ref":"#/components/schemas/o11y.MattermostAttachment"},"type":"array"},"channel":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"icon_emoji":{"type":"string"},"icon_url":{"type":"string"},"priority":{"$ref":"#/components/schemas/o11y.MattermostPriority"},"props":{"$ref":"#/components/schemas/o11y.MattermostProps"},"text":{"type":"string"},"type":{"type":"string"},"username":{"type":"string"},"webhook_url":{},"webhook_url_file":{"type":"string"}},"type":"object"},"o11y.MattermostField":{"properties":{"short":{"type":"boolean"},"title":{"type":"string"},"value":{"type":"string"}},"type":"object"},"o11y.MattermostPriority":{"properties":{"persistent_notifications":{"type":"boolean"},"priority":{"type":"string"},"requested_ack":{"type":"boolean"}},"type":"object"},"o11y.MattermostProps":{"properties":{"card":{"type":"string"}},"type":"object"},"o11y.MetricsComponentEntry":{"properties":{"associatedComponent":{"$ref":"#/components/schemas/o11y.AssociatedComponent"},"metrics":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.MissingAttributesComponentEntry":{"properties":{"associatedComponent":{"$ref":"#/components/schemas/o11y.AssociatedComponent"},"attributes":{"items":{"type":"string"},"type":"array"},"documentationLink":{"type":"string"},"message":{"type":"string"}},"type":"object"},"o11y.MissingMetricsComponentEntry":{"properties":{"associatedComponent":{"$ref":"#/components/schemas/o11y.AssociatedComponent"},"documentationLink":{"type":"string"},"message":{"type":"string"},"metrics":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.NamespaceListRecord":{"properties":{"countByPhase":{"$ref":"#/components/schemas/o11y.PodCountByPhase"},"cpuUsage":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"namespaceName":{"type":"string"}},"type":"object"},"o11y.NamespaceListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.NamespaceListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.NamespaceListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.NamespaceRecord":{"properties":{"meta":{"additionalProperties":{"type":"string"},"type":"object"},"namespaceCPU":{"type":"number"},"namespaceMemory":{"type":"number"},"namespaceName":{"type":"string"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"}},"type":"object"},"o11y.Namespaces":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.NamespaceRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.NodeCountByCondition":{"properties":{"notReady":{"type":"integer"},"ready":{"type":"integer"},"unknown":{"type":"integer"}},"type":"object"},"o11y.NodeCountsByReadiness":{"properties":{"notReady":{"type":"integer"},"ready":{"type":"integer"}},"type":"object"},"o11y.NodeListRecord":{"properties":{"countByCondition":{"$ref":"#/components/schemas/o11y.NodeCountByCondition"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"nodeCPUAllocatable":{"type":"number"},"nodeCPUUsage":{"type":"number"},"nodeMemoryAllocatable":{"type":"number"},"nodeMemoryUsage":{"type":"number"},"nodeUID":{"type":"string"}},"type":"object"},"o11y.NodeListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.NodeListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.NodeListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.NodeRecord":{"properties":{"condition":{},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"nodeCPU":{"type":"number"},"nodeCPUAllocatable":{"type":"number"},"nodeCountsByReadiness":{"$ref":"#/components/schemas/o11y.NodeCountsByReadiness"},"nodeMemory":{"type":"number"},"nodeMemoryAllocatable":{"type":"number"},"nodeName":{"type":"string"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"}},"type":"object"},"o11y.Nodes":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.NodeRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.NotifierConfig":{"properties":{"send_resolved":{"type":"boolean"}},"type":"object"},"o11y.O11yAPIKey":{"properties":{"createdAt":{"description":"CreatedAt is when the key was minted.","format":"date-time","type":"string"},"expiresAt":{"description":"ExpiresAt is when the key stops working, as a unix timestamp in seconds;\nzero means never.","type":"integer"},"id":{"description":"ID is the key id.","type":"string"},"lastObservedAt":{"description":"LastObservedAt is when the key was last seen authenticating.","format":"date-time","type":"string"},"name":{"description":"Name is the key's name.","type":"string"},"serviceAccountId":{"description":"ServiceAccountID is the account the key belongs to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yAPIKeyCreateIn":{"properties":{"expiresAt":{"description":"ExpiresAt is when the key stops working, as a unix timestamp in seconds.\nZero means it never expires; a past timestamp is refused.","type":"integer"},"name":{"description":"Name is the key's name: a lowercase letter followed by lowercase letters,\ndigits or hyphens, at most 80 characters. Required.","type":"string"}},"type":"object"},"o11y.O11yAPIKeyCreateOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yAPIKeySecret","description":"Data carries the key's id and secret."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAPIKeySecret":{"properties":{"id":{"description":"ID is the key id.","type":"string"},"key":{"description":"Key is the secret the service account authenticates with.","type":"string"}},"type":"object"},"o11y.O11yAPIKeyUpdateIn":{"properties":{"expiresAt":{"description":"ExpiresAt is when the key stops working, as a unix timestamp in seconds.\nZero means it never expires; a past timestamp is refused.","type":"integer"},"name":{"description":"Name is the key's new name, under the same rules it was created with.\nRequired.","type":"string"}},"type":"object"},"o11y.O11yAPIKeysOut":{"properties":{"data":{"description":"Data holds the keys.","items":{"$ref":"#/components/schemas/o11y.O11yAPIKey"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAccountOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Account","description":"Data holds the account."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAccountsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableAccounts","description":"Data holds the connected accounts."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAck":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAffectedAsset":{"properties":{"id":{"description":"ID is the asset's id.","type":"string"},"impactedLabels":{"description":"ImpactedLabels are the rule labels the asset uses.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the asset's name.","type":"string"},"type":{"description":"Type is dashboard or alert_rule.","type":"string"},"widget":{"$ref":"#/components/schemas/o11y.O11yAffectedWidget","description":"Widget is the affected panel, for a dashboard."}},"type":"object"},"o11y.O11yAffectedWidget":{"properties":{"id":{"description":"ID is the panel's id.","type":"string"},"name":{"description":"Name is the panel's name.","type":"string"}},"type":"object"},"o11y.O11yAgentCheckInIn":{"properties":{"account_id":{"type":"string"},"cloudIntegrationId":{},"cloud_account_id":{"type":"string"},"data":{"additionalProperties":{"type":"object"},"type":"object"},"providerAccountId":{"type":"string"}},"type":"object"},"o11y.O11yAgentCheckInOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableAgentCheckIn","description":"Data holds the check-in result."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAggregateAttributesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.AggregateAttributeResponse","description":"Data holds the attribute keys."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAggregation":{"properties":{"alias":{"description":"Alias is the aggregation's alias.","type":"string"},"anomalyScores":{"description":"AnomalyScores are anomaly overlays.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"},"index":{"description":"Index is the aggregation's position in the query.","type":"integer"},"lowerBoundSeries":{"description":"LowerBoundSeries are forecast lower bounds.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"},"meta":{"$ref":"#/components/schemas/o11y.O11yAggregationMeta","description":"Meta describes the aggregation."},"predictedSeries":{"description":"PredictedSeries are forecast overlays, when the query asked for them.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"},"series":{"description":"Series are the aggregated time series.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"},"upperBoundSeries":{"description":"UpperBoundSeries are forecast upper bounds.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"}},"type":"object"},"o11y.O11yAggregationMeta":{"properties":{"unit":{"description":"Unit is the aggregation's unit.","type":"string"}},"type":"object"},"o11y.O11yAlertsOut":{"properties":{"data":{"description":"Data holds the alerts.","items":{"$ref":"#/components/schemas/o11y.DeprecatedGettableAlert"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAnalyzeIn":{"properties":{"query":{"description":"Query is the query text. Required.","type":"string"},"queryType":{"description":"QueryType says which language the query is in — promql or the datastore's\nSQL dialect. Required.","type":"string"}},"required":["query","queryType"],"type":"object"},"o11y.O11yAnalyzeOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yQueryFilterAnalysis","description":"Data is the analysis."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yApdexOut":{"properties":{"data":{"description":"Data holds one settings row per service.","items":{"$ref":"#/components/schemas/o11y.O11yApdexSettings"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yApdexSetIn":{"properties":{"excludeStatusCodes":{"description":"ExcludeStatusCodes are status codes excluded from the score, comma\nseparated.","type":"string"},"serviceName":{"description":"ServiceName is the service the threshold applies to.","type":"string"},"threshold":{"description":"Threshold is the satisfied-response time in seconds.","type":"number"}},"type":"object"},"o11y.O11yApdexSetOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMessage","description":"Data is the acknowledgment."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yApdexSettings":{"properties":{"excludeStatusCodes":{"description":"ExcludeStatusCodes are status codes excluded from the score, comma\nseparated.","type":"string"},"id":{"description":"ID is the settings row's id.","type":"string"},"orgId":{"description":"OrgID is the org the settings belong to.","type":"string"},"serviceName":{"description":"ServiceName is the service.","type":"string"},"threshold":{"description":"Threshold is the satisfied-response time in seconds.","type":"number"}},"type":"object"},"o11y.O11yAttributeKey":{"properties":{"dataType":{"description":"DataType is the attribute's value type — string, int64, float64 or bool.","type":"string"},"isColumn":{"description":"IsColumn marks an attribute materialized as its own column.","type":"boolean"},"isJSON":{"description":"IsJSON marks an attribute extracted from a JSON body.","type":"boolean"},"key":{"description":"Key is the attribute's name.","type":"string"},"type":{"description":"Type says where the attribute lives: tag or resource.","type":"string"}},"type":"object"},"o11y.O11yAttributeKeysOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.FilterAttributeKeyResponse","description":"Data holds the attribute keys."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAttributeMapping":{"properties":{"email":{"description":"Email is the key carrying the email; defaults to \"email\".","type":"string"},"groups":{"description":"Groups is the key carrying the group list; defaults to \"groups\".","type":"string"},"name":{"description":"Name is the key carrying the display name; defaults to \"name\".","type":"string"},"role":{"description":"Role is the key carrying the role; defaults to \"role\".","type":"string"}},"type":"object"},"o11y.O11yAttributeValuesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.FilterAttributeValueResponse","description":"Data holds the values, split by data type."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAuthDomain":{"properties":{"authNProviderInfo":{"$ref":"#/components/schemas/o11y.O11yAuthNProviderInfo","description":"AuthNProviderInfo is provider detail the console needs to finish setup."},"config":{"$ref":"#/components/schemas/o11y.O11yAuthDomainConfig","description":"Config is the domain's SSO configuration."},"createdAt":{"description":"CreatedAt is when it was claimed.","format":"date-time","type":"string"},"id":{"description":"ID is the auth domain id.","type":"string"},"name":{"description":"Name is the email domain, e.g. example.com.","type":"string"},"orgId":{"description":"OrgID is the org that claimed it.","type":"string"},"updatedAt":{"description":"UpdatedAt is when its configuration last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yAuthDomainConfig":{"properties":{"googleAuthConfig":{"$ref":"#/components/schemas/o11y.O11yGoogleConfig","description":"Google is the Google provider's settings, when SSOType is google_auth."},"oidcConfig":{"$ref":"#/components/schemas/o11y.O11yOIDCConfig","description":"OIDC is the OIDC provider's settings, when SSOType is oidc."},"roleMapping":{"$ref":"#/components/schemas/o11y.O11yRoleMapping","description":"RoleMapping maps the provider's groups onto roles for new users."},"samlConfig":{"$ref":"#/components/schemas/o11y.O11ySAMLConfig","description":"SAML is the SAML provider's settings, when SSOType is saml."},"ssoEnabled":{"description":"SSOEnabled turns enforced SSO on for the domain.","type":"boolean"},"ssoType":{"description":"SSOType picks the provider — saml, google_auth or oidc.","type":"string"}},"type":"object"},"o11y.O11yAuthDomainOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yAuthDomain","description":"Data is the domain."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAuthDomainsOut":{"properties":{"data":{"description":"Data holds the domains.","items":{"$ref":"#/components/schemas/o11y.O11yAuthDomain"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yAuthNProviderInfo":{"properties":{"relayStatePath":{"description":"RelayStatePath is the relay-state path a SAML IdP must be configured\nwith, when the provider needs one.","type":"string"}},"type":"object"},"o11y.O11yAuthNSupport":{"properties":{"callback":{"description":"Callback are the SSO routes; each is begun by visiting its URL.","items":{"$ref":"#/components/schemas/o11y.O11yCallbackAuthN"},"type":"array"},"password":{"description":"Password are the password routes.","items":{"$ref":"#/components/schemas/o11y.O11yPasswordAuthN"},"type":"array"}},"type":"object"},"o11y.O11yBulkInviteIn":{"properties":{"invites":{"description":"Invites are the invitations to create; an email may appear only once.","items":{"$ref":"#/components/schemas/o11y.O11yInviteIn"},"type":"array"}},"type":"object"},"o11y.O11yCallbackAuthN":{"properties":{"provider":{"description":"Provider is the route's provider — google_auth, saml or oidc.","type":"string"},"url":{"description":"URL is where the browser goes to begin the flow.","type":"string"}},"type":"object"},"o11y.O11yChangePasswordIn":{"properties":{"newPassword":{"description":"NewPassword is the password to set.","type":"string"},"oldPassword":{"description":"OldPassword is the current password; the change is refused when it does\nnot match.","type":"string"}},"type":"object"},"o11y.O11yChannelOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Channel","description":"Data holds the channel."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yChannelUpdateIn":{"properties":{"Receiver":{"$ref":"#/components/schemas/o11y.Receiver"},"googlechat_configs":{"items":{"$ref":"#/components/schemas/o11y.GoogleChatReceiverConfig"},"type":"array"}},"type":"object"},"o11y.O11yChannelsOut":{"properties":{"data":{"description":"Data holds the channels.","items":{"$ref":"#/components/schemas/o11y.Channel"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCheckOut":{"properties":{"data":{"description":"Data holds the verdicts.","items":{"$ref":"#/components/schemas/o11y.O11yTransactionResult"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yClusterListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.ClusterListResponse","description":"Data holds the cluster records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yColumnInfo":{"properties":{"columnAlias":{"description":"Alias is the column's alias in the query, when it has one.","type":"string"},"columnName":{"description":"Name is the column's name.","type":"string"}},"type":"object"},"o11y.O11yConnectionStatusOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.IntegrationConnectionStatus","description":"Data holds the logs and metrics connection status."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCreateAccountIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.PostableAccountConfig"},"credentials":{"$ref":"#/components/schemas/o11y.Credentials"}},"type":"object"},"o11y.O11yCreateAccountOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableAccountWithConnectionArtifact","description":"Data holds the account and the connection artifact."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCreateLimitIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.LimitConfig"},"signal":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yCreated":{"properties":{"id":{"description":"ID is the new record's id.","type":"string"}},"type":"object"},"o11y.O11yCreatedIngestionKeyOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableCreatedIngestionKey","description":"Data holds the created ingestion key."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCreatedLimitOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableCreatedIngestionKeyLimit","description":"Data holds the created limit."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCreatedOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yCreated","description":"Data carries the id."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yCredentialsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Credentials","description":"Data holds the connection credentials."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDaemonSetListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.DaemonSetListResponse","description":"Data holds the daemonset records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboard":{"properties":{"createdAt":{"description":"CreatedAt is when the dashboard was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created it.","type":"string"},"id":{"description":"ID is the dashboard's id.","type":"string"},"image":{"description":"Image is an optional cover image reference.","type":"string"},"locked":{"description":"Locked reports whether the dashboard is locked against edits.","type":"boolean"},"name":{"description":"Name is the dashboard's unique internal name.","type":"string"},"orgId":{"description":"OrgID is the org the dashboard belongs to.","type":"string"},"schemaVersion":{"description":"SchemaVersion is the dashboard schema version.","type":"string"},"source":{"description":"Source is where the dashboard came from: user, system or integration.","type":"string"},"spec":{"description":"Spec is the Perses dashboard spec, carried verbatim as open JSON."},"tags":{"description":"Tags are the dashboard's tags.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardTag"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is who last changed it.","type":"string"}},"type":"object"},"o11y.O11yDashboardDisplay":{"properties":{"description":{"description":"Description says what the dashboard is for.","type":"string"},"name":{"description":"Name is the human-facing dashboard name.","type":"string"}},"type":"object"},"o11y.O11yDashboardList":{"properties":{"dashboards":{"description":"Dashboards are the rows for this page.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardListItem"},"type":"array"},"tags":{"description":"Tags are all tags in use across the org's dashboards.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardTag"},"type":"array"},"total":{"description":"Total is the count across all pages.","type":"integer"}},"type":"object"},"o11y.O11yDashboardListForUser":{"properties":{"dashboards":{"description":"Dashboards are the rows for this page, each with the caller's pin state.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardListItemForUser"},"type":"array"},"tags":{"description":"Tags are all tags in use across the org's dashboards.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardTag"},"type":"array"},"total":{"description":"Total is the count across all pages.","type":"integer"}},"type":"object"},"o11y.O11yDashboardListForUserOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardListForUser","description":"Data is the list page with pins."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardListItem":{"properties":{"createdAt":{"description":"CreatedAt is when the dashboard was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created it.","type":"string"},"id":{"description":"ID is the dashboard's id.","type":"string"},"image":{"description":"Image is an optional cover image reference.","type":"string"},"locked":{"description":"Locked reports whether the dashboard is locked against edits.","type":"boolean"},"name":{"description":"Name is the dashboard's unique internal name.","type":"string"},"orgId":{"description":"OrgID is the org the dashboard belongs to.","type":"string"},"schemaVersion":{"description":"SchemaVersion is the dashboard schema version.","type":"string"},"source":{"description":"Source is where the dashboard came from: user, system or integration.","type":"string"},"spec":{"$ref":"#/components/schemas/o11y.O11yDashboardListSpec","description":"Spec is the display-only slice of the dashboard spec."},"tags":{"description":"Tags are the dashboard's tags.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardTag"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is who last changed it.","type":"string"}},"type":"object"},"o11y.O11yDashboardListItemForUser":{"properties":{"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"id":{"type":"string"},"image":{"type":"string"},"locked":{"type":"boolean"},"name":{"type":"string"},"orgId":{"type":"string"},"pinned":{"description":"Pinned reports whether the calling user has pinned this dashboard.","type":"boolean"},"schemaVersion":{"type":"string"},"source":{"type":"string"},"spec":{"$ref":"#/components/schemas/o11y.O11yDashboardListSpec"},"tags":{"items":{"$ref":"#/components/schemas/o11y.O11yDashboardTag"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"}},"type":"object"},"o11y.O11yDashboardListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardList","description":"Data is the list page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardListSpec":{"properties":{"display":{"$ref":"#/components/schemas/o11y.O11yDashboardDisplay","description":"Display is the dashboard's display metadata."}},"type":"object"},"o11y.O11yDashboardOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboard","description":"Data is the dashboard."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardPatchIn":{"properties":{"id":{"description":"ID is the dashboard id from the path.","type":"string"},"ops":{"description":"Ops are the JSON Patch operations, applied in order. On the wire this IS the\nrequest body — a bare array, not an object.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardPatchOp"},"type":"array"}},"type":"object"},"o11y.O11yDashboardPatchOp":{"properties":{"from":{"description":"From is the source JSON Pointer for move and copy; ignored otherwise.","type":"string"},"op":{"description":"Op is the verb: add, remove, replace, move, copy or test.","type":"string"},"path":{"description":"Path is a JSON Pointer into the postable dashboard, e.g.\n/spec/display/name, /spec/panels/\u003cid\u003e, /tags/-.","type":"string"},"value":{"description":"Value is the value to add/replace/test; its type depends on Path. Required\nfor add, replace and test; ignored otherwise."}},"type":"object"},"o11y.O11yDashboardPostable":{"properties":{"generateName":{"description":"GenerateName derives a fresh unique name from spec.display.name instead of\ntaking Name.","type":"boolean"},"image":{"description":"Image is an optional cover image reference.","type":"string"},"name":{"description":"Name is the dashboard's unique internal name (a DNS-1123 label). Omit it\nwith generateName to derive one from the display name.","type":"string"},"schemaVersion":{"description":"SchemaVersion is the dashboard schema version; must be the current v6.","type":"string"},"spec":{"description":"Spec is the Perses dashboard spec (display, variables, panels, layouts,\ndatasources). Its plugin-defined leaves are open JSON, carried verbatim."},"tags":{"description":"Tags are the dashboard's tags; at most ten, and none may use a reserved DSL\nkey.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardPostableTag"},"type":"array"}},"type":"object"},"o11y.O11yDashboardPostableTag":{"properties":{"key":{"description":"Key is the tag key.","type":"string"},"value":{"description":"Value is the tag value.","type":"string"}},"type":"object"},"o11y.O11yDashboardTag":{"properties":{"key":{"description":"Key is the tag key.","type":"string"},"value":{"description":"Value is the tag value.","type":"string"}},"type":"object"},"o11y.O11yDashboardUpdateIn":{"properties":{"id":{"description":"ID is the dashboard id from the path.","type":"string"},"image":{"type":"string"},"name":{"type":"string"},"schemaVersion":{"type":"string"},"spec":{},"tags":{"items":{"$ref":"#/components/schemas/o11y.O11yDashboardPostableTag"},"type":"array"}},"type":"object"},"o11y.O11yDashboardVarValues":{"properties":{"variableValues":{"description":"VariableValues are the values, in the order the query produced them.","items":{"type":"object"},"type":"array"}},"type":"object"},"o11y.O11yDashboardVarsIn":{"properties":{"query":{"description":"Query is the variable query to evaluate. Required.","type":"string"},"variables":{"additionalProperties":{"type":"object"},"description":"Variables are the current values of the other dashboard variables, for\nqueries that reference them.","type":"object"}},"required":["query"],"type":"object"},"o11y.O11yDashboardVarsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardVarValues","description":"Data holds the values."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardView":{"properties":{"createdAt":{"description":"CreatedAt is when the view was created.","format":"date-time","type":"string"},"data":{"$ref":"#/components/schemas/o11y.O11yDashboardViewData","description":"Data is the listing state the view captures."},"id":{"description":"ID is the saved view's id.","type":"string"},"name":{"description":"Name is the saved view's name.","type":"string"},"orgId":{"description":"OrgID is the org the view belongs to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yDashboardViewData":{"properties":{"order":{"description":"Order is the captured sort direction.","type":"string"},"query":{"description":"Query is the captured filter DSL.","type":"string"},"sort":{"description":"Sort is the captured sort field.","type":"string"},"version":{"description":"Version is the saved-view schema version; must be v1.","type":"string"}},"type":"object"},"o11y.O11yDashboardViewList":{"properties":{"views":{"description":"Views are the saved views, shared org-wide.","items":{"$ref":"#/components/schemas/o11y.O11yDashboardView"},"type":"array"}},"type":"object"},"o11y.O11yDashboardViewListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardViewList","description":"Data is the saved views."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardViewOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardView","description":"Data is the saved view."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDashboardViewPostable":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardViewData","description":"Data is the listing state the view captures."},"name":{"description":"Name is the saved view's name; at most 32 characters, no surrounding space.","type":"string"}},"type":"object"},"o11y.O11yDashboardViewUpdateIn":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDashboardViewData"},"id":{"description":"ID is the saved view id from the path.","type":"string"},"name":{"type":"string"}},"type":"object"},"o11y.O11yDependency":{"properties":{"callCount":{"description":"CallCount is how many calls crossed the edge in the window.","type":"integer"},"callRate":{"description":"CallRate is calls per second.","type":"number"},"child":{"description":"Child is the called service.","type":"string"},"errorRate":{"description":"ErrorRate is the percentage of calls that erred.","type":"number"},"p50":{"description":"P50 is the median call duration, in nanoseconds.","type":"number"},"p75":{"description":"P75 is the 75th-percentile call duration, in nanoseconds.","type":"number"},"p90":{"description":"P90 is the 90th-percentile call duration, in nanoseconds.","type":"number"},"p95":{"description":"P95 is the 95th-percentile call duration, in nanoseconds.","type":"number"},"p99":{"description":"P99 is the 99th-percentile call duration, in nanoseconds.","type":"number"},"parent":{"description":"Parent is the calling service.","type":"string"}},"type":"object"},"o11y.O11yDependencyGraphIn":{"properties":{"end":{"description":"End is the window end, as epoch nanoseconds. Required.","type":"string"},"start":{"description":"Start is the window start, as epoch nanoseconds. Required.","type":"string"},"tags":{"description":"Tags narrow the graph to spans matching every condition.","items":{"$ref":"#/components/schemas/o11y.O11yTagFilter"},"type":"array"}},"required":["start","end"],"type":"object"},"o11y.O11yDeploymentListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.DeploymentListResponse","description":"Data holds the deployment records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDeprecatedUser":{"properties":{"createdAt":{"description":"CreatedAt is when they joined.","format":"date-time","type":"string"},"displayName":{"description":"DisplayName is what the console shows for them.","type":"string"},"email":{"description":"Email is their address.","type":"string"},"id":{"description":"ID is the user id.","type":"string"},"isRoot":{"description":"IsRoot marks the org's root user.","type":"boolean"},"orgId":{"description":"OrgID is the org they belong to.","type":"string"},"role":{"description":"Role is their legacy role — ADMIN, EDITOR or VIEWER.","type":"string"},"status":{"description":"Status is their lifecycle state — active, pending_invite or deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when their record last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yDeprecatedUserOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUser","description":"Data is the member."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDeprecatedUserUpdate":{"properties":{"displayName":{"description":"DisplayName is the new display name; empty leaves it unchanged.","type":"string"},"role":{"description":"Role is the legacy role to move to — ADMIN, EDITOR or VIEWER; empty\nleaves it unchanged.","type":"string"}},"type":"object"},"o11y.O11yDeprecatedUsersOut":{"properties":{"data":{"description":"Data holds the members.","items":{"$ref":"#/components/schemas/o11y.O11yDeprecatedUser"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDiscoverIn":{"properties":{"aggregations":{"description":"Aggregations are the measures to compute per group. Empty means a count.","items":{"type":"string"},"type":"array"},"filters":{"description":"Filters narrow the scan; each is a field, an operator (eq, neq, like) and\na value, and they combine with AND.","items":{"$ref":"#/components/schemas/o11y.O11yFilter"},"type":"array"},"groupBy":{"description":"GroupBy are the columns to group the rows by.","items":{"type":"string"},"type":"array"},"limit":{"description":"Limit caps how many rows come back.","type":"integer"},"orderBy":{"description":"OrderBy is the column or aggregation to sort the rows on.","type":"string"},"orderDir":{"description":"OrderDir is asc or desc.","type":"string"},"period":{"description":"Period is the window to read, relative to now — 1h, 24h, 7d, 14d, 30d.","type":"string"},"project":{"description":"Project is the project to read, as its id. Required.","type":"string"}},"required":["project"],"type":"object"},"o11y.O11yDiscoverOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yTable","description":"Data is the table."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDisk":{"properties":{"name":{"description":"Name is the disk's name.","type":"string"},"type":{"description":"Type is the disk's type, e.g. local or s3.","type":"string"}},"type":"object"},"o11y.O11yDomainFilter":{"properties":{"expression":{"description":"Expression is the predicate, e.g. `http.status_code \u003e= 500`.","type":"string"}},"type":"object"},"o11y.O11yDomainGroupBy":{"properties":{"description":{"description":"Description describes the field, when known.","type":"string"},"fieldContext":{"description":"FieldContext says which plane the field lives on, e.g. attribute,\nresource, span.","type":"string"},"fieldDataType":{"description":"FieldDataType is the field's type: string, int64, float64 or bool.","type":"string"},"name":{"description":"Name is the field's name. Required.","type":"string"},"signal":{"description":"Signal is the telemetry signal the field belongs to, e.g. traces.","type":"string"},"unit":{"description":"Unit is the field's unit, when known.","type":"string"}},"type":"object"},"o11y.O11yDomainsAnswer":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDomainsData","description":"Data holds the per-query results, emitted LAST — see the field-order note\non the type."},"meta":{"$ref":"#/components/schemas/o11y.O11yQueryStats","description":"Meta reports what the read cost."},"type":{"description":"Type names the result shape: scalar, time_series or raw.","type":"string"},"warning":{"$ref":"#/components/schemas/o11y.O11yQueryWarning","description":"Warning carries the store's warning for this read, when it raised one."}},"type":"object"},"o11y.O11yDomainsData":{"properties":{"results":{"description":"Results is one entry per query. Each entry's shape follows the answer's\ntype — time-series data, scalar data or raw rows — so the bytes pass\nthrough verbatim.","items":{},"type":"array"}},"type":"object"},"o11y.O11yDomainsIn":{"properties":{"domain":{"description":"Domain narrows the read to one external domain (the domain view requires\nit).","type":"string"},"end":{"description":"End is the window's end, epoch milliseconds.","type":"integer"},"endpoint":{"description":"Endpoint narrows the domain view to one endpoint.","type":"string"},"filter":{"$ref":"#/components/schemas/o11y.O11yDomainFilter","description":"Filter is an additional predicate in the query-builder filter syntax."},"groupBy":{"description":"GroupBy adds grouping columns to the result.","items":{"$ref":"#/components/schemas/o11y.O11yDomainGroupBy"},"type":"array"},"show_ip":{"description":"ShowIP keeps rows whose domain is a bare IP address; they are dropped\notherwise.","type":"boolean"},"start":{"description":"Start is the window's start, epoch milliseconds.","type":"integer"}},"type":"object"},"o11y.O11yDomainsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yDomainsAnswer","description":"Data holds the result."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDowntimeScheduleOut":{"properties":{"data":{"description":"Data holds the schedule."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDowntimeSchedulesOut":{"properties":{"data":{"description":"Data holds the schedules.","items":{},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yDowntimeUpdateIn":{"properties":{"alertIds":{"items":{"type":"string"},"type":"array"},"description":{"type":"string"},"name":{"type":"string"},"schedule":{},"scope":{"type":"string"}},"type":"object"},"o11y.O11yDraftFunnelIn":{"properties":{"end_time":{"description":"EndTime is the end of the window, as a millisecond epoch.","type":"integer"},"start_time":{"description":"StartTime is the start of the window, as a millisecond epoch.","type":"integer"},"step_end":{"description":"StepEnd is the step the transition runs to, 1-based.","type":"integer"},"step_start":{"description":"StepStart is the step the transition runs from, 1-based. Ignored by the\nreads that span the whole funnel.","type":"integer"},"steps":{"description":"Steps are the funnel's steps, in order. At least two are needed.","items":{"$ref":"#/components/schemas/o11y.FunnelStep"},"type":"array"}},"type":"object"},"o11y.O11yEmailPasswordSessionIn":{"properties":{"email":{"description":"Email is the account's address. Required.","type":"string"},"orgId":{"description":"OrgID picks the org to sign into when the address belongs to several.","type":"string"},"password":{"description":"Password is the account's password. Required.","type":"string"}},"type":"object"},"o11y.O11yErrorDetail":{"properties":{"code":{"description":"Code is the machine-readable code.","type":"string"},"errors":{"description":"Errors are further details, one message and its suggestions each.","items":{"$ref":"#/components/schemas/o11y.O11yErrorItem"},"type":"array"},"message":{"description":"Message is the human-readable reason.","type":"string"},"retry":{"$ref":"#/components/schemas/o11y.O11yRetry","description":"Retry says when it is worth trying again, for errors that pass."},"suggestions":{"description":"Suggestions say what to try instead.","items":{"type":"string"},"type":"array"},"type":{"description":"Type is the error's category, e.g. invalid_input, not_found.","type":"string"},"url":{"description":"Url points at documentation for the error, when there is any.","type":"string"}},"type":"object"},"o11y.O11yErrorGettableIssue":{"properties":{"issue":{"$ref":"#/components/schemas/o11y.O11yErrorIssue","description":"Issue is the lifecycle row."},"latestEvent":{"$ref":"#/components/schemas/o11y.O11yOccurrence","description":"LatestEvent is the most recent occurrence that landed on the issue."}},"type":"object"},"o11y.O11yErrorGettableIssueOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yErrorGettableIssue","description":"Data is the issue and its latest occurrence."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yErrorIssue":{"properties":{"assignee":{"description":"Assignee is who the issue is assigned to.","type":"string"},"count":{"description":"Count is how many occurrences have landed on the issue.","type":"integer"},"createdAt":{"description":"CreatedAt is when the issue was first recorded.","format":"date-time","type":"string"},"culprit":{"description":"Culprit is where it came from — the function or route blamed for it.","type":"string"},"environment":{"description":"Environment is the deployment the issue was seen in.","type":"string"},"fingerprint":{"description":"Fingerprint is the grouping key that puts like errors in one issue.","type":"string"},"firstSeen":{"description":"FirstSeen is when the earliest occurrence was recorded.","format":"date-time","type":"string"},"id":{"description":"ID is the issue id.","type":"string"},"lastSeen":{"description":"LastSeen is when the latest was.","format":"date-time","type":"string"},"level":{"description":"Level is the issue's severity, e.g. error, warning, info.","type":"string"},"platform":{"description":"Platform is the reporting runtime, e.g. go, python, javascript.","type":"string"},"regressed":{"description":"Regressed marks an issue that reopened after being resolved.","type":"boolean"},"release":{"description":"Release is the version that produced it.","type":"string"},"resolvedAt":{"description":"ResolvedAt is when the issue was resolved, if it is.","format":"date-time","type":"string"},"serviceName":{"description":"ServiceName is the service that reported it.","type":"string"},"status":{"description":"Status is the lifecycle state: unresolved, resolved or ignored.","type":"string"},"type":{"description":"Type is the exception type.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the issue last changed.","format":"date-time","type":"string"},"value":{"description":"Value is the exception value.","type":"string"}},"type":"object"},"o11y.O11yErrorIssueOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yErrorIssue","description":"Data is the issue."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yErrorIssues":{"properties":{"items":{"description":"Items are the issues.","items":{"$ref":"#/components/schemas/o11y.O11yErrorIssue"},"type":"array"},"limit":{"description":"Limit is the page cap that was applied.","type":"integer"},"offset":{"description":"Offset is how many were skipped.","type":"integer"},"total":{"description":"Total is how many matched the filter.","type":"integer"}},"type":"object"},"o11y.O11yErrorIssuesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yErrorIssues","description":"Data holds the issues."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yErrorItem":{"properties":{"message":{"description":"Message is the detail.","type":"string"},"suggestions":{"description":"Suggestions say what to try about this detail.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yErrorUpdateIssueIn":{"properties":{"assignee":{"description":"Assignee is who the issue is assigned to.","type":"string"},"id":{"description":"ID is the issue id.","type":"string"},"status":{"description":"Status is the new lifecycle state: unresolved, resolved or ignored.","type":"string"}},"required":["id"],"type":"object"},"o11y.O11yErrorWithSpan":{"properties":{"errorId":{"description":"ErrorID is the exception instance id.","type":"string"},"exceptionEscaped":{"description":"ExceptionEscaped marks an exception that escaped its span uncaught.","type":"boolean"},"exceptionMessage":{"description":"ExceptionMsg is the exception's message.","type":"string"},"exceptionStacktrace":{"description":"ExceptionStacktrace is the captured stack trace.","type":"string"},"exceptionType":{"description":"ExceptionType is the exception's type.","type":"string"},"groupID":{"description":"GroupID is the exception group it belongs to.","type":"string"},"serviceName":{"description":"ServiceName is the service that reported it.","type":"string"},"spanID":{"description":"SpanID is the span it happened on.","type":"string"},"timestamp":{"description":"Timestamp is when it happened.","format":"date-time","type":"string"},"traceID":{"description":"TraceID is the trace the span belonged to.","type":"string"}},"type":"object"},"o11y.O11yErrorsCountIn":{"properties":{"end":{"description":"End is the window end, as a nanosecond epoch spelled as a string.","type":"string"},"exceptionType":{"description":"ExceptionType narrows to one exception type.","type":"string"},"serviceName":{"description":"ServiceName narrows to one reporting service.","type":"string"},"start":{"description":"Start is the window start, as a nanosecond epoch spelled as a string.","type":"string"},"tags":{"description":"Tags narrow the scan to spans carrying the given tag values.","items":{"$ref":"#/components/schemas/o11y.O11yTagQuery"},"type":"array"}},"type":"object"},"o11y.O11yErrorsListIn":{"properties":{"end":{"description":"End is the window end, as a nanosecond epoch spelled as a string.","type":"string"},"exceptionType":{"description":"ExceptionType narrows to one exception type.","type":"string"},"limit":{"description":"Limit caps how many exception groups come back. Required, non-zero.","type":"integer"},"offset":{"description":"Offset is how many groups to skip.","type":"integer"},"order":{"description":"Order is the direction: ascending or descending.","type":"string"},"orderParam":{"description":"OrderParam is the column to order by, e.g. exceptionCount, lastSeen.","type":"string"},"serviceName":{"description":"ServiceName narrows to one reporting service.","type":"string"},"start":{"description":"Start is the window start, as a nanosecond epoch spelled as a string.","type":"string"},"tags":{"description":"Tags narrow the scan to spans carrying the given tag values.","items":{"$ref":"#/components/schemas/o11y.O11yTagQuery"},"type":"array"}},"type":"object"},"o11y.O11yEvent":{"properties":{"culprit":{"description":"Culprit is where it came from — the function or route blamed for it.","type":"string"},"environment":{"description":"Environment is the deployment it happened in.","type":"string"},"eventId":{"description":"EventID is its own id.","type":"string"},"fingerprint":{"description":"Fingerprint is the grouping key that puts like errors in one issue.","type":"string"},"frames":{"description":"Frames are the stack, innermost first.","items":{"$ref":"#/components/schemas/o11y.O11yFrame"},"type":"array"},"handled":{"description":"Handled says whether the application caught it.","type":"boolean"},"level":{"description":"Level is its severity, e.g. error, warning, info.","type":"string"},"message":{"description":"Message is the human-readable message.","type":"string"},"orgId":{"description":"OrgID is the org that owns it.","type":"string"},"platform":{"description":"Platform is the reporting runtime, e.g. go, python, javascript.","type":"string"},"projectId":{"description":"ProjectID is the project it was captured for.","type":"string"},"receivedAt":{"description":"ReceivedAt is when it arrived here.","format":"date-time","type":"string"},"release":{"description":"Release is the version that produced it.","type":"string"},"serverName":{"description":"ServerName is the host that reported it.","type":"string"},"serviceName":{"description":"ServiceName is the service that reported it.","type":"string"},"spanId":{"description":"SpanID is the span it belonged to.","type":"string"},"tags":{"additionalProperties":{"type":"string"},"description":"Tags are the reporter's own key/value labels.","type":"object"},"timestamp":{"description":"Timestamp is when the error happened, as the reporter recorded it.","format":"date-time","type":"string"},"traceId":{"description":"TraceID is the trace it belonged to.","type":"string"},"transaction":{"description":"Transaction is the operation it happened in.","type":"string"},"type":{"description":"Type is the exception type.","type":"string"},"userEmail":{"description":"UserEmail is that user's email, when attached.","type":"string"},"userId":{"description":"UserID identifies the affected end user, when the reporter attached one.","type":"string"},"userIp":{"description":"UserIP is that user's address, when attached.","type":"string"},"value":{"description":"Value is the exception value.","type":"string"}},"type":"object"},"o11y.O11yEventIn":{"properties":{"attributes":{"additionalProperties":{"type":"object"},"description":"Attributes are free-form event properties.","type":"object"},"eventName":{"description":"EventName names the event; required for track events.","type":"string"},"eventType":{"description":"EventType is the kind of event — track, identify or group. Required.","type":"string"},"rateLimited":{"description":"RateLimited marks an event the reporting client rate-limited.","type":"boolean"}},"required":["eventType"],"type":"object"},"o11y.O11yEventUser":{"properties":{"email":{"description":"Email is that user's email.","type":"string"},"id":{"description":"ID identifies the affected end user.","type":"string"},"ipAddress":{"description":"IP is that user's address.","type":"string"},"username":{"description":"Username is that user's name.","type":"string"}},"type":"object"},"o11y.O11yEvents":{"properties":{"items":{"description":"Items are the events, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yEvent"},"type":"array"}},"type":"object"},"o11y.O11yFeature":{"properties":{"defaultVariant":{"description":"DefaultVariant is the variant used when nothing overrides it.","type":"string"},"description":{"description":"Description says what the flag gates.","type":"string"},"kind":{"description":"Kind is the flag's value kind, e.g. boolean.","type":"string"},"name":{"description":"Name is the flag's name.","type":"string"},"resolvedValue":{"description":"ResolvedValue is the value resolved for the caller's org.","type":"object"},"stage":{"description":"Stage is the flag's lifecycle stage, e.g. stable.","type":"string"},"variants":{"additionalProperties":{"type":"object"},"description":"Variants are the flag's possible values, by variant name.","type":"object"}},"type":"object"},"o11y.O11yFeaturesOut":{"properties":{"data":{"description":"Data are the features.","items":{"$ref":"#/components/schemas/o11y.O11yFeature"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFieldCatalogOut":{"properties":{"interesting":{"description":"Interesting are fields seen in the data that could be selected.","items":{"$ref":"#/components/schemas/o11y.O11yTelemetryField"},"type":"array"},"selected":{"description":"Selected are the fields materialized as their own columns.","items":{"$ref":"#/components/schemas/o11y.O11yTelemetryField"},"type":"array"}},"type":"object"},"o11y.O11yFieldKeysOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFieldKeys","description":"Data holds the field keys grouped by name, and whether the catalog is\ncomplete."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFieldSetting":{"properties":{"dataType":{"description":"DataType is the field's data type, e.g. string, int64, float64, bool.\nRequired.","type":"string"},"index":{"description":"Index is the index expression to put on the column, e.g. minmax,\nset(N), bloom_filter(P), tokenbf_v1(S,H,SEED). Empty keeps the default.","type":"string"},"indexGranularity":{"description":"IndexGranularity is the index granularity in rows.","type":"integer"},"name":{"description":"Name is the field to tune. Required.","type":"string"},"selected":{"description":"Selected materializes the field as its own column when true.","type":"boolean"},"type":{"description":"Type is where the field lives: attributes or resources. Required.","type":"string"}},"required":["name","dataType","type"],"type":"object"},"o11y.O11yFieldValuesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFieldValues","description":"Data holds the values by data type, and whether the value list is complete."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFilter":{"properties":{"field":{"description":"Field is the column to test.","type":"string"},"op":{"description":"Op is how to test it: eq, neq or like.","type":"string"},"value":{"description":"Value is what to test it against.","type":"string"}},"type":"object"},"o11y.O11yFilterItem":{"properties":{"key":{"$ref":"#/components/schemas/o11y.O11yAttributeKey","description":"Key is the attribute tested."},"op":{"description":"Operator is the comparison, e.g. =, !=, in, contains.","type":"string"},"value":{"description":"Value is what it is tested against; its type follows the attribute's.","type":"object"}},"type":"object"},"o11y.O11yFilterKey":{"properties":{"dataType":{"description":"DataType is the attribute's value type — string, int64, float64 or bool.","type":"string"},"isColumn":{"description":"IsColumn marks an attribute stored as its own column.","type":"boolean"},"isJSON":{"description":"IsJSON marks an attribute read out of a JSON body.","type":"boolean"},"key":{"description":"Key is the attribute name.","type":"string"},"type":{"description":"Type says where the attribute lives — tag or resource.","type":"string"}},"type":"object"},"o11y.O11yFilterSet":{"properties":{"items":{"description":"Items are the conditions.","items":{"$ref":"#/components/schemas/o11y.O11yFilterItem"},"type":"array"},"op":{"description":"Operator combines the items — AND or OR.","type":"string"}},"type":"object"},"o11y.O11yFilterSuggestions":{"properties":{"attributes":{"description":"Attributes are the suggested attribute keys.","items":{"$ref":"#/components/schemas/o11y.O11yAttributeKey"},"type":"array"},"example_queries":{"description":"ExampleQueries are ready-to-run filter sets.","items":{"$ref":"#/components/schemas/o11y.O11yFilterSet"},"type":"array"}},"type":"object"},"o11y.O11yFilterSuggestionsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yFilterSuggestions","description":"Data holds the suggestions."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yForgotPasswordIn":{"properties":{"email":{"description":"Email is the address to mail the reset link to. Required.","type":"string"},"frontendBaseURL":{"description":"FrontendBaseURL is the console origin the reset link is built on.","type":"string"},"orgId":{"description":"OrgID is the org the address belongs to. Required.","type":"string"}},"type":"object"},"o11y.O11yFrame":{"properties":{"column":{"description":"Column is the column number.","type":"integer"},"file":{"description":"File is the file it is in.","type":"string"},"function":{"description":"Function is the function the frame is in.","type":"string"},"line":{"description":"Line is the line number.","type":"integer"},"own":{"description":"Own marks a frame in the reporting application's own code rather than in a\ndependency or the runtime.","type":"boolean"}},"type":"object"},"o11y.O11yFunnelCreateIn":{"properties":{"funnel_name":{"description":"Name is the funnel's name.","type":"string"},"timestamp":{"description":"Timestamp is when the funnel was created, as a millisecond epoch. Zero\ntakes the runtime's own clock.","type":"integer"}},"type":"object"},"o11y.O11yFunnelDeleteOut":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFunnelOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFunnel","description":"Data is the funnel with its steps."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFunnelRow":{"properties":{"data":{"additionalProperties":{"type":"object"},"description":"Data are the row's columns, keyed by column name.","type":"object"},"timestamp":{"description":"Timestamp is the row's time.","type":"string"}},"type":"object"},"o11y.O11yFunnelRowsOut":{"properties":{"data":{"description":"Data are the rows.","items":{"$ref":"#/components/schemas/o11y.O11yFunnelRow"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yFunnelStepWindowIn":{"properties":{"end_time":{"type":"integer"},"start_time":{"type":"integer"},"step_end":{"type":"integer"},"step_start":{"type":"integer"}},"type":"object"},"o11y.O11yFunnelStepsUpdateIn":{"properties":{"description":{"description":"Description replaces the funnel's description. Empty leaves it as it was.","type":"string"},"funnel_id":{"description":"FunnelID is the funnel to update.","type":"string"},"funnel_name":{"description":"Name replaces the funnel's name. Empty leaves it as it was.","type":"string"},"steps":{"description":"Steps are the funnel's steps, in order. At least two are needed before\nany analytics read will answer.","items":{"$ref":"#/components/schemas/o11y.FunnelStep"},"type":"array"},"timestamp":{"description":"Timestamp is when the change was made, as a millisecond epoch.","type":"integer"}},"type":"object"},"o11y.O11yFunnelUpdateIn":{"properties":{"description":{"description":"Description replaces the funnel's description. Empty leaves it as it was.","type":"string"},"funnel_name":{"description":"Name replaces the funnel's name. Empty leaves it as it was.","type":"string"},"timestamp":{"description":"Timestamp is when the change was made, as a millisecond epoch.","type":"integer"}},"type":"object"},"o11y.O11yFunnelWindowIn":{"properties":{"end_time":{"type":"integer"},"start_time":{"type":"integer"}},"type":"object"},"o11y.O11yFunnelsOut":{"properties":{"data":{"description":"Data are the funnels.","items":{"$ref":"#/components/schemas/o11y.GettableFunnel"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yGettableHostOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableHost","description":"Data holds the host info."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yGlobalConfig":{"properties":{"ai_assistant_url":{"description":"AIAssistantURL is the AI assistant endpoint, when one is exposed.","type":"string"},"external_url":{"description":"ExternalURL is the deployment's public URL.","type":"string"},"identN":{"$ref":"#/components/schemas/o11y.O11yIdentN","description":"IdentN says which identity providers are enabled."},"ingestion_url":{"description":"IngestionURL is where telemetry is sent.","type":"string"},"mcp_url":{"description":"MCPURL is the MCP endpoint, when one is exposed.","type":"string"}},"type":"object"},"o11y.O11yGlobalConfigOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yGlobalConfig","description":"Data is the configuration."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yGoogleConfig":{"properties":{"allowedGroups":{"description":"AllowedGroups, when set, admits only members of these groups.","items":{"type":"string"},"type":"array"},"clientId":{"description":"ClientID is the OAuth application's id.","type":"string"},"clientSecret":{"description":"ClientSecret is the OAuth application's secret.","type":"string"},"domainToAdminEmail":{"additionalProperties":{"type":"string"},"description":"DomainToAdminEmail maps each Workspace domain to the admin the service\naccount impersonates; \"*\" is the fallback.","type":"object"},"fetchGroups":{"description":"FetchGroups reads the user's Workspace groups for role mapping.","type":"boolean"},"fetchTransitiveGroupMembership":{"description":"FetchTransitiveGroupMembership also reads groups held through other\ngroups.","type":"boolean"},"insecureSkipEmailVerified":{"description":"InsecureSkipEmailVerified admits addresses Google has not verified.","type":"boolean"},"redirectURI":{"description":"RedirectURI is the callback the flow returns to.","type":"string"},"serviceAccountJson":{"description":"ServiceAccountJSON is the service-account credential used to read\ngroups, when FetchGroups is on.","type":"string"}},"type":"object"},"o11y.O11yHealthOut":{"properties":{"status":{"description":"Status is \"ok\".","type":"string"}},"type":"object"},"o11y.O11yHostListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.HostListResponse","description":"Data holds the host records and the fleet's reporting state."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yIdentN":{"properties":{"apikey":{"$ref":"#/components/schemas/o11y.O11yToggle","description":"APIKey is the API-key provider."},"impersonation":{"$ref":"#/components/schemas/o11y.O11yToggle","description":"Impersonation is the impersonation provider."},"tokenizer":{"$ref":"#/components/schemas/o11y.O11yToggle","description":"Tokenizer is the token-based provider."}},"type":"object"},"o11y.O11yIdentifiable":{"properties":{"id":{"description":"ID is the created resource's id.","type":"string"}},"type":"object"},"o11y.O11yIdentifiableOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yIdentifiable","description":"Data is the created resource's id."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraAttributeKeysOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.FilterAttributeKeyResponse","description":"Data holds the keys."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraAttributeValuesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.FilterAttributeValueResponse","description":"Data holds the values, split by data type."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraChecksOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Checks","description":"Data holds what is present, what is missing and whether the section is\nready."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraClustersOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Clusters","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraDaemonSetsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.DaemonSets","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraDeploymentsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Deployments","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraHostsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Hosts","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraJobsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Jobs","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraNamespacesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Namespaces","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraNodesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Nodes","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraPodsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Pods","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraStatefulSetsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.StatefulSets","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInfraVolumesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Volumes","description":"Data holds the rows."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yIngestionKeysOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableIngestionKeys","description":"Data holds the ingestion keys."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInstallOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.IntegrationsListItem","description":"Data holds the installed integration."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yIntegrationAck":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yIntegrationOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Integration","description":"Data holds the integration and its installation record."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yIntegrationsListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.IntegrationsListResponse","description":"Data holds the available integrations and their installed state."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yInvite":{"properties":{"createdAt":{"description":"CreatedAt is when it was created.","format":"date-time","type":"string"},"email":{"description":"Email is the address it was sent to.","type":"string"},"id":{"description":"ID is the invitation's id.","type":"string"},"inviteLink":{"description":"InviteLink is the full link mailed to them.","type":"string"},"name":{"description":"Name is the invitee's display name.","type":"string"},"orgId":{"description":"OrgID is the org they are invited into.","type":"string"},"role":{"description":"Role is the role the invitee will hold.","type":"string"},"token":{"description":"Token is the secret that redeems it.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yInviteIn":{"properties":{"email":{"description":"Email is the address the invitation goes to.","type":"string"},"frontendBaseUrl":{"description":"FrontendBaseUrl is the console origin the invite link is built on.","type":"string"},"name":{"description":"Name is the invitee's display name.","type":"string"},"role":{"description":"Role is the role they will hold on accepting — ADMIN, EDITOR or VIEWER.","type":"string"}},"type":"object"},"o11y.O11yInviteOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yInvite","description":"Data is the invitation."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yJobListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.JobListResponse","description":"Data holds the job records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yK8sOnboarding":{"properties":{"didSendClusterMetrics":{"description":"DidSendClusterMetrics says whether cluster metrics have arrived.","type":"boolean"},"didSendNodeMetrics":{"description":"DidSendNodeMetrics says whether node metrics have arrived.","type":"boolean"},"didSendPodMetrics":{"description":"DidSendPodMetrics says whether pod metrics have arrived.","type":"boolean"},"isSendingOptionalPodMetrics":{"description":"IsSendingOptionalPodMetrics says whether optional pod metrics are\nflowing.","type":"boolean"},"isSendingRequiredMetadata":{"description":"IsSendingRequiredMetadata reports, per pod, which required metadata\nlabels are present.","items":{"$ref":"#/components/schemas/o11y.O11yPodOnboarding"},"type":"array"}},"type":"object"},"o11y.O11yLLMAnnotation":{"properties":{"author":{"description":"Author is who wrote it.","type":"string"},"content":{"description":"Content is the note itself.","type":"string"},"createdAt":{"description":"CreatedAt is when the annotation was stored.","format":"date-time","type":"string"},"id":{"description":"ID is the annotation's id.","type":"string"},"observationId":{"description":"ObservationID is the observation the annotation attaches to, when narrowed.","type":"string"},"queue":{"description":"Queue is the review queue the annotation sits in, when queued.","type":"string"},"status":{"description":"Status is the annotation's review status, e.g. PENDING.","type":"string"},"traceId":{"description":"TraceID is the trace the annotation attaches to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the annotation last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yLLMAnnotationOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMAnnotation","description":"Data is the annotation."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMAnnotationsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMAnnotationsPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMAnnotationsPage":{"properties":{"items":{"description":"Items are the annotations, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMAnnotation"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"},"total":{"description":"Total is how many annotations match, across all pages.","type":"integer"}},"type":"object"},"o11y.O11yLLMIngestAnnotation":{"properties":{"content":{"description":"Content is the note itself. Required.","type":"string"},"observationId":{"description":"ObservationID is the single observation the annotation attaches to, when\nnarrowed to one.","type":"string"},"queue":{"description":"Queue is the review queue to file the annotation in.","type":"string"},"status":{"description":"Status is the annotation's initial review status. Defaults to PENDING.","type":"string"},"traceId":{"description":"TraceID is the trace the annotation attaches to. Required.","type":"string"}},"type":"object"},"o11y.O11yLLMIngestScore":{"properties":{"comment":{"description":"Comment is a free-text note.","type":"string"},"dataType":{"description":"DataType is the score's kind — NUMERIC or CATEGORICAL. Defaults from the\nvalue when empty.","type":"string"},"name":{"description":"Name is the score's name, e.g. helpfulness. Required.","type":"string"},"observationId":{"description":"ObservationID is the single observation the score attaches to, when\nnarrowed to one.","type":"string"},"source":{"description":"Source is where the score came from, e.g. API, EVAL. Defaults to API.","type":"string"},"stringValue":{"description":"StringValue is the categorical score, when the score is categorical.","type":"string"},"traceId":{"description":"TraceID is the trace the score attaches to. Required.","type":"string"},"value":{"description":"Value is the numeric score.","type":"number"}},"type":"object"},"o11y.O11yLLMObservation":{"properties":{"completionTokens":{"description":"CompletionTokens is the output token count.","type":"integer"},"id":{"description":"ID is the observation's id (the span id).","type":"string"},"latencyMs":{"description":"LatencyMs is how long it took, in milliseconds.","type":"number"},"model":{"description":"Model is the model that served it.","type":"string"},"name":{"description":"Name is the observation's name.","type":"string"},"parentObservationId":{"description":"ParentID is the parent observation, when the span has one.","type":"string"},"promptTokens":{"description":"PromptTokens is the input token count.","type":"integer"},"provider":{"description":"Provider is the model's provider.","type":"string"},"serviceName":{"description":"ServiceName is the app that emitted it.","type":"string"},"sessionId":{"description":"SessionID is the conversation the observation belongs to.","type":"string"},"startTime":{"description":"StartTime is when the observation started.","format":"date-time","type":"string"},"statusCode":{"description":"StatusCode is the observation's status, e.g. OK, ERROR.","type":"string"},"totalCost":{"description":"TotalCost is the observation's cost.","type":"number"},"totalTokens":{"description":"TotalTokens is the sum of prompt and completion tokens.","type":"integer"},"traceId":{"description":"TraceID is the trace the observation belongs to.","type":"string"},"type":{"description":"Type is the observation kind, e.g. chat, embeddings, tool.","type":"string"},"userId":{"description":"UserID is the end user the observation is attributed to.","type":"string"}},"type":"object"},"o11y.O11yLLMObservationsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMObservationsPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMObservationsPage":{"properties":{"items":{"description":"Items are the observations, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMObservation"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"}},"type":"object"},"o11y.O11yLLMPricingCacheCosts":{"properties":{"mode":{"description":"Mode is how cached tokens are counted — subtract (inside input_tokens,\nOpenAI-style), additive (reported separately, Anthropic-style) or unknown.","type":"string"},"read":{"description":"Read is the cost per unit of cache-read tokens.","type":"number"},"write":{"description":"Write is the cost per unit of cache-write tokens.","type":"number"}},"type":"object"},"o11y.O11yLLMPricingRule":{"properties":{"createdAt":{"description":"CreatedAt is when the rule was stored.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created the rule.","type":"string"},"enabled":{"description":"Enabled says whether the rule is on.","type":"boolean"},"id":{"description":"ID is the rule's id.","type":"string"},"isOverride":{"description":"IsOverride marks the rule user-pinned; when true the sync job skips it.","type":"boolean"},"modelName":{"description":"Model is the model the rule prices.","type":"string"},"modelPattern":{"description":"ModelPattern are the model-name globs the rule matches.","items":{"type":"string"},"type":"array"},"orgId":{"description":"OrgID is the org the rule belongs to.","type":"string"},"pricing":{"$ref":"#/components/schemas/o11y.O11yLLMRulePricing","description":"Pricing is the per-unit cost."},"provider":{"description":"Provider is the model's provider.","type":"string"},"sourceId":{"description":"SourceID is the upstream source the rule was synced from, when synced.","type":"string"},"syncedAt":{"description":"SyncedAt is when the rule was last synced, when it is synced.","format":"date-time","type":"string"},"unit":{"description":"Unit is the pricing unit, e.g. per_million_tokens.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the rule last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is who last changed it.","type":"string"}},"type":"object"},"o11y.O11yLLMPricingRuleOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMPricingRule","description":"Data is the rule."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMPricingRulesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMPricingRulesPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMPricingRulesPage":{"properties":{"items":{"description":"Items are the rules.","items":{"$ref":"#/components/schemas/o11y.O11yLLMPricingRule"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"},"total":{"description":"Total is how many rules match, across all pages.","type":"integer"}},"type":"object"},"o11y.O11yLLMRulePricing":{"properties":{"cache":{"$ref":"#/components/schemas/o11y.O11yLLMPricingCacheCosts","description":"Cache is the cost of cached tokens, when the model prices them."},"input":{"description":"Input is the cost per unit of input tokens.","type":"number"},"output":{"description":"Output is the cost per unit of output tokens.","type":"number"}},"type":"object"},"o11y.O11yLLMScore":{"properties":{"comment":{"description":"Comment is a free-text note.","type":"string"},"createdAt":{"description":"CreatedAt is when the score was stored.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created the score.","type":"string"},"dataType":{"description":"DataType is the score's kind — NUMERIC or CATEGORICAL.","type":"string"},"id":{"description":"ID is the score's id.","type":"string"},"name":{"description":"Name is the score's name.","type":"string"},"observationId":{"description":"ObservationID is the observation the score attaches to, when narrowed.","type":"string"},"source":{"description":"Source is where the score came from, e.g. API, EVAL.","type":"string"},"stringValue":{"description":"StringValue is the categorical score, when the score is categorical.","type":"string"},"timestamp":{"description":"Timestamp is the score's own event time.","format":"date-time","type":"string"},"traceId":{"description":"TraceID is the trace the score attaches to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the score last changed.","format":"date-time","type":"string"},"value":{"description":"Value is the numeric score.","type":"number"}},"type":"object"},"o11y.O11yLLMScoreOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMScore","description":"Data is the score."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMScoresOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMScoresPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMScoresPage":{"properties":{"items":{"description":"Items are the scores, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMScore"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"},"total":{"description":"Total is how many scores match, across all pages.","type":"integer"}},"type":"object"},"o11y.O11yLLMSession":{"properties":{"completionTokens":{"description":"CompletionTokens is the conversation's total output tokens.","type":"integer"},"id":{"description":"ID is the session id.","type":"string"},"observations":{"description":"Observations is how many observations the conversation holds.","type":"integer"},"promptTokens":{"description":"PromptTokens is the conversation's total input tokens.","type":"integer"},"totalCost":{"description":"TotalCost is the conversation's total cost.","type":"number"},"totalTokens":{"description":"TotalTokens is the conversation's total tokens.","type":"integer"},"traces":{"description":"Traces is how many traces the conversation holds.","type":"integer"},"userId":{"description":"UserID is the end user the conversation is attributed to.","type":"string"}},"type":"object"},"o11y.O11yLLMSessionsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMSessionsPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMSessionsPage":{"properties":{"items":{"description":"Items are the conversations, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMSession"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"}},"type":"object"},"o11y.O11yLLMTrace":{"properties":{"completionTokens":{"description":"CompletionTokens is the trace's total output tokens.","type":"integer"},"id":{"description":"ID is the trace id.","type":"string"},"latencyMs":{"description":"LatencyMs is the trace's span, in milliseconds.","type":"number"},"observations":{"description":"Observations is how many observations the trace holds.","type":"integer"},"promptTokens":{"description":"PromptTokens is the trace's total input tokens.","type":"integer"},"serviceName":{"description":"ServiceName is the app that emitted it.","type":"string"},"sessionId":{"description":"SessionID is the conversation the trace belongs to.","type":"string"},"totalCost":{"description":"TotalCost is the trace's total cost.","type":"number"},"totalTokens":{"description":"TotalTokens is the trace's total tokens.","type":"integer"},"userId":{"description":"UserID is the end user the trace is attributed to.","type":"string"}},"type":"object"},"o11y.O11yLLMTracesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMTracesPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMTracesPage":{"properties":{"items":{"description":"Items are the traces, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMTrace"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"}},"type":"object"},"o11y.O11yLLMUpdatablePricingRule":{"properties":{"enabled":{"description":"Enabled turns the rule on.","type":"boolean"},"id":{"description":"ID matches an existing rule by its id.","type":"string"},"isOverride":{"description":"IsOverride pins the rule so the sync job skips it. Omit to leave a matched\noverride untouched.","type":"boolean"},"modelName":{"description":"Model is the model the rule prices. Required.","type":"string"},"modelPattern":{"description":"ModelPattern are the model-name globs the rule matches. Required.","items":{"type":"string"},"type":"array"},"pricing":{"$ref":"#/components/schemas/o11y.O11yLLMRulePricing","description":"Pricing is the per-unit cost. Required."},"provider":{"description":"Provider is the model's provider. Required.","type":"string"},"sourceId":{"description":"SourceID matches an existing rule by its upstream source id.","type":"string"},"unit":{"description":"Unit is the pricing unit, e.g. per_million_tokens. Required.","type":"string"}},"type":"object"},"o11y.O11yLLMUpdatablePricingRules":{"properties":{"rules":{"description":"Rules are the rules to create or update, matched per rule.","items":{"$ref":"#/components/schemas/o11y.O11yLLMUpdatablePricingRule"},"type":"array"}},"type":"object"},"o11y.O11yLLMUser":{"properties":{"completionTokens":{"description":"CompletionTokens is their total output tokens.","type":"integer"},"id":{"description":"ID is the end user's id (user.id).","type":"string"},"observations":{"description":"Observations is how many observations they produced.","type":"integer"},"promptTokens":{"description":"PromptTokens is their total input tokens.","type":"integer"},"sessions":{"description":"Sessions is how many conversations they had.","type":"integer"},"totalCost":{"description":"TotalCost is their total cost.","type":"number"},"totalTokens":{"description":"TotalTokens is their total tokens.","type":"integer"},"traces":{"description":"Traces is how many traces they produced.","type":"integer"}},"type":"object"},"o11y.O11yLLMUsersOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLLMUsersPage","description":"Data is the page."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLLMUsersPage":{"properties":{"items":{"description":"Items are the end users, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLLMUser"},"type":"array"},"limit":{"description":"Limit is the page cap the read ran with.","type":"integer"},"offset":{"description":"Offset is the row offset this page started at.","type":"integer"}},"type":"object"},"o11y.O11yLicenseActiveOut":{"properties":{"data":{"description":"Data is the license.","type":"object"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLicensesOut":{"properties":{"data":{"description":"Data are the licenses.","items":{"type":"object"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yListError":{"properties":{"exceptionCount":{"description":"ExceptionCount is how many instances the group holds in the window.","type":"integer"},"exceptionMessage":{"description":"ExceptionMsg is its message.","type":"string"},"exceptionType":{"description":"ExceptionType is the exception's type.","type":"string"},"firstSeen":{"description":"FirstSeen is when the earliest was.","format":"date-time","type":"string"},"groupID":{"description":"GroupID is the group's id.","type":"string"},"lastSeen":{"description":"LastSeen is when the latest instance was recorded.","format":"date-time","type":"string"},"serviceName":{"description":"ServiceName is the service that reported them.","type":"string"}},"type":"object"},"o11y.O11yLogAggregateBucket":{"properties":{"groupBy":{"additionalProperties":{},"description":"GroupBy carries the group's key values when the aggregate grouped.","type":"object"},"timestamp":{"description":"Timestamp is the start of the bucket.","type":"integer"},"value":{"description":"Value is the bucket's aggregated value."}},"type":"object"},"o11y.O11yLogAggregateOut":{"properties":{"items":{"additionalProperties":{"$ref":"#/components/schemas/o11y.O11yLogAggregateBucket"},"description":"Items are the buckets, keyed by bucket timestamp.","type":"object"}},"type":"object"},"o11y.O11yLogConfigVersion":{"properties":{"config":{"description":"Config is the rendered collector config the version deployed.","type":"string"},"createdAt":{"description":"CreatedAt is when the version was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is the id of who created the version.","type":"string"},"createdByName":{"description":"CreatedByName is the display name of who created the version.","type":"string"},"deployResult":{"description":"DeployResult is the deployment's outcome message.","type":"string"},"deploySequence":{"description":"DeploySequence orders this deployment among the version's deployments.","type":"integer"},"deployStatus":{"description":"DeployStatus is where the deployment stands, e.g. dirty, deploying,\ndeployed, in_progress, failed, unknown.","type":"string"},"elementType":{"description":"ElementType is the config element the version carries — log_pipelines.","type":"string"},"id":{"description":"ID is the version record's id.","type":"string"},"lastHash":{"description":"LastHash is the deployed config's hash.","type":"string"},"orgId":{"description":"OrgID is the org the version belongs to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the version last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is the id of who last changed it.","type":"string"},"version":{"description":"Version is the config version number.","type":"integer"}},"type":"object"},"o11y.O11yLogFilter":{"properties":{"items":{"description":"Items are the predicates.","items":{"$ref":"#/components/schemas/o11y.O11yLogFilterItem"},"type":"array"},"op":{"description":"Op combines the items: AND or OR.","type":"string"}},"type":"object"},"o11y.O11yLogFilterItem":{"properties":{"key":{"$ref":"#/components/schemas/o11y.O11yLogFilterKey","description":"Key is the field the predicate tests."},"op":{"description":"Op is the comparison, e.g. =, !=, in, contains.","type":"string"},"value":{"description":"Value is what it tests against, in the value's own JSON type."}},"type":"object"},"o11y.O11yLogFilterKey":{"properties":{"dataType":{"description":"DataType is the field's data type, e.g. string, int64, float64, bool.","type":"string"},"isColumn":{"description":"IsColumn marks a field materialized as its own column.","type":"boolean"},"isJSON":{"description":"IsJSON marks a path into the record's JSON body.","type":"boolean"},"key":{"description":"Key is the field's name.","type":"string"},"type":{"description":"Type is where the field lives: tag or resource.","type":"string"}},"type":"object"},"o11y.O11yLogParseFrom":{"properties":{"parse_from":{"description":"ParseFrom is the field to read.","type":"string"}},"type":"object"},"o11y.O11yLogPipeline":{"properties":{"alias":{"description":"Alias is the pipeline's short name.","type":"string"},"config":{"description":"Config is the pipeline's processors, in order.","items":{"$ref":"#/components/schemas/o11y.O11yLogPipelineOperator"},"type":"array"},"createdAt":{"description":"CreatedAt is when the pipeline was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is the id of who created the pipeline.","type":"string"},"description":{"description":"Description says what the pipeline is for.","type":"string"},"enabled":{"description":"Enabled says whether the pipeline is on.","type":"boolean"},"filter":{"$ref":"#/components/schemas/o11y.O11yLogFilter","description":"Filter selects which records the pipeline processes."},"id":{"description":"ID is the pipeline's id.","type":"string"},"name":{"description":"Name is the pipeline's display name.","type":"string"},"orderId":{"description":"OrderID is the pipeline's 1-based position in the set.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when the pipeline last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is the id of who last changed it.","type":"string"}},"type":"object"},"o11y.O11yLogPipelineCreateIn":{"properties":{"pipelines":{"description":"Pipelines are the pipelines the new version holds, in order.","items":{"$ref":"#/components/schemas/o11y.O11yLogPostablePipeline"},"type":"array"}},"type":"object"},"o11y.O11yLogPipelineOperator":{"properties":{"default":{"description":"Default is the id of the processor a router falls through to.","type":"string"},"enable_flattening":{"description":"EnableFlattening flattens parsed JSON one level when true.","type":"boolean"},"enable_paths":{"description":"EnablePaths keeps the JSON path in flattened keys when true.","type":"boolean"},"enabled":{"description":"Enabled turns the processor on.","type":"boolean"},"expr":{"description":"Expr is a router route's expression.","type":"string"},"field":{"description":"Field is the field an add/remove processor works on.","type":"string"},"fields":{"description":"Fields are the fields a retain processor keeps.","items":{"type":"string"},"type":"array"},"from":{"description":"From is the source field of a move or copy.","type":"string"},"id":{"description":"ID is the processor's id, unique within the pipeline.","type":"string"},"if":{"description":"If gates the processor on an expression.","type":"string"},"layout":{"description":"Layout is a time parser's layout.","type":"string"},"layout_type":{"description":"LayoutType is the layout's kind, e.g. strptime, gotime, epoch.","type":"string"},"mapping":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Mapping maps severity levels (or flattened keys) to the values that\nmean them.","type":"object"},"name":{"description":"Name is the processor's display name.","type":"string"},"on_error":{"description":"OnError says what to do when the processor fails, e.g. send, drop.","type":"string"},"orderId":{"description":"OrderID is the processor's 1-based position in the pipeline.","type":"integer"},"output":{"description":"Output is the id of the processor that runs next.","type":"string"},"overwrite_text":{"description":"OverwriteSeverityText rewrites the severity text alongside the number\nwhen true.","type":"boolean"},"parse_from":{"description":"ParseFrom is where a parser reads from.","type":"string"},"parse_to":{"description":"ParseTo is where a parser writes its result.","type":"string"},"path_prefix":{"description":"PathPrefix prefixes flattened keys.","type":"string"},"pattern":{"description":"Pattern is a grok parser's pattern.","type":"string"},"regex":{"description":"Regex is a regex parser's expression.","type":"string"},"routes":{"description":"Routes are a router processor's routes.","items":{"$ref":"#/components/schemas/o11y.O11yLogPipelineRoute"},"type":"array"},"span_id":{"$ref":"#/components/schemas/o11y.O11yLogParseFrom","description":"SpanID says where a trace parser reads the span id from."},"to":{"description":"To is the destination field of a move or copy.","type":"string"},"trace_flags":{"$ref":"#/components/schemas/o11y.O11yLogParseFrom","description":"TraceFlags says where a trace parser reads the trace flags from."},"trace_id":{"$ref":"#/components/schemas/o11y.O11yLogParseFrom","description":"TraceID says where a trace parser reads the trace id from."},"type":{"description":"Type is the processor type, e.g. grok_parser, regex_parser, json_parser,\ntrace_parser, time_parser, severity_parser, add, remove, move, copy.","type":"string"},"value":{"description":"Value is the value an add processor writes.","type":"string"}},"type":"object"},"o11y.O11yLogPipelinePreview":{"properties":{"collectorLogs":{"description":"CollectorLogs is what the collector logged while simulating.","items":{"type":"string"},"type":"array"},"logs":{"description":"Logs are the sample records after the pipelines ran over them.","items":{"$ref":"#/components/schemas/o11y.O11yLogRecord"},"type":"array"}},"type":"object"},"o11y.O11yLogPipelinePreviewIn":{"properties":{"logs":{"description":"Logs are the sample records to transform.","items":{"$ref":"#/components/schemas/o11y.O11yLogRecord"},"type":"array"},"pipelines":{"description":"Pipelines are the pipelines to simulate, in order.","items":{"$ref":"#/components/schemas/o11y.O11yLogPipeline"},"type":"array"}},"type":"object"},"o11y.O11yLogPipelinePreviewOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLogPipelinePreview","description":"Data is the dry run's result."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLogPipelineRoute":{"properties":{"expr":{"description":"Expr is the expression that selects the route.","type":"string"},"output":{"description":"Output is the id of the processor the route sends to.","type":"string"}},"type":"object"},"o11y.O11yLogPipelines":{"properties":{"config":{"description":"Config is the rendered collector config the version deployed.","type":"string"},"createdAt":{"description":"CreatedAt is when the version was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is the id of who created the version.","type":"string"},"createdByName":{"description":"CreatedByName is the display name of who created the version.","type":"string"},"deployResult":{"description":"DeployResult is the deployment's outcome message.","type":"string"},"deploySequence":{"description":"DeploySequence orders this deployment among the version's deployments.","type":"integer"},"deployStatus":{"description":"DeployStatus is where the deployment stands, e.g. dirty, deploying,\ndeployed, in_progress, failed, unknown.","type":"string"},"elementType":{"description":"ElementType is the config element this version carries — log_pipelines.","type":"string"},"history":{"description":"History is the recent version history, newest first.","items":{"$ref":"#/components/schemas/o11y.O11yLogConfigVersion"},"type":"array"},"id":{"description":"ID is the version record's id.","type":"string"},"lastHash":{"description":"LastHash is the deployed config's hash.","type":"string"},"orgId":{"description":"OrgID is the org the version belongs to.","type":"string"},"pipelines":{"description":"Pipelines are the version's pipelines, in order.","items":{"$ref":"#/components/schemas/o11y.O11yLogPipeline"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when the version last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is the id of who last changed it.","type":"string"},"version":{"description":"Version is the config version number.","type":"integer"}},"type":"object"},"o11y.O11yLogPipelinesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yLogPipelines","description":"Data is the config version."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLogPostablePipeline":{"properties":{"alias":{"description":"Alias is the pipeline's short name.","type":"string"},"config":{"description":"Config is the pipeline's processors, in order.","items":{"$ref":"#/components/schemas/o11y.O11yLogPipelineOperator"},"type":"array"},"description":{"description":"Description says what the pipeline is for.","type":"string"},"enabled":{"description":"Enabled turns the pipeline on.","type":"boolean"},"filter":{"$ref":"#/components/schemas/o11y.O11yLogFilter","description":"Filter selects which records the pipeline processes."},"id":{"description":"ID is the pipeline's id. Empty on a new pipeline; the id it was listed\nwith to keep an existing one.","type":"string"},"name":{"description":"Name is the pipeline's display name.","type":"string"},"orderId":{"description":"OrderID is the pipeline's 1-based position in the set.","type":"integer"}},"type":"object"},"o11y.O11yLogPromoteIndex":{"properties":{"fieldDataType":{"description":"FieldDataType is the path's data type, e.g. string, number, bool.","type":"string"},"granularity":{"description":"Granularity is the index granularity in rows.","type":"integer"},"type":{"description":"Type is the index type, e.g. minmax, set(N), bloom_filter(P).","type":"string"}},"type":"object"},"o11y.O11yLogPromoteOut":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLogPromotePath":{"properties":{"indexes":{"description":"Indexes are the indexes to put on the path.","items":{"$ref":"#/components/schemas/o11y.O11yLogPromoteIndex"},"type":"array"},"path":{"description":"Path is the body path, e.g. body.user.id on the way in; listed without\nthe body. prefix.","type":"string"},"promote":{"description":"Promote lifts the path into its own column when true.","type":"boolean"}},"type":"object"},"o11y.O11yLogPromotedOut":{"properties":{"data":{"description":"Data holds the paths.","items":{"$ref":"#/components/schemas/o11y.O11yLogPromotePath"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yLogRecord":{"properties":{"attributes_bool":{"additionalProperties":{"type":"boolean"},"description":"AttributesBool are the record's boolean attributes.","type":"object"},"attributes_float":{"additionalProperties":{"type":"number"},"description":"AttributesFloat are the record's float attributes.","type":"object"},"attributes_int":{"additionalProperties":{"type":"integer"},"description":"AttributesInt are the record's integer attributes.","type":"object"},"attributes_string":{"additionalProperties":{"type":"string"},"description":"AttributesString are the record's string attributes.","type":"object"},"body":{"description":"Body is the record's body.","type":"string"},"id":{"description":"ID is the record's id.","type":"string"},"resources_string":{"additionalProperties":{"type":"string"},"description":"ResourcesString are the record's string resource attributes.","type":"object"},"severity_number":{"description":"SeverityNumber is the record's severity as a number.","type":"integer"},"severity_text":{"description":"SeverityText is the record's severity as text, e.g. ERROR.","type":"string"},"span_id":{"description":"SpanID is the span the record belongs to.","type":"string"},"timestamp":{"description":"Timestamp is the record's time as a nanosecond epoch.","type":"integer"},"trace_flags":{"description":"TraceFlags are the record's trace flags.","type":"integer"},"trace_id":{"description":"TraceID is the trace the record belongs to.","type":"string"}},"type":"object"},"o11y.O11yLogRecordsOut":{"properties":{"results":{"description":"Results are the records, newest first.","items":{"additionalProperties":{},"type":"object"},"type":"array"}},"type":"object"},"o11y.O11yLogsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yEvents","description":"Data holds the events."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMessage":{"properties":{"data":{"description":"Data is the acknowledgment message.","type":"string"}},"type":"object"},"o11y.O11yMetricAckOut":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricAlert":{"properties":{"alertId":{"description":"AlertID is the alert rule's id.","type":"string"},"alertName":{"description":"AlertName is the alert rule's name.","type":"string"}},"type":"object"},"o11y.O11yMetricAlerts":{"properties":{"alerts":{"description":"Alerts are the alert rules referencing the metric.","items":{"$ref":"#/components/schemas/o11y.O11yMetricAlert"},"type":"array"}},"type":"object"},"o11y.O11yMetricAlertsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricAlerts","description":"Data holds the alerts."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricAttribute":{"properties":{"key":{"description":"Key is the attribute's name.","type":"string"},"valueCount":{"description":"ValueCount is how many distinct values the attribute has.","type":"integer"},"values":{"description":"Values are the attribute's distinct values.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yMetricAttributes":{"properties":{"attributes":{"description":"Attributes are the keys, each with its values.","items":{"$ref":"#/components/schemas/o11y.O11yMetricAttribute"},"type":"array"},"totalKeys":{"description":"TotalKeys is how many keys the metric has.","type":"integer"}},"type":"object"},"o11y.O11yMetricAttributesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricAttributes","description":"Data holds the attributes."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricDashboards":{"properties":{"dashboards":{"description":"Dashboards are the panels referencing the metric.","items":{"$ref":"#/components/schemas/o11y.O11yMetricPanel"},"type":"array"}},"type":"object"},"o11y.O11yMetricDashboardsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricDashboards","description":"Data holds the panels."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricField":{"properties":{"description":{"description":"Description describes the field.","type":"string"},"fieldContext":{"description":"FieldContext is the context the field lives in, e.g. resource, attribute.","type":"string"},"fieldDataType":{"description":"FieldDataType is the field's data type.","type":"string"},"name":{"description":"Name is the field's name.","type":"string"},"signal":{"description":"Signal is the telemetry signal the field belongs to.","type":"string"},"unit":{"description":"Unit is the field's unit.","type":"string"}},"type":"object"},"o11y.O11yMetricFilter":{"properties":{"expression":{"description":"Expression is the filter, in the query-builder filter syntax.","type":"string"}},"type":"object"},"o11y.O11yMetricHighlights":{"properties":{"activeTimeSeries":{"description":"ActiveTimeSeries is how many of them are active.","type":"integer"},"dataPoints":{"description":"DataPoints is how many data points the metric has.","type":"integer"},"lastReceived":{"description":"LastReceived is when the metric last arrived, as a Unix timestamp in\nmilliseconds.","type":"integer"},"totalTimeSeries":{"description":"TotalTimeSeries is how many time series the metric has ever had.","type":"integer"}},"type":"object"},"o11y.O11yMetricHighlightsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricHighlights","description":"Data holds the highlights."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricInspectIn":{"properties":{"end":{"description":"End is the end of the window as a Unix timestamp in milliseconds, at most\nthirty minutes after start. Required.","type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.O11yMetricFilter","description":"Filter narrows the series returned."},"metricName":{"description":"MetricName is the metric to inspect. Required.","type":"string"},"start":{"description":"Start is the start of the window as a Unix timestamp in milliseconds. Required.","type":"integer"}},"required":["metricName","start","end"],"type":"object"},"o11y.O11yMetricInspectOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricSeriesSet","description":"Data holds the series."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricLabel":{"properties":{"key":{"$ref":"#/components/schemas/o11y.O11yMetricField","description":"Key is the label's field."},"value":{"description":"Value is the label's value.","type":"object"}},"type":"object"},"o11y.O11yMetricList":{"properties":{"metrics":{"description":"Metrics are the metrics, with their metadata.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSummary"},"type":"array"}},"type":"object"},"o11y.O11yMetricListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricList","description":"Data holds the metrics."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricMetadata":{"properties":{"description":{"description":"Description describes the metric.","type":"string"},"isMonotonic":{"description":"IsMonotonic marks a sum that only ever increases.","type":"boolean"},"temporality":{"description":"Temporality is delta or cumulative.","type":"string"},"type":{"description":"Type is the metric type, e.g. gauge, sum, histogram.","type":"string"},"unit":{"description":"Unit is the metric's unit.","type":"string"}},"type":"object"},"o11y.O11yMetricMetadataOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricMetadata","description":"Data holds the metadata."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricMetadataSaveIn":{"properties":{"description":{"description":"Description describes the metric.","type":"string"},"isMonotonic":{"description":"IsMonotonic marks a sum that only ever increases.","type":"boolean"},"metricName":{"description":"MetricName is the metric to update. Required.","type":"string"},"temporality":{"description":"Temporality is delta or cumulative.","type":"string"},"type":{"description":"Type is the metric type, e.g. gauge, sum, histogram.","type":"string"},"unit":{"description":"Unit is the metric's unit.","type":"string"}},"required":["metricName"],"type":"object"},"o11y.O11yMetricOnboarding":{"properties":{"hasMetrics":{"description":"HasMetrics is true once any non-O11y metric has been ingested.","type":"boolean"}},"type":"object"},"o11y.O11yMetricOnboardingOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricOnboarding","description":"Data holds the flag."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricOrder":{"properties":{"direction":{"description":"Direction is asc or desc.","type":"string"},"key":{"$ref":"#/components/schemas/o11y.O11yMetricField","description":"Key is the field to order by."}},"type":"object"},"o11y.O11yMetricPanel":{"properties":{"dashboardId":{"description":"DashboardID is the dashboard's id.","type":"string"},"dashboardName":{"description":"DashboardName is the dashboard's name.","type":"string"},"filterBy":{"description":"FilterBy are the labels the panel filters the metric by.","items":{"type":"string"},"type":"array"},"groupBy":{"description":"GroupBy are the labels the panel groups the metric by.","items":{"type":"string"},"type":"array"},"panelId":{"description":"PanelID is the panel's id.","type":"string"},"panelName":{"description":"PanelName is the panel's name.","type":"string"}},"type":"object"},"o11y.O11yMetricPoint":{"properties":{"partial":{"description":"Partial marks a point whose bucket the window only partly covers.","type":"boolean"},"timestamp":{"description":"Timestamp is the point's time as a Unix timestamp in milliseconds.","type":"integer"},"value":{"description":"Value is the point's value.","type":"number"},"values":{"description":"Values carries the bucket values of a heatmap point.","items":{"type":"number"},"type":"array"}},"type":"object"},"o11y.O11yMetricSeries":{"properties":{"labels":{"description":"Labels identify the series.","items":{"$ref":"#/components/schemas/o11y.O11yMetricLabel"},"type":"array"},"values":{"description":"Values are the series' points, in time order.","items":{"$ref":"#/components/schemas/o11y.O11yMetricPoint"},"type":"array"}},"type":"object"},"o11y.O11yMetricSeriesSet":{"properties":{"series":{"description":"Series are the time series.","items":{"$ref":"#/components/schemas/o11y.O11yMetricSeries"},"type":"array"}},"type":"object"},"o11y.O11yMetricStat":{"properties":{"description":{"description":"Description describes the metric.","type":"string"},"metricName":{"description":"MetricName is the metric's name.","type":"string"},"samples":{"description":"Samples is how many samples the metric had.","type":"integer"},"timeseries":{"description":"TimeSeries is how many time series the metric had.","type":"integer"},"type":{"description":"Type is the metric type.","type":"string"},"unit":{"description":"Unit is the metric's unit.","type":"string"}},"type":"object"},"o11y.O11yMetricStats":{"properties":{"metrics":{"description":"Metrics are the counted metrics.","items":{"$ref":"#/components/schemas/o11y.O11yMetricStat"},"type":"array"},"total":{"description":"Total is how many metrics matched, across all pages.","type":"integer"}},"type":"object"},"o11y.O11yMetricStatsIn":{"properties":{"end":{"description":"End is the end of the window as a Unix timestamp in milliseconds. Required.","type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.O11yMetricFilter","description":"Filter narrows the metrics counted."},"limit":{"description":"Limit caps how many metrics come back, between 1 and 5000. Required.","type":"integer"},"offset":{"description":"Offset is how many metrics to skip, for paging.","type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.O11yMetricOrder","description":"OrderBy sorts the page, by samples or timeseries."},"start":{"description":"Start is the start of the window as a Unix timestamp in milliseconds. Required.","type":"integer"}},"required":["start","end","limit"],"type":"object"},"o11y.O11yMetricStatsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricStats","description":"Data holds the statistics."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricSummary":{"properties":{"description":{"description":"Description describes the metric.","type":"string"},"isMonotonic":{"description":"IsMonotonic marks a sum that only ever increases.","type":"boolean"},"metricName":{"description":"MetricName is the metric's name.","type":"string"},"temporality":{"description":"Temporality is delta or cumulative.","type":"string"},"type":{"description":"Type is the metric type, e.g. gauge, sum, histogram.","type":"string"},"unit":{"description":"Unit is the metric's unit.","type":"string"}},"type":"object"},"o11y.O11yMetricTreemap":{"properties":{"samples":{"description":"Samples are the entries when measuring by sample count.","items":{"$ref":"#/components/schemas/o11y.O11yTreemapEntry"},"type":"array"},"timeseries":{"description":"TimeSeries are the entries when measuring by time-series count.","items":{"$ref":"#/components/schemas/o11y.O11yTreemapEntry"},"type":"array"}},"type":"object"},"o11y.O11yMetricTreemapIn":{"properties":{"end":{"description":"End is the end of the window as a Unix timestamp in milliseconds. Required.","type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.O11yMetricFilter","description":"Filter narrows the metrics counted."},"limit":{"description":"Limit caps how many entries come back, between 1 and 5000. Required.","type":"integer"},"mode":{"description":"Mode picks the measure: timeseries or samples. Required.","type":"string"},"start":{"description":"Start is the start of the window as a Unix timestamp in milliseconds. Required.","type":"integer"}},"required":["start","end","limit","mode"],"type":"object"},"o11y.O11yMetricTreemapOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yMetricTreemap","description":"Data holds the treemap."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMetricsQueryRangeOut":{"properties":{"data":{"description":"Data is the result — its result type, the value, and query stats when\nasked."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yMyServiceAccountUpdateIn":{"properties":{"name":{"description":"Name is the account's new name, under the same rules it was created with.\nRequired.","type":"string"}},"type":"object"},"o11y.O11yNamespaceListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.NamespaceListResponse","description":"Data holds the namespace records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yNextPrevErrorIDs":{"properties":{"groupID":{"description":"GroupID is the group both belong to.","type":"string"},"nextErrorID":{"description":"NextErrorID is the id of the instance immediately after this one.","type":"string"},"nextTimestamp":{"description":"NextTimestamp is that instance's time.","format":"date-time","type":"string"},"prevErrorID":{"description":"PrevErrorID is the id of the instance immediately before this one.","type":"string"},"prevTimestamp":{"description":"PrevTimestamp is that instance's time.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yNodeListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.NodeListResponse","description":"Data holds the node records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yOIDCConfig":{"properties":{"claimMapping":{"$ref":"#/components/schemas/o11y.O11yAttributeMapping","description":"ClaimMapping names the token claims to read identity from."},"clientId":{"description":"ClientID is the OAuth application's id.","type":"string"},"clientSecret":{"description":"ClientSecret is the OAuth application's secret.","type":"string"},"getUserInfo":{"description":"GetUserInfo also queries the userinfo endpoint, for providers whose id\ntokens are thin.","type":"boolean"},"insecureSkipEmailVerified":{"description":"InsecureSkipEmailVerified admits addresses the provider has not\nverified.","type":"boolean"},"issuer":{"description":"Issuer is the provider's issuer URL.","type":"string"},"issuerAlias":{"description":"IssuerAlias overrides the issuer for providers whose discovery document\ndisagrees with their issuer URL.","type":"string"}},"type":"object"},"o11y.O11yObject":{"properties":{"resource":{"$ref":"#/components/schemas/o11y.O11yResourceRef","description":"Resource is the resource's type and kind."},"selector":{"description":"Selector picks the instance — an FGA object string, wildcard allowed.","type":"string"}},"type":"object"},"o11y.O11yObjectGroup":{"properties":{"resource":{"$ref":"#/components/schemas/o11y.O11yResourceRef","description":"Resource is the objects' type and kind."},"selectors":{"description":"Selectors pick the instances; a wildcard selects them all.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yOccurrence":{"properties":{"culprit":{"description":"Culprit is where it came from.","type":"string"},"environment":{"description":"Environment is the deployment it happened in.","type":"string"},"eventId":{"description":"EventID is the occurrence's id.","type":"string"},"fingerprint":{"description":"Fingerprint is the grouping key it was bucketed by.","type":"string"},"frames":{"description":"Frames are the stack, innermost first.","items":{"$ref":"#/components/schemas/o11y.O11yOccurrenceFrame"},"type":"array"},"level":{"description":"Level is its severity, e.g. error, warning, info.","type":"string"},"platform":{"description":"Platform is the reporting runtime.","type":"string"},"release":{"description":"Release is the version that produced it.","type":"string"},"serverName":{"description":"ServerName is the host that reported it.","type":"string"},"serviceName":{"description":"ServiceName is the service that reported it.","type":"string"},"spanId":{"description":"SpanID is the span it belonged to.","type":"string"},"tags":{"additionalProperties":{"type":"string"},"description":"Tags are the reporter's own key/value labels.","type":"object"},"timestamp":{"description":"Timestamp is when the error happened.","format":"date-time","type":"string"},"traceId":{"description":"TraceID is the trace it belonged to.","type":"string"},"transaction":{"description":"Transaction is the operation it happened in.","type":"string"},"type":{"description":"Type is the exception type.","type":"string"},"user":{"$ref":"#/components/schemas/o11y.O11yEventUser","description":"User is the affected end-user context, when the reporter attached one."},"value":{"description":"Value is the exception value.","type":"string"}},"type":"object"},"o11y.O11yOccurrenceFrame":{"properties":{"absPath":{"description":"AbsPath is the file's absolute path.","type":"string"},"colno":{"description":"Colno is the column number.","type":"integer"},"filename":{"description":"Filename is the file it is in.","type":"string"},"function":{"description":"Function is the function the frame is in.","type":"string"},"inApp":{"description":"InApp marks a frame in the reporting application's own code.","type":"boolean"},"lineno":{"description":"Lineno is the line number.","type":"integer"},"module":{"description":"Module is the module the function is in.","type":"string"}},"type":"object"},"o11y.O11yOnboardingOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yK8sOnboarding","description":"Data is the progress report."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yOperation":{"properties":{"errorCount":{"description":"ErrorCount is how many of those runs errored.","type":"integer"},"name":{"description":"Name is the operation (span name).","type":"string"},"numCalls":{"description":"NumCalls is how many times it ran in the window.","type":"integer"},"p50":{"description":"P50 is its median latency, nanoseconds.","type":"number"},"p95":{"description":"P95 is its p95 latency, nanoseconds.","type":"number"},"p99":{"description":"P99 is its p99 latency, nanoseconds.","type":"number"}},"type":"object"},"o11y.O11yOperationsIn":{"properties":{"end":{"description":"End is the window's end, epoch nanoseconds as a string.","type":"string"},"limit":{"description":"Limit caps how many operations come back.","type":"integer"},"service":{"description":"Service is the service whose operations are read.","type":"string"},"start":{"description":"Start is the window's start, epoch nanoseconds as a string.","type":"string"},"tags":{"description":"Tags narrow the spans counted, each a span-attribute predicate.","items":{"$ref":"#/components/schemas/o11y.O11yServiceTag"},"type":"array"}},"type":"object"},"o11y.O11yOperationsOut":{"properties":{"data":{"description":"Data holds one entry per operation.","items":{"$ref":"#/components/schemas/o11y.O11yOperation"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yOrgStatsOut":{"properties":{"data":{"additionalProperties":{"type":"object"},"description":"Data are the statistics, keyed by the reporter's own counter names.","type":"object"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yOrganization":{"properties":{"alias":{"description":"Alias is an alternate name the org also answers to.","type":"string"},"createdAt":{"description":"CreatedAt is when the org was created.","format":"date-time","type":"string"},"displayName":{"description":"DisplayName is what the console shows for the org.","type":"string"},"id":{"description":"ID is the org id. On update it is ignored: the call always addresses the\ncaller's own org.","type":"string"},"key":{"description":"Key is the org's stable numeric key, derived from its id.","type":"integer"},"name":{"description":"Name is the org's short name.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yOrganizationOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yOrganization","description":"Data is the organization."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yOverallStateTransitionsOut":{"properties":{"data":{"description":"Data holds the windows.","items":{"$ref":"#/components/schemas/o11y.ReleStateItem"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPasswordAuthN":{"properties":{"provider":{"description":"Provider is the route's provider, e.g. email_password.","type":"string"}},"type":"object"},"o11y.O11yPercentilePosition":{"properties":{"description":{"description":"Description says the same in words.","type":"string"},"percentile":{"description":"Percentile is the percentile the duration lands at.","type":"number"}},"type":"object"},"o11y.O11yPercentiles":{"properties":{"p50":{"description":"P50 is the median.","type":"number"},"p90":{"description":"P90 is the 90th percentile.","type":"number"},"p99":{"description":"P99 is the 99th percentile.","type":"number"}},"type":"object"},"o11y.O11yPodListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.PodListResponse","description":"Data holds the pod records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPodOnboarding":{"properties":{"clusterName":{"description":"ClusterName is the pod's cluster.","type":"string"},"hasClusterName":{"description":"HasClusterName says whether the cluster label is present.","type":"boolean"},"hasCronjobName":{"description":"HasCronjobName says whether the cronjob label is present.","type":"boolean"},"hasDaemonsetName":{"description":"HasDaemonsetName says whether the daemonset label is present.","type":"boolean"},"hasDeploymentName":{"description":"HasDeploymentName says whether the deployment label is present.","type":"boolean"},"hasJobName":{"description":"HasJobName says whether the job label is present.","type":"boolean"},"hasNamespaceName":{"description":"HasNamespaceName says whether the namespace label is present.","type":"boolean"},"hasNodeName":{"description":"HasNodeName says whether the node label is present.","type":"boolean"},"hasStatefulsetName":{"description":"HasStatefulsetName says whether the statefulset label is present.","type":"boolean"},"namespaceName":{"description":"NamespaceName is the pod's namespace.","type":"string"},"nodeName":{"description":"NodeName is the pod's node.","type":"string"},"podName":{"description":"PodName is the pod.","type":"string"}},"type":"object"},"o11y.O11yPostableAuthDomain":{"properties":{"config":{"$ref":"#/components/schemas/o11y.O11yAuthDomainConfig","description":"Config is the domain's SSO configuration."},"name":{"description":"Name is the email domain being claimed, e.g. example.com.","type":"string"}},"type":"object"},"o11y.O11yPostableUser":{"properties":{"displayName":{"description":"DisplayName is the new member's display name.","type":"string"},"email":{"description":"Email is the new member's address. Required.","type":"string"},"frontendBaseUrl":{"description":"FrontendBaseUrl is the console origin the invite link is built on.","type":"string"},"userRoles":{"description":"UserRoles are the roles the member starts with, each by id.","items":{"$ref":"#/components/schemas/o11y.O11yRoleID"},"type":"array"}},"type":"object"},"o11y.O11yPreference":{"properties":{"allowedScopes":{"description":"AllowedScopes are the scopes the preference may be set at — org, user.","items":{"type":"string"},"type":"array"},"allowedValues":{"description":"AllowedValues restricts a string preference to these values.","items":{"type":"string"},"type":"array"},"defaultValue":{"description":"DefaultValue is the value before anyone set one.","type":"object"},"description":{"description":"Description says what the preference does.","type":"string"},"name":{"description":"Name is the preference name.","type":"string"},"value":{"description":"Value is the current value.","type":"object"},"valueType":{"description":"ValueType is the JSON type a value must have — string, integer, float or\nboolean.","type":"string"}},"type":"object"},"o11y.O11yPreferenceOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yPreference","description":"Data is the preference."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPreferencesOut":{"properties":{"data":{"description":"Data holds the preferences.","items":{"$ref":"#/components/schemas/o11y.O11yPreference"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yProcessListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.ProcessListResponse","description":"Data holds the process records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPromQueryOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yPromResult","description":"Data is the evaluation result."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPromResult":{"properties":{"result":{"description":"Result is the result in PromQL's own wire shape for ResultType, carried\nverbatim."},"resultType":{"description":"ResultType discriminates Result: matrix, vector, scalar or string.","type":"string"},"stats":{"description":"Stats holds query statistics when the caller asked for them."}},"type":"object"},"o11y.O11yPublicDashboard":{"properties":{"defaultTimeRange":{"description":"DefaultTimeRange is the fixed window when the range is not caller-chosen.","type":"string"},"publicPath":{"description":"PublicPath is the public URL path the share is reachable at.","type":"string"},"timeRangeEnabled":{"description":"TimeRangeEnabled reports whether the public page may pick its own range.","type":"boolean"}},"type":"object"},"o11y.O11yPublicDashboardData":{"properties":{"dashboard":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardV1","description":"Dashboard is the sanitized dashboard."},"publicDashboard":{"$ref":"#/components/schemas/o11y.O11yPublicDashboard","description":"PublicDashboard is the public-sharing config."}},"type":"object"},"o11y.O11yPublicDashboardDataOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yPublicDashboardData","description":"Data is the sanitized dashboard and its share config."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPublicDashboardOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yPublicDashboard","description":"Data is the public-sharing config."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yPublicDashboardV1":{"properties":{"createdAt":{"description":"CreatedAt is when the dashboard was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created it.","type":"string"},"data":{"description":"Data is the sanitized widget data as stored, carried verbatim as open JSON."},"id":{"description":"ID is the dashboard's id.","type":"string"},"locked":{"description":"Locked reports whether the dashboard is locked.","type":"boolean"},"org_id":{"description":"OrgID is the org the dashboard belongs to.","type":"string"},"source":{"description":"Source is where the dashboard came from.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is who last changed it.","type":"string"}},"type":"object"},"o11y.O11yPublicDashboardWriteIn":{"properties":{"defaultTimeRange":{"type":"string"},"id":{"description":"ID is the dashboard id from the path.","type":"string"},"timeRangeEnabled":{"type":"boolean"}},"type":"object"},"o11y.O11yPvcListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.VolumeListResponse","description":"Data holds the volume records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQueryFilterAnalysis":{"properties":{"groups":{"description":"Groups are the columns the query groups by.","items":{"$ref":"#/components/schemas/o11y.O11yColumnInfo"},"type":"array"},"metricNames":{"description":"MetricNames are the metrics the query reads.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yQueryRange":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yQueryRangeData","description":"Data holds the results."},"meta":{"$ref":"#/components/schemas/o11y.O11yQueryStats","description":"Meta reports what the query scanned."},"type":{"description":"Type is the result kind; time_series here.","type":"string"},"warning":{"$ref":"#/components/schemas/o11y.O11yQueryWarning","description":"Warning carries a non-fatal warning, when the query raised one."}},"type":"object"},"o11y.O11yQueryRangeData":{"properties":{"results":{"description":"Results are the per-query results, each a set of aggregated series.","items":{"$ref":"#/components/schemas/o11y.O11yReductionSeriesResult"},"type":"array"}},"type":"object"},"o11y.O11yQueryRangeFormatOut":{"properties":{"data":{"description":"Data is the query, normalized to the v3 shape."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQueryRangeOut":{"properties":{"data":{"description":"Data is the query result — the result type, the result set, execution stats\nand any warning — as the engine rendered it."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQueryRangePreviewIn":{"properties":{"compositeQuery":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.CompositeQuery"},"end":{"type":"integer"},"formatOptions":{"$ref":"#/components/schemas/o11y.FormatOptions"},"noCache":{"type":"boolean"},"requestType":{},"schemaVersion":{"type":"string"},"start":{"type":"integer"},"variables":{"additionalProperties":{"$ref":"#/components/schemas/o11y.VariableItem"},"type":"object"},"verbose":{"description":"Verbose selects the answer's depth. Empty or \"true\" renders the underlying\nDatastore SQL with EXPLAIN and granule analysis; \"false\" returns only the\nper-query valid/error/warnings verdict with no Datastore round trips.","type":"string"}},"type":"object"},"o11y.O11yQueryRangePreviewOut":{"properties":{"data":{"description":"Data holds one preview per query, keyed by query name — the rendered\nstatements with optional EXPLAIN and granule analysis, or the per-query\nverdict."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQueryStats":{"properties":{"bytesScanned":{"description":"BytesScanned is how many bytes the query read.","type":"integer"},"durationMs":{"description":"DurationMS is how long the query took, in milliseconds.","type":"integer"},"rowsScanned":{"description":"RowsScanned is how many rows the query read.","type":"integer"},"stepIntervals":{"additionalProperties":{"type":"integer"},"description":"StepIntervals is the step used per query, in seconds.","type":"object"}},"type":"object"},"o11y.O11yQueryWarning":{"properties":{"message":{"description":"Message is the warning.","type":"string"},"url":{"description":"URL points at the relevant documentation.","type":"string"},"warnings":{"description":"Warnings carries additional notes.","items":{"$ref":"#/components/schemas/o11y.O11yQueryWarningNote"},"type":"array"}},"type":"object"},"o11y.O11yQueryWarningNote":{"properties":{"message":{"description":"Message is the note.","type":"string"}},"type":"object"},"o11y.O11yQueueCheck":{"properties":{"attribute":{"description":"Attribute is the span attribute or telemetry the check looked for.","type":"string"},"error_message":{"description":"Message says what is missing when the check fails; empty on a pass. Its\nwire key is error_message.","type":"string"},"status":{"description":"Status is \"1\" when the telemetry is present, \"0\" when it is not.","type":"string"}},"type":"object"},"o11y.O11yQueueChecksOut":{"properties":{"data":{"description":"Data holds one check per required attribute, sorted by attribute.","items":{"$ref":"#/components/schemas/o11y.O11yQueueCheck"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQueueFilterKey":{"properties":{"dataType":{"description":"DataType is the attribute's type: string, int64, float64 or bool.","type":"string"},"isColumn":{"description":"IsColumn marks an attribute materialized as its own store column.","type":"boolean"},"isJSON":{"description":"IsJSON marks an attribute read out of the span's JSON body.","type":"boolean"},"key":{"description":"Key is the attribute name.","type":"string"},"type":{"description":"Type says which plane the attribute lives on: tag or resource.","type":"string"}},"type":"object"},"o11y.O11yQueueFilterRule":{"properties":{"key":{"$ref":"#/components/schemas/o11y.O11yQueueFilterKey","description":"Key names the attribute the predicate tests."},"op":{"description":"Op is the comparison, e.g. =, !=, in, contains.","type":"string"},"value":{"description":"Value is the operand; its JSON type follows the attribute's dataType.","type":"object"}},"type":"object"},"o11y.O11yQueueFilterSet":{"properties":{"items":{"description":"Items are the predicates.","items":{"$ref":"#/components/schemas/o11y.O11yQueueFilterRule"},"type":"array"},"op":{"description":"Op combines the items: AND or OR.","type":"string"}},"type":"object"},"o11y.O11yQueueIn":{"properties":{"end":{"description":"End is the window's end, epoch nanoseconds.","type":"integer"},"eval_time":{"description":"EvalTime bounds the span-evaluation scan, nanoseconds; only the\nspan/evaluation view reads it.","type":"integer"},"start":{"description":"Start is the window's start, epoch nanoseconds.","type":"integer"},"variables":{"additionalProperties":{"type":"string"},"description":"Variables name what the view drills into — topic, partition, service,\nconsumer_group — keyed by the name the view expects.","type":"object"}},"type":"object"},"o11y.O11yQueueListIn":{"properties":{"end":{"description":"End is the window's end, epoch nanoseconds.","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.O11yQueueFilterSet","description":"Filters narrow the rows by span attribute; null means all rows."},"limit":{"description":"Limit caps how many rows come back.","type":"integer"},"start":{"description":"Start is the window's start, epoch nanoseconds.","type":"integer"}},"type":"object"},"o11y.O11yQueueRow":{"properties":{"data":{"additionalProperties":{},"description":"Data holds the row's cells keyed by column name; each cell's JSON type is\nthe column's own, so the bytes pass through verbatim.","type":"object"},"timestamp":{"description":"Timestamp anchors the row in time.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yQueueRowsOut":{"properties":{"data":{"description":"Data holds one row per messaging destination.","items":{"$ref":"#/components/schemas/o11y.O11yQueueRow"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yQuickFiltersOut":{"properties":{"data":{"description":"Data holds one entry per signal.","items":{"$ref":"#/components/schemas/o11y.O11ySignalFilters"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yReductionRule":{"properties":{"active":{"description":"Active says whether the rule is in force.","type":"boolean"},"createdAt":{"description":"CreatedAt is when the rule was created.","format":"date-time","type":"string"},"createdBy":{"description":"CreatedBy is who created it.","type":"string"},"effectiveFrom":{"description":"EffectiveFrom is when the rule took effect.","format":"date-time","type":"string"},"id":{"description":"ID is the rule's id.","type":"string"},"ingestedSamples":{"description":"IngestedSamples is how many samples arrived while the rule was active.","type":"integer"},"ingestedSeries":{"description":"IngestedSeries is how many series arrived while the rule was active.","type":"integer"},"labels":{"description":"Labels are the label names the rule matches.","items":{"type":"string"},"type":"array"},"matchType":{"description":"MatchType is drop or keep.","type":"string"},"metricName":{"description":"MetricName is the metric the rule governs.","type":"string"},"retainedSamples":{"description":"RetainedSamples is how many of them were kept.","type":"integer"},"retainedSeries":{"description":"RetainedSeries is how many of them were kept.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when the rule last changed.","format":"date-time","type":"string"},"updatedBy":{"description":"UpdatedBy is who last changed it.","type":"string"}},"type":"object"},"o11y.O11yReductionRuleCreateIn":{"properties":{"labels":{"description":"Labels are the label names the rule matches. Required, at least one.","items":{"type":"string"},"type":"array"},"matchType":{"description":"MatchType is drop or keep: drop the named labels, or keep only them. Required.","type":"string"},"metricName":{"description":"MetricName is the metric the rule governs; one rule per metric. Required.","type":"string"}},"required":["metricName","matchType","labels"],"type":"object"},"o11y.O11yReductionRuleListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yReductionRules","description":"Data holds the rules."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yReductionRuleOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yReductionRule","description":"Data holds the rule."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yReductionRulePreview":{"properties":{"affectedAssets":{"description":"AffectedAssets are the dashboards and alerts the rule would touch.","items":{"$ref":"#/components/schemas/o11y.O11yAffectedAsset"},"type":"array"},"currentRetainedSeries":{"description":"CurrentRetainedSeries is how many survive the rules in force today.","type":"integer"},"droppedLabels":{"description":"DroppedLabels are the labels the rule would drop.","items":{"type":"string"},"type":"array"},"effectiveFrom":{"description":"EffectiveFrom is when the rule would take effect.","format":"date-time","type":"string"},"ingestedSeries":{"description":"IngestedSeries is how many series the metric ingests today.","type":"integer"},"reductionPercent":{"description":"ReductionPercent is the estimated reduction, in percent.","type":"number"},"retainedSeries":{"description":"RetainedSeries is how many would survive with the candidate rule.","type":"integer"}},"type":"object"},"o11y.O11yReductionRulePreviewIn":{"properties":{"labels":{"description":"Labels are the label names the rule would match. Required, at least one.","items":{"type":"string"},"type":"array"},"lookbackMs":{"description":"LookbackMs is how far back to sample when estimating.","type":"integer"},"matchType":{"description":"MatchType is drop or keep. Required.","type":"string"},"metricName":{"description":"MetricName is the metric the rule would govern. Required.","type":"string"}},"required":["metricName","matchType","labels"],"type":"object"},"o11y.O11yReductionRulePreviewOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yReductionRulePreview","description":"Data holds the estimate."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yReductionRuleSaveIn":{"properties":{"id":{"description":"ID is the rule's id.","type":"string"},"labels":{"description":"Labels are the label names the rule matches. Required, at least one.","items":{"type":"string"},"type":"array"},"matchType":{"description":"MatchType is drop or keep. Required.","type":"string"}},"required":["id","matchType","labels"],"type":"object"},"o11y.O11yReductionRules":{"properties":{"rules":{"description":"Rules are the rules.","items":{"$ref":"#/components/schemas/o11y.O11yReductionRule"},"type":"array"},"total":{"description":"Total is how many rules matched, across all pages.","type":"integer"}},"type":"object"},"o11y.O11yReductionSeriesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yQueryRange","description":"Data holds the query-range answer."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yReductionSeriesResult":{"properties":{"aggregations":{"description":"Aggregations are the query's aggregation buckets.","items":{"$ref":"#/components/schemas/o11y.O11yAggregation"},"type":"array"},"queryName":{"description":"QueryName names the query the result answers.","type":"string"}},"type":"object"},"o11y.O11yReductionStats":{"properties":{"estimatedMonthlySavingsUsd":{"description":"EstimatedMonthlySavingsUsd is the estimated monthly savings, in USD.","type":"number"},"ingestedSamples":{"description":"IngestedSamples is how many samples arrived across all rules.","type":"integer"},"ingestedSeries":{"description":"IngestedSeries is how many series arrived across all rules.","type":"integer"},"retainedSamples":{"description":"RetainedSamples is how many of them were kept.","type":"integer"},"retainedSeries":{"description":"RetainedSeries is how many of them were kept.","type":"integer"}},"type":"object"},"o11y.O11yReductionStatsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yReductionStats","description":"Data holds the totals."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRegisterIn":{"properties":{"email":{"description":"Email is the admin's email. Required.","type":"string"},"name":{"description":"Name is the admin's display name.","type":"string"},"orgDisplayName":{"description":"OrgDisplayName is the organization's display name.","type":"string"},"orgName":{"description":"OrgName is the organization's name.","type":"string"},"password":{"description":"Password is the admin's password.","type":"string"}},"required":["email"],"type":"object"},"o11y.O11yRegisterOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yUser","description":"Data is the user. The runtime answers register with the same user shape\nthe identity face reads, so it is the ONE O11yUser (identity.go) — a\ncreated user is a user, and the document names it once."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yResetPasswordIn":{"properties":{"password":{"description":"Password is the new password.","type":"string"},"token":{"description":"Token is the reset-password token authorizing the change.","type":"string"}},"type":"object"},"o11y.O11yResetToken":{"properties":{"expiresAt":{"description":"ExpiresAt is when it stops working.","format":"date-time","type":"string"},"id":{"description":"ID is the grant's id.","type":"string"},"passwordId":{"description":"PasswordID is the password record it resets.","type":"string"},"token":{"description":"Token is the secret that redeems it.","type":"string"}},"type":"object"},"o11y.O11yResetTokenOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yResetToken","description":"Data is the token."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yResetTokenRef":{"properties":{"token":{"description":"Token is the reset-password token.","type":"string"}},"type":"object"},"o11y.O11yResourceRef":{"properties":{"kind":{"description":"Kind is the resource kind the type belongs to.","type":"string"},"type":{"description":"Type is the resource type, e.g. role, dashboard, serviceaccount.","type":"string"}},"type":"object"},"o11y.O11yRetentionMatch":{"properties":{"key":{"description":"Key is the label to test.","type":"string"},"values":{"description":"Values are the label values the rule matches.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yRetentionOut":{"properties":{"cold_storage_ttl_days":{"description":"ColdStorageTTLDays is how old data must be before it moves, in days.","type":"integer"},"cold_storage_volume":{"description":"ColdStorageVolume names the volume aged data moves to.","type":"string"},"default_ttl_days":{"description":"DefaultTTLDays is the retention for data no rule matches, in days.","type":"integer"},"expected_logs_move_ttl_duration_hrs":{"description":"ExpectedLogsMoveTTLHours is the pending logs cold-storage move TTL, in\nhours.","type":"integer"},"expected_logs_ttl_duration_hrs":{"description":"ExpectedLogsTTLHours is the pending logs TTL, in hours.","type":"integer"},"status":{"description":"Status is the last TTL operation's state.","type":"string"},"ttl_conditions":{"description":"TTLConditions are the ordered per-label rules; the first match wins.","items":{"$ref":"#/components/schemas/o11y.O11yRetentionRule"},"type":"array"},"version":{"description":"Version is the policy format version.","type":"string"}},"type":"object"},"o11y.O11yRetentionRule":{"properties":{"conditions":{"description":"Conditions all have to hold for the rule to match.","items":{"$ref":"#/components/schemas/o11y.O11yRetentionMatch"},"type":"array"},"ttlDays":{"description":"TTLDays is the retention applied when it does, in days.","type":"integer"}},"type":"object"},"o11y.O11yRetentionSetIn":{"properties":{"coldStorageDurationDays":{"description":"ColdStorageDurationDays is how old data must be before it moves, in days.","type":"integer"},"coldStorageVolume":{"description":"ColdStorageVolume names the volume aged data moves to, when set.","type":"string"},"defaultTTLDays":{"description":"DefaultTTLDays is the retention for data no rule matches, in days.","type":"integer"},"ttlConditions":{"description":"TTLConditions are ordered per-label rules; the first matching rule wins.","items":{"$ref":"#/components/schemas/o11y.O11yRetentionRule"},"type":"array"},"type":{"description":"Type is the signal the policy applies to — traces, metrics or logs.","type":"string"}},"type":"object"},"o11y.O11yRetentionSetOut":{"properties":{"message":{"description":"Message says what was done.","type":"string"}},"type":"object"},"o11y.O11yRetry":{"properties":{"delay":{"description":"Delay is how long to wait before retrying, in nanoseconds.","type":"integer"}},"type":"object"},"o11y.O11yRole":{"properties":{"createdAt":{"description":"CreatedAt is when the role was created.","format":"date-time","type":"string"},"description":{"description":"Description says what the role is for.","type":"string"},"id":{"description":"ID is the role id.","type":"string"},"name":{"description":"Name is the role's name.","type":"string"},"orgId":{"description":"OrgID is the org the role belongs to.","type":"string"},"type":{"description":"Type is how the role came to be — managed by the platform or custom.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yRoleCreateIn":{"properties":{"description":{"description":"Description says what the role is for.","type":"string"},"name":{"description":"Name is the role's name: lowercase letters and hyphens, at most 50\ncharacters, not starting with the reserved managed-role prefix. Required.","type":"string"},"transactionGroups":{"description":"TransactionGroups are the grants the role carries.","items":{"$ref":"#/components/schemas/o11y.O11yTransactionGroup"},"type":"array"}},"type":"object"},"o11y.O11yRoleCreateOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yCreated","description":"Data carries the new role's id."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRoleDetail":{"properties":{"createdAt":{"description":"CreatedAt is when the role was created.","format":"date-time","type":"string"},"description":{"description":"Description says what the role is for.","type":"string"},"id":{"description":"ID is the role id.","type":"string"},"name":{"description":"Name is the role's name.","type":"string"},"orgId":{"description":"OrgID is the org the role belongs to.","type":"string"},"transactionGroups":{"description":"TransactionGroups are the grants the role carries.","items":{"$ref":"#/components/schemas/o11y.O11yTransactionGroup"},"type":"array"},"type":{"description":"Type is custom or managed.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yRoleID":{"properties":{"id":{"description":"ID is the role id.","type":"string"}},"type":"object"},"o11y.O11yRoleMapping":{"properties":{"defaultRole":{"description":"DefaultRole is the role when no group mapping applies.","type":"string"},"groupMappings":{"additionalProperties":{"type":"string"},"description":"GroupMappings maps a provider group name to a role name.","type":"object"},"useRoleAttribute":{"description":"UseRoleAttribute reads the role straight from the provider's role claim\ninstead of the group mappings.","type":"boolean"}},"type":"object"},"o11y.O11yRoleOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yRoleDetail","description":"Data holds the role and its transaction groups."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRoleUpdateIn":{"properties":{"description":{"description":"Description says what the role is for. Required — send an empty string to\nclear it.","type":"string"},"transactionGroups":{"description":"TransactionGroups are the grants the role carries. Required — send an\nempty array to clear them.","items":{"$ref":"#/components/schemas/o11y.O11yTransactionGroup"},"type":"array"}},"type":"object"},"o11y.O11yRolesOut":{"properties":{"data":{"description":"Data holds the roles.","items":{"$ref":"#/components/schemas/o11y.O11yRole"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRotateSessionIn":{"properties":{"refreshToken":{"description":"RefreshToken is the refresh token being redeemed.","type":"string"}},"type":"object"},"o11y.O11yRoutePoliciesOut":{"properties":{"data":{"description":"Data holds the policies.","items":{"$ref":"#/components/schemas/o11y.GettableRoutePolicy"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRoutePolicyOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableRoutePolicy","description":"Data holds the policy."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRoutePolicyUpdateIn":{"properties":{"channels":{"items":{"type":"string"},"type":"array"},"description":{"type":"string"},"expression":{"type":"string"},"kind":{},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yRuleHistoryContributorsOut":{"properties":{"data":{"description":"Data holds the contributors.","items":{"$ref":"#/components/schemas/o11y.GettableRuleStateHistoryContributor"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleHistoryFilterKeysOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFieldKeys","description":"Data holds the keys."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleHistoryFilterValuesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFieldValues","description":"Data holds the values."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleHistoryOverallStatusOut":{"properties":{"data":{"description":"Data holds the windows.","items":{"$ref":"#/components/schemas/o11y.GettableRuleStateWindow"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleHistoryQueryIn":{"properties":{"end":{"type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"limit":{"type":"integer"},"offset":{"type":"integer"},"order":{"type":"string"},"start":{"type":"integer"},"state":{"type":"string"}},"type":"object"},"o11y.O11yRuleHistoryStatsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableRuleStateHistoryStats","description":"Data holds the statistics."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleHistoryTimelineOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableRuleStateTimeline","description":"Data holds the timeline and its paging cursor."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleOut":{"properties":{"data":{"description":"Data holds the rule."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleStateContributorsOut":{"properties":{"data":{"description":"Data holds the contributors.","items":{"$ref":"#/components/schemas/o11y.RuleStateHistoryContributor"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleStateTimelineOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.RuleStateTimeline","description":"Data holds the timeline."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRuleStatsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Stats","description":"Data holds the statistics."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yRulesOut":{"properties":{"data":{"description":"Data holds the rules.","items":{},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySAMLConfig":{"properties":{"attributeMapping":{"$ref":"#/components/schemas/o11y.O11yAttributeMapping","description":"AttributeMapping names the assertion attributes to read identity from."},"insecureSkipAuthNRequestsSigned":{"description":"InsecureSkipAuthNRequestsSigned skips signing outgoing AuthN requests,\nfor IdPs that refuse signed ones.","type":"boolean"},"samlCert":{"description":"SamlCert is the IdP's signing certificate.","type":"string"},"samlEntity":{"description":"SamlEntity is the IdP's entityID.","type":"string"},"samlIdp":{"description":"SamlIdp is the IdP's single-sign-on endpoint.","type":"string"}},"type":"object"},"o11y.O11ySavedViewCreateOut":{"properties":{"data":{"description":"Data is the new view's id."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySavedViewDeleteOut":{"properties":{"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySavedViewListOut":{"properties":{"data":{"description":"Data holds the views.","items":{"$ref":"#/components/schemas/o11y.SavedView"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySavedViewOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.SavedView","description":"Data holds the view."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySavedViewUpdateIn":{"properties":{"category":{"type":"string"},"compositeQuery":{"$ref":"#/components/schemas/o11y.CompositeQuery"},"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"extraData":{"type":"string"},"id":{},"name":{"type":"string"},"sourcePage":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"},"viewId":{"description":"ViewID is the id of the view to replace, taken from the URL.","type":"string"}},"type":"object"},"o11y.O11ySentryEventOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yEvent","description":"Data is the event."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySentryIssueEventsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yEvents","description":"Data holds the occurrences."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySentryPostableProject":{"properties":{"name":{"description":"Name is the project's display name. Required.","type":"string"},"platform":{"description":"Platform is the reporting runtime, e.g. go, python, javascript.","type":"string"},"slug":{"description":"Slug is the project's short name. Server-assigned from Name when empty.","type":"string"}},"required":["name"],"type":"object"},"o11y.O11ySentryProject":{"properties":{"createdAt":{"description":"CreatedAt is when the project was created.","format":"date-time","type":"string"},"dsn":{"description":"DSN is the project's freshly-derived ingest DSN.","type":"string"},"id":{"description":"ID is the project id.","type":"string"},"name":{"description":"Name is the project's display name.","type":"string"},"platform":{"description":"Platform is the reporting runtime, e.g. go, python, javascript.","type":"string"},"slug":{"description":"Slug is the project's short name.","type":"string"},"status":{"description":"Status is the project's lifecycle state: active or disabled.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the project last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11ySentryProjectOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11ySentryProject","description":"Data is the project."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySentryProjects":{"properties":{"items":{"description":"Items are the projects.","items":{"$ref":"#/components/schemas/o11y.O11ySentryProject"},"type":"array"},"total":{"description":"Total is how many the org has.","type":"integer"}},"type":"object"},"o11y.O11ySentryProjectsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11ySentryProjects","description":"Data holds the projects."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySentryUpdateIssueIn":{"properties":{"assignee":{"description":"Assignee is who the issue is assigned to.","type":"string"},"id":{"description":"ID is the issue id.","type":"string"},"status":{"description":"Status is the new lifecycle state: unresolved, resolved or ignored.","type":"string"}},"required":["id"],"type":"object"},"o11y.O11yService":{"properties":{"avgDuration":{"description":"AvgDuration is their average latency, nanoseconds.","type":"number"},"callRate":{"description":"CallRate is calls per second over the window.","type":"number"},"dataWarning":{"$ref":"#/components/schemas/o11y.O11yServiceWarning","description":"DataWarning carries the entry-point operations the numbers were computed\nover."},"errorRate":{"description":"ErrorRate is the percentage of calls that errored.","type":"number"},"fourXXRate":{"description":"FourXXRate is the percentage of calls that answered 4xx.","type":"number"},"num4XX":{"description":"Num4XX is how many of the calls answered 4xx.","type":"integer"},"numCalls":{"description":"NumCalls is how many entry-point spans landed in the window.","type":"integer"},"numErrors":{"description":"NumErrors is how many of the calls errored.","type":"integer"},"p99":{"description":"Percentile99 is the p99 latency of its entry-point spans, nanoseconds.","type":"number"},"serviceName":{"description":"ServiceName is the service.","type":"string"}},"type":"object"},"o11y.O11yServiceAccount":{"properties":{"createdAt":{"description":"CreatedAt is when the account was created.","format":"date-time","type":"string"},"email":{"description":"Email is the address the account authenticates as.","type":"string"},"id":{"description":"ID is the service account id.","type":"string"},"name":{"description":"Name is the account's name.","type":"string"},"orgId":{"description":"OrgID is the org the account belongs to.","type":"string"},"status":{"description":"Status is active or deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yServiceAccountCreateIn":{"properties":{"name":{"description":"Name is the account's name: a lowercase letter followed by lowercase\nletters, digits or hyphens, at most 50 characters. Required.","type":"string"}},"type":"object"},"o11y.O11yServiceAccountCreateOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yCreated","description":"Data carries the new account's id."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServiceAccountDetail":{"properties":{"createdAt":{"description":"CreatedAt is when the account was created.","format":"date-time","type":"string"},"email":{"description":"Email is the address the account authenticates as.","type":"string"},"id":{"description":"ID is the service account id.","type":"string"},"name":{"description":"Name is the account's name.","type":"string"},"orgId":{"description":"OrgID is the org the account belongs to.","type":"string"},"serviceAccountRoles":{"description":"ServiceAccountRoles are the account's role assignments.","items":{"$ref":"#/components/schemas/o11y.O11yServiceAccountRole"},"type":"array"},"status":{"description":"Status is active or deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yServiceAccountOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yServiceAccountDetail","description":"Data holds the account and its role assignments."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServiceAccountRole":{"properties":{"createdAt":{"description":"CreatedAt is when the role was assigned.","format":"date-time","type":"string"},"id":{"description":"ID is the assignment's own id.","type":"string"},"role":{"$ref":"#/components/schemas/o11y.O11yRole","description":"Role is the role itself."},"roleId":{"description":"RoleID is the role held.","type":"string"},"serviceAccountId":{"description":"ServiceAccountID is the account holding the role.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the assignment last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yServiceAccountRoleGrantIn":{"properties":{"id":{"description":"RoleID is the id of the role to assign. Required.","type":"string"}},"type":"object"},"o11y.O11yServiceAccountRolesOut":{"properties":{"data":{"description":"Data holds the roles.","items":{"$ref":"#/components/schemas/o11y.O11yRole"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServiceAccountUpdateIn":{"properties":{"name":{"description":"Name is the account's new name, under the same rules it was created with.\nRequired.","type":"string"}},"type":"object"},"o11y.O11yServiceAccountsOut":{"properties":{"data":{"description":"Data holds the service accounts.","items":{"$ref":"#/components/schemas/o11y.O11yServiceAccount"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServiceOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.Service","description":"Data holds the service."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServiceTag":{"properties":{"BoolValues":{"description":"BoolValues are the boolean operands, when the attribute is a bool.","items":{"type":"boolean"},"type":"array"},"Key":{"description":"Key is the span attribute to test.","type":"string"},"NumberValues":{"description":"NumberValues are the numeric operands, when the attribute is a number.","items":{"type":"number"},"type":"array"},"Operator":{"description":"Operator is how to test it, e.g. in, not_in.","type":"string"},"StringValues":{"description":"StringValues are the string operands, when the attribute is a string.","items":{"type":"string"},"type":"array"},"TagType":{"description":"TagType says which plane the attribute lives on, e.g. tag or resource.","type":"string"}},"type":"object"},"o11y.O11yServiceWarning":{"properties":{"topLevelOps":{"description":"TopLevelOps are the entry-point operations the profile was computed over.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yServicesIn":{"properties":{"end":{"description":"End is the window's end, epoch nanoseconds as a string.","type":"string"},"start":{"description":"Start is the window's start, epoch nanoseconds as a string.","type":"string"},"tags":{"description":"Tags narrow the spans counted, each a span-attribute predicate.","items":{"$ref":"#/components/schemas/o11y.O11yServiceTag"},"type":"array"}},"type":"object"},"o11y.O11yServicesMetadataOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableServicesMetadata","description":"Data holds the services metadata."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yServicesOut":{"properties":{"data":{"description":"Data holds one entry per service.","items":{"$ref":"#/components/schemas/o11y.O11yService"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySessionContext":{"properties":{"exists":{"description":"Exists says whether any account carries the address.","type":"boolean"},"orgs":{"description":"Orgs are the orgs the address belongs to, each with its sign-in routes.","items":{"$ref":"#/components/schemas/o11y.O11ySessionOrg"},"type":"array"}},"type":"object"},"o11y.O11ySessionContextOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11ySessionContext","description":"Data is the context."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySessionOrg":{"properties":{"authNSupport":{"$ref":"#/components/schemas/o11y.O11yAuthNSupport","description":"AuthNSupport lists the org's open sign-in routes."},"id":{"description":"ID is the org id.","type":"string"},"name":{"description":"Name is the org's display name.","type":"string"},"warning":{"$ref":"#/components/schemas/o11y.O11yErrorDetail","description":"Warning reports an org whose SSO is configured but not currently usable,\nin the platform's error shape."}},"type":"object"},"o11y.O11ySetRoleIn":{"properties":{"name":{"description":"Name is the role name to assign.","type":"string"}},"type":"object"},"o11y.O11ySignalFilters":{"properties":{"filters":{"description":"Filters are the attributes offered, in display order.","items":{"$ref":"#/components/schemas/o11y.O11yFilterKey"},"type":"array"},"signal":{"description":"Signal is the signal the filters belong to.","type":"string"}},"type":"object"},"o11y.O11ySignalFiltersOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11ySignalFilters","description":"Data is the signal's entry."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySpanMapperCreateIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.SpanMapperConfig"},"enabled":{"type":"boolean"},"fieldContext":{},"name":{"type":"string"}},"type":"object"},"o11y.O11ySpanMapperGroupOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.SpanMapperGroup","description":"Data is the group."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySpanMapperGroupUpdateIn":{"properties":{"condition":{"$ref":"#/components/schemas/o11y.SpanMapperGroupCondition"},"enabled":{"type":"boolean"},"name":{"type":"string"}},"type":"object"},"o11y.O11ySpanMapperGroupsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableSpanMapperGroups","description":"Data holds the groups."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySpanMapperOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.SpanMapper","description":"Data is the mapper."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySpanMapperUpdateIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.SpanMapperConfig"},"enabled":{"type":"boolean"},"fieldContext":{}},"type":"object"},"o11y.O11ySpanMappersOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableSpanMappers","description":"Data holds the mappers."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySpanPercentile":{"properties":{"percentiles":{"$ref":"#/components/schemas/o11y.O11yPercentiles","description":"Percentiles are the peer group's duration percentiles."},"position":{"$ref":"#/components/schemas/o11y.O11yPercentilePosition","description":"Position is where the given duration lands."}},"type":"object"},"o11y.O11ySpanPercentileIn":{"properties":{"end":{"description":"End is the window end, as epoch nanoseconds.","type":"integer"},"name":{"description":"Name is the span name whose peers are compared. Required.","type":"string"},"resourceAttributes":{"additionalProperties":{"type":"string"},"description":"ResourceAttributes narrow the peer group to spans carrying them all.","type":"object"},"serviceName":{"description":"ServiceName is the service the span belongs to. Required.","type":"string"},"spanDuration":{"description":"SpanDuration is the span's duration in nanoseconds.","type":"integer"},"start":{"description":"Start is the window start, as epoch nanoseconds.","type":"integer"}},"required":["name","serviceName"],"type":"object"},"o11y.O11ySpanPercentileOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11ySpanPercentile","description":"Data is the placement."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yStat":{"properties":{"time":{"description":"Time is the start of the bucket.","format":"date-time","type":"string"},"value":{"description":"Value is how many events fell in it.","type":"integer"}},"type":"object"},"o11y.O11yStatefulSetListOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.StatefulSetListResponse","description":"Data holds the statefulset records."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yStats":{"properties":{"items":{"description":"Items are the buckets, oldest first.","items":{"$ref":"#/components/schemas/o11y.O11yStat"},"type":"array"}},"type":"object"},"o11y.O11yStatsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yStats","description":"Data holds the buckets."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11ySubstituteVarsOut":{"properties":{"data":{"description":"Data is the resolved composite query, with its variables substituted."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTable":{"properties":{"columns":{"description":"Columns names each position in a row.","items":{"type":"string"},"type":"array"},"rows":{"description":"Rows are the result rows, each as long as Columns.","items":{"items":{"type":"object"},"type":"array"},"type":"array"}},"type":"object"},"o11y.O11yTagFilter":{"properties":{"boolValues":{"description":"BoolValues are the values matched when the tag holds booleans.","items":{"type":"boolean"},"type":"array"},"key":{"description":"Key is the tag to test.","type":"string"},"numberValues":{"description":"NumberValues are the values matched when the tag holds numbers.","items":{"type":"number"},"type":"array"},"operator":{"description":"Operator is the comparison to apply — in, not_in, equals, contains and\nthe other operators the trace filter grammar names.","type":"string"},"stringValues":{"description":"StringValues are the values matched when the tag holds strings.","items":{"type":"string"},"type":"array"},"tagType":{"description":"TagType says which kind of value the tag holds: string, number or bool.","type":"string"}},"type":"object"},"o11y.O11yTagQuery":{"properties":{"boolValues":{"description":"BoolValues are the boolean values to test against.","items":{"type":"boolean"},"type":"array"},"key":{"description":"Key is the tag to test.","type":"string"},"numberValues":{"description":"NumberValues are the numeric values to test against.","items":{"type":"number"},"type":"array"},"operator":{"description":"Operator is the comparison, e.g. in, nin, contains, exists.","type":"string"},"stringValues":{"description":"StringValues are the string values to test against.","items":{"type":"string"},"type":"array"},"tagType":{"description":"TagType is where the tag lives, e.g. ResourceAttribute, SpanAttribute.","type":"string"}},"type":"object"},"o11y.O11yTelemetryField":{"properties":{"dataType":{"description":"DataType is the field's data type, e.g. string, int64, float64, bool.","type":"string"},"name":{"description":"Name is the field's name.","type":"string"},"type":{"description":"Type is where the field lives: attributes or resources.","type":"string"}},"type":"object"},"o11y.O11yTestNotificationOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yTestNotificationResult","description":"Data holds the fired-series count and a status message."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTestNotificationResult":{"properties":{"alertCount":{"description":"AlertCount is how many series would alert for the tested rule.","type":"integer"},"message":{"description":"Message is a human-readable status, e.g. \"notification sent\".","type":"string"}},"type":"object"},"o11y.O11yTestRuleOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableTestRule","description":"Data holds how many series would alert."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yToggle":{"properties":{"enabled":{"description":"Enabled says whether the feature is on.","type":"boolean"}},"type":"object"},"o11y.O11yToken":{"properties":{"accessToken":{"description":"AccessToken authenticates requests until it expires.","type":"string"},"expiresIn":{"description":"ExpiresIn is the access token's lifetime in seconds.","type":"integer"},"refreshToken":{"description":"RefreshToken buys the next pair via rotateSession.","type":"string"},"tokenType":{"description":"TokenType is how to present the access token, e.g. bearer.","type":"string"}},"type":"object"},"o11y.O11yTokenOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yToken","description":"Data is the pair."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTopLevelOpsIn":{"properties":{"end":{"description":"End is the window's end, epoch nanoseconds as a string; empty means\nunbounded.","type":"string"},"service":{"description":"Service narrows the map to one service when set.","type":"string"},"start":{"description":"Start is the window's start, epoch nanoseconds as a string; empty means\nunbounded.","type":"string"}},"type":"object"},"o11y.O11yTrace":{"properties":{"count":{"description":"Count is how many captured errors carried it.","type":"integer"},"firstSeen":{"description":"FirstSeen is when the earliest of them was recorded.","format":"date-time","type":"string"},"lastSeen":{"description":"LastSeen is when the latest was.","format":"date-time","type":"string"},"message":{"description":"Message is the latest error message seen on the trace.","type":"string"},"traceId":{"description":"TraceID is the trace id.","type":"string"}},"type":"object"},"o11y.O11yTraceAggregationsIn":{"properties":{"aggregations":{"items":{"$ref":"#/components/schemas/o11y.SpanAggregation"},"type":"array"}},"type":"object"},"o11y.O11yTraceAggregationsOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableTraceAggregations","description":"Data holds one result per aggregation asked for."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTraceDetail":{"properties":{"events":{"description":"Events are the error events carrying the trace id.","items":{"$ref":"#/components/schemas/o11y.O11yEvent"},"type":"array"},"traceId":{"description":"TraceID is the trace that was read.","type":"string"}},"type":"object"},"o11y.O11yTraceFlamegraphIn":{"properties":{"selectFields":{"items":{"$ref":"#/components/schemas/o11y.TelemetryFieldKey"},"type":"array"},"selectedSpanId":{"type":"string"}},"type":"object"},"o11y.O11yTraceFlamegraphOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableFlamegraphTrace","description":"Data holds the flamegraph."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTraceOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yTraceDetail","description":"Data holds the trace and its events."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTraceSpanWindow":{"properties":{"columns":{"description":"Columns names the fields each row carries, in row order.","items":{"type":"string"},"type":"array"},"endTimestampMillis":{"description":"EndTimestampMillis is when it closes.","type":"integer"},"events":{"description":"Events are the rows, each positionally matching Columns.","items":{"items":{"type":"object"},"type":"array"},"type":"array"},"isSubTree":{"description":"IsSubTree says the window is a subtree of the trace rather than the whole\nof it.","type":"boolean"},"startTimestampMillis":{"description":"StartTimestampMillis is when the window opens.","type":"integer"}},"type":"object"},"o11y.O11yTraceWaterfallIn":{"properties":{"selectedSpanId":{"type":"string"},"uncollapsedSpans":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yTraceWaterfallOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.GettableWaterfallTrace","description":"Data holds the waterfall."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTraces":{"properties":{"items":{"description":"Items are the traces, most recent first.","items":{"$ref":"#/components/schemas/o11y.O11yTrace"},"type":"array"}},"type":"object"},"o11y.O11yTracesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yTraces","description":"Data holds the traces."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yTransaction":{"properties":{"object":{"$ref":"#/components/schemas/o11y.O11yObject","description":"Object is the resource the verb would act on."},"relation":{"description":"Relation is the verb being asked about, e.g. read, create, update, delete.","type":"string"}},"type":"object"},"o11y.O11yTransactionGroup":{"properties":{"objectGroup":{"$ref":"#/components/schemas/o11y.O11yObjectGroup","description":"ObjectGroup is the set of objects it allows the verb on."},"relation":{"description":"Relation is the verb the grant allows.","type":"string"}},"type":"object"},"o11y.O11yTransactionResult":{"properties":{"authorized":{"description":"Authorized says whether the caller may do it.","type":"boolean"},"object":{"$ref":"#/components/schemas/o11y.O11yObject","description":"Object is the resource it would act on."},"relation":{"description":"Relation is the verb that was asked about.","type":"string"}},"type":"object"},"o11y.O11yTreemapEntry":{"properties":{"metricName":{"description":"MetricName is the metric's name.","type":"string"},"percentage":{"description":"Percentage is the metric's share, in percent.","type":"number"},"totalValue":{"description":"TotalValue is the metric's absolute count.","type":"integer"}},"type":"object"},"o11y.O11yUpdatableAuthDomain":{"properties":{"config":{"$ref":"#/components/schemas/o11y.O11yAuthDomainConfig","description":"Config is the SSO configuration to store."}},"type":"object"},"o11y.O11yUpdatablePreference":{"properties":{"value":{"description":"Value is the value to set; its JSON type must match the preference's\ndeclared value type.","type":"object"}},"type":"object"},"o11y.O11yUpdatableQuickFilters":{"properties":{"filters":{"description":"Filters are the attributes to offer, in the order to offer them.","items":{"$ref":"#/components/schemas/o11y.O11yFilterKey"},"type":"array"},"signal":{"description":"Signal is the signal whose filters are being replaced.","type":"string"}},"type":"object"},"o11y.O11yUpdatableUser":{"properties":{"displayName":{"description":"DisplayName is the new display name.","type":"string"}},"type":"object"},"o11y.O11yUpdateAccountIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.UpdatableAccountConfig"}},"type":"object"},"o11y.O11yUpdateIngestionKeyIn":{"properties":{"expires_at":{"format":"date-time","type":"string"},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yUpdateLimitIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.LimitConfig"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.O11yUpdateServiceIn":{"properties":{"config":{"$ref":"#/components/schemas/o11y.ServiceConfig"}},"type":"object"},"o11y.O11yUsageItem":{"properties":{"count":{"description":"Count is how many spans were ingested in the bucket.","type":"integer"},"time":{"description":"Time is the bucket start.","format":"date-time","type":"string"},"timestamp":{"description":"Timestamp is the bucket start, as epoch nanoseconds.","type":"integer"}},"type":"object"},"o11y.O11yUser":{"properties":{"createdAt":{"description":"CreatedAt is when they joined.","format":"date-time","type":"string"},"displayName":{"description":"DisplayName is what the console shows for them.","type":"string"},"email":{"description":"Email is their address.","type":"string"},"id":{"description":"ID is the user id.","type":"string"},"isRoot":{"description":"IsRoot marks the org's root user, which cannot be deleted or demoted.","type":"boolean"},"orgId":{"description":"OrgID is the org they belong to.","type":"string"},"status":{"description":"Status is their lifecycle state — active, pending_invite or deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when their record last changed.","format":"date-time","type":"string"}},"type":"object"},"o11y.O11yUserRole":{"properties":{"createdAt":{"description":"CreatedAt is when it was assigned.","format":"date-time","type":"string"},"id":{"description":"ID is the assignment's own id.","type":"string"},"role":{"$ref":"#/components/schemas/o11y.O11yRole","description":"Role is the role itself."},"roleId":{"description":"RoleID is the role held.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the assignment last changed.","format":"date-time","type":"string"},"userId":{"description":"UserID is the user holding the role.","type":"string"}},"type":"object"},"o11y.O11yUserUpdate":{"properties":{"displayName":{"description":"DisplayName is the new display name.","type":"string"}},"type":"object"},"o11y.O11yUserWithRoles":{"properties":{"createdAt":{"description":"CreatedAt is when they joined.","format":"date-time","type":"string"},"displayName":{"description":"DisplayName is what the console shows for them.","type":"string"},"email":{"description":"Email is their address.","type":"string"},"id":{"description":"ID is the user id.","type":"string"},"isRoot":{"description":"IsRoot marks the org's root user.","type":"boolean"},"orgId":{"description":"OrgID is the org they belong to.","type":"string"},"status":{"description":"Status is their lifecycle state — active, pending_invite or deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when their record last changed.","format":"date-time","type":"string"},"userRoles":{"description":"UserRoles are their role assignments.","items":{"$ref":"#/components/schemas/o11y.O11yUserRole"},"type":"array"}},"type":"object"},"o11y.O11yUserWithRolesOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yUserWithRoles","description":"Data is the member."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yUsersOut":{"properties":{"data":{"description":"Data holds the members.","items":{"$ref":"#/components/schemas/o11y.O11yUser"},"type":"array"},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.O11yVersionOut":{"properties":{"ee":{"description":"EE says whether an enterprise edition is present; \"N\" in this build.","type":"string"},"setupCompleted":{"description":"SetupCompleted says whether the first user has been created.","type":"boolean"},"version":{"description":"Version is the build version.","type":"string"}},"type":"object"},"o11y.O11yWidgetQueryRange":{"properties":{"data":{"description":"Data is the query result payload, carried verbatim as open JSON."},"meta":{"description":"Meta is the execution stats, carried verbatim as open JSON."},"type":{"description":"Type is the request type the result answers, e.g. time_series, scalar.","type":"string"},"warning":{"description":"Warning is any query warning, carried verbatim as open JSON when present."}},"type":"object"},"o11y.O11yWidgetQueryRangeOut":{"properties":{"data":{"$ref":"#/components/schemas/o11y.O11yWidgetQueryRange","description":"Data is the query-range result."},"status":{"description":"Status is \"success\".","type":"string"}},"type":"object"},"o11y.OAuth2":{"properties":{"TLSConfig":{"$ref":"#/components/schemas/o11y.TLSConfig"},"audience":{"description":"Audience optionally specifies the intended audience of the\nrequest.  If empty, the value of TokenURL is used as the\nintended audience. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".","type":"string"},"claims":{"additionalProperties":{"type":"object"},"description":"Claims is a map of claims to be added to the JWT token. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".","type":"object"},"client_certificate_key":{},"client_certificate_key_file":{"type":"string"},"client_certificate_key_id":{"type":"string"},"client_certificate_key_ref":{"description":"ClientCertificateKeyRef is the name of the secret within the secret manager to use as the client\nsecret.","type":"string"},"client_id":{"type":"string"},"client_secret":{},"client_secret_file":{"type":"string"},"client_secret_ref":{"description":"ClientSecretRef is the name of the secret within the secret manager to use as the client\nsecret.","type":"string"},"endpoint_params":{"additionalProperties":{"type":"string"},"type":"object"},"grant_type":{"description":"GrantType is the OAuth2 grant type to use. It can be one of\n\"client_credentials\" or \"urn:ietf:params:oauth:grant-type:jwt-bearer\" (RFC 7523).\nDefault value is \"client_credentials\"","type":"string"},"iss":{"description":"Iss is the OAuth client identifier used when communicating with\nthe configured OAuth provider. Default value is client_id. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".","type":"string"},"no_proxy":{"type":"string"},"proxy_connect_header":{"additionalProperties":{"items":{},"type":"array"},"type":"object"},"proxy_from_environment":{"type":"boolean"},"proxy_url":{},"scopes":{"items":{"type":"string"},"type":"array"},"signature_algorithm":{"description":"SignatureAlgorithm is the RSA algorithm used to sign JWT token. Only used if\nGrantType is set to \"urn:ietf:params:oauth:grant-type:jwt-bearer\".\nDefault value is RS256 and valid values RS256, RS384, RS512","type":"string"},"token_url":{"type":"string"}},"type":"object"},"o11y.OldAWSCollectionStrategy":{"properties":{"aws_logs":{"$ref":"#/components/schemas/o11y.OldAWSLogsStrategy"},"aws_metrics":{"$ref":"#/components/schemas/o11y.OldAWSMetricsStrategy"},"provider":{"type":"string"},"s3_buckets":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}},"type":"object"},"o11y.OldAWSLogsStrategy":{"properties":{"cloudwatch_logs_subscriptions":{"items":{"properties":{"filter_pattern":{"type":"string"},"log_group_name_prefix":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"o11y.OldAWSMetricsStrategy":{"properties":{"cloudwatch_metric_stream_filters":{"items":{"properties":{"MetricNames":{"items":{"type":"string"},"type":"array"},"Namespace":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"o11y.OpsGenieConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"actions":{"type":"string"},"api_key":{},"api_key_file":{"type":"string"},"api_url":{},"description":{"type":"string"},"details":{"additionalProperties":{"type":"string"},"type":"object"},"entity":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"note":{"type":"string"},"priority":{"type":"string"},"responders":{"items":{"$ref":"#/components/schemas/o11y.OpsGenieConfigResponder"},"type":"array"},"source":{"type":"string"},"tags":{"type":"string"},"update_alerts":{"type":"boolean"}},"type":"object"},"o11y.OpsGenieConfigResponder":{"properties":{"id":{"description":"One of those 3 should be filled.","type":"string"},"name":{"type":"string"},"type":{"description":"team, user, escalation, schedule etc.","type":"string"},"username":{"type":"string"}},"type":"object"},"o11y.OrderBy":{"properties":{"columnName":{"type":"string"},"order":{"type":"string"}},"type":"object"},"o11y.OrderByKey":{"properties":{"description":{"type":"string"},"fieldContext":{},"fieldDataType":{},"name":{"type":"string"},"signal":{},"unit":{"type":"string"}},"required":["name"],"type":"object"},"o11y.OtelSpanRef":{"properties":{"refType":{"type":"string"},"spanId":{"type":"string"},"traceId":{"type":"string"}},"type":"object"},"o11y.PagerdutyConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"class":{"type":"string"},"client":{"type":"string"},"client_url":{"type":"string"},"component":{"type":"string"},"description":{"type":"string"},"details":{"additionalProperties":{"type":"object"},"type":"object"},"group":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"images":{"items":{"$ref":"#/components/schemas/o11y.PagerdutyImage"},"type":"array"},"links":{"items":{"$ref":"#/components/schemas/o11y.PagerdutyLink"},"type":"array"},"routing_key":{},"routing_key_file":{"type":"string"},"service_key":{},"service_key_file":{"type":"string"},"severity":{"type":"string"},"source":{"type":"string"},"timeout":{"description":"Timeout is the maximum time allowed to invoke the pagerduty. Setting this to 0\ndoes not impose a timeout.","type":"integer"},"url":{}},"type":"object"},"o11y.PagerdutyImage":{"properties":{"alt":{"type":"string"},"href":{"type":"string"},"src":{"type":"string"}},"type":"object"},"o11y.PagerdutyLink":{"properties":{"href":{"type":"string"},"text":{"type":"string"}},"type":"object"},"o11y.Pagination":{"properties":{"page":{"type":"integer"},"pages":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}},"type":"object"},"o11y.PodCountByPhase":{"properties":{"failed":{"type":"integer"},"pending":{"type":"integer"},"running":{"type":"integer"},"succeeded":{"type":"integer"},"unknown":{"type":"integer"}},"type":"object"},"o11y.PodCountsByPhase":{"properties":{"failed":{"type":"integer"},"pending":{"type":"integer"},"running":{"type":"integer"},"succeeded":{"type":"integer"},"unknown":{"type":"integer"}},"type":"object"},"o11y.PodListRecord":{"properties":{"countByPhase":{"$ref":"#/components/schemas/o11y.PodCountByPhase"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podCPU":{"type":"number"},"podCPULimit":{"type":"number"},"podCPURequest":{"type":"number"},"podMemory":{"type":"number"},"podMemoryLimit":{"type":"number"},"podMemoryRequest":{"type":"number"},"podUID":{"type":"string"},"restartCount":{"type":"integer"}},"type":"object"},"o11y.PodListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.PodListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.PodListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.PodRecord":{"properties":{"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podAge":{"type":"integer"},"podCPU":{"type":"number"},"podCPULimit":{"type":"number"},"podCPURequest":{"type":"number"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"},"podMemory":{"type":"number"},"podMemoryLimit":{"type":"number"},"podMemoryRequest":{"type":"number"},"podPhase":{},"podUID":{"type":"string"}},"type":"object"},"o11y.Pods":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.PodRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.PostableAccountConfig":{"properties":{"AgentVersion":{"description":"as agent version is common for all providers, we can keep it at top level of this struct","type":"string"},"aws":{"$ref":"#/components/schemas/o11y.AWSPostableAccountConfig"},"azure":{"$ref":"#/components/schemas/o11y.AzureAccountConfig"},"gcp":{"$ref":"#/components/schemas/o11y.GCPAccountConfig"}},"type":"object"},"o11y.PostableChannel":{"properties":{"Receiver":{"$ref":"#/components/schemas/o11y.Receiver"},"googlechat_configs":{"items":{"$ref":"#/components/schemas/o11y.GoogleChatReceiverConfig"},"type":"array"}},"type":"object"},"o11y.PostableClusters":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableDaemonSets":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableDeployments":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableHost":{"properties":{"name":{"type":"string"}},"type":"object"},"o11y.PostableHosts":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.HostFilter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableIngestionKey":{"properties":{"expires_at":{"format":"date-time","type":"string"},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.PostableJobs":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableNamespaces":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableNodes":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostablePipeline":{"properties":{"alias":{"type":"string"},"config":{"items":{},"type":"array"},"description":{"type":"string"},"enabled":{"type":"boolean"},"filter":{"$ref":"#/components/schemas/o11y.FilterSet"},"id":{"type":"string"},"name":{"type":"string"},"orderId":{"type":"integer"}},"type":"object"},"o11y.PostablePlannedMaintenance":{"properties":{"alertIds":{"items":{"type":"string"},"type":"array"},"description":{"type":"string"},"name":{"type":"string"},"schedule":{},"scope":{"type":"string"}},"type":"object"},"o11y.PostablePods":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableProfile":{"properties":{"existing_observability_tool":{"type":"string"},"has_existing_observability_tool":{"type":"boolean"},"logs_scale_per_day_in_gb":{"type":"integer"},"number_of_hosts":{"type":"integer"},"number_of_services":{"type":"integer"},"reasons_for_interest_in_o11y":{"items":{"type":"string"},"type":"array"},"timeline_for_migrating_to_o11y":{"type":"string"},"uses_otel":{"type":"boolean"},"where_did_you_discover_o11y":{"type":"string"}},"type":"object"},"o11y.PostableRoutePolicy":{"properties":{"channels":{"items":{"type":"string"},"type":"array"},"description":{"type":"string"},"expression":{"type":"string"},"kind":{},"name":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.PostableSpanMapperGroup":{"properties":{"condition":{"$ref":"#/components/schemas/o11y.SpanMapperGroupCondition"},"enabled":{"type":"boolean"},"name":{"type":"string"}},"type":"object"},"o11y.PostableStatefulSets":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.PostableVolumes":{"properties":{"end":{"type":"integer"},"filter":{"$ref":"#/components/schemas/o11y.Filter"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.GroupByKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.OrderBy"},"start":{"type":"integer"}},"type":"object"},"o11y.ProcessListRecord":{"properties":{"meta":{"additionalProperties":{"type":"string"},"type":"object"},"processCMD":{"type":"string"},"processCMDLine":{"type":"string"},"processCPU":{"type":"number"},"processID":{"type":"string"},"processMemory":{"type":"number"},"processName":{"type":"string"}},"type":"object"},"o11y.ProcessListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.ProcessListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.ProcessListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.PromQuery":{"properties":{"disabled":{"type":"boolean"},"legend":{"type":"string"},"query":{"type":"string"},"stats":{"type":"string"}},"type":"object"},"o11y.ProviderIntegrationConfig":{"properties":{"aws":{"$ref":"#/components/schemas/o11y.AWSIntegrationConfig"},"azure":{"$ref":"#/components/schemas/o11y.AzureIntegrationConfig"},"gcp":{"$ref":"#/components/schemas/o11y.GCPIntegrationConfig"}},"type":"object"},"o11y.PushoverConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"device":{"type":"string"},"expire":{"type":"integer"},"html":{"type":"boolean"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"monospace":{"type":"boolean"},"priority":{"type":"string"},"retry":{"type":"integer"},"sound":{"type":"string"},"title":{"type":"string"},"token":{},"token_file":{"type":"string"},"ttl":{"type":"integer"},"url":{"type":"string"},"url_title":{"type":"string"},"user_key":{},"user_key_file":{"type":"string"}},"type":"object"},"o11y.QueryEnvelope":{"properties":{"spec":{"description":"Spec is the deferred decoding of the query if any.","type":"object"},"type":{"description":"Type is the type of the query."}},"type":"object"},"o11y.QueryRangeParamsV3":{"properties":{"compositeQuery":{"$ref":"#/components/schemas/o11y.CompositeQuery"},"end":{"type":"integer"},"formatForWeb":{"type":"boolean"},"noCache":{"type":"boolean"},"start":{"type":"integer"},"step":{"description":"step is in seconds; used for prometheus queries","type":"integer"},"variables":{"additionalProperties":{"type":"object"},"type":"object"}},"type":"object"},"o11y.QueryRangeRequest":{"properties":{"compositeQuery":{"$ref":"#/components/schemas/o11y.querybuildertypesv5.CompositeQuery","description":"CompositeQuery is the composite query to use for the request."},"end":{"description":"End is the end time of the query in epoch milliseconds.","type":"integer"},"formatOptions":{"$ref":"#/components/schemas/o11y.FormatOptions"},"noCache":{"description":"NoCache is a flag to disable caching for the request.","type":"boolean"},"requestType":{"description":"RequestType is the type of the request."},"schemaVersion":{"description":"SchemaVersion is the version of the schema to use for the request payload.","type":"string"},"start":{"description":"Start is the start time of the query in epoch milliseconds.","type":"integer"},"variables":{"additionalProperties":{"$ref":"#/components/schemas/o11y.VariableItem"},"description":"Variables is the variables to use for the request.","type":"object"}},"type":"object"},"o11y.QueryWarnData":{"properties":{"message":{"type":"string"},"url":{"type":"string"},"warnings":{"items":{"$ref":"#/components/schemas/o11y.QueryWarnDataAdditional"},"type":"array"}},"type":"object"},"o11y.QueryWarnDataAdditional":{"properties":{"message":{"type":"string"}},"type":"object"},"o11y.Receiver":{"properties":{"discord_configs":{"items":{"$ref":"#/components/schemas/o11y.DiscordConfig"},"type":"array"},"email_configs":{"items":{"$ref":"#/components/schemas/o11y.EmailConfig"},"type":"array"},"incidentio_configs":{"items":{"$ref":"#/components/schemas/o11y.IncidentioConfig"},"type":"array"},"jira_configs":{"items":{"$ref":"#/components/schemas/o11y.JiraConfig"},"type":"array"},"mattermost_configs":{"items":{"$ref":"#/components/schemas/o11y.MattermostConfig"},"type":"array"},"msteams_configs":{"items":{"$ref":"#/components/schemas/o11y.MSTeamsConfig"},"type":"array"},"msteamsv2_configs":{"items":{"$ref":"#/components/schemas/o11y.MSTeamsV2Config"},"type":"array"},"name":{"description":"A unique identifier for this receiver.","type":"string"},"opsgenie_configs":{"items":{"$ref":"#/components/schemas/o11y.OpsGenieConfig"},"type":"array"},"pagerduty_configs":{"items":{"$ref":"#/components/schemas/o11y.PagerdutyConfig"},"type":"array"},"pushover_configs":{"items":{"$ref":"#/components/schemas/o11y.PushoverConfig"},"type":"array"},"rocketchat_configs":{"items":{"$ref":"#/components/schemas/o11y.RocketchatConfig"},"type":"array"},"slack_configs":{"items":{"$ref":"#/components/schemas/o11y.SlackConfig"},"type":"array"},"sns_configs":{"items":{"$ref":"#/components/schemas/o11y.SNSConfig"},"type":"array"},"telegram_configs":{"items":{"$ref":"#/components/schemas/o11y.TelegramConfig"},"type":"array"},"victorops_configs":{"items":{"$ref":"#/components/schemas/o11y.VictorOpsConfig"},"type":"array"},"webex_configs":{"items":{"$ref":"#/components/schemas/o11y.WebexConfig"},"type":"array"},"webhook_configs":{"items":{"$ref":"#/components/schemas/o11y.WebhookConfig"},"type":"array"},"wechat_configs":{"items":{"$ref":"#/components/schemas/o11y.WechatConfig"},"type":"array"}},"type":"object"},"o11y.ReleStateItem":{"properties":{"end":{"type":"integer"},"start":{"type":"integer"},"state":{}},"type":"object"},"o11y.RocketchatAttachmentAction":{"properties":{"image_url":{"type":"string"},"is_webview":{"type":"boolean"},"msg":{"type":"string"},"msg_in_chat_window":{"type":"boolean"},"msg_processing_type":{"type":"string"},"text":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"},"webview_height_ratio":{"type":"string"}},"type":"object"},"o11y.RocketchatAttachmentField":{"properties":{"short":{"type":"boolean"},"title":{"type":"string"},"value":{"type":"string"}},"type":"object"},"o11y.RocketchatConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"actions":{"items":{"$ref":"#/components/schemas/o11y.RocketchatAttachmentAction"},"type":"array"},"api_url":{},"channel":{"description":"RocketChat channel override, (like #other-channel or @username).","type":"string"},"color":{"type":"string"},"emoji":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/o11y.RocketchatAttachmentField"},"type":"array"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"icon_url":{"type":"string"},"image_url":{"type":"string"},"link_names":{"type":"boolean"},"short_fields":{"type":"boolean"},"text":{"type":"string"},"thumb_url":{"type":"string"},"title":{"type":"string"},"title_link":{"type":"string"},"token":{},"token_file":{"type":"string"},"token_id":{},"token_id_file":{"type":"string"}},"type":"object"},"o11y.RuleStateHistory":{"properties":{"fingerprint":{"type":"integer"},"labels":{},"overallState":{"description":"One of [\"normal\", \"firing\"]"},"overallStateChanged":{"type":"boolean"},"relatedLogsLink":{"type":"string"},"relatedTracesLink":{"type":"string"},"ruleID":{"type":"string"},"ruleName":{"type":"string"},"state":{"description":"One of [\"normal\", \"firing\", \"nodata\", \"muted\"]"},"stateChanged":{"type":"boolean"},"unixMilli":{"type":"integer"},"value":{"type":"number"}},"type":"object"},"o11y.RuleStateHistoryContributor":{"properties":{"count":{"type":"integer"},"fingerprint":{"type":"integer"},"labels":{},"relatedLogsLink":{"type":"string"},"relatedTracesLink":{"type":"string"}},"type":"object"},"o11y.RuleStateTimeline":{"properties":{"items":{"items":{"$ref":"#/components/schemas/o11y.RuleStateHistory"},"type":"array"},"labels":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"total":{"type":"integer"}},"type":"object"},"o11y.SNSConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"api_url":{"type":"string"},"attributes":{"additionalProperties":{"type":"string"},"type":"object"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"phone_number":{"type":"string"},"sigv4":{"$ref":"#/components/schemas/o11y.SigV4Config"},"subject":{"type":"string"},"target_arn":{"type":"string"},"topic_arn":{"type":"string"}},"type":"object"},"o11y.SavedView":{"properties":{"category":{"type":"string"},"compositeQuery":{"$ref":"#/components/schemas/o11y.CompositeQuery"},"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"extraData":{"description":"ExtraData is JSON encoded data used by frontend to store additional data","type":"string"},"id":{},"name":{"type":"string"},"sourcePage":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"}},"type":"object"},"o11y.Series":{"properties":{"labels":{"additionalProperties":{"type":"string"},"type":"object"},"labelsArray":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array"},"values":{"items":{},"type":"array"}},"type":"object"},"o11y.Service":{"properties":{"assets":{"$ref":"#/components/schemas/o11y.ServiceAssets"},"cloudIntegrationService":{"$ref":"#/components/schemas/o11y.CloudIntegrationService"},"dataCollected":{"$ref":"#/components/schemas/o11y.DataCollected"},"icon":{"type":"string"},"id":{"type":"string"},"overview":{"description":"markdown","type":"string"},"supportedSignals":{"$ref":"#/components/schemas/o11y.SupportedSignals"},"title":{"type":"string"}},"type":"object"},"o11y.ServiceAssets":{"properties":{"dashboards":{"items":{"$ref":"#/components/schemas/o11y.ServiceDashboard"},"type":"array"}},"type":"object"},"o11y.ServiceConfig":{"properties":{"aws":{"$ref":"#/components/schemas/o11y.AWSServiceConfig"},"azure":{"$ref":"#/components/schemas/o11y.AzureServiceConfig"},"gcp":{"$ref":"#/components/schemas/o11y.GCPServiceConfig"}},"type":"object"},"o11y.ServiceDashboard":{"properties":{"description":{"type":"string"},"integrationDashboard":{"$ref":"#/components/schemas/o11y.StorableIntegrationDashboard"},"title":{"type":"string"}},"type":"object"},"o11y.ServiceMetadata":{"properties":{"enabled":{"description":"if the service is enabled for the account","type":"boolean"},"icon":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}},"type":"object"},"o11y.SigV4Config":{"properties":{"AccessKey":{"type":"string"},"ExternalID":{"type":"string"},"Profile":{"type":"string"},"Region":{"type":"string"},"RoleARN":{"type":"string"},"SecretKey":{},"ServiceName":{"type":"string"},"UseFIPSSTSEndpoint":{"type":"boolean"}},"type":"object"},"o11y.SignalConnectionStatus":{"properties":{"last_received_from":{"description":"resource identifier","type":"string"},"last_received_ts_ms":{"description":"epoch milliseconds","type":"integer"}},"type":"object"},"o11y.SlackAction":{"properties":{"confirm":{"$ref":"#/components/schemas/o11y.SlackConfirmationField"},"name":{"type":"string"},"style":{"type":"string"},"text":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"},"value":{"type":"string"}},"type":"object"},"o11y.SlackConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"actions":{"items":{"$ref":"#/components/schemas/o11y.SlackAction"},"type":"array"},"api_url":{},"api_url_file":{"type":"string"},"app_token":{},"app_token_file":{"type":"string"},"app_url":{},"callback_id":{"type":"string"},"channel":{"description":"Slack channel override, (like #other-channel or @username).","type":"string"},"color":{"type":"string"},"fallback":{"type":"string"},"fields":{"items":{"$ref":"#/components/schemas/o11y.SlackField"},"type":"array"},"footer":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"icon_emoji":{"type":"string"},"icon_url":{"type":"string"},"image_url":{"type":"string"},"link_names":{"type":"boolean"},"message_text":{"type":"string"},"mrkdwn_in":{"items":{"type":"string"},"type":"array"},"pretext":{"type":"string"},"short_fields":{"type":"boolean"},"text":{"type":"string"},"thumb_url":{"type":"string"},"timeout":{"description":"Timeout is the maximum time allowed to invoke the slack. Setting this to 0\ndoes not impose a timeout.","type":"integer"},"title":{"type":"string"},"title_link":{"type":"string"},"username":{"type":"string"}},"type":"object"},"o11y.SlackConfirmationField":{"properties":{"dismiss_text":{"type":"string"},"ok_text":{"type":"string"},"text":{"type":"string"},"title":{"type":"string"}},"type":"object"},"o11y.SlackField":{"properties":{"short":{"type":"boolean"},"title":{"type":"string"},"value":{"type":"string"}},"type":"object"},"o11y.SpanAggregation":{"properties":{"aggregation":{},"field":{"$ref":"#/components/schemas/o11y.TelemetryFieldKey"}},"type":"object"},"o11y.SpanAggregationResult":{"properties":{"aggregation":{},"field":{"$ref":"#/components/schemas/o11y.TelemetryFieldKey"},"value":{"additionalProperties":{"type":"integer"},"type":"object"}},"type":"object"},"o11y.SpanMapper":{"properties":{"config":{"$ref":"#/components/schemas/o11y.SpanMapperConfig"},"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"enabled":{"type":"boolean"},"fieldContext":{},"group_id":{},"id":{},"name":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"}},"type":"object"},"o11y.SpanMapperConfig":{"properties":{"sources":{"items":{"$ref":"#/components/schemas/o11y.SpanMapperSource"},"type":"array"}},"type":"object"},"o11y.SpanMapperGroup":{"properties":{"condition":{"$ref":"#/components/schemas/o11y.SpanMapperGroupCondition"},"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"enabled":{"type":"boolean"},"id":{},"name":{"type":"string"},"orgId":{},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"}},"type":"object"},"o11y.SpanMapperGroupCondition":{"properties":{"attributes":{"items":{"type":"string"},"type":"array"},"resource":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.SpanMapperSource":{"properties":{"context":{},"key":{"type":"string"},"operation":{},"priority":{"type":"integer"}},"type":"object"},"o11y.StatefulSetListRecord":{"properties":{"availablePods":{"type":"integer"},"cpuLimit":{"type":"number"},"cpuRequest":{"type":"number"},"cpuUsage":{"type":"number"},"desiredPods":{"type":"integer"},"memoryLimit":{"type":"number"},"memoryRequest":{"type":"number"},"memoryUsage":{"type":"number"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"restarts":{"type":"integer"},"statefulSetName":{"type":"string"}},"type":"object"},"o11y.StatefulSetListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.StatefulSetListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.StatefulSetListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.StatefulSetRecord":{"properties":{"currentPods":{"type":"integer"},"desiredPods":{"type":"integer"},"meta":{"additionalProperties":{"type":"string"},"type":"object"},"podCountsByPhase":{"$ref":"#/components/schemas/o11y.PodCountsByPhase"},"statefulSetCPU":{"type":"number"},"statefulSetCPULimit":{"type":"number"},"statefulSetCPURequest":{"type":"number"},"statefulSetMemory":{"type":"number"},"statefulSetMemoryLimit":{"type":"number"},"statefulSetMemoryRequest":{"type":"number"},"statefulSetName":{"type":"string"}},"type":"object"},"o11y.StatefulSets":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.StatefulSetRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.Stats":{"properties":{"currentAvgResolutionTime":{"type":"string"},"currentAvgResolutionTimeSeries":{"$ref":"#/components/schemas/o11y.Series"},"currentTriggersSeries":{"$ref":"#/components/schemas/o11y.Series"},"pastAvgResolutionTime":{"type":"string"},"pastAvgResolutionTimeSeries":{"$ref":"#/components/schemas/o11y.Series"},"pastTriggersSeries":{"$ref":"#/components/schemas/o11y.Series"},"totalCurrentTriggers":{"type":"integer"},"totalPastTriggers":{"type":"integer"}},"type":"object"},"o11y.StatusComponent":{"properties":{"current_status":{"description":"CurrentStatus is this component's own condition: \"full_outage\" for a\nservice that did not answer its health probe at all.","type":"string"},"id":{"type":"string"},"name":{"type":"string"}},"type":"object"},"o11y.StatusIncident":{"properties":{"affected_components":{"items":{"$ref":"#/components/schemas/o11y.StatusComponent"},"type":"array"},"current_worst_impact":{"description":"CurrentWorstImpact is the incident's impact on the PLATFORM, which is not\nthe same question as the component's own condition above.","type":"string"},"id":{"type":"string"},"last_update_at":{"description":"LastUpdateAt is when the failing measurement this incident reports was\nread, RFC3339 UTC.","type":"string"},"last_update_message":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"url":{"type":"string"}},"type":"object"},"o11y.StatusMaintenance":{"properties":{"affected_components":{"items":{"$ref":"#/components/schemas/o11y.StatusComponent"},"type":"array"},"ends_at":{"type":"string"},"id":{"type":"string"},"last_update_at":{"type":"string"},"last_update_message":{"type":"string"},"name":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string"},"url":{"type":"string"}},"type":"object"},"o11y.StatusSummary":{"properties":{"checked_at":{"description":"CheckedAt is when the underlying availability read was taken, RFC3339 UTC.\nNot part of the status-page schema the panel parses (which ignores unknown\nfields); it is here because a status document with no timestamp cannot be\ntold apart from a stale one.","type":"string"},"in_progress_maintenances":{"items":{"$ref":"#/components/schemas/o11y.StatusMaintenance"},"type":"array"},"ongoing_incidents":{"items":{"$ref":"#/components/schemas/o11y.StatusIncident"},"type":"array"},"page_title":{"type":"string"},"page_url":{"description":"PageURL is the HUMAN status page — an HTML page for people, distinct from\nthis JSON endpoint. Every link in this document points there.","type":"string"},"scheduled_maintenances":{"items":{"$ref":"#/components/schemas/o11y.StatusMaintenance"},"type":"array"}},"type":"object"},"o11y.StorableFunnel":{"properties":{"createdAt":{"format":"date-time","type":"string"},"createdBy":{"type":"string"},"description":{"type":"string"},"funnel_name":{"type":"string"},"id":{},"org_id":{},"steps":{"items":{"$ref":"#/components/schemas/o11y.FunnelStep"},"type":"array"},"tags":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"},"updatedBy":{"type":"string"},"user":{"$ref":"#/components/schemas/o11y.User"}},"type":"object"},"o11y.StorableIntegrationDashboard":{"properties":{"createdAt":{"format":"date-time","type":"string"},"dashboardId":{"type":"string"},"id":{"type":"string"},"provider":{},"slug":{"type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.SupportedSignals":{"properties":{"logs":{"type":"boolean"},"metrics":{"type":"boolean"}},"type":"object"},"o11y.TLSConfig":{"properties":{"ca":{"description":"Text of the CA cert to use for the targets.","type":"string"},"ca_file":{"description":"The CA cert to use for the targets.","type":"string"},"ca_ref":{"description":"CARef is the name of the secret within the secret manager to use as the CA cert for the\ntargets.","type":"string"},"cert":{"description":"Text of the client cert file for the targets.","type":"string"},"cert_file":{"description":"The client cert file for the targets.","type":"string"},"cert_ref":{"description":"CertRef is the name of the secret within the secret manager to use as the client cert for\nthe targets.","type":"string"},"insecure_skip_verify":{"description":"Disable target certificate validation.","type":"boolean"},"key":{"description":"Text of the client key file for the targets."},"key_file":{"description":"The client key file for the targets.","type":"string"},"key_ref":{"description":"KeyRef is the name of the secret within the secret manager to use as the client key for\nthe targets.","type":"string"},"max_version":{"description":"Maximum TLS version."},"min_version":{"description":"Minimum TLS version."},"server_name":{"description":"Used to verify the hostname for the targets.","type":"string"}},"type":"object"},"o11y.TelegramConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"api_url":{},"chat":{"type":"integer"},"chat_file":{"type":"string"},"disable_notifications":{"type":"boolean"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"message_thread_id":{"type":"integer"},"parse_mode":{"type":"string"},"token":{},"token_file":{"type":"string"}},"type":"object"},"o11y.TelemetryFieldKey":{"properties":{"description":{"type":"string"},"fieldContext":{},"fieldDataType":{},"name":{"type":"string"},"signal":{},"unit":{"type":"string"}},"required":["name"],"type":"object"},"o11y.TelemetryFieldValues":{"properties":{"boolValues":{"items":{"type":"boolean"},"type":"array"},"numberValues":{"items":{"type":"number"},"type":"array"},"relatedValues":{"items":{"type":"string"},"type":"array"},"stringValues":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.ThreadingConfig":{"properties":{"enabled":{"type":"boolean"},"thread_by_date":{"type":"string"}},"type":"object"},"o11y.TimeSeries":{"properties":{"labels":{"items":{"$ref":"#/components/schemas/o11y.Label"},"type":"array"},"values":{"items":{},"type":"array"}},"type":"object"},"o11y.UninstallIntegrationRequest":{"properties":{"integration_id":{"type":"string"}},"type":"object"},"o11y.UpdatableAccountConfig":{"properties":{"aws":{"$ref":"#/components/schemas/o11y.AWSAccountConfig"},"azure":{"$ref":"#/components/schemas/o11y.UpdatableAzureAccountConfig"},"gcp":{"$ref":"#/components/schemas/o11y.UpdatableGCPAccountConfig"}},"type":"object"},"o11y.UpdatableAzureAccountConfig":{"properties":{"resourceGroups":{"items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.UpdatableGCPAccountConfig":{"properties":{"deploymentProjectId":{"description":"Project ID where central pub/sub for logs exist","type":"string"},"deploymentRegion":{"description":"Compute service region where otel collector will be deployed","type":"string"},"projectIds":{"description":"List of project IDs to monitor","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.User":{"properties":{"createdAt":{"format":"date-time","type":"string"},"displayName":{"type":"string"},"email":{},"id":{},"isRoot":{"type":"boolean"},"orgId":{},"status":{},"updatedAt":{"format":"date-time","type":"string"}},"type":"object"},"o11y.VariableItem":{"properties":{"type":{},"value":{"type":"object"}},"type":"object"},"o11y.VictorOpsConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"api_key":{},"api_key_file":{"type":"string"},"api_url":{},"custom_fields":{"additionalProperties":{"type":"string"},"type":"object"},"entity_display_name":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message_type":{"type":"string"},"monitoring_tool":{"type":"string"},"routing_key":{"type":"string"},"state_message":{"type":"string"}},"type":"object"},"o11y.VolumeListRecord":{"properties":{"meta":{"additionalProperties":{"type":"string"},"type":"object"},"persistentVolumeClaimName":{"type":"string"},"volumeAvailable":{"type":"number"},"volumeCapacity":{"type":"number"},"volumeInodes":{"type":"number"},"volumeInodesFree":{"type":"number"},"volumeInodesUsed":{"type":"number"},"volumeUsage":{"type":"number"}},"type":"object"},"o11y.VolumeListRequest":{"properties":{"end":{"description":"epoch time in ms","type":"integer"},"filters":{"$ref":"#/components/schemas/o11y.FilterSet"},"groupBy":{"items":{"$ref":"#/components/schemas/o11y.AttributeKey"},"type":"array"},"limit":{"type":"integer"},"offset":{"type":"integer"},"orderBy":{"$ref":"#/components/schemas/o11y.OrderBy"},"start":{"description":"epoch time in ms","type":"integer"}},"type":"object"},"o11y.VolumeListResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/o11y.VolumeListRecord"},"type":"array"},"total":{"type":"integer"},"type":{"type":"string"}},"type":"object"},"o11y.VolumeRecord":{"properties":{"meta":{"additionalProperties":{"type":"string"},"type":"object"},"persistentVolumeClaimName":{"type":"string"},"volumeAvailable":{"type":"number"},"volumeCapacity":{"type":"number"},"volumeInodes":{"type":"number"},"volumeInodesFree":{"type":"number"},"volumeInodesUsed":{"type":"number"},"volumeUsage":{"type":"number"}},"type":"object"},"o11y.Volumes":{"properties":{"endTimeBeforeRetention":{"type":"boolean"},"records":{"items":{"$ref":"#/components/schemas/o11y.VolumeRecord"},"type":"array"},"total":{"type":"integer"},"type":{},"warning":{"$ref":"#/components/schemas/o11y.QueryWarnData"}},"type":"object"},"o11y.WaterfallSpan":{"properties":{"attributes":{"additionalProperties":{"type":"object"},"type":"object"},"db_name":{"description":"Calculated fields https://o11y.io/docs/traces-management/guides/derived-fields-spans","type":"string"},"db_operation":{"type":"string"},"duration_nano":{"type":"integer"},"events":{"items":{"$ref":"#/components/schemas/o11y.Event"},"type":"array"},"external_http_method":{"type":"string"},"external_http_url":{"type":"string"},"flags":{"type":"integer"},"has_children":{"type":"boolean"},"has_error":{"type":"boolean"},"http_host":{"type":"string"},"http_method":{"type":"string"},"http_url":{"type":"string"},"is_remote":{"type":"string"},"kind_string":{"type":"string"},"level":{"type":"integer"},"name":{"type":"string"},"parent_span_id":{"type":"string"},"references":{"items":{"$ref":"#/components/schemas/o11y.OtelSpanRef"},"type":"array"},"resource":{"additionalProperties":{"type":"string"},"type":"object"},"response_status_code":{"type":"string"},"span_id":{"type":"string"},"status_code":{"type":"integer"},"status_code_string":{"type":"string"},"status_message":{"type":"string"},"sub_tree_node_count":{"type":"integer"},"time_unix":{"type":"integer"},"trace_id":{"type":"string"},"trace_state":{"type":"string"}},"type":"object"},"o11y.WebexConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"api_url":{},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"room_id":{"type":"string"}},"type":"object"},"o11y.WebhookConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"max_alerts":{"description":"MaxAlerts is the maximum number of alerts to be sent per webhook message.\nAlerts exceeding this threshold will be truncated. Setting this to 0\nallows an unlimited number of alerts.","type":"integer"},"timeout":{"description":"Timeout is the maximum time allowed to invoke the webhook. Setting this to 0\ndoes not impose a timeout.","type":"integer"},"url":{"description":"URL to send POST request to."},"url_file":{"type":"string"}},"type":"object"},"o11y.WechatConfig":{"properties":{"NotifierConfig":{"$ref":"#/components/schemas/o11y.NotifierConfig"},"agent_id":{"type":"string"},"api_secret":{},"api_secret_file":{"type":"string"},"api_url":{},"corp_id":{"type":"string"},"http_config":{"$ref":"#/components/schemas/o11y.HTTPClientConfig"},"message":{"type":"string"},"message_type":{"type":"string"},"to_party":{"type":"string"},"to_tag":{"type":"string"},"to_user":{"type":"string"}},"type":"object"},"o11y.addItemsIn":{"properties":{"id":{"description":"ID is the annotation queue to add to, from the path.","type":"string"},"items":{"description":"Items are the objects to enqueue for review, 1–200 per request. Each names\nexactly one object.","items":{"$ref":"#/components/schemas/o11y.itemInput"},"type":"array"}},"type":"object"},"o11y.alertmanagertypes.Receiver":{"properties":{"Receiver":{"$ref":"#/components/schemas/o11y.Receiver"},"googlechat_configs":{"items":{"$ref":"#/components/schemas/o11y.GoogleChatReceiverConfig"},"type":"array"}},"type":"object"},"o11y.annItemList":{"properties":{"data":{"description":"Data is the page of items.","items":{"$ref":"#/components/schemas/o11y.annItemView"},"type":"array"},"meta":{"$ref":"#/components/schemas/o11y.listMeta","description":"Meta is the paging that produced it."}},"type":"object"},"o11y.annItemView":{"properties":{"assignee":{"description":"Assignee is the reviewer it is for, omitted when unassigned.","type":"string"},"completedAt":{"description":"CompletedAt is when it was reviewed, omitted while pending.","type":"string"},"createdAt":{"description":"CreatedAt is when it was enqueued, RFC3339 in UTC.","type":"string"},"id":{"description":"ID is the item's id.","type":"string"},"objectId":{"description":"ObjectID is the referenced object's id.","type":"string"},"objectType":{"description":"ObjectType is what it references: TRACE, OBSERVATION or SESSION.","type":"string"},"observationId":{"description":"ObservationID echoes objectId when objectType is OBSERVATION.","type":"string"},"queueId":{"description":"QueueID is the queue it belongs to.","type":"string"},"sessionId":{"description":"SessionID echoes objectId when objectType is SESSION.","type":"string"},"status":{"description":"Status is PENDING or COMPLETED.","type":"string"},"traceId":{"description":"TraceID echoes objectId when objectType is TRACE.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed, RFC3339 in UTC.","type":"string"}},"type":"object"},"o11y.annItemsCreated":{"properties":{"data":{"description":"Data is every item created by this request, in request order.","items":{"$ref":"#/components/schemas/o11y.annItemView"},"type":"array"}},"type":"object"},"o11y.annQueueDeleted":{"properties":{"deleted":{"description":"Deleted is true when the queue (and its items) were removed.","type":"boolean"}},"type":"object"},"o11y.annQueueDetailView":{"properties":{"completedCount":{"description":"CompletedCount is how many have been reviewed.","type":"integer"},"createdAt":{"description":"CreatedAt is when it was created, RFC3339 in UTC.","type":"string"},"description":{"description":"Description is its free text, omitted when empty.","type":"string"},"id":{"description":"ID is the queue's id.","type":"string"},"items":{"description":"Items is the queue's first page of items (up to 100).","items":{"$ref":"#/components/schemas/o11y.annItemView"},"type":"array"},"name":{"description":"Name is its display handle.","type":"string"},"pendingCount":{"description":"PendingCount is how many of its items are still awaiting review.","type":"integer"},"scoreConfigIds":{"description":"ScoreConfigIDs are the eval score-configs reviewers grade against.","items":{"type":"string"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when it last changed, RFC3339 in UTC.","type":"string"}},"type":"object"},"o11y.annQueueList":{"properties":{"data":{"description":"Data is the page of queues.","items":{"$ref":"#/components/schemas/o11y.annQueueView"},"type":"array"},"meta":{"$ref":"#/components/schemas/o11y.listMeta","description":"Meta is the paging that produced it."}},"type":"object"},"o11y.annQueueView":{"properties":{"createdAt":{"description":"CreatedAt is when it was created, RFC3339 in UTC.","type":"string"},"description":{"description":"Description is its free text, omitted when empty.","type":"string"},"id":{"description":"ID is the queue's id.","type":"string"},"name":{"description":"Name is its display handle.","type":"string"},"scoreConfigIds":{"description":"ScoreConfigIDs are the eval score-configs reviewers grade against.","items":{"type":"string"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when it last changed, RFC3339 in UTC.","type":"string"}},"type":"object"},"o11y.availabilityPoint":{"properties":{"t":{"description":"T is the bucket start, RFC3339 in UTC.","type":"string"},"total":{"description":"Total is how many services reported at all inside the bucket. It can be\nlower than the current total: a target added last week reported nothing\nthe week before, and saying so is the point.","type":"integer"},"up":{"description":"Up is how many services were up at the end of the bucket.","type":"integer"}},"type":"object"},"o11y.availabilityResponse":{"properties":{"range":{"properties":{"sinceSec":{"type":"integer"},"stepSec":{"type":"integer"}},"type":"object"},"series":{"description":"Series is the trend, oldest bucket first.","items":{"$ref":"#/components/schemas/o11y.availabilityPoint"},"type":"array"},"services":{"description":"Services is the current inventory, sorted by name so two reads of an\nunchanged fleet are byte-identical.","items":{"$ref":"#/components/schemas/o11y.serviceUp"},"type":"array"},"total":{"description":"Total is how many services the prober currently watches.","type":"integer"},"up":{"description":"Up is how many services are up right now.","type":"integer"}},"type":"object"},"o11y.createQueueReq":{"properties":{"description":{"description":"Description is optional free text, up to 512 characters.","type":"string"},"name":{"description":"Name is the queue's display handle, 1–128 printable characters. It must be\nunique within the org's project. Required.","type":"string"},"scoreConfigIds":{"description":"ScoreConfigIDs are the eval score-configs reviewers grade against.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.deployment":{"properties":{"instance":{"type":"string"},"up":{"type":"boolean"}},"type":"object"},"o11y.integrations.CollectedLogAttribute":{"properties":{"name":{"type":"string"},"path":{"type":"string"},"type":{"type":"string"}},"type":"object"},"o11y.integrations.CollectedMetric":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"unit":{"type":"string"}},"type":"object"},"o11y.itemInput":{"properties":{"assignee":{"description":"Assignee is the reviewer this item is for, up to 512 characters.","type":"string"},"objectId":{"description":"ObjectID is the referenced object's id, paired with objectType.","type":"string"},"objectType":{"description":"ObjectType is TRACE, OBSERVATION or SESSION — the generic form, paired\nwith objectId.","type":"string"},"observationId":{"description":"ObservationID references an observation — the console-friendly form of\nobjectType=OBSERVATION.","type":"string"},"sessionId":{"description":"SessionID references a session — the console-friendly form of\nobjectType=SESSION.","type":"string"},"traceId":{"description":"TraceID references a trace — the console-friendly form of\nobjectType=TRACE.","type":"string"}},"type":"object"},"o11y.listMeta":{"properties":{"limit":{"description":"Limit is how many rows one page holds.","type":"integer"},"page":{"description":"Page is the 1-based page this response is.","type":"integer"},"totalItems":{"description":"TotalItems is how many rows match in total.","type":"integer"},"totalPages":{"description":"TotalPages is ceil(totalItems/limit), at least 1.","type":"integer"}},"type":"object"},"o11y.metricsResponse":{"properties":{"product":{"type":"string"},"range":{"properties":{"sinceSec":{"type":"integer"},"stepSec":{"type":"integer"}},"type":"object"},"series":{"properties":{"errors":{"items":{"$ref":"#/components/schemas/o11y.point"},"type":"array"},"latencyP50Ms":{"items":{"$ref":"#/components/schemas/o11y.point"},"type":"array"},"latencyP95Ms":{"items":{"$ref":"#/components/schemas/o11y.point"},"type":"array"},"requests":{"items":{"$ref":"#/components/schemas/o11y.point"},"type":"array"}},"type":"object"},"summary":{"properties":{"errorRate":{"type":"number"},"errors":{"type":"integer"},"p95Ms":{"type":"number"},"requests":{"type":"integer"}},"type":"object"},"usage":{"properties":{"calls":{"type":"integer"},"costCents":{"type":"integer"},"series":{"items":{"$ref":"#/components/schemas/o11y.usageBucket"},"type":"array"},"tokens":{"type":"integer"}},"type":"object"}},"type":"object"},"o11y.point":{"properties":{"t":{"description":"T is the bucket start, RFC3339 in UTC.","type":"string"},"v":{"description":"V is the bucket's value.","type":"number"}},"type":"object"},"o11y.querybuildertypesv5.CompositeQuery":{"properties":{"queries":{"description":"Queries is the queries to use for the request.","items":{"$ref":"#/components/schemas/o11y.QueryEnvelope"},"type":"array"}},"type":"object"},"o11y.querybuildertypesv5.OrderBy":{"properties":{"direction":{"description":"direction to order by"},"key":{"$ref":"#/components/schemas/o11y.OrderByKey","description":"key to order by"}},"type":"object"},"o11y.serviceUp":{"properties":{"name":{"description":"Name is the service as the fleet prober knows it (probes.go's target name,\nwhich is the `service` label on hanzo_service_up).","type":"string"},"up":{"description":"Up is true when the service answered its own health URL on the last cycle.","type":"boolean"}},"type":"object"},"o11y.statusResult":{"properties":{"checkedAt":{"type":"string"},"deployments":{"items":{"$ref":"#/components/schemas/o11y.deployment"},"type":"array"},"latencyMs":{"type":"integer"},"product":{"type":"string"},"source":{"type":"string"},"up":{"type":"boolean"}},"type":"object"},"o11y.traceRow":{"properties":{"durationMs":{"description":"DurationMs is End minus Start in milliseconds: the trace's wall clock,\nnot the sum of its spans, which double-counts everything concurrent.","type":"number"},"end":{"description":"End is the latest span end, RFC3339 with nanoseconds, in UTC.","type":"string"},"numSpans":{"description":"NumSpans is how many spans the trace carries.","type":"integer"},"start":{"description":"Start is the earliest span start, RFC3339 with nanoseconds, in UTC.","type":"string"},"traceId":{"description":"TraceID is the trace's id — the {traceId} of the detail read.","type":"string"}},"type":"object"},"o11y.tracesOut":{"properties":{"count":{"description":"Count is how many traces this page carries.","type":"integer"},"limit":{"description":"Limit is the page cap actually applied, after clamping.","type":"integer"},"sinceSec":{"description":"SinceSec is the window actually read, in seconds, after clamping.","type":"integer"},"traces":{"description":"Traces are the caller org's traces, most recently active first.","items":{"$ref":"#/components/schemas/o11y.traceRow"},"type":"array"}},"type":"object"},"o11y.updateItemIn":{"properties":{"assignee":{"description":"Assignee replaces the reviewer this item is for, up to 512 characters.","type":"string"},"id":{"description":"ID is the annotation queue the item belongs to, from the path.","type":"string"},"itemId":{"description":"ItemID is the item to update, from the path.","type":"string"},"status":{"description":"Status is the item's new review state: PENDING or COMPLETED. Required.","type":"string"}},"type":"object"},"o11y.updateQueueIn":{"properties":{"description":{"description":"Description replaces the free text when present, up to 512 characters.","type":"string"},"id":{"description":"ID is the annotation queue to update, from the path.","type":"string"},"name":{"description":"Name replaces the queue's display handle when present, 1–128 printable\ncharacters and unique within the project.","type":"string"},"scoreConfigIds":{"description":"ScoreConfigIDs replaces the whole score-config set when present.","items":{"type":"string"},"type":"array"}},"type":"object"},"o11y.usageBucket":{"properties":{"calls":{"description":"Calls is how many LLM calls landed in the bucket.","type":"integer"},"costCents":{"description":"CostCents is what they cost, in cents.","type":"integer"},"t":{"description":"T is the bucket start, RFC3339 in UTC.","type":"string"},"tokens":{"description":"Tokens is how many tokens they consumed.","type":"integer"}},"type":"object"},"o11yGlobal":{"properties":{"end":{"type":"string"},"llm":{"$ref":"#/components/schemas/o11yLLM"},"logSeries":{"items":{"$ref":"#/components/schemas/o11yLogPoint"},"type":"array"},"range":{"type":"string"},"series":{"items":{"$ref":"#/components/schemas/o11ySeries"},"type":"array"},"start":{"type":"string"},"topModels":{"items":{"$ref":"#/components/schemas/o11yModelStat"},"type":"array"},"topOrgs":{"items":{"$ref":"#/components/schemas/o11yOrgStat"},"type":"array"},"topServices":{"items":{"$ref":"#/components/schemas/o11ySvcStat"},"type":"array"},"totals":{"$ref":"#/components/schemas/o11yTotals"}},"type":"object"},"o11yLLM":{"properties":{"costUsd":{"type":"number"},"generations":{"type":"integer"}},"type":"object"},"o11yLogPoint":{"properties":{"count":{"type":"integer"},"ts":{"type":"string"}},"type":"object"},"o11yModelStat":{"properties":{"costCents":{"type":"integer"},"model":{"type":"string"},"requests":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"o11yOrgStat":{"properties":{"costCents":{"type":"integer"},"org":{"type":"string"},"requests":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"o11yOut":{"properties":{"data":{"$ref":"#/components/schemas/o11yGlobal"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"o11ySeries":{"properties":{"costCents":{"type":"integer"},"errors":{"type":"integer"},"requests":{"type":"integer"},"tokens":{"type":"integer"},"ts":{"type":"string"}},"type":"object"},"o11ySvcStat":{"properties":{"errorRate":{"description":"percent (0..100)","type":"number"},"latencyP95Ms":{"type":"number"},"requests":{"type":"integer"},"service":{"type":"string"}},"type":"object"},"o11yTotals":{"properties":{"completionTokens":{"type":"integer"},"costCents":{"type":"integer"},"errors":{"type":"integer"},"latencyP50Ms":{"type":"number"},"latencyP95Ms":{"type":"number"},"latencyP99Ms":{"type":"number"},"logVolume":{"description":"Logs (event.log), fleet volume over the window.","type":"integer"},"models":{"type":"integer"},"orgs":{"type":"integer"},"promptTokens":{"type":"integer"},"requests":{"description":"LLM usage (hanzo.cloud_usage), all orgs.","type":"integer"},"services":{"type":"integer"},"tokens":{"type":"integer"},"traceCount":{"description":"Traces (event.span), all services.","type":"integer"},"traceErrorRate":{"description":"percent (0..100)","type":"number"}},"type":"object"},"oauthBundleIn":{"properties":{"access":{"description":"Access is the access token.","type":"string"},"account":{"description":"Account is the account label the flow reported; sanitized on ingest.","type":"string"},"refresh":{"description":"Refresh is the refresh token. It is sealed and NEVER handed back out.","type":"string"}},"type":"object"},"onboardReq":{"properties":{"name":{"description":"Name is the organization's display name. Ignored when personal is true, which\nderives the name from the caller's own username instead.","type":"string"},"personal":{"description":"Personal asks for the caller's own workspace: the name is derived from their\nusername and the slug auto-suffixes to stay unique. Meaningless — and refused\n— for a caller who already has an organization.","type":"boolean"}},"type":"object"},"onboardResp":{"properties":{"accessKey":{"description":"AccessKey is the identifier of the org-scoped credential provisioning minted\nwith the organization. Present on a first run that actually minted one.","type":"string"},"accessSecret":{"description":"AccessSecret is that credential's confidential half, returned ONCE — on the\nresponse that mints it and never again. IAM keeps only its argon2id digest\nand blanks the plaintext, so this is the single moment it exists in a form\nits owner can read; a replay of the same provision re-reveals nothing.","type":"string"},"additional":{"description":"Additional is true when the caller already had an organization and this one\nwas created WITHOUT moving them into it — they reach it via the org switcher.","type":"boolean"},"displayName":{"description":"DisplayName is the organization's human name.","type":"string"},"org":{"description":"Org is the created organization's slug, which is what X-Org-Id carries.","type":"string"}},"type":"object"},"operatorUser":{"properties":{"created":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"forbidden":{"type":"boolean"},"isAdmin":{"type":"boolean"},"isSuperAdmin":{"type":"boolean"},"lastSignin":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"},"tag":{"type":"string"}},"type":"object"},"oppList":{"properties":{"data":{"description":"Data is the page of opportunities, most recently updated first.","items":{"$ref":"#/components/schemas/Opportunity"},"type":"array"}},"type":"object"},"oppReq":{"properties":{"amount":{"description":"Amount is the deal value in minor units (cents) of Currency.","type":"integer"},"closeDate":{"description":"CloseDate is the expected close, as a unix second (0 = unset).","type":"integer"},"companyId":{"description":"CompanyID links the deal to one of the org's companies.","type":"string"},"currency":{"description":"Currency is the ISO code Amount is denominated in; empty defaults to USD.","type":"string"},"id":{"description":"ID names the opportunity to update and comes from the path. A create\nignores it: the server mints the id.","type":"string"},"name":{"description":"Name is the deal name. Required.","type":"string"},"pointOfContactId":{"description":"PointOfContact links the deal to one of the org's contacts.","type":"string"},"stage":{"description":"Stage is the pipeline stage: NEW, SCREENING, MEETING, PROPOSAL or CUSTOMER\n(case-insensitive). Empty defaults to NEW.","type":"string"}},"type":"object"},"optinView":{"properties":{"org":{"$ref":"#/components/schemas/orgOptinView","description":"Org is the caller's org's listing preference on the cross-org board, and whether\nthis caller is allowed to change it. It is read for every caller — a member sees\nwhere their org stands even though only an admin may edit it."},"user":{"$ref":"#/components/schemas/userOptinView","description":"User is the caller's OWN listing preference, and whether they may change it."}},"type":"object"},"oracleView":{"properties":{"feed":{"description":"Feed is the trading pair this feed prices.","type":"string"},"id":{"description":"ID is the feed's own id, else its trading pair.","type":"string"},"name":{"description":"Name is the feed's display name — the trading pair, e.g. \"LUX/USD\".","type":"string"},"source":{"description":"Source is the oracle network the feed originates from; \"O-Chain\" by default.","type":"string"},"status":{"description":"Status is \"active\" for a listed feed.","type":"string"},"updatedAt":{"description":"UpdatedAt is the feed's own timestamp, RFC 3339 UTC.","type":"string"},"value":{"description":"Value is the feed's price, verbatim as the registry carries it.","type":"string"}},"type":"object"},"oraclesOut":{"properties":{"oracles":{"description":"Oracles is one row per on-chain price feed, or an empty list when the graph is\nunreachable or carries none — never a fabricated feed.","items":{"$ref":"#/components/schemas/oracleView"},"type":"array"}},"type":"object"},"orgEarningView":{"properties":{"commissionCents":{"description":"CommissionCents is what the caller earned from that org across ALL periods, in\ncents. Deliberately the caller's own share and nothing else: that org's spend\nand the margin on it are not restated here.","type":"integer"},"referredOrg":{"description":"ReferredOrg is the org slug this contribution came from — one the caller\nreferred, directly or up to three levels down.","type":"string"}},"type":"object"},"orgOptinReq":{"properties":{"display":{"description":"Display is the name shown for the org on that board: 1-40 characters of\nletters, digits, space, dot, underscore, apostrophe or hyphen. Left empty on a\nlisting opt-in it defaults to the org id.","type":"string"},"listed":{"description":"Listed publishes the org on the cross-org global board when true, and withdraws\nit when false.","type":"boolean"}},"type":"object"},"orgOptinView":{"properties":{"canManage":{"description":"CanManage is true only for an admin of this org (or a platform SuperAdmin) — the\ncallers whose write of the org preference will be accepted.","type":"boolean"},"display":{"description":"Display is the name shown for the org on that board. Empty when none was chosen;\nopting in without one defaults it to the org id.","type":"string"},"listed":{"description":"Listed is true when the org has opted onto the cross-org global board. False —\nthe default — keeps the org off it entirely; the org's own members still see\ntheir own board. Listing consents to publishing usage VOLUME, never spend.","type":"boolean"}},"type":"object"},"orgRow":{"properties":{"created":{"type":"string"},"creditsCents":{"type":"integer"},"display":{"type":"string"},"org":{"type":"string"},"products":{"type":"integer"},"spendCents":{"type":"integer"},"tokens":{"type":"integer"},"users":{"type":"integer"}},"type":"object"},"orgView":{"properties":{"badgeMarkdown":{"description":"BadgeMarkdown is the ready-to-paste README snippet, DERIVED for each response\nfrom this deployment's badge host and never stored — here it deep-links the\nOWNER's template import rather than one repository's.","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds when the owner claim was first recorded — equal to\nverifiedAt on the first proof, then fixed while verifiedAt moves.","type":"integer"},"method":{"description":"Method is HOW the owner was proven, always against its \".github\" control\nrepository: \"oauth\" — an IAM-linked forge token showed admin or push on it; or\n\"file\" — a hanzo.json on its default branch carried this author's verify code.\nThe \"maintainer\" shortcut is a per-repository attribution and never appears\nhere. Omitted on a row written before the method was recorded.","type":"string"},"ownerUrl":{"description":"OwnerURL is the claim key in canonical form — lowercased \"host/owner\" with NO\nrepository segment, host ∈ {github.com, gitlab.com}. It covers every repository\nunder that owner, so code with no claim of its own still earns; a per-repository\nclaim outranks it. UNIQUE across every author: first proven claim wins.","type":"string"},"verified":{"description":"Verified reports that ownership of the WHOLE owner was proven — against that\nowner's \".github\" control repository, which is exactly as strong as a\nper-repository claim. Only a proven claim is written, so every row returned\nhere is true.","type":"boolean"},"verifiedAt":{"description":"VerifiedAt is unix seconds of the most recent successful proof of the owner;\nre-verifying refreshes it, and the method beside it, in place.","type":"integer"}},"type":"object"},"orgsOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/orgRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"overviewData":{"properties":{"activeProducts":{"type":"integer"},"creditsCents":{"type":"integer"},"drift":{"type":"integer"},"lastSync":{"type":"string"},"orgs":{"type":"integer"},"products":{"type":"integer"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"spendCents30d":{"type":"integer"},"tokens30d":{"type":"integer"},"users":{"type":"integer"}},"type":"object"},"overviewOut":{"properties":{"data":{"$ref":"#/components/schemas/overviewData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"overviewView":{"properties":{"custom":{"type":"boolean"},"funnel":{"$ref":"#/components/schemas/Funnel"},"progress":{"$ref":"#/components/schemas/progressView"},"steps":{"items":{"$ref":"#/components/schemas/stepView"},"type":"array"},"title":{"type":"string"},"version":{"type":"string"}},"type":"object"},"pairingApproved":{"properties":{"ownerBootstrapped":{"description":"OwnerBootstrapped is true when this approval was the org's FIRST on the\nchannel and therefore also made the sender its owner.","type":"boolean"},"sender":{"description":"Sender is the external chat identity that is now allowed to DM the org's bot.","type":"string"}},"type":"object"},"pairingQueue":{"properties":{"pending":{"description":"Pending is every unexpired pairing request waiting on an org admin, each\ncarrying the channel, the requesting sender and the code to approve it with.","items":{"$ref":"#/components/schemas/pairingView"},"type":"array"}},"type":"object"},"pairingView":{"properties":{"channel":{"description":"Channel is the transport the request arrived on — discord, slack, teams or\ntelegram — and half of what approval names. The cap of three unapproved\nrequests applies per (org, channel); while it is full no further code is\nminted until one is approved or expires.","type":"string"},"code":{"description":"Code is the CAPABILITY that authorises the approval: eight characters from a\n32-symbol uppercase alphabet (A-Z0-9 minus the confusables 0, O, 1 and I),\nminted with crypto/rand and also sent to the requester in chat. An org admin\npasses it with the channel to POST /v1/channels/pairing/approve, which\nCONSUMES it — the request row is deleted, so a code approves once — and which\ntakes org admin as well as the code. It lives ONE HOUR from CreatedAt;\nexpired requests are not listed here, and approving one is a 404. It is shown\non this admin surface and NEVER logged.","type":"string"},"createdAt":{"description":"CreatedAt is Unix SECONDS of FIRST contact: when the request was minted and\nthe code sent. Expiry is measured from here and from nowhere else.","type":"integer"},"lastSeen":{"description":"LastSeen is Unix SECONDS of the MOST RECENT message from this sender while\nthe request has been pending. It moves as they keep writing, which is how an\nadmin tells a live request from an abandoned one — but it does not extend the\nhour and does not re-send the code, since one request sends exactly one chat\nreply.","type":"integer"},"sender":{"description":"Sender is the transport-native user id waiting for access — the same\nidentity inbox messages carry. Approving mints a DM allow entry for exactly\nthis value and nothing wider: pairing never grants group access.","type":"string"}},"type":"object"},"patchApplicationIn":{"properties":{"id":{"description":"ID is the application to move, from the path.","type":"string"},"note":{"description":"Note is a free-text comment recorded on the timeline, with or without a\nstage change.","type":"string"},"reason":{"description":"Reason records WHY, and is required to reject.","type":"string"},"stage":{"description":"Stage is the stage to move to: applied, screened, qualified,\ncredits-offered, onboarded or rejected. Omit to leave the stage alone.","type":"string"}},"type":"object"},"patchFlowIn":{"properties":{"externalId":{"description":"ExternalID sets the caller's own id for this flow.","type":"string"},"folderId":{"description":"FolderID moves the flow in the builder's tree.","type":"string"},"id":{"description":"ID is the flow to update, from the path.","type":"string"},"metadata":{"description":"Metadata replaces the caller's opaque JSON."},"publishedVersionId":{"description":"PublishedVersionID pins the version runs execute. It must name a version OF\nTHIS FLOW; empty clears the pin, so runs take the latest version again.","type":"string"}},"type":"object"},"patchIn":{"properties":{"name":{"description":"Name is the repo to update, from the :name path segment.","type":"string"},"public":{"description":"Public flips anonymous read access. Omit it and the request is refused —\nthere is nothing else to update yet.","type":"boolean"}},"type":"object"},"patchSessionIn":{"properties":{"cwd":{"description":"Cwd is where the session is working NOW.\n\nIt was write-once — captured at register and never again — which is right\nfor a run that starts in a directory and stays there, and wrong for a linked\nshell, which is a place a person moves around in. The console showed the\ndirectory `hanzo link` happened to be run from and kept showing it after the\nshell had walked away, so the field answered \"which work is this\" with an\nanswer that was true once. A pointer, so an unchanged path is an omitted\nfield rather than a repeated write.","type":"string"},"id":{"description":"ID is the session to update, from the path.","type":"string"},"project":{"description":"Project tags the product this session built; Published is the author's\ndecision to let anyone read the story (provenance.go). Both are pointers so\n\"absent\" and \"cleared\" are different requests.","type":"string"},"published":{"type":"boolean"},"status":{"type":"string"},"target":{"description":"Target re-dispatches a session to a run-target (the #48 association). \"\" detaches.","type":"string"},"terminal":{"description":"Terminal publishes (or, with \"\", withdraws) the URL this session's live\nterminal can be watched at. A pointer so \"absent\" and \"withdrawn\" are\ndifferent requests: a session that stops sharing must be able to say so.","type":"string"},"title":{"type":"string"}},"type":"object"},"patchSyncIn":{"properties":{"actor":{"description":"Actor is the loop-guard identity the sync writes as. Omitted, the stored actor\nstands.","type":"string"},"direction":{"description":"Direction is both, pull, push or off. Omitted, the stored direction stands.","type":"string"},"id":{"description":"ID is the sync to update, from the path.","type":"string"},"trigger":{"description":"Trigger is webhook, poll or manual. Omitted, the stored trigger stands.","type":"string"}},"type":"object"},"patchTargetIn":{"properties":{"capacity":{"type":"string"},"host":{"type":"string"},"id":{"description":"ID is the target to update, from the path.","type":"string"},"kind":{"type":"string"},"label":{"type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics","description":"present =\u003e a heartbeat; the server stamps its time"},"spec":{"$ref":"#/components/schemas/Spec"},"status":{"type":"string"}},"type":"object"},"payoutData":{"properties":{"author":{"$ref":"#/components/schemas/adminAuthorView","description":"Author is the author record after the payout, with the balances updated."},"payout":{"$ref":"#/components/schemas/payoutView","description":"Payout is the recorded payout, including where it settled."}},"type":"object"},"payoutOut":{"properties":{"data":{"$ref":"#/components/schemas/settlement","description":"Data is the recorded payout and the balances it left behind."},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"payoutRequest":{"properties":{"amountCents":{"description":"AmountCents is how much to pay, in cents. Must be positive and can never\nexceed the author's pending royalty (accrued minus paid).","type":"integer"},"id":{"description":"ID is the author to pay, from the path.","type":"string"},"method":{"description":"Method is how it settles: \"credits\" issues a grant into the author's wallet;\nwire, paypal and the like are record-only. Required.","type":"string"},"reference":{"description":"Reference is the operator's external reference for a cash settlement — a wire\nconfirmation, a PayPal transaction id.","type":"string"}},"type":"object"},"payoutResult":{"properties":{"data":{"$ref":"#/components/schemas/payoutData","description":"Data carries the payout and the author."},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is \"ok\".","type":"string"}},"type":"object"},"payoutView":{"properties":{"amountCents":{"description":"AmountCents is the amount RESERVED against pending royalty, in integer USD\ncents, always positive. The reservation is atomic and can never exceed\naccrued − paid, so this is owed money moved out of pending — not money moved.","type":"integer"},"createdAt":{"description":"CreatedAt is unix seconds when the payout was RECORDED — the moment the amount\nleft pending, not the moment a human moved the money.","type":"integer"},"id":{"description":"ID is the payout row's server-minted handle, \"apo_\"-prefixed. A caller never\nsupplies it; it is what an operator quotes when reconciling a settlement.","type":"string"},"method":{"description":"Method is how the operator says this settles, lowercased as recorded.\n\"credits\" is the one method that means the author's own wallet; anything else\n— wire, paypal, check — is a cash disbursement a human performs. Recording it\npays nobody either way.","type":"string"},"reference":{"description":"Reference is the operator's external handle for the settlement: a wire\nconfirmation, a PayPal transaction id. Absent when none was given.","type":"string"},"settlement":{"description":"Settlement discloses treasury-vs-wallet-vs-cash on every payout, to the author\nand to the admin mirror alike — the disclosure that keeps a first-party\nsettlement legible as internal accounting.","type":"string"},"txn":{"description":"Txn is the commerce ledger transaction id of a SETTLED credits payout, and it\nis absent on every payout this service records. Recording moves no money, and\nauthors asks the money plane exactly one question — what has this org spent? —\nwith no write to answer it with, so there is no receipt to carry. It fills in\nonly when a settlement stamps its transaction back onto the row.","type":"string"}},"type":"object"},"periodEarningView":{"properties":{"commissionCents":{"description":"CommissionCents is what the caller earned that period, in cents: the sum over\neach referred org and upline level of margin × that level's rate. Always ≤\nmarginCents, by construction.","type":"integer"},"marginCents":{"description":"MarginCents is the margin Hanzo earned in that period on the spend of every\norg the caller referred, in cents — the base commission is a rate OF. It is\nthe aggregate base, never any one customer's bill.","type":"integer"},"period":{"description":"Period is the accrual bucket: the UTC year-month, \"YYYY-MM\". Commission is\nlatched at most once per referred org per period, so one row is one month.","type":"string"}},"type":"object"},"pickOut":{"properties":{"consumers":{"description":"Consumers is the page, ordered by name.","items":{"$ref":"#/components/schemas/Consumer"},"type":"array"},"total":{"description":"Total is the stream's consumer count before paging.","type":"integer"}},"type":"object"},"pipelineBoard":{"properties":{"pipelines":{"description":"Pipelines are one per application in the caller's org.","items":{"$ref":"#/components/schemas/pipelineRow"},"type":"array"}},"type":"object"},"pipelineReq":{"properties":{"feeds":{"description":"Feeds is the RSS/Atom feed URLs to read, at most 64. Each must be an\nhttp(s) URL whose host is on the server's allowlist — the SSRF guard is\napplied here, at the write, so a stored pipeline can never name a host the\nfetcher would refuse. Blank entries are dropped and duplicates collapse.","items":{"type":"string"},"type":"array"},"filters":{"$ref":"#/components/schemas/Filters","description":"Filters narrows the merged feed. Terms are trimmed, de-duplicated\ncase-insensitively, and capped at 64 per axis."}},"type":"object"},"pipelineRow":{"properties":{"duration":{"description":"Duration is how long that run took; empty while it is still queued or\nbuilding.","type":"string"},"id":{"description":"ID is the application id — one pipeline is one application.","type":"string"},"lastRun":{"description":"LastRun is when the most recent deployment started, RFC3339 UTC.","type":"string"},"name":{"description":"Name is the application's name.","type":"string"},"repo":{"description":"Repo is the git repo or image the pipeline builds from.","type":"string"},"status":{"description":"Status is the latest deployment's status, or the app's when it has none.","type":"string"}},"type":"object"},"pipelineView":{"properties":{"createdAt":{"description":"CreatedAt is when the pipeline was first stored, RFC3339 UTC. Absent on the\ndefault.","type":"string"},"default":{"description":"Default is true when no pipeline is stored for this project and these are\nthe built-in world feeds. Writing one turns it false.","type":"boolean"},"feeds":{"description":"Feeds is the RSS/Atom feed URLs the pipeline reads. Every host is on the\nserver's allowlist — a URL that is not cannot be stored.","items":{"type":"string"},"type":"array"},"filters":{"$ref":"#/components/schemas/Filters","description":"Filters narrows the merged feed."},"org":{"description":"Org is the tenant the pipeline belongs to, resolved server-side from the\nvalidated principal.","type":"string"},"project":{"description":"Project is the org sub-scope the pipeline belongs to.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it was last written, RFC3339 UTC. Absent on the default.","type":"string"}},"type":"object"},"planEntitlements":{"properties":{"entitlements":{"description":"Entitlements is the canonical namespaced entitlement block derived from the\nplan's limits and addons, where -1 means unlimited."},"id":{"description":"ID is the plan id or slug that was resolved, as it was requested.","type":"string"},"license_features":{"description":"LicenseFeatures is the flat, sorted feature list a signed license carries,\nderived from the entitlements.","items":{"type":"string"},"type":"array"}},"type":"object"},"planHealth":{"properties":{"service":{"description":"Service names the subsystem that answered.","type":"string"},"status":{"description":"Status is \"ok\" whenever this subsystem is mounted.","type":"string"}},"type":"object"},"planInfo":{"properties":{"active":{"description":"Active is whether that plan's entitlement is live.","type":"boolean"},"guestLimit":{"description":"GuestLimit is the plan's team.guests cap, when the plan carries one.","type":"integer"},"guests":{"description":"Guests is how many of those seats are guests.","type":"integer"},"plan":{"description":"Plan is the licensed plan id, empty when it cannot be resolved here — an\nhonest dash on the page, never a fabricated tier.","type":"string"},"seats":{"description":"Seats is the org's distinct active human members.","type":"integer"},"upgradeUrl":{"description":"UpgradeURL is where the page sends a caller who wants a bigger plan.","type":"string"}},"type":"object"},"planList":{"properties":{"plans":{"description":"Plans are the plans in this section, each an opaque object exactly as the\n@hanzo/plans catalog emits it — typically id, name, description,\npriceMonthly, category, a feature list, a limits block and a price_ref.","items":{},"type":"array"}},"type":"object"},"planRegionList":{"properties":{"regions":{"description":"Regions are the regions cloud capacity is offered in, each an opaque object\nexactly as the catalog emits it — typically id, name, location and flag.","items":{},"type":"array"}},"type":"object"},"planResolution":{"properties":{"entitlements":{"description":"Entitlements is the canonical namespaced entitlement block derived from the\nplan's limits and addons — keys like \"ai.tokens_per_min\" and\n\"world.api_rate_limit\", where -1 means unlimited."},"id":{"description":"ID is the plan's catalog id.","type":"string"},"license_features":{"description":"LicenseFeatures is the flat, sorted feature list a signed license carries,\nderived from the entitlements — \"ai.premium\", \"licensing.product:team\".","items":{"type":"string"},"type":"array"},"price_ref":{"description":"PriceRef is the plan's billing reference — currency, the recurring monthly\nand annual amounts, whether it prices per seat, its Stripe lookup key and\nits metered components."},"tenant_id":{"description":"TenantID is the catalog the record came from: \"hanzo\" for the canonical\ncatalog, a reseller org for that reseller's override.","type":"string"}},"type":"object"},"planSchemas":{"properties":{"entitlements":{"description":"Entitlements is entitlements.schema.json — the JSON Schema every\nentitlement key is declared in, including its type, unit and enum."},"plan":{"description":"Plan is plan.schema.json — the JSON Schema a catalog plan record conforms\nto."}},"type":"object"},"planTierList":{"properties":{"tiers":{"description":"Tiers are the rentable GPU configurations, each an opaque object exactly as\nthe catalog emits it — typically id, name, GPU count and model, VRAM, vCPUs,\nhost memory and hourly price.","items":{},"type":"array"}},"type":"object"},"planToolList":{"properties":{"tools":{"description":"Tools are the metered tools, each an opaque object exactly as the catalog\nemits it — typically name, billing unit and price.","items":{},"type":"array"}},"type":"object"},"planVocab":{"properties":{"engine_features":{"description":"EngineFeatures are the inference-engine capabilities a license can grant:\ninference, embeddings, rerank, training, vision, audio, tools.","items":{"type":"string"},"type":"array"},"keys":{"additionalProperties":{},"description":"Keys maps every entitlement key to its descriptor — key, namespace, JSON\ntype(s), nullability, unit, enum and title, as the schema declares them.","type":"object"},"namespaces":{"description":"Namespaces are the entitlement key namespaces: the prefix before the dot in\n\"ai.tokens_per_min\".","items":{"type":"string"},"type":"array"}},"type":"object"},"pluginDeleted":{"properties":{"deleted":{"description":"Deleted is the plugin id that is now gone.","type":"string"}},"type":"object"},"pluginMount":{"properties":{"enabled":{"description":"Enabled is whether this subsystem is switched on in this deployment.","type":"boolean"},"name":{"description":"Name is the subsystem's name, the same label a traced request resolves to.","type":"string"},"prefixes":{"description":"Prefixes are the URL prefixes this subsystem serves.","items":{"type":"string"},"type":"array"}},"type":"object"},"pluginMountList":{"properties":{"plugins":{"description":"Plugins is every subsystem the composition root declared, filtered to the\nenabled ones unless all=true.","items":{"$ref":"#/components/schemas/pluginMount"},"type":"array"}},"type":"object"},"policyData":{"properties":{"policy":{"$ref":"#/components/schemas/SharePolicy","description":"Policy is the revenue-share configuration as stored."}},"type":"object"},"policyOut":{"properties":{"data":{"$ref":"#/components/schemas/policyData","description":"Data is the stored policy."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"policyRequest":{"properties":{"revenueShareBps":{"description":"RevenueShareBps is the share of net platform revenue a sweep accrues into the\nreserve fund, in basis points. 0–10000; 2000 (20%) is the platform default.","type":"integer"}},"type":"object"},"poolCreate":{"properties":{"autoScale":{"description":"AutoScale turns the provider's cluster autoscaler on for this pool.","type":"boolean"},"clusterId":{"description":"ClusterID is the cluster to add the pool to, from the URL path.","type":"string"},"count":{"description":"Count is how many nodes the pool starts with.","type":"integer"},"maxNodes":{"type":"integer"},"minNodes":{"description":"MinNodes and MaxNodes bound the autoscaler; they are ignored unless\nAutoScale is set.","type":"integer"},"name":{"description":"Name is the pool's name.","type":"string"},"provider":{"description":"Provider is the cloud the cluster lives on (e.g. \"digitalocean\"). Required —\nVisor routes the create by it. Accepted from the body or ?provider=.","type":"string"},"size":{"description":"Size is the provider size slug for each node (e.g. \"s-4vcpu-8gb\").","type":"string"}},"type":"object"},"poolScale":{"properties":{"clusterId":{"description":"ClusterID and PoolID address the pool, from the URL path.","type":"string"},"count":{"description":"Count is the node count to scale TO — an absolute target, not a delta, and\nnever negative.","type":"integer"},"poolId":{"type":"string"},"provider":{"description":"Provider is the cloud the cluster lives on. Required; body or ?provider=.","type":"string"}},"type":"object"},"populatedFlow":{"properties":{"created":{"description":"Created and Updated are unix milliseconds.","type":"integer"},"externalId":{"description":"ExternalID is the caller's own id for this flow, if it set one.","type":"string"},"folderId":{"description":"FolderID groups the flow in the builder's tree.","type":"string"},"id":{"description":"ID is the flow's id.","type":"string"},"metadata":{"description":"Metadata is the caller's opaque JSON, stored and returned verbatim."},"projectId":{"description":"Org is the owning org, which this surface names projectId. Server-derived from\nthe validated principal — never read from a request.","type":"string"},"publishedVersionId":{"description":"PublishedVersionID is the version a run executes when set; empty means the\nlatest version runs.","type":"string"},"status":{"description":"Status is ENABLED or DISABLED — whether the flow's trigger is armed.","type":"string"},"updated":{"type":"integer"},"version":{"$ref":"#/components/schemas/FlowVersion","description":"Version is the flow's latest version — its display name and step tree."}},"type":"object"},"prefsView":{"properties":{"prefs":{"description":"Prefs is the caller's preference document: an opaque JSON object whose keys\nthe surfaces own, returned verbatim. `{}` when nothing has been saved."},"updatedAt":{"description":"UpdatedAt is when the document was last written, unix seconds. Absent when\nnothing has been saved.","type":"integer"}},"type":"object"},"previewReq":{"properties":{"app":{"description":"App is the parent application's slug, from the path.","type":"string"},"branch":{"description":"Branch is the branch to preview; defaults to the parent app's branch.","type":"string"},"image":{"description":"Image is the already-built image ref to deploy. Required — a preview never\nbuilds.","type":"string"},"project":{"description":"Project is the project the parent application lives under, from the path.","type":"string"}},"type":"object"},"previewView":{"properties":{"app":{"description":"App is the preview application's own slug, `\u003capp\u003e-\u003cbranch\u003e`.","type":"string"},"branch":{"description":"Branch is the branch this preview maps.","type":"string"},"deployment":{"$ref":"#/components/schemas/deploymentView","description":"Deployment is the deployment the preview recorded."},"url":{"description":"URL is the preview's live HTTPS address.","type":"string"}},"type":"object"},"pricingHealth":{"properties":{"service":{"description":"Service names the subsystem that answered.","type":"string"},"status":{"description":"Status is \"ok\" whenever this subsystem is mounted.","type":"string"}},"type":"object"},"pricingModelList":{"properties":{"models":{"description":"Models are the catalog entries visible to the caller, each an opaque\nobject exactly as the pricing source emits it, with any admin override\nmerged on top. An admin additionally sees hidden entries, each annotated\nunder \"_overlay\".","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"},"total":{"description":"Total is how many models this answer carries — recounted over the visible\nset, not the catalog's own total.","type":"integer"},"updated":{"description":"Updated is when the catalog was last refreshed, as the pricing source\nrecorded it.","type":"object"}},"type":"object"},"pricingPlanList":{"properties":{"plans":{"description":"Plans are the plans in this section, each an opaque object exactly as the\npricing source emits it — typically id, name, description, price and a\nfeature list.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"pricingPresetList":{"properties":{"presets":{"description":"Presets are the named compute sizes, each an opaque object exactly as the\npricing source emits it — typically id, name, provider slug, vCPU, memory,\ndisk and price.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"pricingProviderList":{"properties":{"providers":{"additionalProperties":{"type":"object"},"description":"Providers maps a provider name to its opaque info object. A provider\nhidden for the caller's org is absent entirely.","type":"object"},"updated":{"description":"Updated is when the catalog was last refreshed, as the pricing source\nrecorded it.","type":"object"}},"type":"object"},"pricingRegionList":{"properties":{"regions":{"description":"Regions are the regions cloud instances can be placed in, each an opaque\nobject exactly as the pricing source emits it — typically id, name and\nlocation.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"pricingSyncOut":{"properties":{"status":{"description":"Status is \"ok\" when the sync completed.","type":"string"},"updated":{"description":"Updated is the RFC 3339 time the refreshed catalog was stamped with.","type":"string"}},"type":"object"},"pricingTierList":{"properties":{"tiers":{"description":"Tiers are the rentable GPU configurations, each an opaque object exactly\nas the pricing source emits it — typically id, name, accelerator count and\nmodel, VRAM, vCPU, memory and hourly price.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"pricingToolList":{"properties":{"tools":{"description":"Tools are the metered tools, each an opaque object exactly as the pricing\nsource emits it — typically name, billing unit and price.","items":{"additionalProperties":{"type":"object"},"type":"object"},"type":"array"}},"type":"object"},"productEvent":{"properties":{"distinctId":{"description":"DistinctID is the person/visitor the event is attributed to.","type":"string"},"event":{"description":"Event is the event name, e.g. page_viewed or signup_completed.","type":"string"},"id":{"description":"ID is the row's stable event id — the client's own idempotency id when it sent\none, else the server-minted one.","type":"string"},"path":{"description":"Path is the URL's path component, the key the topPages lens groups by.","type":"string"},"product":{"description":"Product is the surface that emitted the event. Omitted when absent.","type":"string"},"properties":{"description":"Properties is the row's attributes map as a JSON object (string values — the\nplane stores Map(String,String), so a nested value the caller sent is a\nJSON-encoded string). Omitted when the row carries none."},"sessionId":{"description":"SessionID groups the events of one visit. Omitted when the client sent none.","type":"string"},"timestamp":{"description":"Timestamp is when the event happened, RFC3339 UTC.","type":"string"},"type":{"description":"Type is the row's kind — the plane's discriminator: page, track, identify or\ngroup. (Errors are not here at all: they land on event.error and are read at\n/v1/errors.)","type":"string"},"url":{"description":"URL is the full page address the event fired on. Omitted when absent.","type":"string"}},"type":"object"},"productRow":{"properties":{"cluster":{"description":"hanzo-k8s","type":"string"},"declaredTag":{"description":"spec.image.tag on the App CR (declared truth)","type":"string"},"drift":{"description":"any drift flag present","type":"boolean"},"driftSeverity":{"description":"ok|yellow|red (rolled-up)","type":"string"},"env":{"description":"main|test|dev (lifecycle namespace)","type":"string"},"health":{"description":"green|yellow|red|unknown","type":"string"},"kind":{"description":"operator App CR spec.role (sql|kv|generic|ingress) or \"\"","type":"string"},"latestTag":{"description":"newest released tag (GH release reader — empty until wired)","type":"string"},"name":{"type":"string"},"namespace":{"description":"k8s namespace","type":"string"},"org":{"description":"image namespace (hanzoai|luxfi|docker.io/…)","type":"string"},"phase":{"description":"operator status.phase (Running/Creating/…)","type":"string"},"repo":{"description":"owner/repo image coordinate","type":"string"},"runningTag":{"description":"observed from the live Deployment","type":"string"},"tier":{"description":"derived: cloud|data|edge|daemon|paas|app (grouping)","type":"string"},"updated":{"type":"string"}},"type":"object"},"productsOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/productRow"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"profileMetrics":{"properties":{"funnel":{"$ref":"#/components/schemas/Funnel"},"launchProgress":{"$ref":"#/components/schemas/progressView"},"records":{"type":"integer"},"revenueCents":{"type":"integer"}},"type":"object"},"profileResponse":{"properties":{"keyMetrics":{"$ref":"#/components/schemas/profileMetrics"},"signals":{"additionalProperties":{"type":"boolean"},"type":"object"},"stage":{"type":"string"}},"type":"object"},"progressView":{"properties":{"done":{"type":"integer"},"next":{"type":"string"},"percent":{"type":"integer"},"total":{"type":"integer"}},"type":"object"},"projectPatch":{"properties":{"description":{"description":"Description is the board's free-form blurb, at most 32768 characters.","type":"string"},"key":{"description":"Key is the project to update, from the path.","type":"string"},"name":{"description":"Name is the project's display name. Non-empty, at most 256 characters.","type":"string"}},"type":"object"},"projectView":{"properties":{"applications":{"type":"integer"},"createdAt":{"type":"integer"},"description":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"slug":{"type":"string"}},"type":"object"},"projectionView":{"properties":{"apps":{"additionalProperties":{"type":"boolean"},"description":"Apps says, per console app, whether the org may open it. The SAME six keys are\nalways present (studio, bot, world, platform, team, admin), so a client maps\nover it unconditionally; a key is false both when the plan does not grant the\napp and when commerce could not be reached, because a read that decides what to\nSHOW fails to LOCKED rather than to an error.","type":"object"},"tier":{"description":"Tier is the plan slug commerce resolved for the org, or \"\" when the org has no\nactive licensing subscription — which the console treats as its free default.","type":"string"}},"type":"object"},"projectsBoundDomains":{"properties":{"bound":{"description":"Bound is the result of THIS call, one row per host in the request: live for\nan already-vouched host, pending with the DNS records to publish otherwise.","items":{"$ref":"#/components/schemas/projectsDomain"},"type":"array"},"domains":{"description":"Domains are the hostnames that are VERIFIED and routing right now, after\nthis bind.","items":{"type":"string"},"type":"array"},"org":{"description":"Org and Slug identify the site the hosts were bound to.","type":"string"},"slug":{"type":"string"}},"type":"object"},"projectsBuildSite":{"properties":{"brief":{"type":"string"},"model":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"type":"object"},"projectsComplete":{"properties":{"bytes":{"type":"integer"},"commit":{"type":"string"},"files":{"type":"integer"},"id":{"description":"ID is the queued deployment to complete, from the path.","type":"string"},"keys":{"description":"Keys is the manifest CI just uploaded, RELATIVE to the deployment prefix. It\nis what replaces `aws s3 sync --delete`: an upload grant authorizes writes\nonly, so CI cannot remove a file, and cloud reconciles the prefix against\nthis list instead (grant.go). Omit it and nothing is deleted — the prefix\nonly grows, which is the old pre-grant behaviour and a safe default.","items":{"type":"string"},"type":"array"},"liveUrl":{"type":"string"},"message":{"type":"string"},"slug":{"description":"Slug is the project the deployment belongs to, from the path.","type":"string"},"status":{"description":"live | error","type":"string"}},"type":"object"},"projectsCreate":{"properties":{"analytics":{"description":"Analytics is the opt-OUT for the wired-by-default analytics beacon: absent\n(nil) ⇒ ON (the default); explicit false ⇒ off. A pointer so \"unset\" is\ndistinguishable from \"false\" — the only way to turn the default off.","type":"boolean"},"description":{"type":"string"},"framework":{"type":"string"},"license":{"type":"string"},"name":{"type":"string"},"repo":{"properties":{"branch":{"type":"string"},"url":{"type":"string"}},"type":"object"},"slug":{"type":"string"},"upstream":{"description":"Upstream/License credit the third-party work this project was published\nfrom. Taken from any caller: disclaiming authorship can only cost the\npublisher credit, so it needs no gate (see Project.Upstream).","type":"string"},"visibility":{"description":"Visibility is \"public\" (the default when absent) or \"private\". Publishing\npublicly is ungated — that is the point of a community. Going PRIVATE is\nthe paid feature, so an unfunded org asking for it is refused rather than\nsilently downgraded (see resolve).","type":"string"}},"type":"object"},"projectsDeploySite":{"properties":{"files":{"items":{"$ref":"#/components/schemas/projectsFile"},"type":"array"},"name":{"type":"string"},"slug":{"type":"string"}},"type":"object"},"projectsDeployment":{"properties":{"bucket":{"type":"string"},"bytes":{"type":"integer"},"commit":{"type":"string"},"createdAt":{"type":"integer"},"files":{"type":"integer"},"id":{"type":"string"},"liveUrl":{"type":"string"},"message":{"type":"string"},"prefix":{"type":"string"},"projectId":{"type":"string"},"source":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"integer"},"upload":{"$ref":"#/components/schemas/projectsUploadGrant","description":"Upload is the prefix-scoped, short-lived S3 write grant handed to CI with a\nqueued git deployment, so it needs no bucket credential (grant.go). Present\nONLY on the 202 that creates the deployment — it is never stored and never\nreplayed on a later read, so a grant cannot outlive the build it was minted\nfor by being fetched again."},"version":{"type":"integer"}},"type":"object"},"projectsDomain":{"properties":{"createdAt":{"type":"integer"},"detail":{"type":"string"},"host":{"type":"string"},"records":{"items":{"$ref":"#/components/schemas/Record"},"type":"array"},"status":{"type":"string"},"url":{"type":"string"},"verified":{"type":"boolean"}},"type":"object"},"projectsDomains":{"properties":{"claims":{"description":"Claims is one row per host — live, or pending with the DNS records it still\nowes.","items":{"$ref":"#/components/schemas/projectsDomain"},"type":"array"},"domains":{"description":"Domains are the hostnames that are VERIFIED and routing right now.","items":{"type":"string"},"type":"array"},"org":{"description":"Org and Slug identify the site the panel belongs to.","type":"string"},"slug":{"type":"string"}},"type":"object"},"projectsDomainsBind":{"properties":{"domains":{"description":"Domains are the custom hostnames to attach, in order. An empty list is a\n400 rather than a clear — releasing a host is its own call.","items":{"type":"string"},"type":"array"},"slug":{"description":"Slug is the site the hosts attach to, from the path.","type":"string"}},"type":"object"},"projectsFile":{"properties":{"content":{"type":"string"},"path":{"type":"string"}},"type":"object"},"projectsFork":{"properties":{"name":{"description":"target project name (optional; defaults to the parent's title)","type":"string"},"slug":{"description":"parent slug to fork — catalog template or published project (required)","type":"string"},"target":{"description":"Target overrides the derived project slug (optional; defaults to the\nparent slug). Kept distinct from Slug so callers can rename on fork.","type":"string"},"variant":{"description":"Variant picks a template's format/page/theme (optional; defaults to the\ntemplate's first shape). This is the axis the catalog used to spend\nsibling slugs on, so it is expressed here, where the user's preference is.","type":"string"}},"type":"object"},"projectsOut":{"properties":{"data":{"description":"Data are the org's projects with canonical + retained counts.","items":{"$ref":"#/components/schemas/ProjectSummary"},"type":"array"},"total":{"description":"Total is len(data).","type":"integer"}},"type":"object"},"projectsProject":{"properties":{"analytics":{"description":"Analytics is the wired-by-default web-analytics flag (default true). It is the\nvalue the app's static-builder reads as deployment.analytics to inject the\nbeacon. Space is the project's Base data space (\"\u003corg\u003e/\u003cslug\u003e\") a deployed\nsite posts form/forum/data submissions to under /v1/base.","type":"boolean"},"bucket":{"type":"string"},"cacheControl":{"description":"Cache is the site's edge-cache state: the HTML/document Cache-Control policy\nin effect (TTL) and the last edge-purge time, so a console can show freshness.","type":"string"},"createdAt":{"type":"integer"},"currentDeploymentId":{"type":"string"},"description":{"type":"string"},"forkedFrom":{"description":"ForkedFrom is the parent this project was forked from (\"\u003corg\u003e/\u003cslug\u003e\" of a\npublished project, or a catalog template slug) — the attribution edge a\ngallery credits.","type":"string"},"framework":{"type":"string"},"hidden":{"type":"boolean"},"hiddenReason":{"type":"string"},"id":{"type":"string"},"key":{"description":"Key is the project's publishable ingest key, minted at create. It is the\nvalue the injected beacon carries and the ONE thing that attributes this\nsite's events; the static-builder reads it beside analytics.\n\nPublishable means it belongs in a page's source: it names a write scope and\nmints no principal, so it is returned in full rather than masked. Masking it\nwould only mean every caller needed a second endpoint to get the thing the\npage already ships.","type":"string"},"lastPurgeAt":{"type":"integer"},"license":{"type":"string"},"liveUrl":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"repo":{"$ref":"#/components/schemas/projectsRepo"},"slug":{"type":"string"},"space":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"integer"},"upstream":{"description":"Upstream/License credit the third-party work this project was published\nfrom, and the terms it carries. Omitted when nothing is declared: an absent\ncredit means \"nobody has said\", not \"there is nothing to say\".","type":"string"},"visibility":{"description":"Visibility is \"public\" or \"private\", and Hidden reports platform\nmoderation. Both are always present (never omitempty) so a consumer can\ntell a real answer from \"this API is too old to say\" — and so a console\nnever renders a project as public because a field was missing.\n\nAuthorship is deliberately absent: it is Org, above.","type":"string"}},"type":"object"},"projectsPublish":{"properties":{"slug":{"description":"Slug is the site to publish, from the path.","type":"string"},"source":{"description":"Source is the build output to promote, as a path RELATIVE to your org's own\nstorage space — never a URL and never a bucket. The org segment is prepended\nserver-side from the validated principal, so the worst a hostile source can\naddress is something your own org already owns.","type":"string"}},"type":"object"},"projectsRelease":{"properties":{"active":{"type":"boolean"},"bytes":{"type":"integer"},"createdAt":{"type":"integer"},"objects":{"type":"integer"},"releaseId":{"type":"string"},"slug":{"type":"string"},"source":{"type":"string"},"url":{"type":"string"}},"type":"object"},"projectsRepo":{"properties":{"branch":{"type":"string"},"provider":{"type":"string"},"url":{"type":"string"}},"type":"object"},"projectsSite":{"properties":{"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string"},"updatedAt":{"type":"integer"},"url":{"type":"string"}},"type":"object"},"projectsSiteDeploy":{"properties":{"deploymentId":{"description":"DeploymentID is the deployment this publish recorded, for the history.","type":"string"},"files":{"description":"Files are the site-relative paths that were uploaded, sorted.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the project's display name.","type":"string"},"slug":{"description":"Slug is the project the site was published into, created on the fly when\nthe slug was free.","type":"string"},"status":{"description":"Status is the deployment status, \"live\" on success.","type":"string"},"url":{"description":"URL is the canonical live URL, https://\u003cslug\u003e.\u003capex\u003e — empty when the\nsubdomain belongs to another tenant and this site has none.","type":"string"}},"type":"object"},"projectsUpdate":{"properties":{"cacheControl":{"type":"string"},"description":{"type":"string"},"framework":{"type":"string"},"hidden":{"description":"Hidden is MODERATION, and the only admin-gated field on this body: it pulls\na public project out of the catalogue from admin.hanzo.ai without editing\nthe publisher's own visibility choice, so un-hiding restores exactly what\nthey asked for. A tenant sending it is ignored.","type":"boolean"},"hiddenReason":{"type":"string"},"license":{"type":"string"},"name":{"type":"string"},"repo":{"properties":{"branch":{"type":"string"},"url":{"type":"string"}},"type":"object"},"slug":{"description":"Slug is the project to update, from the path. The URL is the addressing\nauthority — a `slug` in the body cannot move the write to another project.","type":"string"},"upstream":{"description":"Upstream/License credit the third-party work this app was published from —\nsettable after the fact, because the demos that need crediting most are the\nones already live. Pointers so \"\" clears a credit and absent leaves it.","type":"string"},"visibility":{"description":"Visibility flips an existing project between \"public\" and \"private\". Same\nONE rule as at create: public is free, private needs a paid plan.","type":"string"}},"type":"object"},"projectsUploadGrant":{"properties":{"expiresAt":{"type":"integer"},"fields":{"additionalProperties":{"type":"string"},"type":"object"},"maxBytes":{"type":"integer"},"prefix":{"type":"string"},"url":{"type":"string"}},"type":"object"},"promoIn":{"properties":{"active":{"description":"Active is the master switch: false parks the offer without deleting it.","type":"boolean"},"end":{"description":"End is when the offer closes (RFC3339).","type":"string"},"percentOff":{"description":"PercentOff is the discount, 0-100.","type":"integer"},"plans":{"description":"Plans are the plan ids the offer applies to.","items":{"type":"string"},"type":"array"},"start":{"description":"Start is when the offer opens (RFC3339).","type":"string"}},"type":"object"},"promoteReq":{"properties":{"app":{"description":"App is the application's slug, from the path.","type":"string"},"deploymentId":{"description":"DeploymentID promotes that deployment's exact built image. One of this and\nTag is required.","type":"string"},"project":{"description":"Project is the project the application lives under, from the path.","type":"string"},"tag":{"description":"Tag promotes an image tag, resolved the same way a deploy resolves one.","type":"string"}},"type":"object"},"promptDetail":{"properties":{"createdAt":{"description":"CreatedAt is when version 1 was written, RFC 3339 UTC. Appending a version\ndoes not move it.","type":"string"},"labels":{"description":"Labels is the current version's free-form taxonomy. `[]` when none, never\nnull.","items":{"type":"string"},"type":"array"},"lastUpdatedAt":{"description":"UpdatedAt is when the current version was appended, RFC 3339 UTC. Equal to\ncreatedAt for a prompt that has only ever had one version.","type":"string"},"name":{"description":"Name is the prompt's org-unique handle and the URL segment it is addressed by.","type":"string"},"prompt":{"description":"Prompt is the CURRENT version's template body — the only content this service\nreturns. Earlier versions are listed in versionHistory by number and date, and\ntheir bodies are not served in bulk.","type":"string"},"tags":{"description":"Tags is the second free-form taxonomy, same rules as Labels.","items":{"type":"string"},"type":"array"},"type":{"description":"Type labels the current version's kind; \"text\" unless the creator said\notherwise.","type":"string"},"version":{"description":"Version is the current version number, starting at 1 and incremented by one on\nevery create against an existing name.","type":"integer"},"versionHistory":{"description":"Versions is the history METADATA, newest first, capped at the last 100 — no\nbodies, so a long history cannot inflate this response. It always includes the\ncurrent version as its first entry.","items":{"$ref":"#/components/schemas/versionView"},"type":"array"}},"type":"object"},"promptList":{"properties":{"data":{"description":"Data is one row per prompt the org owns, each with its version numbers and\ntaxonomy — never the template bodies.","items":{"$ref":"#/components/schemas/promptMeta"},"type":"array"}},"type":"object"},"promptMeta":{"properties":{"labels":{"description":"Labels is the creator's free-form taxonomy, stored as given after trimming and\nde-duplication. Always present, `[]` when none — never null.","items":{"type":"string"},"type":"array"},"lastUpdatedAt":{"description":"LastUpdatedAt is when the newest version was appended, RFC 3339 UTC. Empty\nonly if the record carries no timestamp at all.","type":"string"},"name":{"description":"Name is the prompt's org-unique handle and the URL segment it is fetched by:\nGET /v1/prompts/\u003cname\u003e.","type":"string"},"tags":{"description":"Tags is the second free-form taxonomy under the same rules as Labels. Nothing\nin this service interprets either; they are yours to organize by.","items":{"type":"string"},"type":"array"},"type":{"description":"Type labels the template's kind, \"text\" unless the creator said otherwise. It\nis the CURRENT version's type; earlier versions may carry a different one.","type":"string"},"versions":{"description":"Versions lists every version NUMBER this prompt has, newest first, capped at\nthe last 100. The highest is the current one. (On a metrics row the same key\nis a count, not a list.)","items":{"type":"integer"},"type":"array"}},"type":"object"},"promptReq":{"properties":{"labels":{"description":"Labels is free-form taxonomy, each up to 64 characters, capped at 32 entries.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the org-unique handle AND the URL segment the prompt is addressed by:\n1-64 characters matching ^[A-Za-z0-9][A-Za-z0-9._-]*$. \"metrics\", \"new\" and\n\"catalog\" are reserved. A name that already exists appends a new version.","type":"string"},"prompt":{"description":"Prompt is the template body, capped at 64 KiB. It holds template text only —\nnever a secret.","type":"string"},"tags":{"description":"Tags is free-form taxonomy under the same bounds as Labels.","items":{"type":"string"},"type":"array"},"type":{"description":"Type labels the template's kind; defaults to \"text\".","type":"string"}},"type":"object"},"providerCard":{"properties":{"id":{"description":"ID is the provider slug used in the path: digitalocean, aws, gcp, azure.","type":"string"},"keyless":{"description":"Keyless is whether the provider can be linked WITHOUT storing a long-lived\nsecret — AWS by role assumption, GCP by workload identity federation, Azure\nby federated credential. DigitalOcean is not: it needs a stored token.","type":"boolean"},"name":{"description":"Name is the provider's display name.","type":"string"},"requires":{"description":"Requires names the credential fields a link body must carry for this\nprovider.","items":{"type":"string"},"type":"array"}},"type":"object"},"providerPatchIn":{"properties":{"beta":{"type":"boolean"},"betaOrgs":{"items":{"type":"string"},"type":"array"},"enabled":{"type":"boolean"},"name":{"description":"Name is the provider the overlay belongs to, from the URL.","type":"string"},"overrides":{},"state":{"type":"string"}},"type":"object"},"providerView":{"properties":{"available":{"description":"Available is whether THIS DEPLOYMENT has the provider's app credentials, so\nconnect can succeed. False renders the card without a working Connect button.","type":"boolean"},"category":{"description":"Category groups the card (\"Communication\", \"Developer\", \"Marketing\").","type":"string"},"connected":{"description":"Connected is whether this org has a live connection to the provider.","type":"boolean"},"connection":{"$ref":"#/components/schemas/connectionView","description":"Connection is the connected account's non-secret detail. Absent when the org\nhas no connection; tokens NEVER appear here (they live only in KMS)."},"description":{"description":"Description is the one-line pitch the console card shows.","type":"string"},"id":{"description":"ID is the provider's registry id and the :provider path segment (\"slack\").","type":"string"},"name":{"description":"Name is the provider's display name (\"Slack\").","type":"string"}},"type":"object"},"providersView":{"properties":{"providers":{"description":"Providers is every cloud this deployment can link, with what each needs.","items":{"$ref":"#/components/schemas/providerCard"},"type":"array"}},"type":"object"},"provisionRequest":{"properties":{"instance":{"type":"string"},"name":{"type":"string"}},"type":"object"},"provisionResult":{"properties":{"connectionString":{"type":"string"},"database":{"type":"string"},"host":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"name":{"type":"string"},"password":{"type":"string"},"port":{"type":"integer"},"status":{"type":"string"},"username":{"type":"string"}},"type":"object"},"provisionedResource":{"properties":{"database":{"description":"Database is the logical database, collection, index or bucket this\nresource resolves to on its backend.","type":"string"},"host":{"description":"Host is the address that actually routes to this resource — a dedicated\ninstance's own in-cluster Service, or the public gateway for a shared one.","type":"string"},"id":{"description":"ID is the resource's server-minted handle, \"rs_\"-prefixed.","type":"string"},"kind":{"description":"Kind is the product: sql, vector, datastore, kv, search, s3 or docdb.","type":"string"},"name":{"description":"Name is the org-unique slug the caller provisioned the resource under.","type":"string"},"port":{"description":"Port is the port a client connects to on Host.","type":"integer"},"status":{"description":"Status is \"ready\", or \"provisioning\" while a dedicated instance is still\nbeing materialized. A dedicated resource's status is reconciled from the\noperator's live CR before this is answered, so it is never a stale ready.","type":"string"},"username":{"description":"Username is the credential's user, for the kinds that mint one per\nresource. Absent for a kind whose backend authenticates with a shared,\nout-of-band key.","type":"string"}},"type":"object"},"provisionedSummary":{"properties":{"createdAt":{"description":"CreatedAt is when the resource was provisioned, in unix seconds.","type":"integer"},"host":{"description":"Host is the address that actually routes to this resource — a dedicated\ninstance's own in-cluster Service, or the public gateway for a shared one.\nNever the internal admin address of a shared backend.","type":"string"},"id":{"description":"ID is the resource's server-minted handle, \"rs_\"-prefixed.","type":"string"},"kind":{"description":"Kind is the product: sql, vector, datastore, kv, search, s3 or docdb.","type":"string"},"name":{"description":"Name is the org-unique slug the caller provisioned the resource under.","type":"string"},"port":{"description":"Port is the port a client connects to on Host.","type":"integer"},"status":{"description":"Status is \"ready\", or \"provisioning\" while a dedicated instance is still\nbeing materialized by the operator.","type":"string"}},"type":"object"},"publishKitIn":{"properties":{"category":{"description":"Category groups the kit in the gallery browser.","type":"string"},"demo":{"description":"Demo is the deployed site itself, when there is one.","type":"string"},"description":{"description":"Description is the browse-card blurb, max 4096 characters.","type":"string"},"features":{"description":"Features are the highlights the card lists, at most 32.","items":{"type":"string"},"type":"array"},"framework":{"description":"Framework is the stack the kit is built on (\"Next.js 14\").","type":"string"},"preview":{"description":"Preview is the still image the browse card renders, max 4096 characters.","type":"string"},"slug":{"description":"Slug is the kit's identity — lowercase alphanumeric with dashes, max 40.","type":"string"},"source":{"description":"Source is the repository the kit is forked from, max 4096 characters.","type":"string"},"title":{"description":"Title is the display name. Required, max 200 characters.","type":"string"},"useCase":{"description":"UseCase is what the kit is for, in a phrase.","type":"string"},"variants":{"description":"Variants are the shapes this kit ships in, at most 32; the fork picks one.","items":{"$ref":"#/components/schemas/Variant"},"type":"array"}},"type":"object"},"publishReq":{"properties":{"category":{"description":"Category groups the listing in the shop window.","type":"string"},"currency":{"description":"Currency denominates Price.","type":"string"},"description":{"description":"Description is the long copy, clipped at 4096 characters.","type":"string"},"price":{"description":"Price is the per-call price as a decimal USD string, exact to 18 places —\n\"0.0025\" is a quarter of a cent and stays one. Empty or \"0\" (the default)\npublishes it free; any positive price makes the listing monetized and\nrequires Recipient.","type":"string"},"public":{"description":"Public makes the listing discoverable by other orgs. Private otherwise.","type":"boolean"},"recipient":{"description":"Recipient is the seller's payout wallet ID, in the publishing org — the\nwallet x402 pays. Required for a monetized listing.","type":"string"},"title":{"description":"Title is the shop-window name, 1-200 characters. Required.","type":"string"},"tool":{"description":"Tool is the registry name of the capability being offered. It must already\nresolve in the publisher's own scope — there are no phantom listings.","type":"string"}},"type":"object"},"purgeIn":{"properties":{"files":{"description":"Files purges exactly the listed URLs — at most 30, Cloudflare's per-request cap.","items":{"type":"string"},"type":"array"},"purge_everything":{"description":"Everything drops the zone's entire edge cache.","type":"boolean"},"zone":{"description":"Zone is the 32-hex Cloudflare zone id, from the path.","type":"string"}},"type":"object"},"purgeOut":{"properties":{"purged":{"description":"Purged is the number of messages removed.","type":"integer"}},"type":"object"},"pushFile":{"properties":{"content":{"description":"Content is the file's bytes, carried per Encoding.","type":"string"},"encoding":{"description":"Encoding is \"base64\", or \"utf-8\" (the default, also \"utf8\" / \"text\").","type":"string"},"path":{"description":"Path is repo-relative. Absolute or traversing paths are refused.","type":"string"}},"type":"object"},"pushReq":{"properties":{"branch":{"description":"Branch to advance; empty means \"main\". A fresh branch that is the repo's\nfirst also becomes HEAD.","type":"string"},"files":{"description":"Files are added to or overwritten on the branch tip — files already there\nand not listed SURVIVE. At least one, at most 5000, 32 MiB each.","items":{"$ref":"#/components/schemas/pushFile"},"type":"array"},"message":{"description":"Message is the commit message; empty gets a generated one.","type":"string"},"name":{"description":"Name is the repo to push into, from the :name path segment. It is CREATED\non first push if it does not exist.","type":"string"}},"type":"object"},"pushResp":{"properties":{"branch":{"description":"Branch is the branch that was advanced, resolved (never empty).","type":"string"},"cloneUrl":{"description":"CloneURL is the repo's HTTPS remote.","type":"string"},"commit":{"description":"Commit is the new commit's full hash.","type":"string"},"sshUrl":{"description":"SSHURL is the repo's scp-style SSH remote.","type":"string"}},"type":"object"},"rateSet":{"properties":{"id":{"description":"ID is the affiliate whose direct rate moves, from the path.","type":"string"},"rateBps":{"description":"RateBps is the direct commission rate, in basis points of Hanzo's margin;\ncapped so the whole L1+L2+L3 schedule never exceeds the margin. Body-only\n(`url:\"-\"`): a money parameter must never ride the URL into access logs.","type":"integer"}},"type":"object"},"rawOut":{"properties":{"data":{"type":"object"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"readOut":{"properties":{"messages":{"description":"Messages is what was read, stream-ordered.","items":{"$ref":"#/components/schemas/Message"},"type":"array"}},"type":"object"},"readiness":{"properties":{"crd":{"description":"CRD is whether the operator App CRD was found, and is absent when no\ncluster client resolved and the question could not be asked.","type":"boolean"},"error":{"description":"Error is the real reason the plane is degraded; absent when it is not.","type":"string"},"k8s":{"description":"K8s is whether a cluster client resolved at all. False means no kubeconfig.","type":"boolean"},"service":{"description":"Service is always \"platform\" — which control plane answered.","type":"string"},"status":{"description":"Status is \"ok\" when this plane can deploy, \"degraded\" when it cannot.","type":"string"}},"type":"object"},"readingReq":{"properties":{"account":{"description":"Account is the provider-side account the sample belongs to.","type":"string"},"cachedInputTokens":{"description":"CachedInputTokens is the window's cached-prompt-token count.","type":"integer"},"confidence":{"description":"Confidence says how real the counters are, as the meter graded itself.","type":"string"},"costCents":{"description":"CostCents is the window's spend in cents, as the provider's meter states it.","type":"integer"},"costLimitCents":{"description":"CostLimitCents is the window's spend cap in cents, when the meter knows one.","type":"integer"},"currency":{"description":"Currency is the ISO currency the cost fields are stated in.","type":"string"},"inputTokens":{"description":"InputTokens is the window's prompt-token count.","type":"integer"},"kind":{"description":"Kind is subscription or apikey; anything else is refused.","type":"string"},"lane":{"description":"Lane names the meter's own lane label for this measurement.","type":"string"},"machine":{"description":"Machine is the machine the collector observed the account on. Required.","type":"string"},"outputTokens":{"description":"OutputTokens is the window's completion-token count.","type":"integer"},"plan":{"description":"Plan is the provider plan label the account is on.","type":"string"},"provider":{"description":"Provider is the AI provider whose meter reported this sample. Required.","type":"string"},"requests":{"description":"Requests is the window's request count.","type":"integer"},"resetsAt":{"description":"ResetsAt is when the window resets, RFC 3339, bounded.","type":"string"},"synthetic":{"description":"Synthetic marks a sample the collector derived rather than observed.","type":"boolean"},"totalTokens":{"description":"TotalTokens is the window's total token count.","type":"integer"},"usedPct":{"description":"UsedPct is how much of the window's allowance is consumed, clamped 0..100.","type":"number"},"window":{"description":"Window is the window class, one of 6h, day, week, month; anything else is\nrefused rather than silently reclassified.","type":"string"},"windowMinutes":{"description":"WindowMinutes is the window's length as the meter reported it.","type":"integer"},"windowStart":{"description":"WindowStart is when the measured window opened, RFC 3339, bounded to a\nsane interval around now.","type":"string"}},"type":"object"},"readingView":{"properties":{"account":{"description":"Account is the provider-side account the sample belongs to.","type":"string"},"cachedInputTokens":{"description":"CachedInputTokens is the window's cached-prompt-token count.","type":"integer"},"confidence":{"description":"Confidence says whether the counters that remain mean anything, as the\nmeter graded itself.","type":"string"},"costCents":{"description":"CostCents is the window's spend in cents, as the provider's meter states it.","type":"integer"},"costLimitCents":{"description":"CostLimitCents is the window's spend cap in cents, when the meter knows one.","type":"integer"},"currency":{"description":"Currency is the ISO currency the cost fields are stated in.","type":"string"},"inputTokens":{"description":"InputTokens is the window's prompt-token count.","type":"integer"},"lane":{"description":"Lane names the meter's own lane label for this measurement.","type":"string"},"machine":{"description":"Machine is the machine the collector observed the account on.","type":"string"},"outputTokens":{"description":"OutputTokens is the window's completion-token count.","type":"integer"},"plan":{"description":"Plan is the provider plan label the account is on.","type":"string"},"requests":{"description":"Requests is the window's request count.","type":"integer"},"resetsAt":{"description":"ResetsAt is when the window resets, RFC 3339 UTC.","type":"string"},"synthetic":{"description":"Synthetic marks a sample the collector derived rather than observed.","type":"boolean"},"totalTokens":{"description":"TotalTokens is the window's total token count.","type":"integer"},"usedPct":{"description":"UsedPct is how much of the window's allowance is consumed, 0..100.","type":"number"},"window":{"description":"Window is the window class: 6h, day, week or month.","type":"string"},"windowMinutes":{"description":"WindowMinutes is the window's length as the meter reported it.","type":"integer"},"windowStart":{"description":"WindowStart is when the measured window opened, RFC 3339 UTC.","type":"string"}},"type":"object"},"readmeJSON":{"properties":{"content":{"description":"Content is the file's text, verbatim and unrendered.","type":"string"},"encoding":{"description":"Encoding is always \"utf8\" — a README is text by definition.","type":"string"},"path":{"description":"Path is the file the README was found at (README.md, README, …).","type":"string"}},"type":"object"},"recordList":{"properties":{"accreditation":{"description":"Accreditation is the org's tracked accreditation-state records.","items":{"$ref":"#/components/schemas/accView"},"type":"array"},"disclaimer":{"description":"Disclaimer states that statuses are provider-reported or tracked, never a\nplatform assertion of legal or regulatory compliance.","type":"string"},"verifications":{"description":"Verifications is the org's KYC/KYB checks, provider-reported statuses only.","items":{"$ref":"#/components/schemas/checkView"},"type":"array"}},"type":"object"},"refJSON":{"properties":{"name":{"description":"Name is the short ref name (\"main\", \"v1.2.0\"), not the full refs/… path.","type":"string"},"sha":{"description":"SHA is the full commit hash the ref resolves to.","type":"string"}},"type":"object"},"referralBoard":{"properties":{"accrualByLevel":{"$ref":"#/components/schemas/levelSplit","description":"AccrualByLevel splits the lifetime accrual across the three upline levels —\nhow much of the liability comes from direct referrals versus the chain above."},"conversion":{"$ref":"#/components/schemas/funnel","description":"Conversion is the funnel: referred orgs against those that actually earned."},"summary":{"$ref":"#/components/schemas/tally","description":"Summary is the fleet tally — population by status, and lifetime accrued, paid\nand still-owed commission."},"topReferrers":{"description":"TopReferrers is the 25 affiliates with the most lifetime accrued commission,\ndescending, orgs named.","items":{"$ref":"#/components/schemas/referrerRow"},"type":"array"}},"type":"object"},"referralsOut":{"properties":{"data":{"$ref":"#/components/schemas/referralBoard","description":"Data is the referral board: leaders, funnel, tally and per-level liability."},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"referrerRow":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued, in cents. The board is sorted by\nthis, descending.","type":"integer"},"code":{"description":"Code is that affiliate's minted referral code; empty if it is not approved.","type":"string"},"org":{"description":"Org is the partner's own org slug. Named only here, on the SuperAdmin board —\nthe partner-facing leaderboard shows an opt-in handle and never an org.","type":"string"},"pendingCents":{"description":"PendingCents is accrued minus paid, in cents — what is still owed to this\naffiliate. Never negative.","type":"integer"},"referredCount":{"description":"ReferredCount is how many orgs this affiliate is the DIRECT referrer of —\nits level-1 downline, not the whole three-level chain.","type":"integer"},"status":{"description":"Status is \"applied\", \"approved\" or \"suspended\".","type":"string"}},"type":"object"},"refreshOut":{"properties":{"connector":{"$ref":"#/components/schemas/connView","description":"Connector is the connector with its new expiry."},"refreshed":{"description":"Refreshed is always true — a failed rotation is an HTTP error.","type":"boolean"}},"type":"object"},"refsJSON":{"properties":{"branches":{"description":"Branches are the repo's heads; empty on a repo with no commits.","items":{"$ref":"#/components/schemas/refJSON"},"type":"array"},"default":{"description":"Default is the branch name a caller gets when it asks for no ref.","type":"string"},"tags":{"description":"Tags are the repo's tags; empty when there are none.","items":{"$ref":"#/components/schemas/refJSON"},"type":"array"}},"type":"object"},"registerCounts":{"properties":{"byStage":{"additionalProperties":{"type":"integer"},"description":"ByStage counts formations per stage, keyed by the stage name.","type":"object"},"total":{"description":"Total is every formation in the register.","type":"integer"}},"type":"object"},"registerKeyReq":{"properties":{"publicKey":{"description":"PublicKey is one OpenSSH authorized-key line (\"ssh-ed25519 AAAA… you@host\").\nRequired; a line that does not parse is refused and never stored.","type":"string"},"title":{"description":"Title labels the key in the console. Max 256 chars; when omitted the\ncomment on the key line is used.","type":"string"}},"type":"object"},"registerPage":{"properties":{"count":{"description":"Count is how many rows this page holds.","type":"integer"},"formations":{"description":"Formations are the rows, newest activity first.","items":{"$ref":"#/components/schemas/Registration"},"type":"array"},"limit":{"description":"Limit is the page size that was applied.","type":"integer"},"offset":{"description":"Offset is the offset that was applied.","type":"integer"}},"type":"object"},"registerReq":{"properties":{"account":{"type":"string"},"actor":{"type":"string"},"agent":{"type":"string"},"cwd":{"type":"string"},"host":{"description":"Execution context — where this session runs (all optional).","type":"string"},"parentSessionId":{"type":"string"},"project":{"description":"The readable build (provenance.go): which product this session builds, and\nwhether its story may be read by the world.","type":"string"},"provider":{"description":"Account tag — the linked AI account this session ran under (login manager).","type":"string"},"published":{"type":"boolean"},"repo":{"type":"string"},"status":{"type":"string"},"target":{"type":"string"},"taskRunId":{"type":"string"},"taskWorkflowId":{"type":"string"},"terminal":{"description":"Terminal is the URL this session's live terminal is published at, so the\nconsole can watch it. Optional — a session that publishes nothing is still\na session.","type":"string"},"title":{"type":"string"}},"type":"object"},"registrationView":{"properties":{"id":{"description":"ID is the registration's handle.","type":"string"},"nodeID":{"description":"NodeID is the luxd node the registration is for.","type":"string"},"status":{"description":"Status is the registration's lifecycle state; \"pending_owner_approval\" until\nthe owner co-signs it out of band.","type":"string"}},"type":"object"},"registryImage":{"properties":{"name":{"description":"Name is the repository name inside the org's namespace (e.g. \"cloud\").","type":"string"},"ref":{"description":"Ref is the full pullable reference (host + org + name) the docker CLI\ntakes verbatim.","type":"string"}},"type":"object"},"registryImageList":{"properties":{"data":{"description":"Data is the org's repositories.","items":{"$ref":"#/components/schemas/registryImage"},"type":"array"},"truncated":{"description":"Truncated is true when the catalog walk hit its page bound before the\nregistry was exhausted — the list is a prefix, not the whole.","type":"boolean"}},"type":"object"},"registryMint":{"properties":{"image":{"description":"Image is the repository name inside the org's namespace (e.g. \"cloud\").","type":"string"}},"type":"object"},"registryPackage":{"properties":{"description":{"description":"Description says what the package is, as published.","type":"string"},"name":{"description":"Name is the package name (`\u003corg\u003e` or `@\u003corg\u003e/…`).","type":"string"},"updated":{"description":"Updated is when the package last changed, as the registry reports it.","type":"string"},"version":{"description":"Version is the latest published version.","type":"string"}},"type":"object"},"registryPackageList":{"properties":{"data":{"description":"Data is the packages in the org's scope.","items":{"$ref":"#/components/schemas/registryPackage"},"type":"array"}},"type":"object"},"registryProject":{"properties":{"images":{"description":"Images is how many of the org's repositories the OCI catalog holds.","type":"integer"},"packages":{"description":"Packages is how many of the org's packages the npm registry reports.","type":"integer"},"project":{"description":"Project is the namespace: the org's slug, which prefixes its image names\nand scopes its npm packages.","type":"string"}},"type":"object"},"registryProjectList":{"properties":{"data":{"description":"Data is the namespaces visible to the caller.","items":{"$ref":"#/components/schemas/registryProject"},"type":"array"}},"type":"object"},"registryStatus":{"properties":{"host":{"description":"Host is the OCI registry host clients push to and pull from.","type":"string"},"oci":{"description":"Oci is true when the OCI registry answered its /v2/ probe.","type":"boolean"},"pkg":{"description":"Pkg is true when the npm registry answered its ping.","type":"boolean"},"pkgHost":{"description":"PkgHost is the npm registry host.","type":"string"},"realm":{"description":"Realm is the token endpoint the OCI registry's challenge advertises,\npresent only when the registry is reachable and auth-gated.","type":"string"},"service":{"description":"Service is the token service name from the same challenge.","type":"string"}},"type":"object"},"registryTagList":{"properties":{"data":{"description":"Data is the tag names, as the registry reports them.","items":{"type":"string"},"type":"array"},"image":{"description":"Image is the repository name inside the org's namespace.","type":"string"},"ref":{"description":"Ref is the full repository reference the tags belong to.","type":"string"}},"type":"object"},"registryToken":{"properties":{"expires":{"description":"Expires is the token's lifetime in seconds.","type":"integer"},"ref":{"description":"Ref is the one repository reference the token can pull.","type":"string"},"token":{"description":"Token is the bearer to present on the OCI wire\n(`Authorization: Bearer …` against the host's /v2/ routes).","type":"string"}},"type":"object"},"releaseBoard":{"properties":{"releases":{"description":"Releases are the deployments that genuinely reached the cluster.","items":{"$ref":"#/components/schemas/releaseRow"},"type":"array"}},"type":"object"},"releaseRow":{"properties":{"environment":{"description":"Environment is the deploy target the application names.","type":"string"},"id":{"description":"ID is the deployment's id — a release IS a deployment that reached the\ncluster.","type":"string"},"name":{"description":"Name is the application the release belongs to.","type":"string"},"releasedAt":{"description":"ReleasedAt is when the deployment last changed, RFC3339 UTC.","type":"string"},"status":{"description":"Status is deploying or live — the two states that mean released.","type":"string"},"version":{"description":"Version is the released image tag, or v\u003cn\u003e when the image carries none.","type":"string"}},"type":"object"},"remittance":{"properties":{"amountCents":{"description":"AmountCents is the amount disbursed, in cents. It was reserved against pending\ncommission atomically when recorded, so it never exceeds what was owed.","type":"integer"},"createdAt":{"description":"CreatedAt is when the payout was recorded, Unix seconds UTC — when the balance\nmoved, not necessarily when the cash landed.","type":"integer"},"id":{"description":"ID is the payout row's server-minted handle, \"apo_\"-prefixed.","type":"string"},"method":{"description":"Method is how it was settled. \"credits\" issued a commerce grant into the\naffiliate org's own wallet; any other value (wire, paypal, check, …) is a\nRECORD of cash a human moved out of band.","type":"string"},"reference":{"description":"Reference is the operator's settlement note — a bank id, a ledger ref. Free\ntext, absent when none was given.","type":"string"},"txn":{"description":"Txn is the commerce ledger transaction id, set ONLY where a \"credits\" payout\nactually issued the grant. Absent for cash methods, which write no ledger row.","type":"string"}},"type":"object"},"replaceKitIn":{"properties":{"category":{"description":"Category groups the kit in the gallery browser.","type":"string"},"demo":{"description":"Demo is the deployed site itself, when there is one.","type":"string"},"description":{"description":"Description is the browse-card blurb, max 4096 characters.","type":"string"},"features":{"description":"Features are the highlights the card lists, at most 32.","items":{"type":"string"},"type":"array"},"framework":{"description":"Framework is the stack the kit is built on (\"Next.js 14\").","type":"string"},"preview":{"description":"Preview is the still image the browse card renders, max 4096 characters.","type":"string"},"slug":{"description":"Slug is the kit to replace, from the path.","type":"string"},"source":{"description":"Source is the repository the kit is forked from, max 4096 characters.","type":"string"},"title":{"description":"Title is the display name. Required, max 200 characters.","type":"string"},"useCase":{"description":"UseCase is what the kit is for, in a phrase.","type":"string"},"variants":{"description":"Variants are the shapes this kit ships in, at most 32; the fork picks one.","items":{"$ref":"#/components/schemas/Variant"},"type":"array"}},"type":"object"},"replayBody":{"properties":{"distinctId":{"type":"string"},"events":{"items":{},"type":"array"},"sessionId":{"type":"string"},"windowId":{"type":"string"}},"type":"object"},"repoList":{"properties":{"data":{"description":"Data holds the repos in scope, most recently updated first.","items":{"$ref":"#/components/schemas/repoView"},"type":"array"}},"type":"object"},"repoTree":{"properties":{"files":{"description":"Files are the repo's indexed files in path order, each with its language\nand how many symbols it defines. Never null.","items":{"$ref":"#/components/schemas/TreeEntry"},"type":"array"},"repo":{"description":"Repo echoes the repository that was walked.","type":"string"}},"type":"object"},"repoView":{"properties":{"branches":{"description":"Branches are the repo's branch names. Read live, so the detail view carries\nthem and a list row does not.","items":{"type":"string"},"type":"array"},"cloneUrl":{"description":"CloneURL is the HTTPS smart-HTTP remote `git clone` takes.","type":"string"},"createdAt":{"description":"CreatedAt is RFC 3339 UTC.","type":"string"},"defaultBranch":{"description":"DefaultBranch is where HEAD points on a fresh repo (\"main\").","type":"string"},"description":{"description":"Description is the caller-supplied blurb (max 4KiB).","type":"string"},"head":{"description":"Head is the resolved HEAD commit, empty on an empty repo.","type":"string"},"id":{"description":"ID is the repo's stable, prefixed identifier (\"repo_\" + 128 random bits).","type":"string"},"name":{"description":"Name is the org-unique handle, and the last path segment of both URLs below.","type":"string"},"org":{"description":"Org owns the repo — the gateway-minted X-Org-Id, and the isolation key.","type":"string"},"project":{"description":"Project is the optional sub-scope the repo lives in; absent for the org's\ndefault scope.","type":"string"},"public":{"description":"Public grants ANONYMOUS read (fetch) only; push and the whole control plane\nstay org-authed.","type":"boolean"},"sizeBytes":{"description":"SizeBytes is the repo's measured on-disk size, re-measured on create, after\neach push, and after a gc. This is the number billing meters.","type":"integer"},"sshUrl":{"description":"SSHURL is the scp-style SSH remote (git@host:org/repo.git).","type":"string"},"updatedAt":{"description":"UpdatedAt is RFC 3339 UTC, empty until the first write.","type":"string"}},"type":"object"},"reportOut":{"properties":{"delivered":{"description":"Delivered is true when a waiting durable owner received this result. False\nmeans there was none to deliver to — an unknown or already-finished run — which\nis a clean no-op, not an error.","type":"boolean"}},"type":"object"},"reportReq":{"properties":{"account":{"description":"Account is the linked account the window was metered from.","type":"string"},"cachedInputTokens":{"description":"CachedInputTokens is the prompt tokens the provider served from cache.","type":"integer"},"confidence":{"description":"Confidence says how much the counters below mean.","type":"string"},"costCents":{"description":"CostCents is what the window cost on the PROVIDER's own plan, in US cents.","type":"integer"},"costLimitCents":{"description":"CostLimitCents is the plan's spend ceiling for the window, in US cents.","type":"integer"},"currency":{"description":"Currency is the provider's currency when it is not US cents.","type":"string"},"inputTokens":{"description":"InputTokens is prompt tokens consumed in the window.","type":"integer"},"kind":{"description":"Kind is subscription or apikey. Empty is accepted; anything else is\nrefused.","type":"string"},"lane":{"description":"Lane is the meter lane within the account.","type":"string"},"machine":{"description":"Machine is the host whose meter read the window. Required on every sample.","type":"string"},"outputTokens":{"description":"OutputTokens is completion tokens produced in the window.","type":"integer"},"plan":{"description":"Plan is the subscription plan the account is on, as the provider names it.","type":"string"},"provider":{"description":"Provider is the upstream the account belongs to, e.g. anthropic. Required\non every sample.","type":"string"},"requests":{"description":"Requests is how many requests the window covers.","type":"integer"},"resetsAt":{"description":"ResetsAt is when the measured window rolls over, RFC3339.","type":"string"},"samples":{"description":"Samples is the batch form: every lane a poller measured, in one call. When\nit is non-empty the top-level sample fields are ignored.","items":{"$ref":"#/components/schemas/sampleReq"},"type":"array"},"synthetic":{"description":"Synthetic marks a window the meter inferred rather than read.","type":"boolean"},"totalTokens":{"description":"TotalTokens is the window's total tokens.","type":"integer"},"usedPct":{"description":"UsedPct is how much of the window's allowance is consumed, 0–100.","type":"number"},"window":{"description":"Window is the window class: 6h, day, week or month. Required, and a class\nthis surface does not know is refused rather than rewritten.","type":"string"},"windowMinutes":{"description":"WindowMinutes is the window's real length in minutes, as the meter reports\nit.","type":"integer"},"windowStart":{"description":"WindowStart is when the measured window opened, RFC3339. This is how a\nbackfill states WHICH window it measured; the server always owns the\nobservation clock, so there is no timestamp field.","type":"string"}},"type":"object"},"reportResp":{"properties":{"accepted":{"description":"Accepted is how many samples passed validation. Every one of them was\naccepted, or the whole report was refused — there is no partial success.","type":"integer"},"stored":{"description":"Stored is whether the warehouse actually persisted them. False means the\ndatastore was unavailable and the poll of history was lost; the request\nstill succeeded, so a device retries without being blocked.","type":"boolean"}},"type":"object"},"reportRunIn":{"properties":{"branch":{"description":"Branch, CommitSha and Diffstat describe what the run produced; Error is the\nfailure when OK is false. Each is clamped, never rejected.","type":"string"},"changed":{"type":"boolean"},"commitSha":{"type":"string"},"diffstat":{"type":"string"},"error":{"type":"string"},"id":{"description":"ID is the machine reporting, from the path.","type":"string"},"ok":{"description":"OK is whether the run succeeded; Changed whether it produced any commit.","type":"boolean"},"runId":{"description":"RunID is the routed run being completed, from the path.","type":"string"}},"type":"object"},"resourceUsage":{"properties":{"costCents":{"type":"number"},"cpuVcpuHours":{"type":"number"},"memGbHours":{"type":"number"},"storageIoBytes":{"type":"number"}},"type":"object"},"restartRef":{"properties":{"app":{"description":"App is the service's CR name, from the path. It must be a DNS-1123 label.","type":"string"},"env":{"description":"Env is REQUIRED and must be main, test or dev. A bare call does not default\nto production, which is what closes the fat-finger and confused-deputy\nhazard.\n\nIt carries no `validate:\"required\"`: the handler already refuses an empty env\nwith the sentence that names the three values, and a validator tag would\nreplace that sentence with a generic one. The requirement is stated here and\nenforced there, once.","type":"string"}},"type":"object"},"restarted":{"properties":{"app":{"description":"App is the service that was restarted.","type":"string"},"env":{"description":"Env is that namespace's lifecycle env.","type":"string"},"namespace":{"description":"Namespace is the namespace its Deployment was patched in.","type":"string"},"ok":{"description":"OK is always true — a failure is an error, not a false here.","type":"boolean"},"restartedAt":{"description":"RestartedAt is the timestamp stamped onto the pod template, RFC3339 UTC.","type":"string"}},"type":"object"},"retentionCohort":{"properties":{"cohort":{"type":"string"},"size":{"type":"integer"},"values":{"items":{"type":"number"},"type":"array"}},"type":"object"},"retentionGrid":{"properties":{"cohorts":{"items":{"$ref":"#/components/schemas/retentionCohort"},"type":"array"},"interval":{"description":"\"month\"","type":"string"},"periods":{"type":"integer"}},"type":"object"},"reviewQueue":{"properties":{"count":{"description":"Count is how many founders are waiting.","type":"integer"},"queue":{"description":"Queue is one entry per unsettled founder, oldest formation first.","items":{"$ref":"#/components/schemas/waiting"},"type":"array"}},"type":"object"},"revokeResp":{"properties":{"links":{"description":"Links is each revoked row with its new status — retained, not deleted, so\nusage history and the audit trail survive the log-out.","items":{"$ref":"#/components/schemas/linkView"},"type":"array"},"revoked":{"description":"Revoked is how many links this call revoked.","type":"integer"},"sessionsStopped":{"description":"SessionsStopped is how many of the caller's own agent sessions stopped. A\nstop that fails does not fail the revoke, so this may honestly report fewer.","type":"integer"}},"type":"object"},"revokedKey":{"properties":{"ok":{"description":"OK is true when the key was revoked. A failure is an error status, never a\nfalse here.","type":"boolean"},"type":{"description":"Type is the key class that was revoked, resolved — so a caller that named\nnothing can see it revoked the secret key.","type":"string"}},"type":"object"},"riskAdoptIn":{"properties":{"address":{"description":"Address is one of YOUR organisation's own published values (GET\n/v1/risk/state reports them, and a search reports the one it fitted for you).\nAn address your organisation has not published is NOT FOUND — including one\nanother organisation published, because an address names a value and never\nauthorises reading it.","type":"string"}},"type":"object"},"riskAggregates":{"properties":{"bound":{"description":"Bound is the most they can hold. It is a per-organisation bound: at it, this\norganisation degrades and no other one notices.","type":"integer"},"forgotten":{"description":"Forgotten is how many of its own subjects have been dropped to stay inside\nthat bound. Each one reads as inactive until it is active again.","type":"integer"},"saturated":{"description":"Saturated is whether the bound is binding right now. The two counts are its\nevidence; this is the state to act on.","type":"boolean"},"subjects":{"description":"Subjects is how many of this organisation's subjects the aggregates hold.","type":"integer"}},"type":"object"},"riskAppetiteIn":{"properties":{"live":{"description":"Live turns the model out of shadow. It defaults to FALSE on every call, so\ngoing live is always an explicit act and never a side effect of changing a\nnumber.\n\nSetting it requires an ADMIN of this organisation. Arming decides whether the\nmodel may change an outcome at all — a payment frozen, a grant refused — for\nevery customer this organisation has, which is a governance act rather than a\ntuning one. Stating the appetite and the sample needs no admin.","type":"boolean"},"review":{"description":"Review is the share of the stream that may be sent for examination, in\n(0, 0.5]. The alert threshold is derived from it as a quantile of the scores\nactually observed, so the level is governed rather than tuned.","type":"number"},"sample":{"description":"Sample is the share of below-the-line events retained for review, in\n[0, 1]. It is the instrument that measures what the model missed; there are\nno labels, so nothing else can.","type":"number"}},"type":"object"},"riskBand":{"properties":{"day":{"description":"Day is the day the band covers.","format":"date-time","type":"string"},"dim":{"description":"Dim is the dimension, named as this API publishes it.","type":"string"},"kind":{"description":"Kind is the subject kind it was computed over.","type":"string"},"n":{"description":"N is how many subject-days went into it.","type":"integer"},"orgs":{"description":"Orgs is how many organisations contributed, each weighted exactly one vote\nwhatever its size. It is published so a reader can judge the band rather\nthan trust it.","type":"integer"},"q10":{"description":"Q10 is the quiet end of the network's day: a tenth of contributing\norganisations sit at or below it.","type":"number"},"q50":{"description":"Q50 is the network's median day.","type":"number"},"q90":{"description":"Q90 is the busy end: a tenth of contributing organisations sit at or above\nit. It is the highest level published.","type":"number"}},"type":"object"},"riskCatalog":{"properties":{"gap":{"description":"Gap says why a lens could not be measured, when that is the case. Each\nreason names its own lens, because \"the surface is unreadable\" and \"the\nnetwork baseline is unreadable\" are different facts.","type":"string"},"model":{"description":"Model is the governed inventory: one entry per dimension of the model\nspace, each carrying the typology it serves and the published standard that\nasks for it. It is the same for every organisation, because it is the\nmodel's shape.","items":{"$ref":"#/components/schemas/riskModelFeature"},"type":"array"},"network":{"description":"Network is the published cross-organisation baseline over the same window,\nso the surface above has something to be read AGAINST. It is the same for\nevery caller and it names nobody.\n\nIt carries no tenant and cannot be made to: the table it reads has no org\ncolumn, every figure is a quantile over at least kAnonOrgs organisations\nweighted one vote each, and a band that does not meet that floor is dropped\non the way out.","items":{"$ref":"#/components/schemas/riskBand"},"type":"array"},"surface":{"description":"Surface is what this organisation's own event surface carries, per\ndimension, measured over the window. A dimension present in no bucket is\nblind here — the model reads its neutral value and a reviewer has to be able\nto see that.","items":{"$ref":"#/components/schemas/riskOrgFeature"},"type":"array"},"tenant":{"description":"Tenant is whose surface was measured.","type":"string"}},"type":"object"},"riskCause":{"properties":{"baseline":{"description":"Baseline is the number it was measured against — always this\norganisation's own history, never a fixed limit and never another\norganisation's.","type":"number"},"citation":{"description":"Citation is where those words come from, so the claim is checkable rather\nthan asserted — which is what a chargeback network or a regulator asks for.","type":"string"},"feature":{"description":"Feature is the dimension that contributed.","type":"string"},"indicator":{"description":"Indicator is the supervisor's own words for the thing being looked for.","type":"string"},"observed":{"description":"Observed is the raw number the coordinate was computed from.","type":"number"},"severity":{"description":"Severity is how much weight this dimension carries.","type":"string"},"share":{"description":"Share is this feature's part of the score, in [0,1]. Zero across every\ncause means no single feature accounts for the alert and the combination\ndoes; the causes are then ordered by how far each sits from unremarkable.","type":"number"},"typology":{"description":"Typology is the laundering or abuse pattern this dimension detects.","type":"string"},"unit":{"description":"Unit is how to read Observed, which is what turns a coordinate into a\nsentence.","type":"string"},"without":{"description":"Without is the score the same event would have received with this\ncoordinate at its neutral value — the counterfactual itself.","type":"number"}},"type":"object"},"riskDataset":{"properties":{"at":{"description":"At is when this version last changed state, and By who.","type":"string"},"by":{"type":"string"},"counts":{"$ref":"#/components/schemas/riskSplitCounts","description":"Counts is how the rows fall across the splits."},"digest":{"description":"Digest fingerprints the SPEC and the ROWS together. Two materialisations of\none spec agree on it or the plane says they do not.","type":"string"},"name":{"description":"Name and Version identify the version.","type":"string"},"oversize":{"description":"Oversize is how many of the window's subjects this version could NOT carry\nbecause their subject identity exceeds the plane's per-subject byte bound.\n\nIt is on the wire, not only in a log, because it is the one degradation a\ncaller cannot otherwise detect: the rows that are here look complete, and a\ndataset silently missing a population is a model silently blind to it.\nNon-zero does not make a version invalid — it makes it a version whose\ncoverage is STATED. Zero is the normal case and omits.","type":"integer"},"refusal":{"description":"Refusal names why there are no bytes, when there are none.","type":"string"},"running":{"description":"Running is true while THIS process is materialising the version. A version\nthat is `materializing` and not running was started by a process that is\ngone — two states the register cannot tell apart, because a register cannot\nknow which processes are alive.","type":"boolean"},"share":{"description":"Share is the fraction of the window's subjects admitted, in thousandths.\n1000 means the whole window fitted under the cap; anything less means the\nversion is a reproducible sample and says by how much.","type":"integer"},"spec":{"$ref":"#/components/schemas/riskDatasetSpec","description":"Spec is the bound query this version was built from, exactly as recorded."},"status":{"description":"Status is declared, materializing, ready or refused. Only `ready` has bytes,\nand `ready` is terminal: a published version is never rewritten.","type":"string"},"truncated":{"description":"Truncated is true when the row cap bound before the window ran out. The\ntrailing subject is dropped whole when that happens, because half a subject\non one side of a split is exactly the leak the grouping prevents.","type":"boolean"},"version":{"type":"integer"}},"type":"object"},"riskDatasetDisposal":{"properties":{"dataset":{"type":"string"},"rows":{"type":"integer"},"versions":{"description":"Versions is how many versions went, and Rows how many rows they held between\nthem, as the register recorded them.","type":"integer"}},"type":"object"},"riskDatasetList":{"properties":{"items":{"description":"Items is one entry per dataset, carrying its newest version. Never null: an\norg that has declared nothing gets an empty array.","items":{"$ref":"#/components/schemas/riskDataset"},"type":"array"}},"type":"object"},"riskDatasetRow":{"properties":{"at":{"description":"At is the row's instant.","type":"string"},"id":{"description":"ID names the row forever. It is DERIVED from the row's own subject and\ninstant, not allocated, so two materialisations of the same fact agree on it\nwithout coordinating.","type":"string"},"kind":{"description":"Kind and Subject name whose row this is.","type":"string"},"point":{"description":"Point is the coordinates, in the order the version's spec names its dims.","items":{"type":"number"},"type":"array"},"split":{"description":"Split is train, val or test.","type":"string"},"subject":{"type":"string"}},"type":"object"},"riskDatasetRows":{"properties":{"dataset":{"type":"string"},"digest":{"description":"Digest is the version's fingerprint. An exported page that did not carry it\nwould be bytes with no way to say which dataset they are.","type":"string"},"dims":{"description":"Dims names what each coordinate of Point means, in Point's own order.","items":{"type":"string"},"type":"array"},"limit":{"type":"integer"},"offset":{"description":"Offset and Limit are the page actually served, which may be smaller than the\none asked for.","type":"integer"},"rows":{"description":"Rows is the page. Never null.","items":{"$ref":"#/components/schemas/riskDatasetRow"},"type":"array"},"version":{"type":"integer"}},"type":"object"},"riskDatasetSpec":{"properties":{"cuts":{"description":"Cuts are the two RFC 3339 instants dividing train | val | test. Omit them to\ntake 70% and 85% of the window by time. Splitting is TEMPORAL and then\ngrouped by subject — a random split puts one device on both sides of the\nline and the model memorises the entity instead of the behaviour.","items":{"type":"string"},"type":"array"},"dims":{"description":"Dims are the coordinates to carry, by published name. Empty takes the whole\nsurface. They are stored in the plane's own order, never the order given, so\ntwo requests naming the same dims produce identical rows.","items":{"type":"string"},"type":"array"},"from":{"description":"From and To bound the event window, half-open, RFC 3339. The window may not\nbe longer than the source's own retention: past that, its older half is\nalready gone and the dataset would silently be shorter than it says.","type":"string"},"horizon":{"description":"Horizon is how many days a row must have aged before it may be admitted. It\nis what keeps a fact that was not yet knowable at scoring time out of a\ntraining set: a chargeback lands 30 to 120 days after the transaction it\ncondemns, so 120 for the payment lane and 14 for signup abuse. Zero admits\nthe whole window and is honest only where the outcome is immediate.","type":"integer"},"kind":{"description":"Kind narrows to one subject kind — person, session or account. Empty takes\nevery kind.","type":"string"},"name":{"description":"Name identifies the dataset across its versions: lower-case letters, digits\nand hyphens, starting with a letter.","type":"string"},"rows":{"description":"Rows caps the materialisation. Zero takes the plane's own bound.","type":"integer"},"seed":{"description":"Seed decides WHICH subjects are admitted when the window holds more rows\nthan the cap allows. It is recorded on the version, so a capped dataset is\nreproducible rather than being whichever rows the store returned first.\nOmit it to seed from the dataset's name.","type":"string"},"to":{"type":"string"}},"type":"object"},"riskDatasetVersions":{"properties":{"items":{"items":{"$ref":"#/components/schemas/riskDataset"},"type":"array"},"name":{"type":"string"}},"type":"object"},"riskDisposeIn":{"properties":{"before":{"description":"Before disposes of assertions WRITTEN before this instant, RFC 3339. It is\nmeasured against the server clock at the write and not against the event\nor observation times, both of which the asserting caller supplies — a\ntenant that could back-date could delete a compliance record on demand.","type":"string"}},"type":"object"},"riskDisposeOut":{"properties":{"before":{"description":"Before echoes the retention boundary that was applied, RFC 3339 in UTC, as\nthis plane parsed it from the request. What was disposed of is every record\nWRITTEN strictly before it and not under litigation hold — written, measured\nagainst the server clock at the write, and not against the event or\nobservation times the asserting caller supplies, because a tenant that could\nback-date could delete a compliance record on demand. A boundary younger than\nthe platform floor of five years is refused before anything is removed.","type":"string"},"disposed":{"description":"Disposed is how many whole records were removed. Records are disposed of\nwhole, never redacted: a partially-erased compliance record is one nobody\ncan attest to.","type":"integer"},"held":{"description":"Held is how many records inside the boundary were kept under litigation\nhold.","type":"integer"},"oldest":{"description":"Oldest is the WRITE time of the oldest assertion this tenant still holds after\nthe sweep, RFC 3339, and it is omitted exactly when nothing remains at all.\nStill older than Before means records survived on purpose and says which\nmechanism kept them: a litigation hold (Held), or the per-call bound with more\nto sweep on the next call (Remaining).","type":"string"},"remaining":{"description":"Remaining is how many disposable records are still older than the\nboundary. A sweep is bounded per call, so a non-zero value here means call\nagain rather than that something failed.","type":"integer"},"restored":{"description":"Restored is how many records this sweep had already removed from the derived\ncolumnar copy and then did NOT dispose of, because a litigation hold arrived\nbetween the identify and the delete — and which were therefore written back\nto the derived copy before this answered.\n\nIt is a NAMED state and not a silent repair. The copy is swept before the\nrecord so nothing is orphaned in the warehouse, which means a record the\ndelete declines to remove is one the warehouse has already lost, with its\nseq behind the delivery cursor and no retry that can reach it. Non-zero here\nsays the collision happened and was repaired; a non-zero that keeps\nrecurring says retention and hold are racing on the same records, which is\nworth an operator's attention rather than a debug line.","type":"integer"},"total":{"description":"Total and Oldest describe what the tenant still holds afterwards, so a\ndisposal that removed nothing is distinguishable from a tenant that had\nnothing.","type":"integer"}},"type":"object"},"riskEvent":{"properties":{"at":{"description":"At is when it happened, RFC 3339. Empty means now. It must sit inside the\nthirty-day window the aggregates keep and no more than two minutes ahead of\nthis plane's clock; anything outside that is REFUSED rather than quietly\naccepted, because a future timestamp moves the aggregates' leading edge and\nleaves every later event for that subject reading as though it never\nhappened. History older than the window is folded in from your own event\nsurface, not through this door.","type":"string"},"device":{"description":"Device is the device fingerprint, if any. It is the axis that surfaces\nseveral nominally unrelated subjects acting as one.","type":"string"},"id":{"description":"ID is the caller's own stable identifier for the event. It selects the\nbelow-the-line review sample by hash, so a counter would make the sample\nsteerable — use the id the event already has.","type":"string"},"kind":{"description":"Kind is whose behaviour this is: person, session or account. It namespaces\nthe subject, so a person and an account that share an identifier stay two\nsubjects.","type":"string"},"nano":{"description":"Nano is the value moved, in nano-USD. Omit it for an event that moves no\nmoney: the value features then read BLIND rather than being told the amount\nwas zero, and the difference is reported on the model state.","type":"integer"},"peer":{"description":"Peer is the counterparty, if any. It is an aggregation axis of its own —\n\"unfamiliar\" is a fact about a relationship and not about either party.","type":"string"},"subject":{"description":"Subject is the identifier on that kind.","type":"string"}},"type":"object"},"riskHoldIn":{"properties":{"hold":{"description":"Hold is the state to put them in: true places the hold, false releases it.\nOne op both ways, because a hold that can be placed and not released pins a\ncompliance record past every retention boundary with nothing able to let it\ngo — and an operator who cannot release a hold stops placing them.","type":"boolean"},"ids":{"description":"IDs are the content digests of the records, as returned by the write and\nby the read. They name records in THIS tenant's plane; an id belonging to\nanybody else names nothing here, because the statement runs against this\ntenant's own file and there is no other file it could reach.","items":{"type":"string"},"type":"array"}},"type":"object"},"riskHoldOut":{"properties":{"changed":{"description":"Changed is how many records moved into that state. A record already in it\nis not counted and is not an error: the op is idempotent, so a retry after\na network failure is safe.","type":"integer"},"held":{"description":"Held is how many records this tenant is now holding, at any age. Retention\nnever disposes of one.","type":"integer"},"hold":{"description":"Hold echoes the state asked for.","type":"boolean"},"missing":{"description":"Missing is how many of the named ids this tenant does not hold. It is\nreported rather than refused, so a sweep over a list that includes disposed\nrecords still places every hold it can — but it is REPORTED, because a hold\nthat silently did nothing is a compliance control that lies.","type":"integer"}},"type":"object"},"riskLabelCoverage":{"properties":{"contested":{"description":"Contested is how many matured events have two visible assertions that\ndisagree. It is the number that says whether the precedence rule is\nload-bearing or decorative, and it is the one to watch after wiring a new\nsource.","type":"integer"},"events":{"description":"Events is how many DISTINCT judged events those assertions name, keyed on\n(kind, subject, at). It counts only events something was ASSERTED about: what\nshare of the whole event stream carries a label is a question about the\nfeature plane's denominator and is not answerable here. Matured + Unmatured is\nEvents.","type":"integer"},"explore":{"description":"Explore is the share of judged events whose winning assertion came from\nthe below-the-line sample. A blocked transaction never produces a\nchargeback, so a training set with no exploration in it is a description of\nthe incumbent block list rather than of the world — and a champion measured\non it is measured on whether it agrees with the incumbent.","type":"number"},"facts":{"description":"Facts is how many assertions the window holds; Events is how many distinct\njudged events they cover. The two differ by exactly the corroboration and\nthe conflict in the plane.","type":"integer"},"from":{"description":"From is the INCLUSIVE start of the EVENT window these counts were folded over,\nRFC 3339, echoed with the defaults filled in — the caller's, or 90 days before\nTo. An assertion is in the window when its event time satisfies at \u003e= From.","type":"string"},"horizon":{"description":"Horizon is the maturity horizon these counts were measured under, IN DAYS —\nthe caller's, or 120. It decides Matured (an event is matured when its `at`\nplus this many days is not after now), it sets each event's own as-of and so\nwhich assertions were visible to it, and when the caller bounds nothing it\nalso places the default window's end.","type":"integer"},"judged":{"description":"Judged is how many MATURED events resolve, at their own as-of, to\nsomething other than unjudged.","type":"integer"},"matured":{"description":"Matured is how many of those events have aged past the horizon and may\ntherefore be admitted to a supervised set at all. It counts every matured\nevent, judged or not — it is the DENOMINATOR an operator divides Judged by,\nand a denominator that excluded the unjudged would read 1.0 on a plane with\none label in it.","type":"integer"},"pending":{"description":"Pending is how many of this tenant's assertions the DERIVED columnar copy is\nnot known to hold yet. Every count above is folded from the record, so they\nare right regardless — but a materialiser that joins in the warehouse while\nthis is non-zero is joining against an incomplete answer key, and a missing\nfraud label is indistinguishable from an honest customer. It is reported at\nthe training gate because that is where somebody is deciding whether the\nground truth is good enough to fit on. Counted under a cap, so it saturates\nrather than costing a full scan on every read.","type":"integer"},"productive":{"description":"Productive is how many matured events resolve, at their own as-of, to a\nWINNING assertion of `productive` — the event led somewhere: escalated,\nreported, charged back. It is the positive class a supervised fit would train\non, and a near-zero count is the number that says the fit is not worth\nrunning.","type":"integer"},"sources":{"description":"Sources breaks the judged events down by the source that WON, so a plane\nthat looks labelled because one noisy source dominates is visible as such.","items":{"$ref":"#/components/schemas/riskSourceCoverage"},"type":"array"},"to":{"description":"To is the EXCLUSIVE end of that window (at \u003c To). Unstated it is one horizon\nbefore now, never now: a window running to now under a maturity horizon can\nhold no matured event at all, so every count below would read zero however\nmuch ground truth the tenant held.","type":"string"},"unlabelled":{"description":"Unlabelled is how many MATURED events had no assertion knowable by their\nown as-of — including every assertion that arrived after that instant. It\nis the field that says WHY judged is low: a tenant whose ground truth was\nfiled long after the events it judges reads matured=n, judged=0,\nunlabelled=n, which is diagnosable, rather than a bare zero, which is not.","type":"integer"},"unmatured":{"description":"Unmatured is how many events in the window have NOT aged past the horizon.\nThey are not unlabelled — they are not yet askable, and a supervised set\nmust exclude them rather than treat them as negatives. Matured + Unmatured\nis Events.","type":"integer"},"unproductive":{"description":"Unproductive is every OTHER judged event: the winner claimed `unproductive`,\njudged not suspicious. Productive + Unproductive is Judged exactly, because a\nwinner of the explicit unjudged is counted in neither — it is a matured event\nsomebody looked at and could not conclude about, and rolling it into the\nnegatives would hand a model a claim nobody made.","type":"integer"}},"type":"object"},"riskLabelEvent":{"properties":{"at":{"description":"At is the event's own instant, RFC 3339. It is part of the event's IDENTITY\nand not a filter: it is matched exactly, to the second, against the `at` the\nassertions were filed under, so an instant a second off names a different\nevent and resolves to nothing. It is also what this event's as-of is measured\nfrom — At plus the horizon.","type":"string"},"kind":{"description":"Kind is the judged entity's type, from the closed set: account, agent,\nmerchant, payout, person, session or transaction. One outside it is refused\nrather than answered `unlabelled`, because it could only ever match nothing\nand the caller would read a real absence into a typo.","type":"string"},"subject":{"description":"Subject is the entity id in the tenant's own namespace, at most 512 bytes. It\nis matched EXACTLY against what was recorded — this is a lookup, not a search,\nand no prefix, pattern or normalisation is applied.","type":"string"}},"type":"object"},"riskLabelFact":{"properties":{"at":{"description":"At is when the judged event happened, RFC 3339.","type":"string"},"confidence":{"description":"Confidence in [0,1]. A processor chargeback is 1; an analyst's hunch is\nnot. It breaks a tie WITHIN a precedence rank and can never lift a weak\nsource above a strong one — otherwise every caller would send 1.\n\nA litigation hold is NOT a field here. It is a fact about the record and\nnot about the world, so it is not part of what was asserted, it is not in\nthe content digest, and it has its own op — which is also the only way one\ncan be released. Carried here it was silently a no-op on any record that\nalready existed: the digest was the same, the insert was ignored, and the\ncaller was told `duplicate` while the hold it asked for was never placed.","type":"number"},"disposition":{"description":"Disposition is productive, unproductive, or empty for an explicit\nunjudged — the AML engine's own vocabulary, verbatim.","type":"string"},"evidence":{"description":"Evidence points at the record this conclusion came from: a dispute id, a\ncase id, a decision id. Required, because a label with no evidence cannot\nbe defended when the adverse action it fed is challenged.","type":"string"},"kind":{"description":"Kind is what the subject is: account, agent, merchant, payout, person,\nsession or transaction. Closed, because a typo in an open field would shard\na tenant's labels into a partition nothing reads and nothing would say so.","type":"string"},"seen":{"description":"Seen is when this assertion became KNOWABLE, RFC 3339. It is required and\nit is not At: a chargeback lands 30 to 120 days after the transaction it\njudges, and a training set joined on At alone knows the future. Everything\nthis plane does to prevent leakage is computed from Seen.","type":"string"},"source":{"description":"Source is who asserted: chargeoff, dispute, case, refund, review or\nsample. It is the primary term of the precedence rule, so it is closed —\nan unknown source has no rank and a conflict with it could not be resolved.","type":"string"},"subject":{"description":"Subject identifies the thing being judged, in the tenant's own namespace.","type":"string"}},"type":"object"},"riskLabelIn":{"properties":{"labels":{"description":"Labels is the batch. Each member is judged on its own: one refusal does\nnot discard the rest, because a webhook redelivering five disputes must\nnot lose four of them to one malformed fifth.","items":{"$ref":"#/components/schemas/riskLabelFact"},"type":"array"}},"type":"object"},"riskLabelOut":{"properties":{"duplicate":{"description":"Duplicate is how many members this tenant already held, byte for byte. The\nidempotency key is the assertion's CONTENT digest — kind, subject, at, seen,\ndisposition, source, evidence, the asserting identity and confidence, folded\nin length-prefixed — so a webhook redelivering one chargeback is a duplicate\nand costs nothing, while an assertion differing in ANY of those fields is a\nDIFFERENT assertion and is recorded beside the first. Nothing was written and\nnothing was overwritten; it is an outcome, never an error. The asserting\nidentity is in the digest, so the same claim filed by a second credential is\ntwo assertions and not a redelivery.","type":"integer"},"mirror":{"description":"Mirror names why the columnar copy did not take this batch, when it did\nnot. The record is already durable in the tenant's own store by then — the\nwarehouse copy exists to make a training join cheap, and its absence is a\ngap in that join, never a lost label.","type":"string"},"pending":{"description":"Pending is how many assertions the derived copy is still to take. Every\nwrite attempt carries the backlog forward as well as its own batch, so a\nwarehouse that was unreachable closes its gap on the next write rather than\nleaving a hole in a training join nothing would report. It is counted under\na cap and saturates there: zero means caught up, and a large number means a\nbacklog to work through rather than an inventory to reconcile.","type":"integer"},"recorded":{"description":"Recorded is how many members became a NEW row in the tenant's record.\nRecorded + Duplicate + Refused is exactly the number of labels sent, so a\ncaller reconciling a webhook delivery can do it on the counts alone.","type":"integer"},"refused":{"description":"Refused is how many members failed admission and were NOT recorded. Refusal\nis per member and never discards the rest of the batch: an empty or\nover-512-byte subject or evidence, a kind, disposition or source outside the\nclosed vocabulary, an `at` or `seen` that is not RFC 3339, a `seen` before the\n`at` it judges, either instant more than five minutes past the server clock,\nor a confidence outside [0,1]. Results names which member and why, so the\nrefused ones are exactly the ones to fix and resend.","type":"integer"},"results":{"description":"Results is per fact, in the order sent, so a caller can retry exactly the\nmembers that were refused and can log the content digest of the ones that\nlanded.","items":{"$ref":"#/components/schemas/riskLabelResult"},"type":"array"}},"type":"object"},"riskLabelRecord":{"properties":{"at":{"description":"At is when the judged EVENT happened, RFC 3339 in UTC, truncated to the\nsecond. The filer supplies it, and it is what a maturity horizon measures\nfrom: this event's as-of is At plus the horizon. A resolve names it back\nexactly, to the second.","type":"string"},"by":{"description":"By is the identity that asserted, stamped server-side at the write.","type":"string"},"confidence":{"description":"Confidence is the filer's own confidence in [0,1] — 1 for a processor\nchargeback, less for an analyst's hunch. Zero is the ordinary value for a\nfiler that stated none, and it means the weakest tie-break there is rather\nthan \"unknown\". It breaks a tie only WITHIN one precedence rank and can never\nlift a weak source above a strong one.","type":"number"},"disposition":{"description":"Disposition is what was concluded, from the closed set: `productive` — the\nevent led somewhere, escalated, reported or charged back; `unproductive` —\njudged not suspicious; or the empty string for an explicit UNJUDGED, which is\na real assertion (\"we looked and could not say\") and not the absence of one.","type":"string"},"evidence":{"description":"Evidence is the pointer to the record this conclusion came from: a dispute id,\na case id, a decision id. At most 512 bytes, required at the write, and opaque\nto this plane — stored and returned verbatim, never resolved. It is what an\nadverse action is defended with, which is why an assertion carrying none is\nrefused at the door.","type":"string"},"hold":{"description":"Hold is true while a litigation hold is on this record: retention will not\ndispose of it, at any age. False — and it is omitted then — leaves the record\ndisposable once it is older than the boundary a sweep names. It is a fact\nabout the RECORD and not about the world, so it is not folded into ID, no\nwrite path can set it, and the hold op is the one way it moves in either\ndirection.","type":"boolean"},"id":{"description":"ID is the assertion's content digest — SHA-256 over every semantic field,\nrendered hex — computed server-side and never supplied. It is the key a\nredelivery collapses onto, and it is the id the hold op names.","type":"string"},"kind":{"description":"Kind is what the subject IS, from the closed set: account, agent, merchant,\npayout, person, session or transaction. With Subject and At it is the IDENTITY\nof the judged event — the triple a resolve names and the triple assertions are\ngrouped by, so a typo in it would file a label against an event nobody asks\nabout.","type":"string"},"knowable":{"description":"Knowable is when THIS PLANE could first have answered with the assertion:\nthe later of Seen and the server clock at the write, derived server-side.\nIt is the instant the leakage guard compares, so it is published beside the\nclaim it was derived from — an answer whose rule nobody can see is one\nnobody can check.","type":"string"},"seen":{"description":"Seen is when the FILER said the assertion became knowable. It is\nprovenance: it is recorded and published, and it decides nothing.","type":"string"},"source":{"description":"Source is WHO asserted, from the closed set: chargeoff, dispute, case, refund,\nreview or sample. It is the primary term of the precedence rule — an unknown\nsource has no rank and a conflict with it could not be resolved — so it is\nwhat decides which of two disagreeing assertions is in force.","type":"string"},"subject":{"description":"Subject is the entity that was judged, named in the TENANT'S OWN namespace and\nat most 512 bytes. It is opaque here: stored, matched and returned verbatim,\nnever dereferenced. It has no meaning outside this tenant — the record is the\ntenant's own file — so an id lifted from another tenant's response names\nnothing.","type":"string"},"wrote":{"description":"Wrote is the server clock at the write. It is the only time on the record\nthe tenant did not supply, and it is what retention measures against.","type":"string"}},"type":"object"},"riskLabelResult":{"properties":{"id":{"description":"ID is the content digest of the assertion — the id a redelivery of the\nsame fact resolves to.","type":"string"},"refusal":{"description":"Refusal states what was wrong, for the refused.","type":"string"},"status":{"description":"Status is recorded, duplicate or refused.","type":"string"}},"type":"object"},"riskLabelVocabulary":{"properties":{"dispositions":{"description":"Dispositions is the closed set a write's `disposition` must be drawn from,\npublished in full so a caller can validate a batch before filing it instead of\ndiscovering a refusal per member: \"productive\", \"unproductive\", and \"\" — the\nEMPTY STRING is a member and means an explicit unjudged, so a client that\nfilters empties out of this list drops a third of the vocabulary and can never\nfile \"we looked and could not say\". They are the AML engine's own spelling,\nverbatim, which is what lets a replay there report against these values.","items":{"type":"string"},"type":"array"},"kinds":{"description":"Kinds, Dispositions and Sources are the closed vocabularies. A value\noutside them is refused at the door.","items":{"type":"string"},"type":"array"},"precedence":{"description":"Precedence is the sources in the order that resolves a conflict, strongest\nfirst. It is DERIVED from the same declaration the resolver reads, so the\npublished order is the enforced order and cannot drift from it.","items":{"type":"string"},"type":"array"},"retention":{"description":"Retention is the platform floor in days: no tenant may dispose of a label\nyounger than this, because a label can be the input to an adverse action.","type":"integer"},"rule":{"description":"Rule states the tie-breaks below rank, in order, so a caller reading a\ncontested resolution can reproduce it.","items":{"type":"string"},"type":"array"}},"type":"object"},"riskLabelsOut":{"properties":{"count":{"description":"Count is how many this page holds. It is not a total: a total over an\nunbounded append-only log is a full scan of a single-writer file.","type":"integer"},"labels":{"description":"Labels is the page, newest event first.","items":{"$ref":"#/components/schemas/riskLabelRecord"},"type":"array"}},"type":"object"},"riskLearnIn":{"properties":{"events":{"description":"Events are the things that happened, oldest first. An empty batch is\nrefused: learning nothing is not an operation.","items":{"$ref":"#/components/schemas/riskEvent"},"type":"array"}},"type":"object"},"riskLearnOut":{"properties":{"learned":{"description":"Learned is how many of the events the model actually learned from, and is\nalso what the call is metered at: one screen per event learned from. It is\nthe batch minus the events already in this organisation's record, so a\nretried batch reports — and is charged — zero.","type":"integer"}},"type":"object"},"riskLineage":{"properties":{"dataset":{"type":"string"},"digest":{"description":"Digest is the version's fingerprint, repeated here so a lineage answer is\nself-contained.","type":"string"},"from":{"description":"From and To are the window actually read — To is the window's end pulled\nback by the maturity horizon, which is usually earlier than the spec's.","type":"string"},"holds":{"description":"Holds is what the source holds for the same window NOW. The difference\nbetween it and Rows is the whole of the reproducibility claim.","type":"integer"},"oversize":{"description":"Oversize is how many subjects the window held that were too large to\nrepresent when this version was built. It is part of the fingerprint, so it\nis part of what \"reproducible\" is measured over.","type":"integer"},"refusal":{"type":"string"},"reproducible":{"description":"Reproducible is true when the source still holds what this version was built\nfrom. Refusal says why not, when it is false.","type":"boolean"},"retention":{"description":"Retention is the source's own expiry rule as the store reports it, read at\nmaterialisation time rather than assumed. A source whose retention is\nshorter than this window cannot re-derive it.","type":"string"},"rows":{"description":"Rows and Subjects are what the source held for that window at\nmaterialisation time.","type":"integer"},"share":{"description":"Share is the fraction of subjects admitted, in thousandths.","type":"integer"},"source":{"description":"Source is the plane the rows were derived from.","type":"string"},"subjects":{"type":"integer"},"to":{"type":"string"},"version":{"type":"integer"}},"type":"object"},"riskModelFeature":{"properties":{"blind":{"description":"Blind is how often this dimension took that neutral value for THIS\norganisation.","type":"integer"},"citation":{"description":"Citation is where those words come from, so the claim is checkable rather\nthan asserted.","type":"string"},"indicator":{"description":"Indicator is the supervisor's own words for the thing being looked for.","type":"string"},"name":{"description":"Name is the dimension.","type":"string"},"neutral":{"description":"Neutral is the value the coordinate takes when the data cannot support it.","type":"number"},"severity":{"description":"Severity is how much weight an alert on it carries.","type":"string"},"typology":{"description":"Typology is the pattern this dimension detects.","type":"string"},"unit":{"description":"Unit is how to read the raw number, which is what turns a coordinate into a\nsentence an investigator can put in a file.","type":"string"},"window":{"description":"Window is the sliding aggregate it reads.","type":"string"}},"type":"object"},"riskModelState":{"properties":{"aggregates":{"$ref":"#/components/schemas/riskAggregates","description":"Aggregates reports the pressure on this organisation's own sliding\naggregates, and whether they have started forgetting subjects to stay inside\ntheir bound."},"blind":{"additionalProperties":{"type":"integer"},"description":"Blind counts, per feature, how often it took its neutral value for want of\ndata. A feature blind on most traffic is not contributing whatever the\ninventory claims for it.","type":"object"},"cut":{"description":"Cut is the threshold in force, derived from Stated as a quantile of the\nscores actually observed.","type":"number"},"descends":{"description":"Descends is the published value the working model grew out of: the newest one\nwhose mass count it has reached or passed. Empty when nothing has been\npublished yet.\n\nIt is DERIVED from the count and never stored, so an instant rollback is right\nfor free — adopting an older value moves the count backward and this answers\nwith that older value, where a stored pointer would be a second fact to keep\nin step. Read with Learned it is also the DRIFT: this model is Descends plus\nhowever many events the two counts differ by.","type":"string"},"disposed":{"description":"Disposed is how many published values retention has taken. It is DERIVED from\nthe lowest surviving sequence, so it cannot drift from what it describes, and\nit is reported because a retention that binds is a fact an operator must be\nable to read rather than a silence.","type":"integer"},"learned":{"description":"Learned is how many events the model has learned from.","type":"integer"},"live":{"description":"Live is false while the model is in shadow — scoring, learning and\nrecording what it WOULD have alerted on, and changing no outcome. Shadow is\nthe default for a new tenant.","type":"boolean"},"policy":{"description":"Policy is the version of the decision regime this model is deciding under,\nfrom your organisation's own policy history (GET /v1/risk/policy). Every\nscore cites it, so it is the join between a past decision and the appetite\nthat produced its threshold. Zero means no regime has ever been stated and\nthe default posture — shadow — is in force.","type":"integer"},"realised":{"description":"Realised is the share that actually was. Reading it beside Stated is what\nmakes the appetite a measured commitment rather than an intention.","type":"number"},"refused":{"additionalProperties":{"type":"integer"},"description":"Refused counts events the model would not score, by reason. None of them\nwas examined; a refusal is counted, never silent.","type":"object"},"sample":{"description":"Sample is the share of below-the-line events retained for review, which is\nhow the miss rate is measured rather than assumed.","type":"number"},"saturated":{"description":"Saturated means no threshold can honour the stated appetite because too\nmuch of the stream scores in the top bucket, so the model is alerting on\nnothing — the one state that must never be mistaken for quiet.","type":"boolean"},"shape":{"description":"Shape is the model's identity, as `\u003cfamily\u003e:\u003cdigest\u003e`: the KIND of model, and\nthat family's own digest over the inventory in order and the detector's geometry\nparameters. It is what an auditor pins an alert to, because learned state is\nonly meaningful against the space that produced it — and the family leads it\nbecause two families' masses are not fitted differently, they are different\nkinds of number.","type":"string"},"stated":{"description":"Stated is the share of the stream this organisation said may be examined.","type":"number"},"surface":{"$ref":"#/components/schemas/riskSurface","description":"Surface reports what of the tenant's OWN event surface has been folded in."},"tenant":{"description":"Tenant is the qualified key the model is held under — the brand whose\nissuer vouched for the caller and the organisation it acts for. It is\nechoed so a reader can see the answer is its own and not a parameter it\npassed.","type":"string"},"values":{"description":"Values is your organisation's own published model values, newest first —\nevery state it deliberately named, each addressed by its own content and\nimmutable. This is what PUT /v1/risk/state/model names, so it is reported\nHERE rather than behind an address of its own: they are part of what a review\nof one model reads, and a list of names is a few hundred bytes.\n\nCompare each one's `shape` with the `shape` above: equal means adopting it\nrestores masses into the space this model already runs, and different means\nadopting it REPLANTS the model into the space that value describes — which is how\nthe shape a search found becomes the shape you are running.\n\nThe working model is NOT in it. Publication is a boundary somebody marked; the\nstate between two boundaries is in-process counters, and calling those a value\nwould be a claim about reproducibility that nothing could honour.","items":{"$ref":"#/components/schemas/riskModelValue"},"type":"array"},"warm":{"description":"Warm is whether that is enough for the model to have an opinion at all.\nBelow it the model declines to score, which is an ordinary state and is not\na clean bill of health.","type":"boolean"}},"type":"object"},"riskModelValue":{"properties":{"address":{"description":"Address names this value by its own content: the model's shape, the geometry\nseed, its position in the window, its threshold, its masses as IEEE-754 bits\nand the fold watermark behind them. Nothing else — no clock, no counter and\ndeliberately NOT the organisation, so an identical model has one name and a\nname is never an authority. Holding another organisation's address resolves\nnothing.","type":"string"},"at":{"description":"At is when it was published, RFC 3339, on the server clock. You do not supply\nit: a record whose date the audited party chose is not a record.","type":"string"},"learned":{"description":"Learned is how many events are behind the masses.","type":"integer"},"sequence":{"description":"Sequence is this value's place in YOUR organisation's own history, from 1 and\ncontiguous until retention disposes of the oldest.","type":"integer"},"shape":{"description":"Shape NAMES the model space the masses are only meaningful against, as\n`\u003cfamily\u003e:\u003cdigest\u003e` — the KIND of model, and that family's own digest over the\nfeature inventory in order and the detector's geometry parameters. Compare it\nwith the `shape` on your model state (GET /v1/risk/state): equal means adopting\nthis value restores masses into the space already running, and different means\nadopting it REPLANTS the model into the space this value describes. That is what\nmakes a searched shape installable.\n\nA DIFFERENT FAMILY IS NOT ADOPTABLE AT ALL, and that is the one difference the\nfamily term makes here: a different geometry in the same family is a replant, and\na different family is a refusal naming both — its masses do not describe your\nmodel in any space.","type":"string"},"warmed":{"description":"Warmed is how far your own event surface had been folded in when this value\nwas published, RFC 3339. It is part of the address because two models with\nidentical masses reached by different routes disagree about what is left to\nfold, and one of them will re-teach history the other will not.","type":"string"}},"type":"object"},"riskOrgFeature":{"properties":{"blind":{"description":"Blind is true when the dimension is present in no bucket at all: this\norganisation's surface does not carry it, and saying so is the difference\nbetween no risk and no data.","type":"boolean"},"buckets":{"description":"Buckets is how many five-minute buckets of this organisation's surface were\nmeasured.","type":"integer"},"max":{"description":"Max is the largest value it reached in the window.","type":"number"},"mean":{"description":"Mean is the dimension's average where it was present.","type":"number"},"name":{"description":"Name is the dimension as this API publishes it.","type":"string"},"present":{"description":"Present is in how many of them the dimension carried a value at all.","type":"integer"},"source":{"description":"Source names the plane it is rolled up from, so a dimension that reads zero\neverywhere traces to a plane the organisation does not use rather than to a\ndefect.","type":"string"},"unit":{"description":"Unit is how to read the numbers below.","type":"string"}},"type":"object"},"riskPolicyOut":{"properties":{"changes":{"description":"Changes is how many DISTINCT regimes may be adopted per Window. A restatement\nidentical to the regime in force mints no version and is not counted against\nit.","type":"integer"},"disposed":{"description":"Disposed is how many versions retention has taken. It is NOT a silence: a\nhistory bounded on disk must say what it no longer holds, because a decision\nciting a disposed version can no longer be reconstructed from this record.","type":"integer"},"history":{"description":"History is the retained versions, newest first.","items":{"$ref":"#/components/schemas/riskPolicyVersion"},"type":"array"},"retained":{"description":"Retained is how many versions this organisation's history holds at most,\nderived from the byte budget its rows are a multiple of.","type":"integer"},"version":{"description":"Version is the version in force — the one every score currently cites. Zero\nmeans no regime has ever been stated and the default posture, shadow, is in\nforce.","type":"integer"},"window":{"description":"Window is the period Changes is measured over.","type":"string"}},"type":"object"},"riskPolicyVersion":{"properties":{"at":{"description":"At is when it entered force, RFC 3339, from the server clock.","type":"string"},"by":{"description":"By is the identity that stated it, stamped server-side from the validated\nprincipal at the moment it entered force.","type":"string"},"live":{"description":"Live is whether the model was permitted to change an outcome under it.","type":"boolean"},"review":{"description":"Review is the share of the stream the regime states may be examined. The\nthreshold in force is derived from it, which is why a decision is only\ndefensible against the version that produced it.","type":"number"},"sample":{"description":"Sample is the share of below-the-line events the regime retains for review.","type":"number"},"version":{"description":"Version names this regime in this organisation's history.","type":"integer"}},"type":"object"},"riskPublishOut":{"properties":{"minted":{"description":"Minted is false when your model was ALREADY published under this name and\nnothing was written. Publication is idempotent on the value itself, which is\nwhat a content address is for — publishing at every boundary costs nothing\nrather than being the cheapest way to fill a disk.","type":"boolean"},"tenant":{"description":"Tenant is whose history it entered.","type":"string"},"value":{"$ref":"#/components/schemas/riskModelValue","description":"Value is the published value: its name and what it is, never its masses."}},"type":"object"},"riskResolveIn":{"properties":{"horizon":{"description":"Horizon is how many days an event must age before it may be resolved at\nall, and it is the whole of the no-leakage rule. 120 for the payment lane\n(past the Visa and Mastercard dispute windows), 14 for signup abuse.\nUnstated takes 120.","type":"integer"},"now":{"description":"Now moves the observation instant BACKWARDS, RFC 3339. It exists so a\nBACKTEST can resolve labels as the plane stood at a past moment; without it,\nevery backtest would score a model against knowledge that arrived after the\ndecision it is being scored on. An instant after the server clock is\nrefused: a backtest resolves the past, and a future one would declare\nunmatured events matured and hand a training set negatives for rows whose\nchargeback has not had time to arrive.","type":"string"},"subjects":{"description":"Subjects are the exact events being judged. Each carries its own event\ntime, because the as-of that keeps the future out is derived from that\ninstant plus the horizon — one as-of over a whole batch would give a\nJanuary row six extra months of hindsight.\n\nOne entry per DISTINCT (kind, subject, at): naming an event twice answers\nonce, because an event resolved twice would list its own winner as a\ncontrary claim and would hand a materialiser duplicate training rows.","items":{"$ref":"#/components/schemas/riskLabelEvent"},"type":"array"}},"type":"object"},"riskResolveOut":{"properties":{"horizon":{"description":"Horizon is the maturity horizon this answer was computed under, IN DAYS — the\ncaller's, or 120 when it stated none. Each event's as-of is its own `at` plus\nthis many days, and that as-of is what decides which assertions were visible\nto it; an event whose as-of falls after Now is not resolved at all and is\ncounted in Unmatured instead.","type":"integer"},"labels":{"description":"Labels is one entry per named event that BOTH matured and had at least one\nassertion knowable by its own as-of, in the order the events were named. The\nthree outcomes partition the ask: len(labels) + Unmatured + Unlabelled is the\nnumber of DISTINCT events named, an event named twice having been answered\nonce.","items":{"$ref":"#/components/schemas/riskResolved"},"type":"array"},"now":{"description":"Now and Horizon echo the observation this answer was computed under. A\nresolved label without them is a claim nobody can check.","type":"string"},"unlabelled":{"description":"Unlabelled is how many matured events had no assertion knowable by their\nown as-of. That is the ordinary state of most traffic and it is reported\nrather than answered as unproductive: manufacturing negatives is how a\nfraud model comes to describe the incumbent block list.","type":"integer"},"unmatured":{"description":"Unmatured is how many named events had not aged past the horizon. They are\nnot unlabelled — they are not yet ASKABLE, and a supervised training set\nmust exclude them rather than treat them as negatives.","type":"integer"}},"type":"object"},"riskResolved":{"properties":{"asOf":{"description":"AsOf is the instant this answer was true at: the event time plus the\nhorizon. Nothing seen after it was visible to this resolution.","type":"string"},"at":{"description":"At is the event's instant, RFC 3339, echoed. It is what the horizon is\nmeasured from, so At plus the horizon is AsOf.","type":"string"},"by":{"description":"By is the identity that filed the WINNING assertion, `\u003chome org\u003e/\u003cuser\u003e`,\nstamped server-side from the validated principal at the write and never taken\nfrom a body — an attribution the caller chose is not attribution. It is the\nwinner's alone; every losing assertion keeps its own and is returned whole in\nConflicts.","type":"string"},"confidence":{"description":"Confidence is the winning assertion's own confidence in [0,1], zero when its\nfiler stated none. It is reported because it is a term of the rule that picked\nthe winner, and it is the weakest term but one: it breaks a tie inside one\nrank and never lifts a weak source above a strong one.","type":"number"},"conflicts":{"description":"Conflicts is every other visible assertion, strongest first, whole. They\nare kept and returned rather than dropped, so an adverse action can show\nthat the plane knew of a contrary claim and say why it lost. They are\nhorizon-filtered exactly like the winner: an assertion that was not\nknowable yet cannot even be named here, because naming it would leak its\nexistence into a past decision.","items":{"$ref":"#/components/schemas/riskLabelRecord"},"type":"array"},"contested":{"description":"Contested is true when a visible assertion claimed a DIFFERENT disposition.\nTwo sources agreeing is corroboration, not conflict.","type":"boolean"},"disposition":{"description":"Disposition is the claim IN FORCE at AsOf: productive, unproductive, or the\nempty string for an explicit unjudged. It is the winning assertion's own\nclaim, never a vote or an average — an average of two adjudications is a third\nclaim nobody made. A matured event nobody judged is not answered here at all;\nit is counted in Unlabelled, because manufacturing a negative there is how a\nfraud model comes to describe the incumbent block list.","type":"string"},"evidence":{"description":"Evidence is the winning assertion's pointer to the record behind it — the\ndispute, case or decision id it was filed with, opaque and verbatim. It\ntravels with the answer so an adverse action can name what judged the subject\nwithout a second read.","type":"string"},"id":{"description":"ID is the winning assertion's content digest, so this answer traces to the\nexact record it came from — and that record can be placed under litigation\nhold by naming this id.","type":"string"},"kind":{"description":"Kind is the judged entity's type, echoed from the event that was named. With\nSubject and At it is how a caller joins this answer back onto the training row\nor the decision it asked about.","type":"string"},"source":{"description":"Source is who filed the winning assertion, and it is the PRIMARY term of the\nrule that picked it. Sources rank by adjudication weight — chargeoff,\ndispute, case, refund, review, sample, strongest first — and only inside one\nrank do the tie-breaks run, in order: the assertion that became KNOWABLE\nlatest, then the higher confidence, then the lower id. The vocabulary op\npublishes that order from the same declaration the resolver reads, so a caller\nholding a contested answer can reproduce it.","type":"string"},"subject":{"description":"Subject is the entity id, echoed from the event that was named — the tenant's\nown key, returned verbatim.","type":"string"}},"type":"object"},"riskScoreIn":{"properties":{"event":{"$ref":"#/components/schemas/riskEvent","description":"Event is the thing to judge. It is judged against the caller's OWN model\nand nothing is learned from it."}},"type":"object"},"riskScoreOut":{"properties":{"alert":{"description":"Alert is whether this would become evidence. It is false in shadow however\nhigh the score.","type":"boolean"},"causes":{"description":"Causes is the per-feature attribution, ordered by contribution. Each is a\nCOUNTERFACTUAL on the model that produced the score — the coordinate moved\nto its neutral value and the event rescored — so the explanation is the\nsame arithmetic the score came from.","items":{"$ref":"#/components/schemas/riskCause"},"type":"array"},"cut":{"description":"Cut is the threshold in force, derived from the stated appetite as a\nquantile of the scores actually observed rather than fixed at a number.","type":"number"},"policy":{"description":"Policy is the version of your organisation's decision regime this verdict\nwas reached under, from its own policy history (GET /v1/risk/policy). Cut is\nderived from the appetite that version states, so it is the record that makes\nthis decision reconstructible after the appetite is restated. Zero means no\nregime has ever been stated and the default posture — shadow — was in force.","type":"integer"},"refusal":{"description":"Refusal names why the model declined, when it did.","type":"string"},"score":{"description":"Score is where the event sits in the tenant's own density: 0 where its\nrecent behaviour is densest, 1 where there is none of it.","type":"number"},"scored":{"description":"Scored is false when the model declined, and Refusal says which refusal it\nwas: warming, unusable or unidentified. None of them is a clean bill of\nhealth, which is why the refusal is stated rather than rendered as a score\nof zero.","type":"boolean"},"shadow":{"description":"Shadow is whether the model is testing rather than deciding — scoring,\nlearning and recording what it WOULD have alerted on, and changing no\noutcome. It is the default for a model no one has reviewed yet.","type":"boolean"},"shape":{"description":"Shape is the model space this verdict was reached in, as `\u003cfamily\u003e:\u003cdigest\u003e`:\nthe KIND of model, and that family's own digest over your organisation's feature\ninventory in order and the detector's geometry parameters. It is what pins an\nadverse decision to a model — a score is only meaningful against the space that\nproduced it, and without this the only answer to \"which model decided this\" was\n\"the one that was running\", which is not an answer.\n\nThe family leads it because everything after it is one family's arithmetic. Two\nspaces are the same space only if they are the same family, so comparing this\nwith the `shape` on your model state or on a published value is a comparison\nthat holds ACROSS families and not only inside one.\n\nIt names the SPACE, not the learned state, and that is deliberate. The masses\nat the instant of a score are in-process counters somewhere between two\npublished values, so citing a published address here would claim that value\nproduced this score — true only for the score taken the instant after a\npublication. This, the policy version and the event's own time are what IS\ntrue, and the published history's clock (GET /v1/risk/state) brackets the\ndecision between two named values from there.","type":"string"},"values":{"description":"Values is every coordinate, including the ones that contributed nothing, so\na reviewer sees what the model read and not only what it concluded.","items":{"$ref":"#/components/schemas/riskValue"},"type":"array"}},"type":"object"},"riskSearchIn":{"properties":{"days":{"description":"Days is how much of the organisation's own history to replay, 1 to 400.\nZero takes thirty.","type":"integer"}},"type":"object"},"riskSearchReport":{"properties":{"done":{"description":"Done is false while the run is still going; the trials below are then the\nones finished so far.","type":"boolean"},"ended":{"description":"Ended is when it finished, RFC 3339. Absent while it is still going.","type":"string"},"events":{"description":"Events is how much of this organisation's history was replayed.","type":"integer"},"fitted":{"$ref":"#/components/schemas/riskModelValue","description":"Fitted is the winning shape FITTED over your own history and published as one of\nyour organisation's own model values. Name its address on PUT\n/v1/risk/state/model and the winning shape becomes the model you are running.\n\nIt is why this op answers something you can act on. A trial keeps counts and not\nthe model that produced them, so a report without this named a shape nobody could\ninstall — and the adoption path refused a shape change besides. Fitting the winner\nonce is a sixty-fifth pass over the same history; keeping all sixty-four fitted\nmodels resident instead would cost a measured 21 MiB per run for sixty-three\nshapes nobody adopts.\n\nTwo things about it are worth knowing before you adopt it. Its realised rate can\ndiffer from the winner's above, because the ranking measures every candidate under\none fixed reference geometry so the comparison is a comparison, while this is\nfitted under YOUR geometry — the one an outsider cannot predict. And it has\nlearned the window this search replayed and nothing older, so adopting it trades\nhistory for fit."},"gap":{"description":"Gap says why the winning shape could not be fitted into an adoptable value, when\nit could not. It is separate from Refusal because they are different facts: a\nrefusal means the ranking below proves nothing, a gap means the ranking stands and\nonly the value is missing.","type":"string"},"id":{"description":"ID is the run.","type":"string"},"refusal":{"description":"Refusal says why the run proves nothing, when it does. An empty history is\nREFUSED rather than reported as zero alerts: \"no alerts\" is exactly what a\nquiet model looks like, and choosing a shape on the strength of an empty\nreplay is the failure a sandbox exists to prevent.","type":"string"},"started":{"description":"Started is when the run was accepted, RFC 3339.","type":"string"},"trials":{"description":"Trials is every shape tried, best first.","items":{"$ref":"#/components/schemas/riskTrial"},"type":"array"},"winner":{"$ref":"#/components/schemas/riskTrial","description":"Winner is the best-fitting shape, absent when nothing fit."}},"type":"object"},"riskSearchRun":{"properties":{"candidates":{"description":"Candidates is how many model shapes will be tried.","type":"integer"},"events":{"description":"Events is how much of the organisation's own history the run will replay.","type":"integer"},"id":{"description":"ID addresses the run. Read the result back with it.","type":"string"}},"type":"object"},"riskSourceCoverage":{"properties":{"facts":{"description":"Facts is how many assertions this source filed; Won is how many judged\nevents it was the assertion in force for. A source with many facts and few\nwins is one that is being outranked, which is worth knowing before\nconcluding it is wired correctly.","type":"integer"},"source":{"description":"Source is the asserter these two counts are for — chargeoff, dispute, case,\nrefund, review or sample. There is one entry per source that either filed in\nthe window or won in it, in precedence order, strongest first. A source no\nlonger in the vocabulary still has rows and is reported after the known ones\nrather than dropped out of a total that is supposed to add up.","type":"string"},"won":{"description":"Won is how many JUDGED events this source's assertion was the one IN FORCE\nfor, at that event's own as-of — it beat every other visible claim under the\nprecedence rule. Summed over the sources it is Judged. Read against Facts it\nis the ratio that matters: many filed and few won is a source being outranked,\nnot a source that is broken, and one source winning nearly everything is a\nplane that looks labelled because one noisy filer dominates it.","type":"integer"}},"type":"object"},"riskSplitCounts":{"properties":{"judged":{"description":"Judged is how many rows carry a disposition. It is zero until a label plane\nwrites one, and reporting it plainly is what lets a model plane refuse to\nrank rather than name a winner it cannot justify.","type":"integer"},"productive":{"description":"Productive and Unproductive are the two judged classes, so the imbalance is\nvisible before anyone trains on it.","type":"integer"},"rows":{"type":"integer"},"subjects":{"description":"Subjects is how many distinct subjects the rows belong to. Every row of one\nsubject is in ONE split, so this is the real sample size — the row count\nflatters it whenever a subject is active.","type":"integer"},"test":{"type":"integer"},"train":{"type":"integer"},"unproductive":{"type":"integer"},"val":{"type":"integer"}},"type":"object"},"riskSurface":{"properties":{"folded":{"description":"Folded is how many buckets of the tenant's own feature surface were folded\ninto the model when it became resident.","type":"integer"},"gap":{"description":"Gap says why the fold did not happen or did not complete, when that is the\ncase. An empty surface and an unreachable warehouse are different facts and\na model must not report them as the same one.","type":"string"},"refused":{"description":"Refused is how many buckets of this organisation's own surface the fold\ncould not fold, because a subject on them is longer than this plane's own\nfield bound. It is history the model does not have, said out loud.","type":"integer"},"replayed":{"description":"Replayed is how many of this organisation's own recorded observations\nrebuilt its sliding aggregates when the model became resident. It is what\nsays a rollout was a rebuild rather than a blindness: the aggregates are a\nprojection of a durable record, so a restart costs a replay and not a\ncontrol.","type":"integer"},"rolled":{"description":"Rolled is how many windows of this organisation's own source planes —\nproduct events, captured failures, metered inference — were rolled up into\nits feature surface before that fold. Zero with no gap means the surface was\nalready current, which is a different fact from the rollup never running.","type":"integer"},"window":{"description":"Window is the lookback the fold covered.","type":"string"}},"type":"object"},"riskTopology":{"properties":{"blend":{"description":"Blend is how much of a closing window folds into the reference: 1 replaces\nit outright, less makes the reference expensive to move.","type":"number"},"depth":{"description":"Depth is how deep each tree is. With Trees it sets how finely the space is\npartitioned, and therefore how much history it takes to fill.","type":"integer"},"family":{"description":"Family is the KIND of model this candidate is: `halfspace` is an ensemble of\nhalf-space trees whose masses are counters, and it is the family this search\ngrid ranks. The parameters below are that family's own — a family that does not\npartition space with trees has different ones — so read them against this.","type":"string"},"review":{"description":"Review is the appetite this shape was tried at.","type":"number"},"trees":{"description":"Trees is how many half-space trees the ensemble holds.","type":"integer"},"window":{"description":"Window is how many events make one reference window.","type":"integer"}},"type":"object"},"riskTrial":{"properties":{"alerted":{"description":"Alerted is how many of those it would have raised.","type":"integer"},"curve":{"description":"Curve is the realised alert rate over successive tenths of the history —\nthe learning curve, which says whether the shape settled or is still moving.","items":{"type":"number"},"type":"array"},"fit":{"description":"Fit ranks the shape, smaller being better: the relative miss of the stated\nappetite, plus flat penalties for never warming and for saturating, plus the\nshare of coordinates that were blind.","type":"number"},"learned":{"description":"Learned is how many events the shape learned from during the replay.","type":"integer"},"realised":{"description":"Realised is what that appetite actually produced. The distance between the\ntwo is what the search is searching over.","type":"number"},"saturated":{"description":"Saturated is whether the appetite could not be honoured by any threshold,\nwhich is a shape that alerts on nothing and reads like a quiet one.","type":"boolean"},"scored":{"description":"Scored is how many it was able to score.","type":"integer"},"stated":{"description":"Stated is the appetite the shape was tried at.","type":"number"},"topology":{"$ref":"#/components/schemas/riskTopology","description":"Topology is the shape."},"warm":{"description":"Warm is whether the shape learned enough to have an opinion at all over\nthis organisation's whole history.","type":"boolean"}},"type":"object"},"riskValue":{"properties":{"baseline":{"description":"Baseline is what Observed was measured against: this organisation's own\nhistory for this subject.","type":"number"},"blind":{"description":"Blind marks a coordinate that could not be computed and took its neutral\nvalue. A model silently reading neutral for a dimension it never has data\nfor is indistinguishable from one reading a genuine absence of risk.","type":"boolean"},"feature":{"description":"Feature is the dimension.","type":"string"},"observed":{"description":"Observed is the raw number X was computed from, quoted so the coordinate\nreads back as a sentence rather than a bare ratio.","type":"number"},"unit":{"description":"Unit is how to read Observed.","type":"string"},"x":{"description":"X is the coordinate in the model space, always dimensionless.","type":"number"}},"type":"object"},"roleList":{"properties":{"data":{"description":"Data is every (user, role) assignment in the caller's org.","items":{"$ref":"#/components/schemas/RoleAssignment"},"type":"array"}},"type":"object"},"rollbackReq":{"properties":{"app":{"description":"App is the application's slug, from the path.","type":"string"},"deploymentId":{"description":"DeploymentID is the deployment to redeploy. Omit it to return to the\nprevious release.","type":"string"},"project":{"description":"Project is the project the application lives under, from the path.","type":"string"}},"type":"object"},"roundOut":{"properties":{"roundId":{"description":"RoundID is the cap table's id for the recorded round.","type":"string"}},"type":"object"},"routeCreateIn":{"properties":{"pattern":{"description":"Pattern is the URL pattern to bind, e.g. \"acme.com/api/*\".","type":"string"},"script":{"description":"Script is the Worker script to dispatch to. Omit it to leave the pattern\nbound to no script, which is how Cloudflare expresses \"bypass the Worker here\".","type":"string"},"zone":{"description":"Zone is the 32-hex Cloudflare zone id, from the path.","type":"string"}},"type":"object"},"routedRunOut":{"properties":{"base":{"type":"string"},"branch":{"type":"string"},"cloneUrl":{"type":"string"},"project":{"type":"string"},"prompt":{"type":"string"},"repo":{"description":"Repo is the repository to work in and CloneURL is how to fetch it.","type":"string"},"sessionId":{"description":"SessionID is the live session opened at dispatch; the machine streams its\nturns into it.","type":"string"},"timeoutSeconds":{"description":"TimeoutSeconds bounds the run on the machine; 0 means the machine's own default.","type":"integer"}},"type":"object"},"routerList":{"properties":{"routers":{"description":"Routers is one row per ZT edge-router tagged with the caller's org role.","items":{"$ref":"#/components/schemas/routerView"},"type":"array"}},"type":"object"},"routerView":{"properties":{"id":{"description":"ID is the ZT edge-router's id.","type":"string"},"name":{"description":"Name is the edge-router's name, falling back to its id when it has none.","type":"string"},"region":{"description":"Region comes from a \"region-\u003cslug\u003e\" role attribute and is omitted when the\nrouter carries none, so the column renders \"—\" rather than a guess.","type":"string"},"status":{"description":"Status is the controller's own health signal: \"online\" when connected,\n\"disabled\" when administratively disabled, \"offline\" otherwise.","type":"string"}},"type":"object"},"rulesOut":{"properties":{"rules":{"description":"Rules is every rule the org has set, highest priority first — the order they\nare matched in.","items":{"$ref":"#/components/schemas/Rule"},"type":"array"}},"type":"object"},"runIn":{"properties":{"action":{"description":"Action is the name of the connector action to invoke.","type":"string"},"auth":{"description":"Auth is the caller's resolved credential for the connector, handed to the\naction verbatim. Its shape is whatever the connector's auth descriptor\ndeclares (a token string, an object), so it is opaque here.","type":"object"},"id":{"description":"ID is the connector to run, from the path.","type":"string"},"props":{"additionalProperties":{"type":"object"},"description":"Props are the action's input properties, keyed by property name.","type":"object"}},"type":"object"},"runList":{"properties":{"runs":{"description":"Runs is the agent's executions, newest first.","items":{"$ref":"#/components/schemas/agentRunView"},"type":"array"}},"type":"object"},"runPage":{"properties":{"data":{"description":"Data is the page of runs, newest first.","items":{"$ref":"#/components/schemas/FlowRun"},"type":"array"}},"type":"object"},"runReq":{"properties":{"env":{"description":"Env is the run's environment. Keys must match `^[A-Za-z_][A-Za-z0-9_]*$`;\na variable marked `secret: true` is sealed into KMS.","items":{"$ref":"#/components/schemas/EnvVarJSON"},"type":"array"},"gpu":{"description":"GPU is how many GPUs the run asks for; a negative value is 400.","type":"integer"},"image":{"description":"Image is the container image to run. Required.","type":"string"},"maxScale":{"description":"MaxScale above the floor declares an autoscaling ceiling; 0 means no\nautoscaler at all — a fixed run at the floor.","type":"integer"},"minScale":{"description":"MinScale is the replica floor, clamped to the deployment's limit.","type":"integer"},"name":{"description":"Name is the run's name, and the slug is derived from it. Required, and it\nmust resolve to `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`. Re-running the same\nname updates that run in place.","type":"string"},"port":{"description":"Port is the container port the run listens on.","type":"integer"},"runtime":{"description":"Runtime is accepted for the client contract and echoed nowhere: the image\nIS the runtime unit.","type":"string"},"shape":{"description":"Shape is a compute size label, echoed back; sizing is the operator's\ndefault. Defaults to \"auto\".","type":"string"}},"type":"object"},"runResp":{"properties":{"error":{"description":"Error is the connector-level failure message when not ok.","type":"string"},"ok":{"description":"Ok reports whether the action ran to completion.","type":"boolean"},"output":{"description":"Output is the action's result when ok. Its shape is the action's own.","type":"object"}},"type":"object"},"runView":{"properties":{"id":{"description":"ID is the application id the run created or converged.","type":"string"},"name":{"description":"Name is the run's name, as stored.","type":"string"},"shape":{"description":"Shape is the compute size label the request asked for, or \"auto\".","type":"string"},"status":{"description":"Status is the application's state — `deploying` on a fresh accept.","type":"string"},"url":{"description":"URL is the run's live HTTPS address.","type":"string"}},"type":"object"},"runnerBuildReq":{"properties":{"arch":{"description":"Arch is the target architecture for the artifact lane.","type":"string"},"binaries":{"description":"Binaries selects the ARTIFACT lane (artifact.go): build what the repo's\nhanzo.yml `binaries:` block declares — a Go binary, an npm tarball, a Rust\nbinary — and publish it to hanzoai/s3 instead of pushing an image. It is the\nsame recipe hanzoai/ci reads, sent verbatim, so `image` is meaningless here\nand must be absent.","items":{"$ref":"#/components/schemas/binarySpec"},"type":"array"},"branch":{"description":"Branch is the branch to build when no SHA or Ref is given.","type":"string"},"bucket":{"description":"Bucket mirrors hanzo.yml's `bucket:` — where the artifact lane publishes.","type":"string"},"context":{"description":"Context is the build context path within the repo.","type":"string"},"dockerTarget":{"description":"DockerTarget is the multi-stage build target to stop at.","type":"string"},"dockerfile":{"description":"Dockerfile is the path to build from; empty uses the zero-config frontend.","type":"string"},"image":{"description":"Image is the output image ref to push. Required on the image lane, and it\nmust target a registry namespace the caller's org owns.","type":"string"},"organizationId":{"description":"OrgID attributes the build to an org. On the IAM path it defaults to the\ncaller's own validated org, and a foreign one is refused unless the caller\nis a platform SuperAdmin.","type":"string"},"os":{"description":"OS is the target operating system for the artifact lane.","type":"string"},"ref":{"description":"Ref is the git ref to build when no SHA is given.","type":"string"},"release":{"description":"Release requests native release semantics for cloud's self-publish: compute\nthe next version, build+push ghcr.io/hanzoai/cloud, smoke it, then tag (the\nreceipt) and notify universe. It owns its output image (release.go), and it\ntakes SuperAdmin.","type":"boolean"},"repo":{"description":"Repo is the repository clone URL to build. Required on the image lane.","type":"string"},"sha":{"description":"SHA is the commit to pin; it wins over Ref and Branch.","type":"string"},"tag":{"description":"Tag is the publish path segment, so both front doors write ONE index at ONE\nURL. It defaults to the pinned ref, and must be named explicitly for a\nbranch.","type":"string"}},"type":"object"},"runnerBuildResp":{"properties":{"buildJobId":{"description":"BuildJobID is the queued build's id, and what a release is followed by.","type":"string"},"image":{"description":"Image is the ref the image lane will push.","type":"string"},"index":{"description":"Index is the binaries.json URL the artifact lane will publish.","type":"string"},"runnerPool":{"description":"RunnerPool is the runner class the build was placed on.","type":"string"},"status":{"description":"Status is `queued` for an ordinary build, `releasing` for a self-publish.","type":"string"},"target":{"description":"Target is the multi-stage build target, echoed back.","type":"string"}},"type":"object"},"safeIn":{"properties":{"documentIds":{"description":"DocumentIDs are data room document ids to raise a signature request over. Required.","items":{"type":"string"},"type":"array"},"signers":{"description":"Signers are the recipients, each a name and an email. Required.","items":{"$ref":"#/components/schemas/Signer"},"type":"array"}},"type":"object"},"safeOut":{"properties":{"esignRef":{"description":"EsignRef is the provider's reference for the signature request.","type":"string"},"provider":{"description":"Provider is the wired e-signature provider's name.","type":"string"}},"type":"object"},"safeProposal":{"properties":{"r":{"description":"R is the r component of the MPC threshold signature over the Safe-tx hash.","type":"string"},"s":{"description":"S is the s component of that signature.","type":"string"},"safeAddress":{"description":"SafeAddress is the Safe contract this transaction is for.","type":"string"},"safeTxHash":{"description":"SafeTxHash is the EIP-712 Safe transaction hash, bound to the Safe contract\nand the chain id — the value the owner approval signs.","type":"string"},"walletId":{"description":"WalletID is the wallet whose Safe this is.","type":"string"}},"type":"object"},"safeTxIn":{"properties":{"chainId":{"description":"ChainID is the EVM chain the Safe transaction is bound to. 0 uses the\nwallet's own chain, or the Hanzo L1 (36963) when it is chain-agnostic.","type":"integer"},"data":{"description":"Data is the call data, hex-encoded.","type":"string"},"nonce":{"description":"Nonce is the Safe's transaction nonce.","type":"integer"},"to":{"description":"To is the transaction's target address.","type":"string"},"value":{"description":"Value is the native-token amount to send, as a decimal string in wei.","type":"string"}},"type":"object"},"sample":{"properties":{"cents":{"type":"integer"},"date":{"type":"string"}},"type":"object"},"sampleAccepted":{"properties":{"recorded":{"description":"Recorded is always true: the response is an acknowledgement, and the\nwarehouse write is detached, so it reports acceptance, not durability.","type":"boolean"}},"type":"object"},"sampleIngest":{"properties":{"gpuModel":{"type":"string"},"gpuUtil":{"description":"GPUUtil is accelerator utilization as a fraction 0..1; the warehouse clamps\nanything outside that.","type":"number"},"gpus":{"description":"GPUs is how many accelerators the reading covers, GPUModel the representative\nmodel name.","type":"integer"},"host":{"description":"Host is the node's hostname, for display.","type":"string"},"memFree":{"type":"integer"},"memUsed":{"description":"MemUsed and MemFree are host memory in bytes.","type":"integer"},"unit":{"description":"Unit is the reporting node's own id — the same id it registered under, and\nthe key the board joins this series onto. Required.","type":"string"}},"type":"object"},"sampleList":{"properties":{"samples":{"description":"Samples are the readings, OLDEST first — the order a chart plots.","items":{"$ref":"#/components/schemas/sampleView"},"type":"array"}},"type":"object"},"sampleReq":{"properties":{"account":{"description":"Account is the linked account the window was metered from.","type":"string"},"cachedInputTokens":{"description":"CachedInputTokens is the prompt tokens the provider served from cache.","type":"integer"},"confidence":{"description":"Confidence says how much the counters below mean.","type":"string"},"costCents":{"description":"CostCents is what the window cost on the PROVIDER's own plan, in US cents.","type":"integer"},"costLimitCents":{"description":"CostLimitCents is the plan's spend ceiling for the window, in US cents.","type":"integer"},"currency":{"description":"Currency is the provider's currency when it is not US cents.","type":"string"},"inputTokens":{"description":"InputTokens is prompt tokens consumed in the window.","type":"integer"},"kind":{"description":"Kind is subscription or apikey. Empty is accepted; anything else is\nrefused.","type":"string"},"lane":{"description":"Lane is the meter lane within the account.","type":"string"},"machine":{"description":"Machine is the host whose meter read the window. Required.","type":"string"},"outputTokens":{"description":"OutputTokens is completion tokens produced in the window.","type":"integer"},"plan":{"description":"Plan is the subscription plan the account is on, as the provider names it.","type":"string"},"provider":{"description":"Provider is the upstream the account belongs to, e.g. anthropic. Required.","type":"string"},"requests":{"description":"Requests is how many requests the window covers.","type":"integer"},"resetsAt":{"description":"ResetsAt is when the measured window rolls over, RFC3339. Empty is\nallowed; anything else that is not RFC3339 is refused.","type":"string"},"synthetic":{"description":"Synthetic marks a window the meter inferred rather than read.","type":"boolean"},"totalTokens":{"description":"TotalTokens is the window's total tokens.","type":"integer"},"usedPct":{"description":"UsedPct is how much of the window's allowance is consumed, 0–100.","type":"number"},"window":{"description":"Window is the window class: 6h, day, week or month. Required, and a class\nthis surface does not know is refused rather than rewritten.","type":"string"},"windowMinutes":{"description":"WindowMinutes is the window's real length in minutes, as the meter reports\nit.","type":"integer"},"windowStart":{"description":"WindowStart is when the measured window opened, RFC3339. Empty is allowed;\nanything else that is not RFC3339 is refused.","type":"string"}},"type":"object"},"sampleView":{"properties":{"at":{"type":"string"},"costCents":{"type":"integer"},"cpus":{"type":"integer"},"gpuModel":{"type":"string"},"gpuUtil":{"type":"number"},"gpus":{"type":"integer"},"host":{"type":"string"},"kind":{"type":"string"},"load1":{"type":"number"},"load15":{"type":"number"},"load5":{"type":"number"},"memFree":{"type":"integer"},"memUsed":{"type":"integer"},"memory":{"type":"integer"},"source":{"type":"string"},"unit":{"type":"string"}},"type":"object"},"searchIn":{"properties":{"doctypes":{"description":"DocTypes restricts retrieval to a subset of the indexed knowledge doctypes\n(kb-page, kb-memory, kb-source). An empty or foreign list reads all of them.","items":{"type":"string"},"type":"array"},"limit":{"description":"Limit bounds the hits returned. Default 10, maximum 50.","type":"integer"},"project":{"description":"Project narrows retrieval to one project scope.","type":"string"},"query":{"description":"Query is the natural-language question. Required.","type":"string"}},"type":"object"},"searchIndex":{"properties":{"createdAt":{"description":"CreatedAt is the index's creation time (RFC 3339); it falls back to now when\nthe index list could not be read.","type":"string"},"docCount":{"description":"DocCount is how many documents the index currently holds.","type":"integer"},"lastIndexedAt":{"description":"LastIndexedAt is the index's last update time (RFC 3339), null when the\nindex list could not be read.","type":"string"},"name":{"description":"Name is the index uid.","type":"string"}},"type":"object"},"searchIndexList":{"properties":{"indexes":{"description":"Indexes is one row per Meilisearch index, sorted by name. Empty — never\nabsent — when the search service cannot be reached.","items":{"$ref":"#/components/schemas/searchIndex"},"type":"array"}},"type":"object"},"searchOut":{"properties":{"degraded":{"description":"Degraded is true when the index was unreachable and this answer is honestly\nempty rather than wrong — a RAG caller continues with no context instead of\nfailing the turn. Absent on a normal answer.","type":"boolean"},"hits":{"description":"Hits are the matching passages, most relevant first.","items":{"$ref":"#/components/schemas/hit"},"type":"array"}},"type":"object"},"searchResults":{"properties":{"degraded":{"description":"Degraded is true when retrieval failed and the empty result set is an\noutage rather than a real absence of matches. Absent on a healthy answer.","type":"boolean"},"query":{"description":"Query echoes the query that was run.","type":"string"},"results":{"description":"Results are the matching spans, best first. Never null — an empty search is\nan empty array.","items":{"$ref":"#/components/schemas/Span"},"type":"array"},"type":{"description":"Type echoes the retrieval tier that ran, after defaulting.","type":"string"}},"type":"object"},"searchStats":{"properties":{"searchesPerDay":{"description":"SearchesPerDay is always empty, for the same reason as totalSearches.","items":{"$ref":"#/components/schemas/dayCount"},"type":"array"},"totalDocuments":{"description":"TotalDocuments is the sum of every index's document count.","type":"integer"},"totalSearches":{"description":"TotalSearches is always 0: Meilisearch keeps no query-history counter, so\nthis surface reports the honest zero rather than an estimate.","type":"integer"},"totalSessions":{"description":"TotalSessions is always 0, for the same reason as totalSearches.","type":"integer"}},"type":"object"},"seedData":{"properties":{"created":{"description":"Created is false when this ref had already been seeded — the injection is at-most-once.","type":"boolean"},"entry":{"$ref":"#/components/schemas/JournalEntry","description":"Entry is the journal entry the injection wrote."},"reserveCents":{"description":"ReserveCents is the fund balance after the injection.","type":"integer"}},"type":"object"},"seedOut":{"properties":{"data":{"$ref":"#/components/schemas/seedData","description":"Data is the injection result."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"seedRequest":{"properties":{"amountCents":{"description":"AmountCents is the capital to inject, in minor units. Must be \u003e 0.","type":"integer"},"memo":{"description":"Memo is the operator's note on the entry. Empty takes \"reserve capital injection\".","type":"string"},"ref":{"description":"Ref is an idempotency key. Without one each seed is a distinct injection.","type":"string"}},"type":"object"},"selfReleaseList":{"properties":{"data":{"description":"Data is the recorded release runs, newest first.","items":{"$ref":"#/components/schemas/ReleaseState"},"type":"array"}},"type":"object"},"seriesLine":{"properties":{"key":{"description":"agent name","type":"string"},"points":{"items":{"$ref":"#/components/schemas/seriesPoint"},"type":"array"}},"type":"object"},"seriesPoint":{"properties":{"t":{"description":"bucket start, RFC3339 UTC","type":"string"},"v":{"description":"real invocation count in the bucket","type":"integer"}},"type":"object"},"serviceList":{"properties":{"services":{"description":"Services is every registered service with its live waitlist mode.","items":{"$ref":"#/components/schemas/ServiceView"},"type":"array"}},"type":"object"},"serviceModeIn":{"properties":{"service":{"description":"Service is the slug to flip, taken from the path.","type":"string"},"waitlistMode":{"description":"WaitlistMode is the new mode: true gates the service behind the waitlist, false\nopens it. This is the launch lever.","type":"boolean"}},"type":"object"},"serviceOne":{"properties":{"service":{"$ref":"#/components/schemas/ServiceView","description":"Service is the row as it stands after the write, live mode included."}},"type":"object"},"serviceOut":{"properties":{"data":{"$ref":"#/components/schemas/serviceOne"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"servicesOut":{"properties":{"data":{"$ref":"#/components/schemas/serviceList"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"sessionDetail":{"properties":{"account":{"type":"string"},"actor":{"type":"string"},"agent":{"type":"string"},"childSessions":{"items":{"$ref":"#/components/schemas/sessionView"},"type":"array"},"children":{"type":"integer"},"createdAt":{"type":"string"},"cwd":{"type":"string"},"endedAt":{"type":"string"},"events":{"type":"integer"},"host":{"type":"string"},"id":{"type":"string"},"lastEvent":{"$ref":"#/components/schemas/lastEventView"},"org":{"type":"string"},"parentSessionId":{"type":"string"},"project":{"type":"string"},"provider":{"type":"string"},"published":{"type":"boolean"},"recentEvents":{"items":{"$ref":"#/components/schemas/eventView"},"type":"array"},"repo":{"type":"string"},"rootSessionId":{"type":"string"},"startedAt":{"type":"string"},"status":{"type":"string"},"target":{"type":"string"},"taskRunId":{"type":"string"},"taskWorkflowId":{"type":"string"},"terminal":{"type":"string"},"title":{"type":"string"},"updatedAt":{"type":"string"}},"type":"object"},"sessionList":{"properties":{"sessions":{"description":"Sessions is the matching sessions, each with its event and child counts and\na one-line preview of its latest event.","items":{"$ref":"#/components/schemas/sessionView"},"type":"array"}},"type":"object"},"sessionUser":{"properties":{"groups":{"description":"Groups is the caller's group list, always empty here: this console\nauthorizes on the platform SuperAdmin fact alone, not on argocd RBAC groups.\nAbsent for an anonymous caller.","items":{"type":"string"},"type":"array"},"iss":{"description":"Iss is the token issuer as the SPA expects to see it — the literal \"argocd\",\nso the UI never triggers an SSO redirect of its own. Absent for an anonymous\ncaller.","type":"string"},"loggedIn":{"description":"LoggedIn reports whether this browser holds a session this console accepts.","type":"boolean"},"loginUrl":{"description":"LoginURL is where an anonymous caller signs in. Absent once signed in.","type":"string"},"logoutUrl":{"description":"LogoutURL is where a signed-in caller ends the session. Absent when anonymous.","type":"string"},"username":{"description":"Username is the validated principal's user ID — the opaque gateway id, which\nis what argocd's UI renders as the signed-in user here — or \"admin\" when the\nprincipal carries none. Absent when anonymous.","type":"string"}},"type":"object"},"sessionView":{"properties":{"account":{"type":"string"},"actor":{"type":"string"},"agent":{"type":"string"},"children":{"type":"integer"},"createdAt":{"type":"string"},"cwd":{"type":"string"},"endedAt":{"type":"string"},"events":{"type":"integer"},"host":{"description":"Execution context (mission-control): the machine/repo/cwd a card shows and\nthe run-target a session is dispatched to. Omitted when a surface didn't report it.","type":"string"},"id":{"type":"string"},"lastEvent":{"$ref":"#/components/schemas/lastEventView","description":"LastEvent is the compact latest-activity line for the list projection (nil in\nregister/patch/tree responses; set by list + detail). It lets a swipe card show\na live one-line preview without fetching full detail."},"org":{"description":"Org is the caller's OWN tenant, echoed so a client can build the public\nbuild URL (/builds/:org/:project) without a second call or a guess. It is\nnever another tenant's — every read is org-scoped before it gets here.","type":"string"},"parentSessionId":{"type":"string"},"project":{"description":"The readable build: the product this session built and whether its story\nis public (provenance.go).","type":"string"},"provider":{"type":"string"},"published":{"type":"boolean"},"repo":{"type":"string"},"rootSessionId":{"type":"string"},"startedAt":{"type":"string"},"status":{"type":"string"},"target":{"type":"string"},"taskRunId":{"type":"string"},"taskWorkflowId":{"type":"string"},"terminal":{"description":"Terminal is where this session can be WATCHED — the URL the machine\npublished for its live terminal. Omitted when it publishes none.","type":"string"},"title":{"type":"string"},"updatedAt":{"type":"string"}},"type":"object"},"setEnablementBody":{"properties":{"betaOrgs":{"description":"BetaOrgs REPLACES the item's beta grant list when present. Omit it to\nleave the existing grants alone.","items":{"type":"string"},"type":"array"},"id":{"description":"ID is the item within that namespace — a model id, a provider name, or a\nfeature's key.","type":"string"},"kind":{"description":"Kind is the item's namespace: \"model\", \"provider\" or \"feature\".","type":"string"},"state":{"description":"State is the item's global enablement: \"off\" (hidden from everyone,\nabsolutely), \"beta\" (visible only to granted orgs) or \"ga\" (visible to\neveryone). Required.","type":"string"}},"type":"object"},"setEnvReq":{"properties":{"app":{"description":"App is the application's slug, from the path.","type":"string"},"env":{"description":"Env is the app's whole environment set, REPLACING what it had. Keys must\nmatch `^[A-Za-z_][A-Za-z0-9_]*$`; a variable marked `secret: true` is\nsealed into KMS and blanked in the database.","items":{"$ref":"#/components/schemas/EnvVarJSON"},"type":"array"},"project":{"description":"Project is the project the application lives under, from the path.","type":"string"}},"type":"object"},"setFlagIn":{"properties":{"active":{"description":"Active is the switch itself: true enables the flag for every evaluation.","type":"boolean"},"filters":{"description":"Filters is the optional rollout/payload block of a VALUED switch, e.g.\n{\"groups\":[{\"properties\":[],\"rollout_percentage\":100}],\"payloads\":{\"true\":250}}."},"key":{"description":"Key is the switch to write, taken from the path (e.g. \"waitlist.chat\").","type":"string"}},"type":"object"},"settingsReq":{"properties":{"config":{"additionalProperties":{"type":"object"},"description":"Config is the product's non-secret configuration, stored verbatim. Bounded at\n64 KiB once serialized. Omit it to store an empty object.","type":"object"},"product":{"description":"Product is the catalog slug, from the PATH. zip binds the path last, so the\nURL names the product being written whatever a body field claims.","type":"string"},"secrets":{"additionalProperties":{"type":"string"},"description":"Secrets are the secret fields, by name. Each VALUE is sealed into KMS and\nnever reaches this deployment's database; a value that is empty or equal to\nthe mask the read path returns means \"unchanged\" and is skipped, so a console\nround-trip cannot blank a stored secret. A key must match\n^[a-z0-9][a-z0-9._-]{0,62}$, a value is bounded at 8 KiB, and an org may hold\nat most 64 secret fields per product.","type":"object"}},"type":"object"},"settingsView":{"properties":{"config":{"description":"Config is the product's non-secret configuration, an opaque JSON object the\nserver stores and returns verbatim. `{}` when nothing has been saved."},"createdAt":{"description":"CreatedAt is when this configuration was first written, RFC 3339 UTC.","type":"string"},"product":{"description":"Product is the catalog slug this configuration belongs to.","type":"string"},"secretKeys":{"description":"SecretKeys names the secret fields that ARE set. Their VALUES live only in KMS\nand are never returned here — the console renders a mask.","items":{"type":"string"},"type":"array"},"updatedAt":{"description":"UpdatedAt is when this configuration was last written, RFC 3339 UTC. Empty\nwhen nothing has been saved.","type":"string"}},"type":"object"},"settlement":{"properties":{"affiliate":{"$ref":"#/components/schemas/adminAffiliateView","description":"Affiliate is the row re-read AFTER the payout, so its paidCents and\npendingCents already account for the row beside it."},"payout":{"$ref":"#/components/schemas/remittance","description":"Payout is the payout row just recorded."}},"type":"object"},"shareView":{"properties":{"backend":{"description":"Backend is the local endpoint the share proxies to.","type":"string"},"backendMode":{"description":"BackendMode is how the tunnel serves the backend, e.g. proxy or web.","type":"string"},"createdAt":{"description":"CreatedAt is when the share was opened, unix seconds.","type":"integer"},"token":{"description":"Token is the share's own identifier, the leaf of its public URL.","type":"string"},"url":{"description":"URL is the share's public address, rendered from the deployment's URL template.","type":"string"}},"type":"object"},"sharesOut":{"properties":{"shares":{"description":"Shares is the org's active shares — empty rather than absent when there are\nnone, or when the controller cannot be reached.","items":{"$ref":"#/components/schemas/shareView"},"type":"array"}},"type":"object"},"signIn":{"properties":{"digest":{"description":"Digest is a pre-computed 32-byte digest as hex, with or without the 0x\nprefix. When present it is signed verbatim and message is ignored.","type":"string"},"message":{"description":"Message is arbitrary text to hash with Keccak256 and sign. Used only when\ndigest is empty.","type":"string"}},"type":"object"},"signReply":{"properties":{"document":{"$ref":"#/components/schemas/documentSummary","description":"Document is the document, now out for signature. Its rendered body is not\nrepeated here."},"esignRef":{"description":"EsignRef is the provider's own reference for the request — what a webhook or\na status poll quotes.","type":"string"},"provider":{"description":"Provider names the e-signature provider that took the request. \"manual\" means\nno provider is wired on this deployment and the org fulfils it out of band.","type":"string"}},"type":"object"},"signRequest":{"properties":{"id":{"description":"ID is the document to send for signature, from the path.","type":"string"},"signers":{"description":"Signers are the people who must sign, by name and email. At least one is\nrequired.","items":{"$ref":"#/components/schemas/legalSigner"},"type":"array"}},"type":"object"},"signature":{"properties":{"address":{"description":"Address is the wallet's on-chain address, the one this signature recovers to.","type":"string"},"digest":{"description":"Digest is the 32-byte digest that was signed, hex with an 0x prefix.","type":"string"},"signature":{"description":"Signature is the 65-byte secp256k1 signature, hex with an 0x prefix.","type":"string"},"walletId":{"description":"WalletID is the wallet that signed.","type":"string"}},"type":"object"},"signerData":{"properties":{"boundAnchorSigner":{"description":"BoundAnchorSigner is the EVM address now signing anchors. Fund it for gas.","type":"string"},"chainId":{"description":"ChainID is the EVM chain the signer is bound for.","type":"integer"},"org":{"description":"Org is the org whose treasury wallet was resolved.","type":"string"}},"type":"object"},"signerOut":{"properties":{"data":{"$ref":"#/components/schemas/signerData","description":"Data is the bound signer."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"skillDeleted":{"properties":{"deleted":{"description":"Deleted is the skill id that is now gone.","type":"string"}},"type":"object"},"skillIn":{"properties":{"content":{"description":"Content is the SKILL.md body. Required, at most 256 KiB.","type":"string"},"description":{"description":"Description is the one-line summary discovery shows for the skill.","type":"string"},"name":{"description":"Name is the skill's id within the org: one lowercase path segment\n(a-z0-9, _ or -). Writing an existing name REVISES that skill.","type":"string"}},"type":"object"},"skillWritten":{"properties":{"skill":{"$ref":"#/components/schemas/Skill","description":"Skill is the skill as stored, with its derived id and creation time."}},"type":"object"},"slotView":{"properties":{"blsPubkey":{"description":"BLSPubkey is the node's BLS public key, hex.","type":"string"},"crName":{"description":"CRName is the LuxNetwork custom resource that materializes the node.","type":"string"},"createdAt":{"description":"CreatedAt is when the slot was first claimed, as a Unix timestamp.","type":"integer"},"namespace":{"description":"Namespace is the Kubernetes namespace the node's CR lives in.","type":"string"},"network":{"description":"Network is the luxd network slug the node joins.","type":"string"},"nodeID":{"description":"NodeID is the luxd node id derived from the sealed staking identity. It is\nstable across re-claims of the same slot.","type":"string"},"nodeStatus":{"description":"NodeStatus is the provisioning state of the node: \"node_created\" once the CR\nis applied, \"node_pending\" when no cluster is reachable (the slot is still\nclaimed and the keys are still sealed).","type":"string"},"registration":{"$ref":"#/components/schemas/registrationView","description":"Registration is the queued owner-gated registration, absent until one exists."},"slot":{"description":"Slot is the validator slot number — the same value as tokenId, under the\nname the portal reads.","type":"integer"},"tokenId":{"description":"TokenID is the GenesisNFT token id that IS this slot.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when the slot last changed, as a Unix timestamp.","type":"integer"},"wallet":{"description":"Wallet is the lowercase Ethereum address that proved ownership of the NFT.","type":"string"}},"type":"object"},"sourceFailure":{"properties":{"reason":{"description":"Reason is a terse, log-safe summary — never the upstream's response body.","type":"string"},"source":{"description":"Source is the dependency that failed, named as an operator names it.","type":"string"}},"type":"object"},"sourceState":{"properties":{"available":{"description":"Available reports whether this ledger answered; false is honest\n\"unavailable\", never a zero that would read as no usage.","type":"boolean"},"note":{"description":"Note says in prose what the ledger's numbers mean.","type":"string"},"scope":{"description":"Scope is whose usage the ledger measures: user or org.","type":"string"},"source":{"description":"Source is the table of record behind the ledger.","type":"string"}},"type":"object"},"sourceToolList":{"properties":{"source":{"description":"Source is the source these tools came from.","type":"string"},"tools":{"description":"Tools is the caller's tools from that source. Never null.","items":{"$ref":"#/components/schemas/Tool"},"type":"array"}},"type":"object"},"stateGraph":{"properties":{"initial":{"type":"string"},"live":{"type":"string"},"states":{"items":{"type":"string"},"type":"array"},"transitions":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}},"type":"object"},"statsOut":{"properties":{"admin":{"description":"Admin is the upstream service's server-panel flag, always false here.","type":"boolean"},"metrics":{"description":"Metrics is the upstream transactor's metrics block. This server does not\npopulate it, so it is always the empty object — the front reads the key,\nnot its contents.","properties":{},"type":"object"},"statistics":{"$ref":"#/components/schemas/statsSessions","description":"Statistics carries the live sessions."}},"type":"object"},"statsSessions":{"properties":{"activeSessions":{"additionalProperties":{"items":{"$ref":"#/components/schemas/statsUser"},"type":"array"},"description":"ActiveSessions maps a workspace uuid to its connected sessions. It carries\nonly the token's OWN workspace, and is empty for a token that names none.","type":"object"}},"type":"object"},"statsUser":{"properties":{"userId":{"description":"UserID is the account the session is authenticated as.","type":"string"}},"type":"object"},"statusCounts":{"properties":{"qualified":{"description":"Qualified is how many referees have made metered spend.","type":"integer"},"signup":{"description":"Signup is how many referees have signed up but not yet spent.","type":"integer"},"total":{"description":"Total is every referral this org has made.","type":"integer"}},"type":"object"},"statusView":{"properties":{"disclaimer":{"description":"Disclaimer states that statuses are provider-reported, never a platform\nassertion of legal or regulatory compliance.","type":"string"},"provider":{"description":"Provider is the wired verification provider's name.","type":"string"},"verifications":{"$ref":"#/components/schemas/verificationTally","description":"Verifications tallies the org's verifications by provider-reported status."}},"type":"object"},"stepView":{"properties":{"args":{"additionalProperties":{"type":"object"},"type":"object"},"automatable":{"description":"Automatable is true when the Business AI can run this step (it names a tool).","type":"boolean"},"available":{"description":"Available is true when every dependency is done or skipped.","type":"boolean"},"blockedBy":{"description":"BlockedBy lists the unfinished dependencies keeping the step unavailable.","items":{"type":"string"},"type":"array"},"deps":{"description":"Dependencies are step ids that must be done/skipped before this step is\navailable. The wire key is `deps` (the blueprint contract).","items":{"type":"string"},"type":"array"},"detail":{"description":"Detail is the prose/juncture — what the Guide asks or explains here.","type":"string"},"draft":{"type":"string"},"draftInto":{"type":"string"},"enabled":{"description":"Enabled is the admin on/off lever; absent reads as enabled.","type":"boolean"},"id":{"description":"ID is the step's id, as it appears in the journey (e.g. \"gsuite\").","type":"string"},"section":{"description":"Section is the phase (section id) this step groups under.","type":"string"},"signal":{"description":"Signal names the machine detector that auto-marks this step done.","type":"string"},"source":{"description":"Source records what marked the state: manual, auto (detected) or agent.","type":"string"},"state":{"description":"State is the step's per-org lifecycle state: todo|in_progress|done|skipped.","type":"string"},"title":{"type":"string"},"tool":{"description":"Tool is the MCP tool the Business AI runs for \"do it for me\"; Args are its\ndefault arguments, Draft an optional AI prompt whose output fills the\nDraftInto arg (default \"brief\").","type":"string"}},"type":"object"},"storageAlert":{"properties":{"level":{"type":"string"},"pct":{"type":"number"},"volume":{"type":"string"}},"type":"object"},"storageFleet":{"properties":{"count":{"type":"integer"},"monthlyUsd":{"type":"integer"},"pct":{"type":"number"},"totalGiB":{"type":"integer"},"usedGiB":{"type":"number"}},"type":"object"},"storageSnapshot":{"properties":{"alerts":{"items":{"$ref":"#/components/schemas/storageAlert"},"type":"array"},"datastore":{"$ref":"#/components/schemas/datastoreVolume"},"fleet":{"$ref":"#/components/schemas/storageFleet"},"volumes":{"items":{"$ref":"#/components/schemas/storageVolume"},"type":"array"}},"type":"object"},"storageVolume":{"properties":{"attached":{"type":"boolean"},"id":{"type":"string"},"name":{"type":"string"},"pct":{"type":"number"},"region":{"type":"string"},"service":{"type":"string"},"sizeGiB":{"type":"integer"},"usedGiB":{"type":"number"}},"type":"object"},"strategyView":{"properties":{"action":{"type":"string"},"category":{"type":"string"},"id":{"type":"string"},"tags":{"items":{"type":"string"},"type":"array"},"workload":{"type":"string"}},"type":"object"},"streamPage":{"properties":{"data":{"description":"Data are the org's streams.","items":{"$ref":"#/components/schemas/streamRecord"},"type":"array"}},"type":"object"},"streamRecord":{"properties":{"bytes":{"description":"Bytes is how many bytes the stream holds right now.","type":"integer"},"consumers":{"description":"Consumers is how many consumers the stream carries.","type":"integer"},"created":{"description":"Created is when the stream was created, RFC3339.","type":"string"},"discard":{"description":"Discard says which end gives way at the limits: old or new.","type":"string"},"firstSeq":{"description":"FirstSeq is the sequence of the oldest retained message.","type":"integer"},"lastSeq":{"description":"LastSeq is the sequence of the newest message.","type":"integer"},"maxAge":{"description":"MaxAge is the age cap in seconds; 0 means unlimited.","type":"integer"},"maxBytes":{"description":"MaxBytes is the retained-byte cap; -1 means unlimited.","type":"integer"},"maxMsgs":{"description":"MaxMsgs is the retained-message cap; -1 means unlimited.","type":"integer"},"messages":{"description":"Messages is how many messages the stream holds right now.","type":"integer"},"name":{"description":"Name is the stream's name within the org.","type":"string"},"retention":{"description":"Retention is the discipline: limits, interest or workqueue.","type":"string"},"storage":{"description":"Storage is the backend: file or memory.","type":"string"},"subjects":{"description":"Subjects are the subjects it captures, in the org's namespace.","items":{"type":"string"},"type":"array"}},"type":"object"},"streamUpdate":{"properties":{"discard":{"type":"string"},"maxAge":{"type":"integer"},"maxBytes":{"type":"integer"},"maxMsgs":{"type":"integer"},"retention":{"type":"string"},"storage":{"type":"string"},"stream":{"description":"Stream is the stream to update, from the path.","type":"string"},"subjects":{"items":{"type":"string"},"type":"array"}},"type":"object"},"streamWrite":{"properties":{"discard":{"type":"string"},"maxAge":{"type":"integer"},"maxBytes":{"type":"integer"},"maxMsgs":{"type":"integer"},"name":{"description":"Name is the stream's name within the org: 1–64 of [A-Za-z0-9_], no dash.","type":"string"},"retention":{"type":"string"},"storage":{"type":"string"},"subjects":{"items":{"type":"string"},"type":"array"}},"type":"object"},"structureIn":{"properties":{"jurisdiction":{"description":"Jurisdiction is the state of formation: DE or WY.","type":"string"},"name":{"description":"Name is the proposed company name.","type":"string"},"structure":{"description":"Structure is the legal entity: c-corp, llc or dao-llc.","type":"string"}},"type":"object"},"subdomainSetIn":{"properties":{"enabled":{"description":"Enabled publishes the script on \u003cscript\u003e.\u003csubdomain\u003e.workers.dev when true,\nand withdraws it when false.","type":"boolean"},"script":{"description":"Script is the Worker script name, from the path.","type":"string"}},"type":"object"},"subjectList":{"properties":{"data":{"description":"Data is the org's subjects, newest first, without contact PII.","items":{"$ref":"#/components/schemas/subjectSummary"},"type":"array"}},"type":"object"},"subjectReq":{"properties":{"email":{"description":"Email is the subject's contact email, sealed at rest.","type":"string"},"kind":{"description":"Kind is the party type: \"individual\" (KYC) or \"business\" (KYB).","type":"string"},"name":{"description":"Name is the subject's name, sealed at rest.","type":"string"},"ref":{"description":"Ref is the org's own opaque external id for this subject.","type":"string"}},"type":"object"},"subjectSummary":{"properties":{"createdAt":{"description":"CreatedAt is the unix second the subject was recorded.","type":"integer"},"hasEmail":{"description":"HasEmail reports whether a contact email is on file, without exposing it.","type":"boolean"},"id":{"description":"ID is the subject's opaque id.","type":"string"},"kind":{"description":"Kind is the party type: \"individual\" (KYC) or \"business\" (KYB).","type":"string"},"ref":{"description":"Ref is the org's own opaque external id for this subject.","type":"string"}},"type":"object"},"subscribeReq":{"properties":{"channel":{"description":"Channel is the Slack channel the notifier posts to — an id (C…/G…), a\n#name, or a bare name. Required.","type":"string"},"events":{"description":"Events narrows delivery to these lifecycle kinds (push.landed,\ndeploy.live, deploy.failed). Omit it to receive every deliverable kind; a\nkind that is never posted to Slack is refused rather than silently dropped.","items":{"type":"string"},"type":"array"},"name":{"description":"Name is the repo to subscribe, from the :name path segment.","type":"string"}},"type":"object"},"subscriptionList":{"properties":{"data":{"description":"Data holds the repo's subscriptions.","items":{"$ref":"#/components/schemas/subscriptionView"},"type":"array"}},"type":"object"},"subscriptionView":{"properties":{"channel":{"description":"Channel is the Slack channel id or name the notifier posts to.","type":"string"},"createdAt":{"description":"CreatedAt is RFC 3339 UTC.","type":"string"},"events":{"description":"Events is the kind filter; absent means every deliverable kind.","items":{"type":"string"},"type":"array"},"id":{"description":"ID is the subscription's identifier (\"sub_…\"), the handle to delete it by.","type":"string"},"repo":{"description":"Repo is the repo whose lifecycle events are delivered.","type":"string"}},"type":"object"},"subsystemBoard":{"properties":{"end":{"type":"string"},"range":{"type":"string"},"rows":{"items":{"$ref":"#/components/schemas/subsystemRow"},"type":"array"},"sources":{"items":{"$ref":"#/components/schemas/SourceStatus"},"type":"array"},"start":{"type":"string"},"totals":{"$ref":"#/components/schemas/subsystemTotals"}},"type":"object"},"subsystemRow":{"properties":{"enabled":{"type":"boolean"},"errorRate":{"description":"percent (0..100)","type":"number"},"errors":{"type":"integer"},"lastErrorAt":{"type":"string"},"lastErrorMessage":{"type":"string"},"lastErrorRoute":{"type":"string"},"lastErrorStatus":{"type":"string"},"latencyP50Ms":{"type":"number"},"latencyP95Ms":{"type":"number"},"latencyP99Ms":{"type":"number"},"name":{"type":"string"},"prefixes":{"items":{"type":"string"},"type":"array"},"requests":{"type":"integer"},"requestsPerMin":{"type":"number"}},"type":"object"},"subsystemTotals":{"properties":{"disabled":{"type":"integer"},"enabled":{"type":"integer"},"errorRate":{"description":"percent (0..100)","type":"number"},"errors":{"type":"integer"},"reporting":{"description":"enabled AND served ≥1 traced request in the window","type":"integer"},"requests":{"type":"integer"},"subsystems":{"type":"integer"}},"type":"object"},"suggestResponse":{"properties":{"funnel":{"$ref":"#/components/schemas/Funnel","description":"Funnel is the org's trailing-window traffic → signups → orders."},"narrative":{"description":"Narrative is the AI's grounded prose over those quests and numbers. Absent\nwhen no AI plane is wired or the completion failed — never fabricated.","type":"string"},"next":{"description":"Next is the id of the single next step the static journey names — the\nlinear answer the ranked Suggestions refine.","type":"string"},"recommendations":{"description":"Recommendations are the next-best GTM actions derived from that funnel.","items":{"type":"string"},"type":"array"},"suggestions":{"description":"Suggestions are the available, non-terminal quests ranked best-first by how\nmuch downstream work each unblocks.","items":{"$ref":"#/components/schemas/suggestion"},"type":"array"}},"type":"object"},"suggestion":{"properties":{"automatable":{"type":"boolean"},"detail":{"type":"string"},"rationale":{"type":"string"},"stepId":{"type":"string"},"title":{"type":"string"},"unlocks":{"description":"Unlocks is how many downstream steps completing this one immediately makes\navailable (its leverage) — the primary ranking key.","type":"integer"}},"type":"object"},"summaryResp":{"properties":{"account":{"$ref":"#/components/schemas/sourceState","description":"Account and Hanzo report each ledger's own availability, so a partial\nwarehouse never fabricates the other half."},"from":{"description":"From and To are the one [from, to) window BOTH halves resolved, RFC 3339 UTC.","type":"string"},"hanzo":{"$ref":"#/components/schemas/sourceState"},"range":{"description":"Range is the resolved period label.","type":"string"},"rows":{"description":"Rows is the union of both ledgers, each row labelled by source and scope —\nconcatenated, NEVER summed: a plan's percentage is not money.","items":{"$ref":"#/components/schemas/totalView"},"type":"array"},"to":{"type":"string"}},"type":"object"},"summaryView":{"properties":{"doctypes":{"description":"DocTypes is how many DocTypes the org has defined.","type":"integer"},"documents":{"description":"Documents is how many documents exist across them.","type":"integer"}},"type":"object"},"sweepCounts":{"properties":{"accrued":{"description":"Accrued is how many new royalty accruals it latched.","type":"integer"},"swept":{"description":"Swept is how many (author, deploying org) pairs the sweep examined.","type":"integer"}},"type":"object"},"sweepData":{"properties":{"accruedCents":{"description":"AccruedCents is the amount moved into the reserve fund.","type":"integer"},"created":{"description":"Created is false when this period had already been swept — the accrual is idempotent.","type":"boolean"},"period":{"description":"Period is the period actually accrued.","type":"string"},"reserveCents":{"description":"ReserveCents is the fund balance after the accrual.","type":"integer"},"revenueCents":{"description":"RevenueCents is the revenue the share was computed from.","type":"integer"}},"type":"object"},"sweepEnvelope":{"properties":{"data":{"$ref":"#/components/schemas/sweepResult","description":"Data is the sweep's counters."},"msg":{"description":"Msg is empty on success; the console surfaces it when status is not \"ok\".","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"sweepOut":{"properties":{"data":{"$ref":"#/components/schemas/sweepData","description":"Data is the accrual result."},"msg":{"description":"Msg carries an operator-facing note; empty on success.","type":"string"},"status":{"description":"Status is \"ok\" on success.","type":"string"}},"type":"object"},"sweepRequest":{"properties":{"period":{"description":"Period is the accrual period as YYYY-MM. Empty takes the current UTC month.","type":"string"},"revenueCents":{"description":"RevenueCents is the net platform revenue measured for the period, in minor units. Must be \u003e= 0.","type":"integer"}},"type":"object"},"sweepResult":{"properties":{"qualified":{"description":"Qualified is how many of those referrals qualified on this pass.","type":"integer"},"swept":{"description":"Swept is how many pending referrals were checked.","type":"integer"}},"type":"object"},"syncList":{"properties":{"data":{"description":"Data is the org's syncs, each with its endpoints, policy and last-synced time.","items":{"$ref":"#/components/schemas/syncView"},"type":"array"}},"type":"object"},"syncOut":{"properties":{"data":{"$ref":"#/components/schemas/syncStarted"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"syncQueued":{"properties":{"id":{"description":"ID is the sync the reconcile was queued for.","type":"string"},"queued":{"description":"Queued is true when the reconcile was accepted; it has not run yet.","type":"boolean"}},"type":"object"},"syncReq":{"properties":{"actor":{"description":"Actor is the identity the sync writes as, used as the loop guard so its own\nwrites do not re-trigger it. Defaults to the deployment's GIT_SYNC_ACTOR.","type":"string"},"direction":{"description":"Direction is both (the default), pull, push or off.","type":"string"},"kind":{"description":"Kind is what is being synced. Only \"git\" today, which is also the default.","type":"string"},"run":{"description":"Run reconciles once immediately after the upsert, in the background.","type":"boolean"},"source":{"$ref":"#/components/schemas/endpointReq","description":"Source is the upstream end. Required."},"target":{"$ref":"#/components/schemas/endpointReq","description":"Target is the downstream end. Optional for git: a native repository named\nafter the source is derived when it is omitted."},"trigger":{"description":"Trigger is what starts a reconcile: webhook (the default), poll or manual.","type":"string"}},"type":"object"},"syncStarted":{"properties":{"started":{"type":"boolean"}},"type":"object"},"syncTally":{"properties":{"live":{"description":"Live is the number of vouchers newly posted to the live ledger.","type":"integer"},"sandbox":{"description":"Sandbox is the number newly posted to the sandbox ledger.","type":"integer"}},"type":"object"},"syncView":{"properties":{"actor":{"type":"string"},"createdAt":{"type":"string"},"direction":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"source":{"$ref":"#/components/schemas/endpointView"},"target":{"$ref":"#/components/schemas/endpointView"},"trigger":{"type":"string"},"updatedAt":{"description":"bumped on every reconcile — the last-synced time","type":"string"}},"type":"object"},"tally":{"properties":{"accruedLifetimeCents":{"description":"AccruedLifetimeCents is all commission ever accrued, summed across every\naffiliate, in cents. It only grows; a payout does not reduce it.","type":"integer"},"affiliates":{"description":"Affiliates is how many affiliate rows the board read, at every status. The\nread is bounded at 1000 rows, so a larger fleet reports the bound.","type":"integer"},"approved":{"description":"Approved is how many of those rows are approved — the only ones whose code\nresolves for attribution and whose balance can grow.","type":"integer"},"paidLifetimeCents":{"description":"PaidLifetimeCents is all commission ever paid out, in cents: credits grants\nplus record-only cash disbursements.","type":"integer"},"pendingLiabilityCents":{"description":"PendingLiabilityCents is accrued minus paid across every affiliate, in cents.\nRead it as money OWED and not yet disbursed — a liability, not spend.","type":"integer"}},"type":"object"},"targetDeleted":{"properties":{"deleted":{"description":"Deleted is true when the target was removed.","type":"boolean"},"id":{"description":"ID is the target that was removed.","type":"string"}},"type":"object"},"targetList":{"properties":{"targets":{"description":"Targets is every target registered to the caller's org.","items":{"$ref":"#/components/schemas/targetView"},"type":"array"}},"type":"object"},"targetReq":{"properties":{"capacity":{"type":"string"},"host":{"type":"string"},"kind":{"type":"string"},"label":{"type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics"},"spec":{"$ref":"#/components/schemas/Spec"},"status":{"type":"string"}},"type":"object"},"targetView":{"properties":{"capacity":{"type":"string"},"createdAt":{"type":"string"},"host":{"type":"string"},"id":{"type":"string"},"kind":{"type":"string"},"label":{"type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics"},"metricsAt":{"type":"string"},"running":{"type":"integer"},"sessions":{"type":"integer"},"spec":{"$ref":"#/components/schemas/Spec"},"status":{"type":"string"},"updatedAt":{"type":"string"}},"type":"object"},"templateCatalog":{"properties":{"data":{"description":"Data is the catalog, metadata and merge fields only — never the template\nbodies, which are fetched one at a time.","items":{"$ref":"#/components/schemas/templateView"},"type":"array"},"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire: Hanzo Legal is document\ntooling, not legal advice.","type":"string"}},"type":"object"},"templateOverride":{"properties":{"body":{"description":"Body is the text/template source. Required. Every {{.key}} it references must\nbe declared in Fields, or the save is refused rather than rendering a blank\ninto a contract later.","type":"string"},"category":{"description":"Category groups the template: formation, equity, ops or sales. Optional when\noverriding a built-in, which supplies its own.","type":"string"},"counselReview":{"description":"CounselReview marks a template whose documents must carry the counsel notice.\nIt can be raised but never lowered: a formation or equity template is always\ncounsel-review, and an override of a counsel-review built-in stays one.","type":"boolean"},"fields":{"description":"Fields declares the merge fields the body consumes. Every declared field is\nREQUIRED at generation — the engine fails closed on a missing one.","items":{"$ref":"#/components/schemas/Field"},"type":"array"},"id":{"description":"ID is the template to override, from the path. Overriding a built-in id\ninherits that built-in's category, title and counsel-review posture.","type":"string"},"title":{"description":"Title is the template's display name. Required unless a built-in supplies it.","type":"string"}},"type":"object"},"templateReply":{"properties":{"disclaimer":{"description":"Disclaimer is the boundary made visible on the wire.","type":"string"},"template":{"$ref":"#/components/schemas/legalTemplate","description":"Template is the resolved template — the org's override if it has one, else\nthe built-in."}},"type":"object"},"templateView":{"properties":{"category":{"type":"string"},"counselReview":{"type":"boolean"},"fields":{"items":{"$ref":"#/components/schemas/Field"},"type":"array"},"id":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"},"version":{"type":"integer"}},"type":"object"},"testResult":{"properties":{"delivered":{"type":"boolean"},"durationMs":{"type":"integer"},"error":{"type":"string"},"httpStatus":{"type":"integer"}},"type":"object"},"toolCall":{"properties":{"arguments":{"additionalProperties":{"type":"object"},"description":"Arguments is the tool's own input object, passed through verbatim to\nwhichever source owns it.","type":"object"},"name":{"description":"Name is the tool to run, exactly as GET /v1/tools reports it.","type":"string"}},"type":"object"},"toolList":{"properties":{"tools":{"description":"Tools is every tool the caller may see, deduplicated by name with source\nprecedence applied.","items":{"$ref":"#/components/schemas/Tool"},"type":"array"}},"type":"object"},"toolResult":{"properties":{"name":{"description":"Name is the tool that ran.","type":"string"},"result":{"description":"Result is the tool's own output, verbatim — its shape is the tool's, not\nthis plane's.","type":"object"}},"type":"object"},"totalView":{"properties":{"confidence":{"description":"Confidence says how real the row's numbers are.","type":"string"},"costCents":{"description":"CostCents is the period's spend in cents, in the row's own ledger.","type":"integer"},"provider":{"description":"Provider is the provider the row totals.","type":"string"},"requests":{"description":"Requests is the period's request count.","type":"integer"},"scope":{"description":"Scope is whose usage the row measures: user or org.","type":"string"},"source":{"description":"Source is whose meter the row came from: account or hanzo.","type":"string"},"tokens":{"description":"Tokens is the period's total token count.","type":"integer"},"usedPct":{"description":"UsedPct is the plan consumption percentage, on the account side.","type":"number"},"window":{"description":"Window is the window class the row totals, on the account side.","type":"string"},"windows":{"description":"Windows is how many window instances the row folds.","type":"integer"}},"type":"object"},"totals":{"properties":{"accruedCents":{"description":"AccruedCents is lifetime commission accrued summed over those rows, in cents.","type":"integer"},"applied":{"description":"Applied is how many of those rows are still awaiting approval — no code, no\naccrual yet.","type":"integer"},"approved":{"description":"Approved is how many are approved: the only rows whose code resolves for\nattribution and whose balance can still grow.","type":"integer"},"paidCents":{"description":"PaidCents is lifetime commission already paid out summed over those rows, in\ncents.","type":"integer"},"pendingCents":{"description":"PendingCents is accrued minus paid summed over those rows, in cents — the\noutstanding liability across the page.","type":"integer"},"suspended":{"description":"Suspended is how many were suspended. What they already accrued stays accrued\nand stays payable.","type":"integer"},"total":{"description":"Total is how many affiliate rows this page covered, at every status. It is the\npage, not the table: a limit that truncates truncates this too.","type":"integer"}},"type":"object"},"trackerProject":{"properties":{"createdAt":{"type":"integer"},"description":{"type":"string"},"id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"org":{"type":"string"},"updatedAt":{"type":"integer"}},"type":"object"},"trailPage":{"properties":{"data":{"description":"Data is one page of the org's events, newest first. Empty, never null.","items":{"$ref":"#/components/schemas/Wire"},"type":"array"},"msg":{"description":"Msg is the envelope's message slot, empty on success.","type":"string"},"status":{"description":"Status is the envelope's status slot, \"ok\" on success.","type":"string"},"total":{"description":"Total is how many events match the filter, across all pages — what a pager\nneeds to size itself.","type":"integer"}},"type":"object"},"transactionsOut":{"properties":{"transactions":{"description":"Transactions is the matching register rows, newest first.","items":{"$ref":"#/components/schemas/Txn"},"type":"array"}},"type":"object"},"transitionIn":{"properties":{"doctype":{"description":"DocType is the content type to act on, from the path.","type":"string"},"name":{"description":"Name is the document to act on, from the path.","type":"string"},"scheduleAt":{"description":"ScheduleAt is an ISO-8601 go-live time handed to the channel's own scheduler;\n\"\" distributes now.","type":"string"},"to":{"description":"To is the lifecycle state to move to. Required, and the move must be a legal\nedge from the item's current state.","type":"string"}},"type":"object"},"treeEntryJSON":{"properties":{"mode":{"description":"Mode is the octal git file mode (\"100644\", \"040000\", \"120000\").","type":"string"},"name":{"description":"Name is the entry's own name, no directory part.","type":"string"},"path":{"description":"Path is the entry's full repo-relative path.","type":"string"},"size":{"description":"Size is the file's byte length; 0 for a directory.","type":"integer"},"type":{"description":"Type is \"tree\" for a directory, \"blob\" for a file.","type":"string"}},"type":"object"},"treeJSON":{"properties":{"entries":{"description":"Entries are the immediate children, directories before files.","items":{"$ref":"#/components/schemas/treeEntryJSON"},"type":"array"}},"type":"object"},"treeNode":{"properties":{"children":{"items":{"$ref":"#/components/schemas/treeNode"},"type":"array"},"session":{"$ref":"#/components/schemas/sessionView"}},"type":"object"},"unlinkedView":{"properties":{"unlinked":{"description":"Unlinked is always true. Unlinking is idempotent: an account this org does\nnot hold answers the same, so a repeated call is not an error and is not an\nexistence oracle either.","type":"boolean"}},"type":"object"},"unreconciledOut":{"properties":{"questions":{"description":"Questions is the open clarifying question per unmatched inflow.","items":{"$ref":"#/components/schemas/BankQuestion"},"type":"array"},"transactions":{"description":"Transactions is every bank row still unmatched against the ledger.","items":{"$ref":"#/components/schemas/BankTxnRow"},"type":"array"}},"type":"object"},"updateAgentIn":{"properties":{"computeRef":{"type":"string"},"description":{"type":"string"},"executionMode":{"type":"string"},"instructions":{"type":"string"},"model":{"type":"string"},"ref":{"description":"Ref is the agent to update — its public id or org-unique name, from the path.","type":"string"},"schedule":{"type":"string"},"serviceAccountId":{"type":"string"},"tools":{"items":{"type":"string"},"type":"array"}},"type":"object"},"updateCampaignIn":{"properties":{"account":{"description":"Account is the provider ad-account this campaign runs on (Meta act_\u003cid\u003e). Optional.","type":"string"},"budget":{"description":"Budget is the campaign budget in MINOR units (cents). Negative values clamp to 0.","type":"integer"},"name":{"description":"Name is the campaign's display label. Required; trimmed and bounded to 1024 bytes.","type":"string"},"objective":{"description":"Objective is the campaign goal as the provider names it. Optional, bounded to 1024 bytes.","type":"string"},"platform":{"description":"Platform is the ad network: meta, google, tiktok or x. Empty defaults to meta.","type":"string"},"spend":{"description":"Spend is the amount spent so far in MINOR units (cents). Negative values clamp to 0.","type":"integer"},"status":{"description":"Status is the lifecycle state: draft, active, paused or completed. Empty defaults to draft.","type":"string"}},"type":"object"},"updateEndpointIn":{"properties":{"description":{"description":"Description is a free-text label for the console. Optional, clipped to 1024 bytes.","type":"string"},"events":{"description":"Events are NATS subject patterns to subscribe to. An empty or omitted list\nmeans EVERY event. Max 64 patterns, each max 256 bytes.","items":{"type":"string"},"type":"array"},"status":{"description":"Status is \"active\" or \"disabled\". Empty defaults to active.","type":"string"},"url":{"description":"URL is the https:// address each matching event is POSTed to. Required,\nmax 2048 bytes; http:// and every other scheme is refused.","type":"string"}},"type":"object"},"usageAnalyticsAccess":{"properties":{"access":{"$ref":"#/components/schemas/usageAnalyticsGrant","description":"Access is what that plan grants."},"plan":{"description":"Plan echoes the plan id that was resolved, exactly as it was asked for.","type":"string"}},"type":"object"},"usageAnalyticsGrant":{"properties":{"datastore":{"description":"Datastore is whether the plan may read GET /v1/usage/analytics at all. The\nfree floor is false, and that is what a catalog outage resolves to.","type":"boolean"},"export":{"description":"Export is whether the plan may export the analytics it can read.","type":"boolean"},"retentionDays":{"description":"RetentionDays is how far back the plan may read. GET /v1/usage/analytics\nclamps a custom window's start to this, so an older `start` returns the\nclamped window rather than an error.","type":"integer"}},"type":"object"},"usageAnalyticsView":{"properties":{"end":{"description":"End is the window's exclusive end, RFC3339 UTC.","type":"string"},"export":{"description":"Export is whether the resolved plan allows exporting these rows.","type":"boolean"},"plan":{"description":"Plan echoes the plan id the entitlement was resolved from.","type":"string"},"providers":{"$ref":"#/components/schemas/ProviderBreakdown","description":"Providers is the per-provider roll-up over the window."},"range":{"description":"Range is the window label that was served, which is what was asked for.","type":"string"},"retentionDays":{"description":"RetentionDays is how far back the resolved plan allows reading.","type":"integer"},"scope":{"$ref":"#/components/schemas/usageScope","description":"Scope is the tenant the rows were read under — the validated principal's org."},"start":{"description":"Start is the window's inclusive start, RFC3339 UTC, AFTER the retention\nclamp — so it may be later than the start that was asked for.","type":"string"}},"type":"object"},"usageByProduct":{"properties":{"product":{"type":"string"},"spendCents":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"usageData":{"properties":{"byProduct":{"items":{"$ref":"#/components/schemas/usageByProduct"},"type":"array"},"series":{"items":{"$ref":"#/components/schemas/usagePoint"},"type":"array"},"totals":{"$ref":"#/components/schemas/usageTotals"}},"type":"object"},"usageLine":{"properties":{"cents":{"type":"integer"},"label":{"type":"string"},"tokens":{"type":"integer"},"units":{"type":"integer"}},"type":"object"},"usageOut":{"properties":{"data":{"$ref":"#/components/schemas/usageData"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"usagePoint":{"properties":{"date":{"type":"string"},"requests":{"type":"integer"},"spendCents":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"usageRepo":{"properties":{"name":{"description":"Name is the repo's org-unique handle.","type":"string"},"project":{"description":"Project is the sub-scope the repo lives in; absent for the default scope.","type":"string"},"sizeBytes":{"description":"SizeBytes is the repo's on-disk size at its last measurement.","type":"integer"}},"type":"object"},"usageScope":{"properties":{"org":{"description":"Org is the IAM org slug the rows were read under — the validated\nprincipal's, never a client header.","type":"string"},"user":{"description":"User is the caller's own subject, whose linked-account rows the accounts\nblock carries. Absent on a read that is org-scoped only.","type":"string"}},"type":"object"},"usageSummary":{"properties":{"accounts":{"$ref":"#/components/schemas/Accounts","description":"Accounts is the caller's own linked provider accounts beside the org's\nHanzo-routed usage, labelled row by row and never summed together."},"end":{"description":"End is the window's exclusive end, RFC3339 UTC.","type":"string"},"interval":{"description":"Interval is the bucket width the spend series is gap-filled at.","type":"string"},"llm":{"$ref":"#/components/schemas/LLM","description":"LLM is the org's Hanzo-routed inference totals from the warehouse."},"range":{"description":"Range is the window label that was served.","type":"string"},"scope":{"$ref":"#/components/schemas/usageScope","description":"Scope is the tenant and subject the roll-up was answered for."},"sources":{"$ref":"#/components/schemas/Sources","description":"Sources says which upstreams actually answered, so a zero can be read as\n\"no data yet\" rather than as a measurement."},"spend":{"$ref":"#/components/schemas/Spend","description":"Spend is the categorized cost roll-up from the billing ledger."},"start":{"description":"Start is the window's inclusive start, RFC3339 UTC.","type":"string"}},"type":"object"},"usageTotals":{"properties":{"requests":{"type":"integer"},"spendCents":{"type":"integer"},"tokens":{"type":"integer"}},"type":"object"},"usageView":{"properties":{"org":{"description":"Org the rollup is for.","type":"string"},"repos":{"description":"Repos is every repo the org owns, across every project sub-scope.","items":{"$ref":"#/components/schemas/usageRepo"},"type":"array"},"totalBytes":{"description":"TotalBytes is the sum over Repos — the org's whole git footprint.","type":"integer"}},"type":"object"},"usageWindowView":{"properties":{"account":{"description":"Account is the linked provider account the window belongs to.","type":"string"},"cachedInputTokens":{"description":"CachedInputTokens is the prompt tokens served from the provider's cache;\nomitted when unknown.","type":"integer"},"confidence":{"description":"Confidence says how much the counters beside it mean — a meter that\nreported only a percentage leaves them at zero, and this is how a reader\ntells that from a true zero.","type":"string"},"costCents":{"description":"CostCents is what the window cost on the PROVIDER's own plan, in US cents.\nIt is not a Hanzo charge.","type":"integer"},"costLimitCents":{"description":"CostLimitCents is the plan's spend ceiling for the window, in US cents.","type":"integer"},"currency":{"description":"Currency is the provider's currency when it is not US cents.","type":"string"},"inputTokens":{"description":"InputTokens is prompt tokens consumed in the window; omitted when unknown.","type":"integer"},"lane":{"description":"Lane is the meter lane this instance belongs to, e.g. a provider's own\nrolling-window meter.","type":"string"},"machine":{"description":"Machine is the host whose meter reported the window.","type":"string"},"outputTokens":{"description":"OutputTokens is completion tokens produced in the window; omitted when\nunknown.","type":"integer"},"plan":{"description":"Plan is the subscription plan the account is on, as the provider names it.","type":"string"},"requests":{"description":"Requests is how many requests were made in the window; omitted when the\nmeter did not report it.","type":"integer"},"resetsAt":{"description":"ResetsAt is when this window rolls over, RFC3339 UTC; omitted when unknown.","type":"string"},"synthetic":{"description":"Synthetic marks an instance the meter inferred rather than read.","type":"boolean"},"totalTokens":{"description":"TotalTokens is the window's total tokens; omitted when unknown.","type":"integer"},"usedPct":{"description":"UsedPct is how much of the window's allowance is consumed, 0–100.","type":"number"},"window":{"description":"Window is the window class: 6h, day, week or month.","type":"string"},"windowMinutes":{"description":"WindowMinutes is the window's real length in minutes when the meter\nreported one; omitted when it did not.","type":"integer"},"windowStart":{"description":"WindowStart is when this window opened, RFC3339 UTC; omitted when unknown.","type":"string"}},"type":"object"},"userEnablementItem":{"properties":{"canOptIn":{"description":"beta \u0026\u0026 not yet opted in","type":"boolean"},"effective":{"description":"visible to the caller's org","type":"boolean"},"id":{"type":"string"},"kind":{"type":"string"},"optedIn":{"description":"caller's org on the beta list","type":"boolean"},"state":{"description":"off|beta|ga","type":"string"}},"type":"object"},"userOptinReq":{"properties":{"handle":{"description":"Handle is the display name shown on a listed row: 1-40 characters of letters,\ndigits, space, dot, underscore, apostrophe or hyphen. Left empty on a listing\nopt-in it defaults to the caller's username.","type":"string"},"listed":{"description":"Listed publishes the caller's row to other viewers of the board when true, and\nanonymizes it when false.","type":"boolean"}},"type":"object"},"userOptinView":{"properties":{"canSet":{"description":"CanSet is false when the caller's ledger identity cannot be resolved (no user\nname on the principal). Writing the preference would fail, so hide the control.","type":"boolean"},"handle":{"description":"Handle is the display name on the caller's listed row. Empty when they never\nchose one; opting in without a handle sets it to their username, so a listed row\nis never blank.","type":"string"},"listed":{"description":"Listed is true when the caller's board row is published under Handle to other\nviewers. False — the default for anyone who never opted in — anonymizes the row;\nthe metric still counts, only the name is withheld.","type":"boolean"}},"type":"object"},"usersOut":{"properties":{"data":{"items":{"$ref":"#/components/schemas/operatorUser"},"type":"array"},"msg":{"type":"string"},"status":{"type":"string"},"total":{"type":"integer"}},"type":"object"},"validatorClaim":{"properties":{"nonce":{"description":"Nonce is the value GET /v1/validators/challenge issued for this slot.","type":"string"},"signature":{"description":"Signature is the wallet's personal_sign over the challenge message, hex with\na 0x prefix.","type":"string"},"tokenId":{"description":"TokenID is the Validator-tier GenesisNFT token id being claimed. It IS the\nvalidator slot.","type":"integer"}},"type":"object"},"validatorList":{"properties":{"data":{"description":"Data is one entry per slot this org has claimed.","items":{"$ref":"#/components/schemas/slotView"},"type":"array"},"network":{"description":"Network is the luxd network slug new nodes join on this deployment.","type":"string"}},"type":"object"},"vectorCollection":{"properties":{"createdAt":{"description":"CreatedAt is the collection's creation time (RFC 3339); Qdrant does not\nreport one, so it is empty today.","type":"string"},"dimension":{"description":"Dimension is the size of one vector in the collection.","type":"integer"},"distanceMetric":{"description":"DistanceMetric is the collection's distance function; \"cosine\" when the\ncollection's detail could not be read.","type":"string"},"name":{"description":"Name is the collection name.","type":"string"},"storageBytes":{"description":"StorageBytes is the collection's on-disk size, omitted when unknown.","type":"integer"},"vectorCount":{"description":"VectorCount is the collection's point count.","type":"integer"}},"type":"object"},"vectorCollectionList":{"properties":{"collections":{"description":"Collections is one row per Qdrant collection, sorted by name. Empty — never\nabsent — when the vector service cannot be reached.","items":{"$ref":"#/components/schemas/vectorCollection"},"type":"array"}},"type":"object"},"vectorStats":{"properties":{"totalCollections":{"description":"TotalCollections is how many collections the store holds.","type":"integer"},"totalStorageBytes":{"description":"TotalStorageBytes is the sum of every collection's on-disk size.","type":"integer"},"totalVectors":{"description":"TotalVectors is the sum of every collection's point count.","type":"integer"}},"type":"object"},"vendorsOut":{"properties":{"vendors":{"description":"Vendors is every vendor the org has recorded, canonical name ascending.","items":{"$ref":"#/components/schemas/VendorRow"},"type":"array"}},"type":"object"},"venueLinkRequest":{"properties":{"clientId":{"description":"ClientID is the Azure AD application id. Azure only.","type":"string"},"clientSecret":{"description":"ClientSecret selects the service-principal flow. LEAVING IT OUT selects\nkeyless workload identity federation instead, so omitting it is a choice\nrather than an omission. Azure only.","type":"string"},"credentialJson":{"description":"CredentialJSON is a Google credentials document — an external_account\n(workload identity federation, keyless) or a service-account key. GCP only.","type":"string"},"externalId":{"description":"ExternalID pins that role assumption to Hanzo, which is what closes the\nconfused-deputy hole. AWS only.","type":"string"},"label":{"description":"Label is the org-chosen name for this account within the provider, which is\nhow a second account at the same provider is addressed later. Empty means\n\"default\"; anything outside 1–64 of [A-Za-z0-9._-] is refused.","type":"string"},"projectIds":{"description":"ProjectIDs bounds the GKE cluster sweep. GCP only.","items":{"type":"string"},"type":"array"},"provider":{"description":"Provider is the cloud being linked, from the path: digitalocean, aws, gcp\nor azure.","type":"string"},"regions":{"description":"Regions bounds the AWS EKS cluster sweep. AWS only.","items":{"type":"string"},"type":"array"},"roleArn":{"description":"RoleARN is the AWS role Hanzo assumes into the account — the keyless path,\nso no access key is ever stored.","type":"string"},"subscriptionIds":{"description":"SubscriptionIDs bounds the AKS cluster sweep. Azure only.","items":{"type":"string"},"type":"array"},"tenantId":{"description":"TenantID is the Azure AD tenant of the app. Azure only.","type":"string"},"token":{"description":"Token is the DigitalOcean personal access token. DigitalOcean only, and it\nis the one provider that requires storing a secret.","type":"string"}},"type":"object"},"verificationDecision":{"properties":{"id":{"description":"ID is the verification to decide, from the path.","type":"string"},"status":{"description":"Status is the reviewer's decision: \"reviewer_confirmed\" (a pass) or\n\"manual_review\" (withheld for review) — never a provider status.","type":"string"}},"type":"object"},"verificationReq":{"properties":{"email":{"description":"Email is an inline subject's contact email, sealed at rest.","type":"string"},"kind":{"description":"Kind is an inline subject's party type: \"individual\" (KYC) or \"business\" (KYB).","type":"string"},"name":{"description":"Name is an inline subject's name, sealed at rest.","type":"string"},"ref":{"description":"Ref is the org's own opaque external id for an inline subject.","type":"string"},"subjectId":{"description":"SubjectID names an existing subject to verify; empty creates one inline.","type":"string"}},"type":"object"},"verificationTally":{"properties":{"byStatus":{"additionalProperties":{"type":"integer"},"description":"ByStatus tallies the org's verifications by provider-reported status.","type":"object"},"total":{"description":"Total is the sum over every status.","type":"integer"}},"type":"object"},"verifyOut":{"properties":{"account":{"description":"Account is the account label the provider reported. Present only when active.","type":"string"},"active":{"description":"Active is whether the stored credential verified live against the provider.","type":"boolean"},"externalId":{"description":"ExternalID is the provider's account id. Present only when active.","type":"string"},"provider":{"description":"Provider is the connector's registry id.","type":"string"},"reason":{"description":"Reason is why the check failed. Present only when active is false.","type":"string"},"scopes":{"description":"Scopes are the permissions the credential carries. Present only when active.","items":{"type":"string"},"type":"array"}},"type":"object"},"verifyRequest":{"properties":{"repoUrl":{"description":"RepoURL is what to claim: a repository (github.com/owner/name) or a whole\nOWNER (github.com/owner, no repository segment). gitlab.com is accepted too.","type":"string"}},"type":"object"},"versionMessage":{"properties":{"BuildDate":{"description":"BuildDate is the time THIS RESPONSE was generated, in RFC 3339 — not a build\ntimestamp. There is no argocd build here to report one for.","type":"string"},"Compiler":{"description":"Compiler is the constant \"gc\" the SPA expects; it is not read from this\nprocess.","type":"string"},"GoVersion":{"description":"GoVersion is always empty.","type":"string"},"Platform":{"description":"Platform is the constant \"linux/amd64\" the SPA expects; it is not this\nprocess's own GOOS/GOARCH.","type":"string"},"Version":{"description":"Version names the projection, \"hanzo-cd (projection)\".","type":"string"}},"type":"object"},"versionPage":{"properties":{"data":{"description":"Data is the page of versions, newest first.","items":{"$ref":"#/components/schemas/FlowVersion"},"type":"array"}},"type":"object"},"versionView":{"properties":{"createdAt":{"description":"CreatedAt is when this revision was appended, RFC 3339 UTC.","type":"string"},"type":{"description":"Type is the kind this revision was written with, which may differ from the\ncurrent one.","type":"string"},"version":{"description":"Version is this revision's number, 1 for the first. Numbers are dense and\nnever reused: deleting the prompt drops the whole history with it.","type":"integer"}},"type":"object"},"volumesOut":{"properties":{"data":{"$ref":"#/components/schemas/storageSnapshot"},"msg":{"type":"string"},"status":{"type":"string"}},"type":"object"},"vpcList":{"properties":{"vpcs":{"description":"VPCs are the caller org's VPCs under their friendly names.","items":{"$ref":"#/components/schemas/vpcView"},"type":"array"}},"type":"object"},"vpcView":{"properties":{"cidr":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"region":{"type":"string"},"status":{"type":"string"},"subnets":{"items":{"type":"string"},"type":"array"}},"type":"object"},"waiting":{"properties":{"email":{"description":"Email is the founder's email — the key a decision is posted against.","type":"string"},"founder":{"description":"Founder is the founder's name.","type":"string"},"kycRef":{"description":"KYCRef is the identity-verification session reference, when one was opened.","type":"string"},"kycStatus":{"description":"KYCStatus is the founder's unsettled status.","type":"string"},"name":{"description":"Name is the proposed company name.","type":"string"},"org":{"description":"Org is the tenant whose formation the founder belongs to.","type":"string"},"since":{"description":"Since is when the formation was last touched, as a unix second.","type":"integer"}},"type":"object"},"waitlistBoostRequest":{"properties":{"email":{"description":"Email identifies the entry to boost. Either this or RefCode is required.","type":"string"},"points":{"description":"Points is how many points to award. Must be positive — this seam exists to move\nsomeone UP toward the cutoff.","type":"integer"},"reason":{"description":"Reason is the operator's justification. Not sent to the engine; it is recorded on\nthe audit row, which is the point of asking for it.","type":"string"},"refCode":{"description":"RefCode identifies the entry by its referral code, when the email is unknown.","type":"string"},"waitlist":{"description":"Waitlist is the waitlist slug the grant lands on. Required.","type":"string"}},"type":"object"},"waitlistModeView":{"properties":{"host":{"description":"Host is the queried host, normalized (lowercased, port stripped).","type":"string"},"known":{"description":"Known is false when no registered service claims this host, or when the\nregistry is unavailable — the guard then lets the request through, which is\nwhy the two cases answer alike.","type":"boolean"},"service":{"description":"Service is the registered service that governs this host, empty when none does.","type":"string"},"waitlistMode":{"description":"WaitlistMode is true when the service is GATED to approved users, false when\nit is open. Always false for an ungoverned host.","type":"boolean"}},"type":"object"},"walletList":{"properties":{"wallets":{"description":"Wallets are the matching wallets, newest first.","items":{"$ref":"#/components/schemas/Wallet"},"type":"array"}},"type":"object"},"workerList":{"properties":{"workers":{"description":"Workers is one row per connected BYO machine, each carrying the host's own\nreport (GPUs, driver versions, capabilities) rather than a normalized view.","items":{"$ref":"#/components/schemas/byoWorker"},"type":"array"}},"type":"object"},"worldIndex":{"properties":{"product":{"description":"Product is the product's name as customers know it.","type":"string"},"summary":{"description":"Summary is one sentence naming what this surface serves.","type":"string"},"wires":{"description":"Wires is every protocol door onto World, REST first. It is deliberately NOT\na list of REST operations: GET /v1/openapi.json is the one enumeration of\nthose, and a second copy here would be a second thing to keep true.","items":{"$ref":"#/components/schemas/worldWire"},"type":"array"}},"type":"object"},"worldWire":{"properties":{"auth":{"description":"Auth states what the wire asks of the caller, including which parts of it\nanswer without a token.","type":"string"},"name":{"description":"Name is the wire's short id — rest, mcp or zap.","type":"string"},"path":{"description":"Path is the address the wire answers on, under this same origin.","type":"string"},"protocol":{"description":"Protocol names what the wire speaks, so a caller knows which client to\npoint at it.","type":"string"},"spec":{"description":"Spec is where this wire's operations are enumerated, when they are\nenumerated in a document at all. Empty for a wire that describes itself\nover its own protocol.","type":"string"}},"type":"object"},"zapProcReq":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"project":{"type":"string"}},"type":"object"}}}}