mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
1 line
3.4 MiB
Plaintext
1 line
3.4 MiB
Plaintext
{"openapi":"3.1.0","info":{"title":"Hanzo Cloud API","description":"The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.","version":"v1"},"servers":[{"url":"https://api.hanzo.ai"}],"security":[{"bearer":[]}],"tags":[{"name":"account","description":"Package account is your own account: API keys you mint and revoke, and org onboarding."},{"name":"ad","description":"Package ads is your paid ad campaigns, launched and paused from one place."},{"name":"affiliate","description":"Package affiliates is a partner program that pays commission on what your referrals spend."},{"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":"allowance","description":"Package allowance is how much a plan lets you do without paying, and how much of it you have left today."},{"name":"ask","description":"Package ask is a plain-language question about your business, answered with real numbers."},{"name":"audit","description":"Package auditlog is your org's tamper-evident audit trail: every security-relevant event, hash-chained and readable."},{"name":"author","description":"Package authors is a royalty for open-source work: your repo runs, you get paid."},{"name":"authz","description":"Package serve is the access decision behind an org-scoped call: who may do what, where, answered allow or deny."},{"name":"auto","description":"Package auto is workflows that run themselves, on a schedule or a webhook."},{"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 bots is a bot doing your work on a real desktop, live, while you watch."},{"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":"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":"cloudflare","description":"Package cloudflare is your Cloudflare account, managed from Hanzo: zones, Pages, Workers, Workers AI, R2, KV and D1."},{"name":"code","description":"Package code is search and symbols across your repos, for you and your agents."},{"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":"compliance","description":"Package compliance is your KYC/KYB onboarding, accreditation records, and the evidence trail behind 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":"dataroom","description":"Package dataroom is a secure document room you share by link and watch page by page."},{"name":"dataset","description":"Package dataset is the per-org dataset plane of /v1/risk: a dataset is a VERSIONED, IMMUTABLE snapshot of one tenant's own event surface, and this is where it is declared, materialised, described, exported and disposed of."},{"name":"deploy","description":"Package deploy is Hanzo CD: see what each app is running, sync it, and roll back a bad release."},{"name":"destination","description":"Package destinations is your events forwarded to the ad and analytics tools you use."},{"name":"domain","description":"Package domain is Hanzo Domains: search a name, see the price, buy it from your prepaid wallet."},{"name":"engine","description":"Package engine is Hanzo Engine: which models the serving runtime has loaded, and the GPUs under it."},{"name":"entitlement","description":"Package entitlements is what your org may run: what the plan grants, and which of those products are switched on."},{"name":"esign","description":"Package esign is a document out for signature, signed and filed with an audit trail."},{"name":"eval","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":"experiment","description":"Package experiments is A/B testing anything: a flag, an ad, a subject line, a model."},{"name":"explorer","description":"Package explorer is chain data: your block indexers and how far each has caught up, plus the on-chain price feeds."},{"name":"flags","description":"Package flags is feature flags: ship it dark, then turn it on for who you pick."},{"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":"git","description":"Package git is Git hosting for your org: create repos, clone, push, and see what they cost."},{"name":"graph","description":"Package graph is one organization's entities and the relations between them, held as assertions: somebody, at some moment, from some evidence, asserted that this thing stands in that relation to that other thing."},{"name":"guide","description":"Package guide is the AI that builds an agentic company with you, one measured step at a time."},{"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":"index","description":"Package index is fast full-text search over your own data, typos forgiven."},{"name":"ingress","description":"Package ingress is your edge: automatic TLS certificates and hostname routing to any backend, changed live."},{"name":"integrations","description":"Package integrations is how your org connects third-party accounts like Slack, and revokes them."},{"name":"kms","description":"Package kms is secret custody: your org's secrets sealed at rest, plus threshold signing."},{"name":"knowledge","description":"Package knowledge is your team's wiki and your agents' memory, searchable by meaning."},{"name":"kv","description":"Package kv is your key-value store: buckets of versioned values your apps read and write by key."},{"name":"label","description":"Package label is the ground-truth plane: what actually turned out to be fraud, who said so, and when they could first have said it."},{"name":"leaderboard","description":"Package leaderboard is the ranking of who uses AI most, in your org and globally."},{"name":"legal","description":"Package legal is the paperwork your company needs, drafted, signed and filed."},{"name":"licensing","description":"Package licensing is what an org has bought and may run: signed licenses to issue, verify and revoke, and the releases they unlock."},{"name":"link","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":"lsp","description":"Package lsp is live semantic code intelligence — definitions, references, types, hover, outline and diagnostics — over a repository AND its resolved dependencies, with no toolchain on the caller's machine."},{"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":"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":"metrics"},{"name":"ml","description":"Package ml is model serving: deploy a model behind an endpoint and call it."},{"name":"mq","description":"Package mq is queue and stream admin for your org: create them, watch them drain, ack what you pulled."},{"name":"network","description":"Package network mounts the Hanzo Cloud NETWORKING surface: the tenant's Hanzo Zero Trust footprint — the overlay network, its routers and its 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":"node","description":"Package nodes is your machines: the ones you own, connected and ready to take a command."},{"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":"openapi","description":"The served contract: the OpenAPI document every client, tool list and command group is generated from, its command projection, the agent endpoint that offers the same operations over MCP, and the index a client follows from the API root to reach any of it."},{"name":"plan","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":"pref","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":"projects","description":"Package projects is where your sites live: create one, deploy a build, roll back to any release."},{"name":"prompt","description":"Package prompts is your prompt library, versioned, so nothing changes silently."},{"name":"provisioning","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":"pubsub","description":"Package pubsub is your message bus: publish, subscribe, and durable streams your apps read at their own pace."},{"name":"reference","description":"Package reference is the lookup data a risk decision needs but cannot derive: which email domains hand out throwaway inboxes, which addresses belong to a datacentre or a Tor exit, which card scheme an issuer prefix belongs to, which browsers the fleet sees everywhere, and how current the designation lists the screening engine holds actually are."},{"name":"referral","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":"risk","description":"Package risk is HANZO RISK's model plane: the per-organisation feature surface and the per-organisation models trained on it."},{"name":"s3","description":"Package s3 is object storage: your buckets and the files in them, with signed URLs for upload and download."},{"name":"sandbox","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":"search","description":"Package search is one ranked result set over everything your org has stored."},{"name":"security","description":"Package security is secret scanning for your code: submit sources, get findings, masked never raw."},{"name":"seo","description":"Package seo is search visibility as data: what a phrase is worth, what a site already places for, who places beside it, who links to it, and what one page gets wrong."},{"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":"social","description":"Package social is posting to every social account you own, now or on a schedule."},{"name":"standing","description":"Package standing prices and tracks what it costs to KEEP a company, as distinct from what it cost to form one."},{"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":"taxonomy","description":"Package taxonomy is the product catalogue's shape: which categories exist, what each product is called, which category it sits in, what it is tagged with, and the order the two are shown in."},{"name":"team","description":"Package team is your org's shared workspace: documents edited together, files, seats, and agents as teammates."},{"name":"tel","description":"Package tel is the telecommunications surface: phone numbers, calls and messages, on whatever carrier the deployment is configured for."},{"name":"template","description":"Package templates is a gallery of starter kits you can deploy as they come."},{"name":"todo","description":"Package todo is Hanzo Todo: boards, the work items on them, and the filters that make a board."},{"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":"translate","description":"Package translate is text in, the same text out in the language you asked for."},{"name":"treasury","description":"Package treasury is the reserve fund behind every payout: real capital, held and accounted for."},{"name":"trust","description":"Package trust is your trust centre: the controls you publish, the coverage they compute to, the documents a reviewer asks for, and who you send data to."},{"name":"usage","description":"Package usage is what your org ran and what it cost, broken down per account."},{"name":"validator","description":"Package validators is one-click validator onboarding: prove your Genesis NFT, get a node provisioned, queue its registration."},{"name":"visor","description":"Package visor is the compute you rent from Hanzo: machines, GPUs and clusters — launch one, resize it, tear it down."},{"name":"wallet","description":"Package wallets is blockchain key custody: create wallets, rotate their keys, and sign with them."},{"name":"web3","description":"Package web3 is the chain-access surface: which chains this deployment can reach, a JSON-RPC endpoint onto each, and the two token reads every wallet UI needs."},{"name":"webhook","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":"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":{"/v1":{"get":{"operationId":"get_capabilities","summary":"Every capability this deployment answers, and where to follow each one","description":"The API root. One row per capability — its name, the address it answers under, whether it is generally available, and the sentence it says about itself — plus the links to the document at /v1/openapi.json and the agent MCP server.\n\nIt is a projection of that same document and carries the same surface a customer calls: the operator's admin product, the relays, the legacy spellings and any capability that is not yet generally available are in neither.\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 capability names grants nothing.","tags":["openapi"],"responses":{"2XX":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Root"}}}}},"security":[],"x-app":"openapi","x-public":true}},"/v1/account/appearance":{"get":{"operationId":"get_account_appearance","summary":"Returns the signed-in caller's own appearance preference — text size, density and accent — read from their IAM account so it is the same on every device and every Hanzo surface.","description":"Returns the signed-in caller's own appearance preference — text\nsize, density and accent — read from their IAM account so it is the same on\nevery device and every Hanzo surface. An unset preference is an empty object.\n\nA transient IAM read failure reports the empty preference rather than a 5xx, so\na surface applies its published default and never error-toasts on load — the\nsame fail-soft the key read uses.","tags":["account"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appearance"}}},"description":"ok"}},"x-app":"account","x-public":true,"x-tool":true},"post":{"operationId":"post_account_appearance","summary":"Stores the caller's appearance preference on their IAM account, preserving every other field of the row.","description":"Stores the caller's appearance preference on their IAM account,\npreserving every other field of the row. The accent is validated as a real\ncolour token before it is stored; an unset or invalid axis is dropped rather\nthan stored.","tags":["account"],"requestBody":{"content":{"application/json":{"example":{"accent":"#8b5cf6","density":"compact","type":1.15},"schema":{"$ref":"#/components/schemas/appearance"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/appearance"}}},"description":"ok"}},"x-app":"account","x-public":true,"x-tool":true}},"/v1/account/avatar":{"post":{"operationId":"post_account_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":["account"],"x-app":"account","x-public":true}},"/v1/account/avatar/{org}/{user}/{digest}":{"get":{"operationId":"get_account_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 `<img>`, 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":["account"],"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","x-public":true}},"/v1/account/csrf":{"get":{"operationId":"get_account_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":["account"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/csrfResp"}}},"description":"ok"}},"x-app":"account","x-public":true,"x-tool":true}},"/v1/account/embed":{"get":{"operationId":"get_account_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 <app>.<this deployment's own brand domain>: no part of\nit comes from the request, so this can never be steered into probing an\narbitrary origin.","tags":["account"],"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","x-public":true,"x-tool":true}},"/v1/account/keys":{"delete":{"operationId":"delete_account_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":["account"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_account_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":["account"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/apiKeyList"}}},"description":"ok"}},"x-app":"account","x-public":true,"x-tool":true},"post":{"operationId":"post_account_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":["account"],"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","x-public":true,"x-tool":true}},"/v1/account/orgs":{"post":{"operationId":"post_account_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":["account"],"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","x-public":true,"x-tool":true}},"/v1/ad/campaigns":{"get":{"operationId":"get_ad_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":["ad"],"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":"ad","x-public":true,"x-tool":true},"post":{"operationId":"post_ad_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/ad/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":["ad"],"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":"ad","x-public":true,"x-tool":true}},"/v1/ad/campaigns/{id}":{"delete":{"operationId":"delete_ad_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":["ad"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"camp_2f9c1d"}],"responses":{"204":{"description":"no content"}},"x-app":"ad","x-public":true,"x-tool":true},"get":{"operationId":"get_ad_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":["ad"],"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":"ad","x-public":true,"x-tool":true},"put":{"operationId":"put_ad_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":["ad"],"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":"ad","x-public":true,"x-tool":true}},"/v1/ad/campaigns/{id}/launch":{"post":{"operationId":"post_ad_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":["ad"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ad","x-public":true}},"/v1/ad/summary":{"get":{"operationId":"get_ad_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":["ad"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/adSummary"}}},"description":"ok"}},"x-app":"ad","x-public":true,"x-tool":true}},"/v1/affiliate":{"get":{"operationId":"get_affiliate","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":["affiliate"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateStanding"}}},"description":"ok"}},"x-app":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/apply":{"post":{"operationId":"post_affiliate_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":["affiliate"],"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":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/attribute":{"post":{"operationId":"post_affiliate_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":["affiliate"],"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":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/click":{"post":{"operationId":"post_affiliate_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":["affiliate"],"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":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/leaderboard":{"get":{"operationId":"get_affiliate_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":["affiliate"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateBoard"}}},"description":"ok"}},"x-app":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/me":{"get":{"operationId":"get_affiliate_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":["affiliate"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateSelf"}}},"description":"ok"}},"x-app":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/me/earnings":{"get":{"operationId":"get_affiliate_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":["affiliate"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateEarnings"}}},"description":"ok"}},"x-app":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/me/handle":{"post":{"operationId":"post_affiliate_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":["affiliate"],"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":"affiliate","x-public":true,"x-tool":true}},"/v1/affiliate/me/links":{"get":{"operationId":"get_affiliate_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":["affiliate"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/affiliateLinks"}}},"description":"ok"}},"x-app":"affiliate","x-public":true,"x-tool":true},"post":{"operationId":"post_affiliate_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":["affiliate"],"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":"affiliate","x-public":true,"x-tool":true}},"/v1/agents":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/activity":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/builds":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/builds/{org}/{project}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/chat":{"post":{"operationId":"post_agents_chat","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":["agents"],"x-app":"agents","x-public":true}},"/v1/agents/chat/conversations":{"get":{"operationId":"get_agents_chat_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":["agents"],"x-app":"agents","x-public":true}},"/v1/agents/chat/conversations/{id}":{"get":{"operationId":"get_agents_chat_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":["agents"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"agents","x-public":true}},"/v1/agents/chat/presets":{"get":{"operationId":"get_agents_chat_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 the round 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":["agents"],"x-app":"agents","x-public":true}},"/v1/agents/coding":{"post":{"operationId":"post_agents_coding","summary":"Start one autonomous coding run against a repo in the caller's org","description":"Runs a coding task on a repository: clones it into a sandbox, lets a model read\nand edit the code, run the tests, and push the work to a branch. Say the thing\nyou want done — \"fix the failing auth test in hanzoai/cloud\" — and the run\ninfers the repo, the branch and the plan. No prefix, no ceremony.\n\nIt answers 202 with the run's handle the moment the run is ADMITTED — not when\nit finishes. A coding run takes minutes; holding a request open for one would\ntie a connection to a model loop and give the caller nothing it cannot get\nbetter from the session stream.\n\nThe handle is a session id, and that is deliberate: the session is already the\nrun's durable record and its live stream (/v1/agents/sessions/{id}/stream), so\nthis op does not grow a progress endpoint, a status endpoint or a cancel\nendpoint of its own. One way to watch a run, whoever started it.\n\nIt is also how work CONTINUES. Pass an earlier run's session as `after` and\nthis one starts from where that one stopped, so \"now add tests for it\" builds\non the branch already pushed instead of a fresh clone. The follow-up still gets\nits own branch and its own session — one run, one branch, always reviewable on\nits own.","tags":["agents"],"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","x-public":true,"x-tool":true}},"/v1/agents/metrics":{"get":{"operationId":"get_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 => 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","x-public":true,"x-tool":true}},"/v1/agents/runs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/sessions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/sessions/stream":{"get":{"operationId":"get_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=<session id> 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","x-public":true}},"/v1/agents/sessions/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/control":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/events":{"post":{"operationId":"post_agents_sessions_by_id_events","summary":"Records one turn of a session's transcript and answers 201 with it.","description":"Records one turn of a session's transcript and answers 201 with it.\n\nTHE TURN IS SCANNED BEFORE IT IS STORED. The same engine the code-security\nsurface runs reads the payload at this boundary, and a credential in it refuses\nthe append with 422 rather than redacting it — a redacted transcript is one\nthat still had the secret in it once, and this way the author learns which\nvalue to rotate. The refusal carries every finding: the rule, the severity, the\nline, a MASKED preview and the fingerprint. The secret is never in the answer.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to append to, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/eventIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/eventView"}}},"description":"created"}},"x-app":"agents","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/message":{"post":{"operationId":"post_agents_sessions_by_id_message","summary":"Sends a steering message to a running session — the endpoint a human or another agent interrupts through.","description":"Sends a steering message to a running session — the endpoint a\nhuman or another agent interrupts through. It requires a `message` or a\n`payload`; the other three commands do not.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to steer, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlResult"}}},"description":"ok"}},"x-app":"agents","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/pause":{"post":{"operationId":"post_agents_sessions_by_id_pause","summary":"Asks a running session to pause.","description":"Asks a running session to pause. Recorded durably, and forwarded\nto the durable-execution engine when the session is task-backed.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to steer, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlResult"}}},"description":"ok"}},"x-app":"agents","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/resume":{"post":{"operationId":"post_agents_sessions_by_id_resume","summary":"Asks a paused session to continue, on the same terms as a pause.","description":"Asks a paused session to continue, on the same terms as a pause.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to steer, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlResult"}}},"description":"ok"}},"x-app":"agents","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/stop":{"post":{"operationId":"post_agents_sessions_by_id_stop","summary":"Ends a running session.","description":"Ends a running session. `message` is recorded as the cancellation\nreason, which is what a later reader of the transcript sees.\n\nSTOPPING IS NOT DELETING: the session, its transcript and anything it produced\nstay readable. A session that has already finished is 409 rather than a second\nstop.","tags":["agents"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the session to steer, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlResult"}}},"description":"ok"}},"x-app":"agents","x-public":true,"x-tool":true}},"/v1/agents/sessions/{id}/tree":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/agents/targets":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/targets/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/agents/targets/{id}/claim":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/targets/{id}/key":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/targets/{id}/runs/{runId}/report":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/agents/{ref}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/agents/{ref}/run":{"post":{"operationId":"post_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","x-public":true}},"/v1/agents/{ref}/runs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/ai/account":{"get":{"operationId":"get_ai_account","summary":"Account","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/activities":{"get":{"operationId":"get_ai_activities","summary":"List activities","description":"List the caller's activities.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/answer":{"get":{"operationId":"get_ai_answer","summary":"Answer","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/articles":{"get":{"operationId":"get_ai_articles","summary":"List articles","description":"List the caller's articles.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_articles","summary":"Create a article","description":"Create one article.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/articles/global":{"get":{"operationId":"get_ai_articles_global","summary":"List articles across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/articles/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/assets":{"get":{"operationId":"get_ai_assets","summary":"List assets","description":"List the caller's assets.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_assets","summary":"Create a asset","description":"Create one asset.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/assets/scan":{"post":{"operationId":"post_ai_assets_scan","summary":"Scan (asset)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/assets/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/assets/{owner}/{name}/scan":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/chats":{"get":{"operationId":"get_ai_chats","summary":"List chats","description":"List the caller's chats.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_chats","summary":"Create a chat","description":"Create one chat.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/chats/global":{"get":{"operationId":"get_ai_chats_global","summary":"List chats across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/chats/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/connections":{"get":{"operationId":"get_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":"ai","x-public":true},"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/connections/{provider}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/connections/{provider}/authorize":{"get":{"operationId":"get_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":"ai","x-public":true}},"/v1/ai/connections/{provider}/callback":{"get":{"operationId":"get_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=<provider> (or ?ai_connect_error=<provider> 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":"ai","x-public":true}},"/v1/ai/connections/{provider}/usage":{"get":{"operationId":"get_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":"ai","x-public":true}},"/v1/ai/dashboards/agents":{"get":{"operationId":"get_ai_dashboards_agents","summary":"Dashboards Agents","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/dashboards/vm":{"get":{"operationId":"get_ai_dashboards_vm","summary":"Dashboards Vm","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/deployments":{"get":{"operationId":"get_ai_deployments","summary":"List deployments","description":"List the caller's deployments.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_deployments","summary":"Create a application","description":"Create one application.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/deployments/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/deployments/{owner}/{name}/deploy":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/deployments/{owner}/{name}/undeploy":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/feedback":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files":{"get":{"operationId":"get_ai_files","summary":"List files","description":"List the caller's files.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_files","summary":"Create a file","description":"Create one file.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files/activate":{"post":{"operationId":"post_ai_files_activate","summary":"Activate (file)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files/active":{"get":{"operationId":"get_ai_files_active","summary":"Active (file)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files/global":{"get":{"operationId":"get_ai_files_global","summary":"List files across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files/upload":{"post":{"operationId":"post_ai_files_upload","summary":"Upload (file)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/files/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/files/{owner}/{name}/vectors":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/finetune/cancel":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/deploy":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/hf/datasets":{"get":{"operationId":"get_ai_finetune_hf_datasets","summary":"Proxies a HuggingFace dataset search (dataset picker).","description":"Proxies a HuggingFace dataset search (dataset picker).","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/hf/models":{"get":{"operationId":"get_ai_finetune_hf_models","summary":"Proxies a HuggingFace model search (base-model picker).","description":"Proxies a HuggingFace model search (base-model picker).","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/hf/repo":{"get":{"operationId":"get_ai_finetune_hf_repo","summary":"Returns a repo's detail (files, gated/private state).","description":"Returns a repo's detail (files, gated/private state). ?id=&kind=model|dataset","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/job":{"get":{"operationId":"get_ai_finetune_job","summary":"Returns one job with refreshed live status.","description":"Returns one job with refreshed live status. ?id=owner/name or ?name=","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/jobs":{"get":{"operationId":"get_ai_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":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/finetune/presets":{"get":{"operationId":"get_ai_finetune_presets","summary":"Returns the new-job catalog plus, when a selection is passed (?baseModel&method&task&preset[&datasetExamples]), 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&method&task&preset[&datasetExamples]), the recommended config so the\nconsole can render \"Recommended\" as a one-click, ready-to-run default.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/forms":{"get":{"operationId":"get_ai_forms","summary":"List forms","description":"List the caller's forms.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_forms","summary":"Create a form","description":"Create one form.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/forms/data":{"get":{"operationId":"get_ai_forms_data","summary":"Data (form)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/forms/global":{"get":{"operationId":"get_ai_forms_global","summary":"List forms across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/forms/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/graphs":{"get":{"operationId":"get_ai_graphs","summary":"List graphs","description":"List the caller's graphs.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_graphs","summary":"Create a graph","description":"Create one graph.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/graphs/global":{"get":{"operationId":"get_ai_graphs_global","summary":"List graphs across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/graphs/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/k8s-status":{"get":{"operationId":"get_ai_k8s-status","summary":"K8s Status","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/mcp/tools":{"get":{"operationId":"aiMCPTools","summary":"Tools reports what THIS PROCESS's MCP server 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 server carries: how many tools its own\nregistry projects, optionally their names, and which subsystems this process\ncomposed. It is the answer to \"is this MCP server up and does it have anything\nbehind it\" — a question a status code cannot answer, since an empty server and\na full one are both 200. What the FLEET's server carries is the fleet server's\nown answer: POST /v1/mcp, tools/list, which asks every subsystem and names the\nones that did 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 to\nanswer (\"is the MCP server up and does it have anything behind it\") is\nanswered by the count.","schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/aiMCPSurface"}}},"description":"ok"}},"x-app":"ai","x-public":true,"x-tool":true}},"/v1/ai/memory/delete":{"post":{"operationId":"post_ai_memory_delete","summary":"Delete one of the authenticated user's memories","description":"Delete one of the authenticated user's memories","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/facts":{"get":{"operationId":"get_ai_memory_facts","summary":"List the authenticated user's stored facts","description":"List the authenticated user's stored facts","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/list":{"get":{"operationId":"get_ai_memory_list","summary":"List the authenticated user's memories, newest first","description":"List the authenticated user's memories, newest first","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/recall":{"get":{"operationId":"get_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/remember":{"post":{"operationId":"post_ai_memory_remember","summary":"Store a memory for the authenticated user","description":"Store a memory for the authenticated user","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/search":{"get":{"operationId":"get_ai_memory_search","summary":"Search the authenticated user's memories (semantic, text fallback)","description":"Search the authenticated user's memories (semantic, text fallback)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/memory/update":{"post":{"operationId":"post_ai_memory_update","summary":"Update one of the authenticated user's memories","description":"Update one of the authenticated user's memories","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/messages":{"get":{"operationId":"get_ai_messages","summary":"List messages","description":"List the caller's messages.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_messages","summary":"Create a message","description":"Create one message.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/messages/global":{"get":{"operationId":"get_ai_messages_global","summary":"List messages across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/messages/welcome":{"delete":{"operationId":"delete_ai_messages_welcome","summary":"Welcome (message)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/messages/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/messages/{owner}/{name}/answer":{"get":{"operationId":"get_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":"ai","x-public":true}},"/v1/ai/nodes":{"get":{"operationId":"get_ai_nodes","summary":"List nodes","description":"List the caller's nodes.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_nodes","summary":"Create a node","description":"Create one node.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/nodes/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/nodes/{owner}/{name}/tunnel":{"get":{"operationId":"get_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":"ai","x-public":true},"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/org/settings":{"delete":{"operationId":"delete_ai_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_org_settings","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/org/settings/list":{"delete":{"operationId":"delete_ai_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_org_settings_list","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/preferences":{"patch":{"operationId":"patch_ai_preferences","summary":"Preferences","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_preferences","summary":"Preferences","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/prometheus":{"get":{"operationId":"get_ai_prometheus","summary":"Prometheus","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/providers":{"get":{"operationId":"get_ai_providers","summary":"List providers","description":"List the caller's providers.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_providers","summary":"Create a provider","description":"Create one provider.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/providers/global":{"get":{"operationId":"get_ai_providers_global","summary":"List providers across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/providers/mcp-tools":{"post":{"operationId":"post_ai_providers_mcp-tools","summary":"Mcp Tools (provider)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/providers/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/rag/context":{"get":{"operationId":"get_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/rag/delete":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/rag/embed":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/rag/ingest":{"post":{"operationId":"post_ai_rag_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/rag/query":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/rag/query-multiple":{"post":{"operationId":"post_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records":{"get":{"operationId":"get_ai_records","summary":"List records","description":"List the caller's records.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_records","summary":"Create a record","description":"Create one record.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/batch":{"post":{"operationId":"post_ai_records_batch","summary":"Batch (record)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/commit":{"post":{"operationId":"post_ai_records_commit","summary":"Commit (record)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/commit-second":{"post":{"operationId":"post_ai_records_commit-second","summary":"Commit Second (record)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/query":{"get":{"operationId":"get_ai_records_query","summary":"Query (record)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/query-second":{"get":{"operationId":"get_ai_records_query-second","summary":"Query Second (record)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/records/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/remote-connections":{"get":{"operationId":"get_ai_remote-connections","summary":"List remote-connections","description":"List the caller's remote-connections.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_remote-connections","summary":"Create a connection","description":"Create one connection.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/remote-connections/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/remote-connections/{owner}/{name}/start":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/remote-connections/{owner}/{name}/stop":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/router/artifact-meta":{"delete":{"operationId":"delete_ai_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_router_artifact-meta","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/data":{"delete":{"operationId":"delete_ai_router_data","summary":"Router Data","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_data","summary":"Router Data","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/defaults":{"delete":{"operationId":"delete_ai_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_router_defaults","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/history":{"get":{"operationId":"get_ai_router_history","summary":"Returns the router-improvement time-series.","description":"Returns the router-improvement time-series. Two scopes, one route,\nmirroring /v1/ai/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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/judge-panel":{"get":{"operationId":"get_ai_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/ai/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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/ledger":{"delete":{"operationId":"delete_ai_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_router_ledger","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/policy":{"delete":{"operationId":"delete_ai_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_router_policy","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/rewards":{"delete":{"operationId":"delete_ai_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"get":{"operationId":"get_ai_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_router_rewards","summary":"The HTTP transport binding for the RESTful router-config nouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and /v1/ai/org/settings[/list]).","description":"The HTTP transport binding for the RESTful router-config\nnouns (/v1/ai/router/{policy,defaults,ledger,rewards,artifact-meta} and\n/v1/ai/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 controller 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 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/ai/router/policy\nsplits GET (read) vs PUT (write), /v1/ai/org/settings GET/PUT/DELETE, and returns\n405 for a verb it does not own.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/router/stats":{"get":{"operationId":"get_ai_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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/routes":{"get":{"operationId":"get_ai_routes","summary":"List routes","description":"List the caller's routes.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_routes","summary":"Create a model-route","description":"Create one model-route.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/routes/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/scales":{"get":{"operationId":"get_ai_scales","summary":"List scales","description":"List the caller's scales.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_scales","summary":"Create a scale","description":"Create one scale.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/scales/global":{"get":{"operationId":"get_ai_scales_global","summary":"List scales across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/scales/public":{"get":{"operationId":"get_ai_scales_public","summary":"Public (scale)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/scales/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/scans":{"get":{"operationId":"get_ai_scans","summary":"List scans","description":"List the caller's scans.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_scans","summary":"Create a scan","description":"Create one scan.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/scans/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/signin":{"post":{"operationId":"post_ai_signin","summary":"Signin","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/signin-sessions":{"get":{"operationId":"get_ai_signin-sessions","summary":"List signin-sessions","description":"List the caller's signin-sessions.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_signin-sessions","summary":"Create a session","description":"Create one session.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/signin-sessions/duplicated":{"get":{"operationId":"get_ai_signin-sessions_duplicated","summary":"Duplicated (session)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/signin-sessions/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/signout":{"post":{"operationId":"post_ai_signout","summary":"Signout","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/stores":{"get":{"operationId":"get_ai_stores","summary":"List stores","description":"List the caller's stores.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_stores","summary":"Create a store","description":"Create one store.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/stores/global":{"get":{"operationId":"get_ai_stores_global","summary":"List stores across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/stores/names":{"get":{"operationId":"get_ai_stores_names","summary":"Names (store)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/stores/providers":{"get":{"operationId":"get_ai_stores_providers","summary":"Providers (store)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/stores/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/stores/{owner}/{name}/vectors":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/system":{"get":{"operationId":"get_ai_system","summary":"System","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/tasks":{"get":{"operationId":"get_ai_tasks","summary":"List tasks","description":"List the caller's tasks.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_tasks","summary":"Create a task","description":"Create one task.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/tasks/global":{"get":{"operationId":"get_ai_tasks_global","summary":"List tasks across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/tasks/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/tasks/{owner}/{name}/analyze":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/tasks/{owner}/{name}/document":{"post":{"operationId":"post_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":"ai","x-public":true}},"/v1/ai/templates":{"get":{"operationId":"get_ai_templates","summary":"List templates","description":"List the caller's templates.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_templates","summary":"Create a template","description":"Create one template.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/templates/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/traffic/globe":{"get":{"operationId":"get_ai_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/ai/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/ai/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":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/training-contribution":{"get":{"operationId":"get_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"ai","x-public":true},"patch":{"operationId":"patch_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"ai","x-public":true},"put":{"operationId":"put_ai_training-contribution","summary":"Training Contribution","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/tree-files":{"post":{"operationId":"post_ai_tree-files","summary":"Create a tree-file","description":"Create one tree-file.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/tree-files/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/usages":{"get":{"operationId":"get_ai_usages","summary":"List usages","description":"List the caller's usages.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/usages/by-user":{"get":{"operationId":"get_ai_usages_by-user","summary":"By User (usage)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/usages/cloud":{"get":{"operationId":"get_ai_usages_cloud","summary":"Cloud (usage)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/usages/range":{"get":{"operationId":"get_ai_usages_range","summary":"Range (usage)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/usages/user-names":{"get":{"operationId":"get_ai_usages_user-names","summary":"User Names (usage)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/vectors":{"get":{"operationId":"get_ai_vectors","summary":"List vectors","description":"List the caller's vectors.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_vectors","summary":"Create a vector","description":"Create one vector.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/vectors/all":{"delete":{"operationId":"delete_ai_vectors_all","summary":"All (vector)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/vectors/global":{"get":{"operationId":"get_ai_vectors_global","summary":"List vectors across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/vectors/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/version":{"get":{"operationId":"get_ai_version","summary":"Version","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/videos":{"get":{"operationId":"get_ai_videos","summary":"List videos","description":"List the caller's videos.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_videos","summary":"Create a video","description":"Create one video.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/videos/global":{"get":{"operationId":"get_ai_videos_global","summary":"List videos across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/videos/upload":{"post":{"operationId":"post_ai_videos_upload","summary":"Upload (video)","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/videos/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/ai/workflows":{"get":{"operationId":"get_ai_workflows","summary":"List workflows","description":"List the caller's workflows.","tags":["ai"],"x-app":"ai","x-public":true},"post":{"operationId":"post_ai_workflows","summary":"Create a workflow","description":"Create one workflow.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/workflows/global":{"get":{"operationId":"get_ai_workflows_global","summary":"List workflows across tenants","description":"Cross-tenant listing. Admin-only; a tenant caller is refused.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/ai/workflows/{owner}/{name}":{"delete":{"operationId":"delete_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":"ai","x-public":true},"get":{"operationId":"get_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":"ai","x-public":true},"patch":{"operationId":"patch_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":"ai","x-public":true},"put":{"operationId":"put_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":"ai","x-public":true}},"/v1/allowance":{"get":{"operationId":"get_allowance","summary":"Answers what the CALLER has left of their plan's free-call allowance this period, and the instant the count starts again.","description":"Answers what the CALLER has left of their plan's free-call allowance this period,\nand the instant the count starts again.\n\nThis is the number a product shows beside the composer — \"17 of 20 left today\" —\nand the moment to offer a plan is when it reaches zero. It READS: asking does not\nspend, so a page that polls it costs the caller nothing.\n\nThe subject is the caller's own, resolved from the verified credential, and can\nnever be named in the request — so this is a mirror, not a lookup of someone else.\nAn unauthenticated caller is refused: there is no allowance without someone to\nhold it.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["allowance"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Allowance"}}},"description":"ok"}},"x-app":"allowance","x-public":true,"x-tool":true}},"/v1/ask":{"post":{"operationId":"post_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), projects (what is built and what of it is deployed), git (the org's repositories and what changed in them), 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","x-public":true}},"/v1/ask/web":{"post":{"operationId":"research_web","summary":"Research a question on the live web and answer it with sources cited","description":"Researches a question on the live web and answers it with its\nsources cited.\n\nThis is the DEEP one. It plans the question into topics, runs several web\nsearches, FETCHES AND READS the pages it finds, ranks them, and writes a\ngrounded answer with inline markdown citations. Use it for anything that needs\nevidence, comparison or current fact — \"what changed in X\", \"compare A and B\",\n\"is this claim true\". For a plain list of links, use search_web instead; for\none page you already have the URL of, use read_page.\n\n`mode` buys depth: `search` is a single fast pass, `news` biases to recency,\n`research` plans and iterates, `deep` surveys widest. `sources` narrows the\nevidence to `web`, `news`, `academic`, `github`, `reddit` or `x` — each becomes\na site-scoped search, which is how this reaches X/Twitter posts.\n\nEVERY CITATION IS A PAGE THIS CALL FETCHED. That is a property of the text and\nnot an instruction to the model: each source is fenced with a per-request nonce\nso a crawled page cannot print itself a source number, and every markdown link\nin the answer is checked against the gathered set before it is returned. So a\nlink in `answer` always appears in `sources`, and a page that was not read\ncannot be cited.\n\nIt is BOUNDED and it degrades rather than failing: a mode's rounds, wall clock\nand token ceiling all cap it, and a search that finds little or a page that\nwill not load yields a thinner answer, never an error. A validated principal is\nrequired, and the answer is billed once to that principal's org.","tags":["ask"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webQuestion"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Report"}}},"description":"ok"}},"x-app":"ask","x-public":true,"x-tool":true}},"/v1/audio/foley":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/audio/music":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/audio/speech":{"post":{"operationId":"post_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.\nThis is the ONE way to synthesize speech: OpenAI-shaped, with no store or\nmessage coupling, so a caller needs no chat to speak.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/audio/transcriptions":{"post":{"operationId":"post_audio_transcriptions","summary":"The OpenAI-compatible STT endpoint (POST /v1/audio/transcriptions, multipart: file + model [+ language + response_format]).","description":"The OpenAI-compatible STT endpoint\n(POST /v1/audio/transcriptions, multipart: file + model [+ language +\nresponse_format]). It mirrors AudioSpeech exactly: authenticate the caller,\nresolve `model` to its STT provider through the SAME model-route resolution\n(so the in-cluster speech service — or any BYO node registered as an STT\nprovider — works transparently), transcribe, and return the OpenAI body.\nThis is the ONE way to transcribe: OpenAI-shaped, with no store coupling, so a\ncaller needs no chat to be heard.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/audio/voice":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/audit":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/author":{"get":{"operationId":"get_author","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":["author"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"object"},"type":"object"}}},"description":"ok"}},"x-app":"author","x-public":true,"x-tool":true}},"/v1/author/basis":{"get":{"operationId":"get_author_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":["author"],"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":"author","x-public":true,"x-tool":true}},"/v1/author/connect":{"post":{"operationId":"post_author_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":["author"],"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":"author","x-public":true,"x-tool":true}},"/v1/author/deploys/record":{"post":{"operationId":"post_author_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":["author"],"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":"author","x-public":true,"x-tool":true}},"/v1/author/repos/verify":{"post":{"operationId":"post_author_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":["author"],"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":"author","x-public":true,"x-tool":true}},"/v1/authz/check":{"post":{"operationId":"post_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","x-public":true}},"/v1/authz/health":{"get":{"operationId":"get_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","x-public":true}},"/v1/authz/readyz":{"get":{"operationId":"get_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","x-public":true}},"/v1/auto/connectors":{"get":{"operationId":"get_auto_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":["auto"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}},"description":"ok"}},"x-app":"auto","x-public":true,"x-tool":true}},"/v1/auto/connectors/{id}/run":{"post":{"operationId":"post_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows":{"get":{"operationId":"get_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true},"post":{"operationId":"post_auto_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/auto/flows/{id}/enable does that.","tags":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows/{id}":{"delete":{"operationId":"delete_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true},"get":{"operationId":"get_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true},"patch":{"operationId":"patch_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows/{id}/disable":{"post":{"operationId":"post_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows/{id}/enable":{"post":{"operationId":"post_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows/{id}/operations":{"post":{"operationId":"post_auto_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":["auto"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"auto","x-public":true}},"/v1/auto/flows/{id}/run":{"post":{"operationId":"post_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/flows/{id}/versions":{"get":{"operationId":"get_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true},"post":{"operationId":"post_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/hooks/{source}/{event}":{"post":{"operationId":"post_auto_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":["auto"],"parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string"}},{"name":"event","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"auto","x-public":true}},"/v1/auto/runs":{"get":{"operationId":"get_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/runs/{id}":{"get":{"operationId":"get_auto_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":["auto"],"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":"auto","x-public":true,"x-tool":true}},"/v1/auto/runs/{id}/resume":{"post":{"operationId":"post_auto_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":["auto"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"auto","x-public":true}},"/v1/base/bases":{"get":{"operationId":"get_base_bases","summary":"Lists every Base the caller can reach, one per org their token carries.","description":"Lists every Base the caller can reach, one per org their token carries.\n\nThe orgs come from IAM's signed membership set, so the list is exactly the\norgs the caller is a member of and cannot be widened by asking. It is the\naccount-wide view: a Base is per org, so this is one entry per org and there\nis nothing to page.\n\nA caller with no membership set — a machine credential, an API key — reaches\nno Base and receives an empty list rather than a refusal, because holding no\nmembership is an answer and not a failure.","tags":["base"],"responses":{"200":{"content":{"application/json":{"example":[{"bytes":430080,"exists":true,"org":"hanzo"}],"schema":{"items":{"$ref":"#/components/schemas/baseView"},"type":"array"}}},"description":"ok"}},"x-app":"base","x-public":true,"x-tool":true}},"/v1/base/bases/{org}":{"get":{"operationId":"get_base_bases_by_org","summary":"Describes ONE org's Base — whether its store exists, and what it occupies.","description":"Describes ONE org's Base — whether its store exists, and what it occupies.\n\nThe org must be one the caller's token carries; any other is not found, so\nthis cannot be used to learn which orgs exist. That check is the same\nmembership set the listing is built from, which is why the two can never\ndisagree about what a caller may see.","tags":["base"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the org whose Base to describe, from the path. An org the caller's\ntoken does not carry is not found — the same answer a nonexistent one\ngets, so the listing cannot be used to discover which orgs exist.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"bytes":430080,"exists":true,"org":"hanzo"},"schema":{"$ref":"#/components/schemas/baseView"}}},"description":"ok"}},"x-app":"base","x-public":true,"x-tool":true}},"/v1/base/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/benchmark/catalog":{"get":{"operationId":"get_benchmark_catalog","summary":"Is the canonical public benchmarks this arena runs — 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.","description":"Is the canonical public benchmarks this arena runs — the id, title, axis,\nitem count and upstream source of each, with native marking the ones the\nstandardized harness runs today; the rest are registered and adapter-pending.\n\nThese ids are the vocabulary the rest of the surface takes: a run names them, and\nthe leaderboard and compare read them from ?benchmark=. The catalog is\ndeployment-wide and identical for every caller — there is no tenant in it.","tags":["benchmark"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/benchmarkCatalog"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/claims":{"get":{"operationId":"get_benchmark_claims","summary":"Lists the effective published claims: what the leaderboard will use for each (benchmark, model) after the seed, the import and any stored correction are layered.","description":"Lists the effective published claims: what the leaderboard will use for\neach (benchmark, model) after the seed, the import and any stored correction\nare layered. It answers the operator's question — what does this arena\ncurrently believe someone else reported, and did we ship that or fix it.\n\nEffective values only. The history of a key lives in the append-only file and\nis not what this op is for; a list that returned every superseded row would\nmake the common question the hard one.","tags":["benchmark"],"parameters":[{"name":"Benchmark","in":"query","required":false,"description":"Benchmark filters to one benchmark id. Empty returns every benchmark.","schema":{"type":"string"}},{"name":"Model","in":"query","required":false,"description":"Model filters to one model. Empty returns every model.","schema":{"type":"string"}},{"name":"Provider","in":"query","required":false,"description":"Provider filters to one lab or leaderboard — the way to read what a single\nsource claims across every model it covers.","schema":{"type":"string"}},{"name":"Source","in":"query","required":false,"description":"Source filters to one citation, which is the finest grain there is: a\nsource is what makes two claims about one model independent rather than a\nrestatement of each other.","schema":{"type":"string"}},{"name":"Protocol","in":"query","required":false,"description":"Protocol filters by HOW a claim was scored, so provider cards can be read\napart from third parties running their own harness.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/claimsOut"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true},"post":{"operationId":"post_benchmark_claims","summary":"Records published claims: one to correct a number, many to import a leaderboard.","description":"Records published claims: one to correct a number, many to import a\nleaderboard. Every row must carry a Source, because a claim without its\ncitation is a number nobody can check — and an unattributed number in the\npublished plane is indistinguishable from a measurement, which is the one\nconfusion this whole surface is built to prevent.\n\nWrites are append-only, so this never destroys the value it replaces. A\nvendor restating a score leaves both rows on disk, which is how the restating\nitself becomes visible.","tags":["benchmark"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/putClaimsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/putClaimsOut"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/compare":{"get":{"operationId":"get_benchmark_compare","summary":"Is the ONLY valid arm-vs-arm test: it pairs the two models on the items BOTH completed, and answers rescue and damage counts with an exact-McNemar p.","description":"Is the ONLY valid arm-vs-arm test: it pairs the two models on the items\nBOTH completed, and answers rescue and damage counts with an exact-McNemar p.\n\nPairing is what prevents the subset artifact — comparing one model's easy subset\nagainst another's full run — so n_common, not either arm's own coverage, is the\nnumber to read this by.\n\nBoth a and b are required. The benchmark defaults to gpqa_diamond.","tags":["benchmark"],"parameters":[{"name":"benchmark","in":"query","required":false,"description":"Benchmark is the catalog id to compare on, defaulting to gpqa_diamond.","schema":{"type":"string"}},{"name":"a","in":"query","required":true,"description":"A is the first model id. It is required.","schema":{"type":"string"}},{"name":"b","in":"query","required":true,"description":"B is the second model id. It is required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pairing"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/history":{"get":{"operationId":"get_benchmark_history","summary":"Returns each model's measured score per run over time, oldest first, with the change between runs.","description":"Returns each model's measured score per run over time, oldest first,\nwith the change between runs.\n\nThis is the counterweight to a leaderboard: the board shows the latest run\nbecause that is what \"how good is it\" means, and a single latest number cannot\ndistinguish a model that has always been strong from one that just improved,\nor from one that regressed after a provider changed something. Both matter for\nrouting, and only one of them is visible on a board.\n\nRuns with no id — attempts recorded before runs existed — group under the\nempty run, which is honestly what they are: one undated measurement.","tags":["benchmark"],"parameters":[{"name":"Benchmark","in":"query","required":false,"description":"Benchmark is the catalog id to read, defaulting to gpqa_diamond.","schema":{"type":"string"}},{"name":"Model","in":"query","required":false,"description":"Model filters to one model. Empty returns every model measured.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/historyOut"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/leaderboard":{"get":{"operationId":"get_benchmark_leaderboard","summary":"Answers one row per model for the benchmark named — what our own harness measured, beside what the vendor claims, and the gap between them.","description":"Answers one row per model for the benchmark named — what our own\nharness measured, beside what the vendor claims, and the gap between them.\n\nThe gap is the point of the arena; provider-reported claims have run materially\nhot against one standardized harness.\n\nThe two planes are NEVER blended, and that is the rule to read the rows by: a\nmodel we have measured but no vendor has claimed for shows published null, a\nmodel with only a claim shows measured null, and gap exists only where both do.\n\nn is coverage and is not decoration: two measured numbers taken over different\nitem counts are not comparable, so read the row's n before reading its accuracy.","tags":["benchmark"],"parameters":[{"name":"benchmark","in":"query","required":false,"description":"Benchmark is the catalog id to read, defaulting to gpqa_diamond.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/leaderboard"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/presets":{"get":{"operationId":"get_benchmark_presets","summary":"Are the router blends available to compose from — 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-<name>.","description":"Are the router blends available to compose from — a named set of model\narms, the rank they escalate through and the panel width that bounds fan-out —\neach served by the model layer as enso-<name>.\n\nToday it answers exactly one row, the reference blend: a worked example written\nin models we name, published as an example of the FORM. It is deliberately not\nthe composition of a Hanzo-served tier — the tier name exists to abstract that —\nso fork it and swap arms by what the leaderboard measures on your own tasks\nrather than reading it as a disclosure.","tags":["benchmark"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/presetList"}}},"description":"ok"}},"x-app":"benchmark","x-public":true,"x-tool":true},"post":{"operationId":"post_benchmark_presets","summary":"Validates a router blend — its name, its arms, the rank they escalate through and the panel fan-out width — and answers 202 with the preset and the enso-<name> it would be served as.","description":"Validates a router blend — its name, its arms, the rank they escalate\nthrough and the panel fan-out width — and answers 202 with the preset and the\nenso-<name> it would be served as.\n\nIt VALIDATES AND ECHOES: the definition is not persisted yet, so a preset\naccepted here is not one the model layer will resolve. Treat the response as a\ncheck on the blend, not a promise to serve it.\n\nDefaults fill the shape rather than refusing it: an omitted rank becomes the arms\nin declared order and a panel below 1 becomes 1. The one real invariant is that\nrank may only name arms the blend declares — the same rule the model catalog\nenforces — and a rank naming anything else is a 422 listing exactly which entries\nwere undeclared. A blend with no name or no arms is a 400.","tags":["benchmark"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Preset"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/presetAccepted"}}},"description":"accepted"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/benchmark/runs":{"post":{"operationId":"post_benchmark_runs","summary":"Admits and queues a benchmark run against a model or your own endpoint, and answers 202 with the receipt.","description":"Admits and queues a benchmark run against a model or your own endpoint, and\nanswers 202 with the receipt.\n\nIt is an ADMISSION, not a result: the work is done by the harness afterwards and\nthe numbers appear on the leaderboard as it completes them.\n\nCost is bounded by the store rather than by a quota: attempts are append-only and\nkeyed by (benchmark, item, model), so an (item, model) pair already attempted is\nskipped instead of re-spent, and re-queuing the same run is close to free.\n\nValidation is up front and total — a request with neither model nor endpoint is a\n400, one with no benchmarks is a 400, and any benchmark id outside the catalog is\na 422 naming exactly which ids were unknown, so a typo never silently queues a\npartial run.","tags":["benchmark"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/suite"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/admission"}}},"description":"accepted"}},"x-app":"benchmark","x-public":true,"x-tool":true}},"/v1/billing/accounts":{"get":{"operationId":"get_billing_accounts","summary":"Answers the caller's billing accounts: the org itself, its currency, when it was opened, and the caller's own standing in it.","description":"Answers the caller's billing accounts: the org itself, its currency, when it\nwas opened, and the caller's own standing in it.\n\nThe standing is the caller's, resolved from the validated principal here and\nsent to the store rather than looked up there — the membership roster is IAM's\nand commerce keeps none, so a callee that answered \"what role is this\" would\nbe inventing it. An anonymous read gets the account with no role rather than\nan implied membership.\n\nScoped to the caller's own org, which is the whole tenancy story: there is no\norg field on the wire and none on the input.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BillingAccount"},"type":"array"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/accounts/{id}/members":{"get":{"operationId":"get_billing_accounts_by_id_members","summary":"Answers one billing account's roster.","description":"Answers one billing account's roster.\n\ncommerce stores no roster — that is IAM's — so the only member it can name is\nthe caller, and that is what comes back. What it does enforce is that the\naccount named in the path is the caller's own: a foreign id is 403, not an\nempty list, because \"no members\" and \"not your account\" are different answers.\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 billing account id, which for this store is the org's own id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Holder"},"type":"array"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/alerts":{"get":{"operationId":"get_billing_alerts","summary":"Lists this org's spend caps: the ceiling, its scope, whether it enforces, and how much of it has been spent this period.","description":"Lists this org's spend caps: the ceiling, its scope, whether it enforces, and\nhow much of it has been spent this period.\n\n`periodSpentCents`, `over` and `warn` are ABSENT rather than zero when the\nspend could not be read, because \"nothing spent\" and \"spend unknown\" are\ndifferent answers and a customer acting on the first when the second is true\nwould be reading a ceiling that is not there. The policy row is reported\neither way.\n\nThe period is the UTC calendar month and `resetsAt` is when the count starts\nagain, so a surface can say \"resets on\" without a second call.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Alert"},"type":"array"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true},"post":{"operationId":"post_billing_alerts","summary":"Opens a spend cap on the caller's own org.","description":"Opens a spend cap on the caller's own org.\n\nAt least one limit must mean something: a threshold above zero (a spend cap)\nor a requests-per-minute above zero (a rate limit). A row that bounds neither\nis refused rather than stored, because a ceiling nothing measures against is a\nceiling a customer believes in and does not have.\n\nThe cap is keyed on the caller's own billing subject, resolved server-side —\nthe SAME key the verdict looks it up under, which is what makes enforcement\nbind rather than merely record.\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/AlertSpec"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Alert"}}},"description":"created"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/alerts/authorize":{"get":{"operationId":"get_billing_alerts_authorize","summary":"Answers whether one proposed spend fits inside this org's caps.","description":"Answers whether one proposed spend fits inside this org's caps.\n\nIt is the per-request verdict the metering edge consumes before every priced\ncall, and its caller is a SERVICE rather than a person: a service token plus\nthe gateway-pinned org, with no user behind it. So this admits that principal\nwhere the CRUD beside it does not.\n\nEvery covering row is evaluated, most-restrictive-wins, and the tightest one\nis what `capCents`, `spentCents` and `reason` describe. Soft rows never deny;\nnor does a project-scoped enforcing row whose project axis the caller could\nnot establish — `pv=1` is how a caller states that it did, and an unproven\nclaim must not be able to refuse traffic.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"project","in":"query","required":false,"description":"Project narrows the verdict to one project's caps. Empty is the org-wide row.","schema":{"type":"string"}},{"name":"service","in":"query","required":false,"description":"Service narrows it to one service's caps. Empty is every service.","schema":{"type":"string"}},{"name":"amount","in":"query","required":false,"description":"Amount is the proposed spend in cents.","schema":{"type":"string"}},{"name":"pv","in":"query","required":false,"description":"PV is \"1\" when the caller ESTABLISHED the project rather than merely\ncarrying a claim of one. An unproven project may not deny traffic.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapVerdict"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/alerts/{id}":{"delete":{"operationId":"delete_billing_alerts_by_id","summary":"Removes one of the caller's spend caps and answers 204.","description":"Removes one of the caller's spend caps and answers 204.\n\nRemoving a cap RAISES what the org may spend, so it takes the same authority\nsetting one does. The caps that remain still bind: this drops one, never the\nwhole policy.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the cap to remove, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"billing","x-public":true,"x-tool":true},"patch":{"operationId":"patch_billing_alerts_by_id","summary":"Changes one spend cap: raise or lower the ceiling, flip enforcement, retune the rate limit.","description":"Changes one spend cap: raise or lower the ceiling, flip enforcement, retune\nthe rate limit.\n\nOnly the fields the body carries move. Every mutable field is optional, and an\nabsent one is PRESERVED rather than reset — so a change that flips enforcement\ncannot silently wipe the threshold it enforces.\n\nA cap belonging to another org is a 404, not a 403: a guessed id must not\nbecome an oracle for what anyone else holds.\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,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertPatch"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Alert"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/balance":{"get":{"operationId":"get_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","x-public":true}},"/v1/billing/credit-balance":{"get":{"operationId":"get_billing_credit-balance","summary":"Answers what the caller can spend right now, one entry per currency.","description":"Answers what the caller can spend right now, one entry per currency.\n\nOnly ACTIVE grants count: a voided, exhausted or lapsed grant contributes\nnothing, which is why this number can be smaller than the grant list suggests\nand why the two reads exist separately. It is credit, not prepaid balance —\n/v1/billing/balance is the wallet, and the two are added by the gate, never by\na reader.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditBalance"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/credit-balance/breakdown":{"get":{"operationId":"get_billing_credit-balance_breakdown","summary":"Answers that same spendable credit split by grant tag, with the earliest expiry under each and the total across all of them.","description":"Answers that same spendable credit split by grant tag, with the earliest\nexpiry under each and the total across all of them.\n\nThe split is the point: it is how trial credit is told apart from bought\ncredit, which is what a surface asks before it decides whether to spend any.\nAn unregistered address answers 404 and a caller reads that as \"no credit\", so\nthis being served is the difference between a customer with a trial grant\nbeing offered their trial and being told they have none.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/credits":{"get":{"operationId":"get_billing_credits","summary":"Lists the caller's credit grants — every one of them, spent and lapsed and voided included.","description":"Lists the caller's credit grants — every one of them, spent and lapsed and\nvoided included.\n\nThat is deliberate and it is what makes the list useful: a grant list is a\nLEDGER, and one that hid its spent rows could not be reconciled against a\nburn-down. What is spendable right now is the sibling read, /v1/billing/\ncredit-balance, and the two are different questions.\n\nScoped to the caller's own wallet, resolved server-side.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditGrants"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/crypto/deposit":{"post":{"operationId":"post_billing_crypto_deposit","summary":"Issues a deposit address the caller can send crypto to, on the asset they ask for.","description":"Issues a deposit address the caller can send crypto to, on the asset they ask\nfor.\n\nThe address credits the CALLER'S own wallet and nobody else's: the payer is\nthe validated principal, never a body value. Asking again reuses the caller's\nopen intent rather than minting a second address, so a refresh cannot spray\nkey generations — and a payer who sent to the address they saw earlier is\nstill credited.\n\nNo balance moves here. The chain watcher credits on real confirmations, so\nwhat comes back is an address and a status, not a receipt.\n\nAn asset this rail cannot mint on is 400 — ask for another. A rail that is\nshut for that asset is 503 — nothing sent now can be credited.\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/cryptoAsset"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CryptoDeposit"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/crypto/deposit/{id}":{"get":{"operationId":"get_billing_crypto_deposit_by_id","summary":"Reads one of the caller's own deposit intents back — pending, confirming, or succeeded.","description":"Reads one of the caller's own deposit intents back — pending, confirming, or\nsucceeded.\n\nAn intent belonging to another payer answers 404, exactly as an id that names\nnothing, so a guessed id cannot confirm that somebody else's deposit exists.\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 deposit intent id.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CryptoDeposit"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/crypto/options":{"get":{"operationId":"get_billing_crypto_options","summary":"Answers which chains and tokens the crypto rail accepts — what an asset picker renders.","description":"Answers which chains and tokens the crypto rail accepts — what an asset picker\nrenders.\n\nIt is the intersection of two live facts rather than a configured list: an\nasset appears only if something is WATCHING it and the custody processor\nsupports it. An address nobody watches credits nobody, so offering one would\ntake a customer's money and lose it. A rail with nothing armed answers 503,\nnot an empty menu — \"no rail\" and \"no assets\" are different, and only one of\nthem means try again later.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CryptoOptions"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/invoices":{"get":{"operationId":"get_billing_invoices","summary":"Lists the caller's invoices, newest first, with the count beside them.","description":"Lists the caller's invoices, newest first, with the count beside them.\n\nIt is scoped to the caller's own billing subject — the wallet this request\nbills from, resolved server-side — so a query cannot widen it to another\ncustomer of the same org. An org with no invoices is an empty list, not a\nrefusal.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoices"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true},"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/RaiseIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}},"description":"created"}},"x-app":"billing","x-public":true,"x-tool":true}},"/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/Invoice"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/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/Collected"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/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/Invoice"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/invoices/{id}/pdf":{"get":{"operationId":"get_billing_invoices_by_id_pdf","summary":"Download one invoice as a PDF","description":"Answers the invoice as an attachment — `application/pdf` under a Content-Disposition naming the invoice number — rather than as a JSON value, which is why this one route is untyped where its five siblings are typed: a PDF is bytes with a filename, and the two headers are the whole contract.\n\nThe render is a PURE function of the invoice: one page, no timestamps and no random ids, so the same invoice renders the same bytes however often it is asked for and a retry after a dropped connection costs a re-render and nothing else.\n\nThe invoice is read from the caller's own org, taken from the VALIDATED IAM owner claim and never from a client header, and the lookup is scoped at the storage layer — so an id belonging to another customer resolves to nothing and answers 404 rather than being found and then refused.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"billing","x-public":true}},"/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/Invoice"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/ledger":{"get":{"operationId":"get_billing_ledger","summary":"Answers the org's own postings inside `range=`, each as a signed entry: a DEPOSIT CREDITS the wallet (positive, account `credits:<org>`) and every other posting DEBITS it (negative, account `usage:<org>`), 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:<org>`) and\nevery other posting DEBITS it (negative, account `usage:<org>`), 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, wider than either half of it: the deposits are the\ngrants /v1/billing/credits lists and the debits are the spend /v1/billing/usage\nrolls up. It answers 503 where this deployment runs no ledger, rather than\nreporting an empty 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/billing/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":["billing"],"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/billing/ledger.","schema":{"type":"string"}}}}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/methods":{"get":{"operationId":"get_billing_methods","summary":"Cards and accounts on file for the caller","description":"Answers every payment method the caller has saved, newest first.\n\nA saved method is a card or account VAULTED at the processor: what is stored here is the processor's token for it plus the last four digits and the expiry a customer recognises it by, never a card number.\n\nThe list is the caller's OWN — the wallet this request bills from, resolved server-side — so a query cannot widen it to another customer of the same org.\n\n`/v1/billing/portal/methods` answers the same list under the name a hosted checkout addresses it by. One set of rows, two spellings; a card added at either is present at both.\n\nA store that cannot be read answers an EMPTY LIST rather than a failure: the saved-cards panel renders empty instead of breaking the page around it.","tags":["billing"],"x-app":"billing","x-public":true},"post":{"operationId":"post_billing_methods","summary":"Save a card or account for the caller","description":"Vaults the instrument at the processor and stores the row.\n\nA saved method is a card or account VAULTED at the processor: what is stored here is the processor's token for it plus the last four digits and the expiry a customer recognises it by, never a card number.\n\nThe list is the caller's OWN — the wallet this request bills from, resolved server-side — so a query cannot widen it to another customer of the same org.\n\n`/v1/billing/portal/methods` answers the same list under the name a hosted checkout addresses it by. One set of rows, two spellings; a card added at either is present at both.\n\nSaving a card ALREADY on file answers with the row that already holds it rather than stacking a duplicate — 200 for that, 201 for a genuinely new row, so a client can tell which happened. A card the processor declines is 402 and nothing is stored.","tags":["billing"],"x-app":"billing","x-public":true}},"/v1/billing/methods/{id}":{"delete":{"operationId":"delete_billing_methods_by_id","summary":"Removes one card or account the caller has saved.","description":"Removes one card or account the caller has saved.\n\nIt detaches only the CALLER'S own — the wallet this request bills from,\nresolved server-side — so an id belonging to another customer of the same org\nis not something this operation can reach. A platform or service caller\ndetaches on the subject's behalf, and that authority is decided HERE, where the\ncredential is, and travels as a value: authority decided twice is authority\nthat eventually disagrees with itself.\n\nThe card is vaulted at the processor, so what goes is our token for it.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the saved method to detach, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Detachment"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/mode":{"post":{"operationId":"post_billing_mode","summary":"Moves this org between sandbox money and real money.","description":"Moves this org between sandbox money and real money.\n\nIt decides whether a charge hits a real card, so it is the one posture change\nthat is not self-service: the platform bar, never an org owner, because an org\nthat could put itself in test mode could take priced work for free.\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/ModeIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mode"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/payouts":{"get":{"operationId":"get_billing_payouts","summary":"Answers the org's outbound payouts, newest first — amount, destination, status, and the failure reason where one applies.","description":"Answers the org's outbound payouts, newest first — amount, destination,\nstatus, and the failure reason where one applies.\n\nA payout is ORG-scoped rather than subject-scoped, so there is nothing to pin\nbeyond the tenant the caller already is, and no query can widen it.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Payout"},"type":"array"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/plans":{"get":{"operationId":"get_billing_plans","summary":"The plan catalog, priced with whatever offer is in force","description":"Answers every plan on sale — its price, what it includes, and the limits it carries — optionally narrowed to one `?category=`.\n\nThe prices are what the CHECKOUT will charge: any active promotion is applied before they leave the store, so a reader never applies a discount a second time and a quote can never disagree with the sale.\n\nIt is the public catalog and needs no tenant: this is what anyone may buy.","tags":["billing"],"x-app":"billing","x-public":true}},"/v1/billing/portal/methods":{"get":{"operationId":"get_billing_portal_methods","summary":"Cards and accounts on file for the caller","description":"Answers every payment method the caller has saved, newest first.\n\nA saved method is a card or account VAULTED at the processor: what is stored here is the processor's token for it plus the last four digits and the expiry a customer recognises it by, never a card number.\n\nThe list is the caller's OWN — the wallet this request bills from, resolved server-side — so a query cannot widen it to another customer of the same org.\n\n`/v1/billing/portal/methods` answers the same list under the name a hosted checkout addresses it by. One set of rows, two spellings; a card added at either is present at both.\n\nA store that cannot be read answers an EMPTY LIST rather than a failure: the saved-cards panel renders empty instead of breaking the page around it.","tags":["billing"],"x-app":"billing","x-public":true},"post":{"operationId":"post_billing_portal_methods","summary":"Save a card or account for the caller","description":"Vaults the instrument at the processor and stores the row.\n\nA saved method is a card or account VAULTED at the processor: what is stored here is the processor's token for it plus the last four digits and the expiry a customer recognises it by, never a card number.\n\nThe list is the caller's OWN — the wallet this request bills from, resolved server-side — so a query cannot widen it to another customer of the same org.\n\n`/v1/billing/portal/methods` answers the same list under the name a hosted checkout addresses it by. One set of rows, two spellings; a card added at either is present at both.\n\nSaving a card ALREADY on file answers with the row that already holds it rather than stacking a duplicate — 200 for that, 201 for a genuinely new row, so a client can tell which happened. A card the processor declines is 402 and nothing is stored.","tags":["billing"],"x-app":"billing","x-public":true}},"/v1/billing/portal/methods/{id}":{"delete":{"operationId":"delete_billing_portal_methods_by_id","summary":"DetachPortalMethod is DetachMethod at the address a hosted checkout addresses it by.","description":"DetachPortalMethod is DetachMethod at the address a hosted checkout addresses\nit by. One set of rows, two spellings: a card detached at either is gone from\nboth, because there is one store behind them.","tags":["billing"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the saved method to detach, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Detachment"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/recharge/run-all":{"post":{"operationId":"post_billing_recharge_run-all","summary":"Sweeps every org's auto-recharge and answers what it did.","description":"Sweeps every org's auto-recharge and answers what it did.\n\nPLATFORM AUTHORITY ONLY. It charges saved cards across every tenant, so an org\nowner reaching it could sweep-charge the estate; a caller without it is\nrefused before anything is charged.\n\nThe answer explains a sweep that charged nobody as readily as one that\ncharged: it names how many orgs were considered and how many needed charging,\nwith a row each.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Recharge"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/settings":{"get":{"operationId":"get_billing_settings","summary":"Answers the PUBLIC half of this org's processor configuration — the ids a browser needs to tokenize a card, and the environment it must tokenize against.","description":"Answers the PUBLIC half of this org's processor configuration — the ids a\nbrowser needs to tokenize a card, and the environment it must tokenize\nagainst.\n\nIt carries no secret: an application id is published to every checkout page by\ndesign. What matters is that it names the SAME processor account the charge\nwill be made on, because a card vaulted against one account and charged\nagainst another is a card that saves and then cannot be used.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentConfig"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/subscribe/card":{"post":{"operationId":"post_billing_subscribe_card","summary":"Buy a plan with a card","description":"Vaults the card (or reuses one already on file), charges the plan's FIRST period at the catalog price, and opens the subscription — one act, all of it server-side.\n\nThere is NO AMOUNT in the request. `level` picks which of the plan's published prices to buy at — an index, never a number — so what the card is charged is decided by the catalog and underpaying cannot be expressed.\n\nA fresh sale answers 201 with the receipt. An identical retry answers 200 with the FIRST sale's body, byte for byte, so a client cannot read a replay as a second subscription having been opened. A caller already on a paid plan is 409 rather than charged again.","tags":["billing"],"x-app":"billing","x-public":true}},"/v1/billing/subscriptions":{"get":{"operationId":"get_billing_subscriptions","summary":"Lists the plans the caller holds, with the count beside them.","description":"Lists the plans the caller holds, with the count beside them.\n\nIt is scoped to the caller's own org, so a query cannot widen it to another\ncustomer's. An org on nothing is an empty list, not a refusal — being on no\nplan is an answer.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscriptions"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/subscriptions/{id}/cancel":{"post":{"operationId":"cancelSubscription","summary":"End a subscription","description":"Ends a subscription.\n\nIt cancels at the END OF THE PAID PERIOD by default, because a customer who\ncancels has already paid for the period they are in and taking it away is\ntaking money for nothing. `atPeriodEnd: false` ends it at once, which is the\ncaller asking for that.\n\nA subscription from another org is not found rather than refused, so an id\ncannot be probed for existence.\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,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/subscriptions/{id}/reactivate":{"post":{"operationId":"reactivateSubscription","summary":"Put a canceled subscription back on its plan","description":"Puts a canceled subscription back on its plan.\n\nWhat asks for this is usually a recovered payment method or a support tool\nrather than a browser, which is most of the argument for it having an address\nat all. The engine decides whether the move is legal; a row it will not\nreactivate comes back with its own reason.\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,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionRef"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/tier":{"get":{"operationId":"get_billing_tier","summary":"Answers which tier the caller is on, what it allows, and what is left to spend.","description":"Answers which tier the caller is on, what it allows, and what is left to spend.\n\n`effectiveAvailable` is the ONLY figure to compare against zero. The others are\nits parts — prepaid money, granted credits and the daily term are three sources\nof one spend, not three balances to add up a second time.\n\nA tier that cannot be READ is an error, never Free. The router in front of the\nmodels maps any non-2xx to Free, so answering Free from a question nobody could\nanswer would pin every paying customer to the most restrictive row with nothing\nanywhere to find.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tier"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/topup":{"post":{"operationId":"post_billing_topup","summary":"Charges a card the caller already saved and credits the balance.","description":"Charges a card the caller already saved and credits the\nbalance. Same receipt and the same retry safety as the token endpoint; the only\ndifference is which card, so a caller topping up from a saved method never\nre-enters one.","tags":["billing"],"parameters":[{"name":"X-Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/topupIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Charged"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/topup/token":{"post":{"operationId":"post_billing_topup_token","summary":"Charges a single-use card token and credits the caller's balance.","description":"Charges a single-use card token and credits the caller's\nbalance.\n\nThe token comes from the payment form and is vaulted as part of the charge, so\nno card number reaches this service and none is stored here. The receipt names\nthe ledger entry, the new balance, and the PROCESSOR's own reference — which is\nthe only field that proves money moved at the gateway rather than only in our\nledger.\n\nRetry-safe on X-Idempotency-Key: the same key settles one charge and returns\nthe first receipt.","tags":["billing"],"parameters":[{"name":"X-Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/topupIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Charged"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/transactions":{"get":{"operationId":"get_billing_transactions","summary":"Answers one page of the caller's own ledger, newest first: what moved, how much, when, and what it was tagged with.","description":"Answers one page of the caller's own ledger, newest first: what moved, how\nmuch, when, and what it was tagged with.\n\n`count` is the size of the WHOLE history rather than of the page, which is how\na reader knows there is more to ask for, and `user` echoes the wallet the page\nwas read for — the same subject the spend gate debits, so a customer can see\nwhich account answered rather than guessing from their own token.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"parameters":[{"name":"currency","in":"query","required":false,"description":"Currency filters to one currency. Empty reads every currency.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit is the page size; absent or non-positive takes the default 100.","schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"description":"Offset is how far into the history the page starts.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transactions"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/usage":{"get":{"operationId":"get_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=<id>` 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","x-public":true}},"/v1/billing/usage/accounts":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/billing/usage/rollup":{"get":{"operationId":"get_billing_usage_rollup","summary":"Answers the caller's month: what their plan includes, what has been consumed against it, and the wallet beside it.","description":"Answers the caller's month: what their plan includes, what has been consumed\nagainst it, and the wallet beside it.\n\nThe two blocks are SEPARATE monies and are never added. One is usage a plan\ngranted; the other is prepaid credit bought with a card. Their sum is not a\nnumber anyone holds, and a reader that formed it would be inventing a balance.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rollup"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/billing/wire":{"get":{"operationId":"get_billing_wire","summary":"Answers where to send a wire top-up: the receiving bank details, with the caller's own payment reference.","description":"Answers where to send a wire top-up: the receiving bank details, with the\ncaller's own payment reference.\n\nThe account is the SERVING BRAND'S — resolved from the host the customer is\npaying on, so paying on one brand never shows another's bank — and the\nreference carries the caller's billing key, which is how an arriving wire\nnames who it credits. Nothing mints here; a receipt is settled by an operator\nonce the bank confirms it.\n\nIt is all-or-nothing: no configured account is 503 rather than a partial form,\nbecause nobody can wire to three fields out of five.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["billing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WireInstructions"}}},"description":"ok"}},"x-app":"billing","x-public":true,"x-tool":true}},"/v1/blueprint":{"get":{"operationId":"get_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=<id>\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","x-public":true,"x-tool":true}},"/v1/blueprint/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/blueprint/sbom":{"get":{"operationId":"get_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=<id>` 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","x-public":true}},"/v1/books/accounts":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/ask":{"post":{"operationId":"post_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 client 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","x-public":true,"x-tool":true}},"/v1/books/bank/exchange":{"post":{"operationId":"post_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","x-public":true}},"/v1/books/bank/import":{"post":{"operationId":"post_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","x-public":true}},"/v1/books/bank/sync":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/books/bank/token":{"post":{"operationId":"post_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","x-public":true}},"/v1/books/bank/transactions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/bank/unreconciled":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/export":{"get":{"operationId":"get_books_export","summary":"Returns the complete financial package for the caller's org over (from, to]: the trial balance, the P&L, 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&L, 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","x-public":true,"x-tool":true}},"/v1/books/gl":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/inbox":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/books/metrics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/pnl":{"get":{"operationId":"get_books_pnl","summary":"Returns the org's accrual-basis Profit & 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 & 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","x-public":true,"x-tool":true}},"/v1/books/position":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/questions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/rules":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/books/scan":{"post":{"operationId":"post_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","x-public":true}},"/v1/books/scan/book":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/books/sync":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/books/transactions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/trial":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/books/vendors":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/bot/runs":{"get":{"operationId":"get_bot_runs","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":["bot"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotRuns"}}},"description":"ok"}},"x-app":"bot","x-public":true,"x-tool":true},"post":{"operationId":"post_bot_runs","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 the collection every run is created in: GET lists them, POST would launch one.\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":["bot"],"x-app":"bot","x-public":true}},"/v1/bot/runs/{runId}/stop":{"post":{"operationId":"post_bot_runs_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":["bot"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotStopped"}}},"description":"ok"}},"x-app":"bot","x-public":true,"x-tool":true}},"/v1/campaign":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/campaign/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/campaign/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/campaign/{id}/channels":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/campaign/{id}/channels/{kind}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/campaign/{id}/launch":{"post":{"operationId":"post_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 client 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","x-public":true}},"/v1/campaign/{id}/metrics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/campaign/{id}/pause":{"post":{"operationId":"post_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","x-public":true}},"/v1/captable/classes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_classes","summary":"Defines a new class of shares.","description":"Defines a new class of shares.\n\nEvery field but convertsToShareClassId is required — a class is the instrument\nevery later issuance prices against, so a partially-specified one would silently\nmis-value every share issued into it. `seniority` orders liquidation preference\nwith LOWER first.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableShareClassIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/classes/{id}":{"patch":{"operationId":"patch_captable_classes_by_id","summary":"Replaces one share class's terms.","description":"Replaces one share class's terms.\n\nIt is a full REPLACE and not a merge, despite the PATCH: every field is written\nas sent, so a field omitted is written empty rather than left alone. Send the\nwhole class. The method is PATCH because the resource is addressed by id, not\nbecause the body is partial — and getting that backwards silently blanks terms\nevery later issuance prices against.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID addresses the resource. The URL is the addressing authority — a path\nsegment binds after the body and after the query — so the address decides\nwhich row is written whatever a body claims.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableShareClassAmend"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableUpdated"}}},"description":"ok"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/company":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/captable/convertibles":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_convertibles","summary":"Records a convertible note.","description":"Records a convertible note.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableConvertibleIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/convertibles/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/captable/investments":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/captable/options":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_options","summary":"Grants options to a stakeholder from an equity plan.","description":"Grants options to a stakeholder from an equity plan.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableOptionIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/options/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/captable/plans":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_plans","summary":"Opens an equity plan that options are granted from.","description":"Opens an equity plan that options are granted from.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableEquityPlanIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/rounds":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_rounds","summary":"Opens a priced round that investments can be added to.","description":"Opens a priced round that investments can be added to.\n\nThe round opens OPEN; investing into a closed one is refused.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableRoundIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/rounds/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/captable/rounds/{id}/close":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/captable/rounds/{id}/investments":{"post":{"operationId":"post_captable_rounds_by_id_investments","summary":"Records one investor's money into an open round.","description":"Records one investor's money into an open round.\n\nThe round must be OPEN; investing into a closed one is refused. Where the round\ncarries a price per share, the investment also issues the shares it buys and the\nanswer names them.","tags":["captable"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the round to invest in. The URL is the addressing authority — a path\nsegment binds after the body and after the query — so the address decides\nwhich round is written whatever a body claims.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableInvestmentIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableInvested"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/safes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_safes","summary":"Records a SAFE — a simple agreement for future equity.","description":"Records a SAFE — a simple agreement for future equity.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableSafeIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/safes/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/captable/shares":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_captable_shares","summary":"Issues a share certificate to a stakeholder.","description":"Issues a share certificate to a stakeholder.\n\nThe certificate id must be UNIQUE within the company — a duplicate is refused\n409, not silently merged — and both the stakeholder and the share class must\nbelong to this company, so an id from another tenant is a 400 rather than a\ncross-company issuance.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableShareIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableCreated"}}},"description":"created"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/shares/transfer":{"post":{"operationId":"post_captable_shares_transfer","summary":"Moves shares from one stakeholder to another.","description":"Moves shares from one stakeholder to another.\n\nOmit `quantity` to transfer the whole certificate, which REASSIGNS it and mints\nno new share. Send a quantity below the amount held to SPLIT it — the source\ncertificate keeps the remainder, and a split additionally requires\n`certificateId` for the new certificate, which must be unique in the company.\nA quantity outside 1..held is refused, so a transfer can never over-issue.\n\nBoth outcomes answer 200: a transfer records a movement between holders and\nmints no security of its own, which is why this is not a 201 the way an\ninvestment is.","tags":["captable"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableShareTransfer"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/captableTransferred"}}},"description":"ok"}},"x-app":"captable","x-public":true,"x-tool":true}},"/v1/captable/shares/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/captable/stakeholders":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/captable/stakeholders/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/captable/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/catalog":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/channels":{"get":{"operationId":"get_channels","summary":"Reports every chat channel this org can send through, and whether it can send through it right now.","description":"Reports every chat channel this org can send through, and whether it can\nsend through it right now.\n\nA channel appears here whether or not it is connected — an empty list would\nleave a caller unable to tell \"this org has no Slack\" from \"Slack is down\",\nwhich are different problems with different fixes. Each entry carries the\nconnection behind it, so the answer to \"why can I not post?\" is in the same\nresponse as the channel that cannot post.","tags":["channels"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/chatChannels"}}},"description":"ok"}},"x-app":"channels","x-public":true,"x-tool":true}},"/v1/channels/allowlist":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/channels/inbox":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/channels/pairing":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/channels/pairing/approve":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/channels/{channel}/send":{"post":{"operationId":"post_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","x-public":true}},"/v1/chat":{"post":{"operationId":"post_chat","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/chat/completions":{"post":{"operationId":"post_chat_completions","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/chat/public":{"post":{"operationId":"post_chat_public","summary":"Serves one completion to a caller with no account.","description":"Serves one completion to a caller with no account.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/cloudflare/d1/databases":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/d1/databases/{database}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/cloudflare/d1/databases/{database}/query":{"post":{"operationId":"post_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","x-public":true}},"/v1/cloudflare/kv/namespaces":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/kv/namespaces/{namespace}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/cloudflare/kv/namespaces/{namespace}/values/{key}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/cloudflare/pages/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/pages/projects/{project}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/pages/projects/{project}/deployments":{"post":{"operationId":"post_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","x-public":true}},"/v1/cloudflare/pages/projects/{project}/domains":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/pages/projects/{project}/domains/{domain}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/cloudflare/r2/buckets":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/r2/buckets/{bucket}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/cloudflare/workers/scripts":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/workers/scripts/{script}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true}},"/v1/cloudflare/workers/scripts/{script}/subdomain":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/workers/subdomain":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/workers/zones/{zone}/routes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/cloudflare/workers/zones/{zone}/routes/{route}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/cloudflare/zones":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/zones/{zone}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/zones/{zone}/analytics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/cloudflare/zones/{zone}/purge":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/code/ask":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/code/context":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/code/file":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/code/index":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/code/search":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/code/tree":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/commerce/admin/catalog":{"get":{"operationId":"get_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","x-public":true}},"/v1/commerce/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":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/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":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/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":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/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":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/catalog":{"get":{"operationId":"get_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","x-public":true}},"/v1/commerce/catalog/entries":{"get":{"operationId":"get_commerce_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":["commerce"],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/catalog/models":{"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/catalog/models/refresh":{"post":{"operationId":"post_commerce_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 endpoint uses, so the rule that a sync owns cost and an administrator owns price holds no matter which endpoint 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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/catalog/seed":{"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/collection/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/collection/{collectionid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/currencies":{"get":{"operationId":"get_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","x-public":true}},"/v1/commerce/deposits":{"get":{"operationId":"get_commerce_deposits","summary":"Read the crypto deposit watcher's runtime state, asset by asset","description":"Reports whether the deposit watcher is running, its poll interval, and one row per armed asset: chain, token, contract, pooled address and the last block that asset's cursor reached. That last block is the only way to see a watcher that is up but no longer advancing, which is what a stalled deposit rail looks like from outside. SuperAdmin only — the reserved admin org's owner claim; an authenticated caller without it is refused 403 and an anonymous one 401. It is READ-ONLY by design: arming an asset stays a CRYPTO_DEPOSIT_* deployment act and is deliberately not a button here, so there is nothing on this surface that can start crediting a customer's balance. The asset's RPC endpoint is reduced to scheme://host before it is returned, because a managed node URL carries its API key in the path or query and echoing it verbatim would publish that credential to every reader of this status.","tags":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/disclosure/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/disclosure/{disclosureid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/discount/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/discount/{discountid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/health":{"get":{"operationId":"get_commerce_health","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: a probe that needs a credential is a probe that\nreports the credential.\n\nA named handler, not a closure, so zipdoc can lift this prose into the registry.","tags":["commerce"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/liveness"}}},"description":"ok"}},"x-app":"commerce","x-public":true,"x-tool":true}},"/v1/commerce/movie/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/movie/{movieid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/note/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/note/{noteid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/org":{"get":{"operationId":"get_commerce_org","summary":"The public org 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 org 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 orgs; a successful answer is cacheable for a minute.","tags":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/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 entry point onto it, and the only\nregistrable one is the second.","tags":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/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":["commerce"],"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","x-public":true,"x-tool":true}},"/v1/commerce/plans/entries":{"get":{"operationId":"get_commerce_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":["commerce"],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/plans/entries/{slug}":{"delete":{"operationId":"delete_commerce_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":["commerce"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"put":{"operationId":"put_commerce_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":["commerce"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/plans/seed":{"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/product/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/product/{productid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/rates/entries":{"get":{"operationId":"get_commerce_rates_entries","summary":"List what one unit of each metered thing costs","description":"Returns the rate authority's rows — the prices every metered charge resolves against. Narrow with ?product= to show one surface at a time rather than every rate at once. SuperAdmin only: a rate is cross-tenant money, so the handler asks for the reserved admin org's owner claim itself rather than trusting the bundle's token gate.","tags":["commerce"],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_rates_entries","summary":"Add a rate","description":"Creates one rate. Product AND meter are both required, because together they are the identity: a rate keyed on the metered thing alone would let one product's price overwrite another's under the same name. A slug that already exists is refused rather than silently replaced. SuperAdmin only.","tags":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/rates/entries/{product}/{meter}":{"delete":{"operationId":"delete_commerce_rates_entries_by_product_by_meter","summary":"Remove a rate outright","description":"Deletes the row. ARCHIVING is usually what is wanted instead — a deleted rate cannot price a historical charge, so a past invoice that has to re-resolve its rate finds nothing to read. Reach for status=archived unless the rate never priced anything. SuperAdmin only.","tags":["commerce"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}},{"name":"meter","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"put":{"operationId":"put_commerce_rates_entries_by_product_by_meter","summary":"Edit a rate, and mark it as operator-set","description":"Edits one rate and MARKS it edited, which is the whole contract with the importer: an operator's price outranks the document it came from, so a later import leaves this row alone. Without that mark a price set here would apply, work, and silently revert on the next import. Only the editable fields move; identity and bookkeeping are not writable from the body. SuperAdmin only.","tags":["commerce"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}},{"name":"meter","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/rates/import":{"post":{"operationId":"post_commerce_rates_import","summary":"Load the published price document, reconciling rather than replacing","description":"Takes an array of rates and seeds the authority from it — the same reconcile the boot catalog runs, driven from admin instead. It RECONCILES: a row that matches is left alone, a row that has drifted is corrected, and a row an operator edited is skipped — so importing the same document twice is a no-op and importing a corrected one moves exactly the rows that changed. Answers what it received, created, corrected and left unchanged, so an import that changes nothing reads as nothing to do rather than as a failure. An empty array is refused 400. SuperAdmin only.","tags":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/return/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/return/{returnid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/saleschannel/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/saleschannel/{saleschannelid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/stocklocation/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/stocklocation/{stocklocationid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/store/":{"get":{"operationId":"get_commerce_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":["commerce"],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/store/access":{"get":{"operationId":"get_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/store/current":{"get":{"operationId":"get_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/store/token":{"post":{"operationId":"post_commerce_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":["commerce"],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}":{"delete":{"operationId":"delete_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"patch":{"operationId":"patch_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"put":{"operationId":"put_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/authorize":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/authorize/{orderid}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/bundle/{key}":{"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/capture/{orderid}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/charge":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/authorize":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/authorize/{orderid}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/capture/{orderid}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"orderid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/charge":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/paypal/cancel/{payKey}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/paypal/confirm/{payKey}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/checkout/paypal/pay":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/listing":{"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/listing/{key}":{"delete":{"operationId":"delete_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"patch":{"operationId":"patch_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true},"put":{"operationId":"put_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/paypal/cancel/{payKey}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/paypal/confirm/{payKey}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"payKey","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/paypal/pay":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/product/{key}":{"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/trial":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/store/{storeid}/variant/{key}":{"get":{"operationId":"get_commerce_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":["commerce"],"parameters":[{"name":"storeid","in":"path","required":true,"schema":{"type":"string"}},{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/commerce/submission/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/submission/{submissionid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/subscriber/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/subscriber/{subscriberid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/tokentransaction/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/tokentransaction/{tokentransactionid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/transfer/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/transfer/{transferid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/variant/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/variant/{variantid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/wallet/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/wallet/{walletid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/watchlist/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/watchlist/{watchlistid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/webhook/":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/commerce/webhook/{webhookid}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/commerce/webhooks/{provider}":{"post":{"operationId":"post_commerce_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":["commerce"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"commerce","x-public":true}},"/v1/company":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/advance":{"post":{"operationId":"post_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 endpoint 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","x-public":true,"x-tool":true}},"/v1/company/documents":{"post":{"operationId":"post_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 client.","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 client.\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","x-public":true,"x-tool":true}},"/v1/company/ein":{"post":{"operationId":"post_company_ein","summary":"Opens the EIN application and answers what it owes.","description":"Opens the EIN application and answers what it owes.\n\nThe answer states whether it can be filed ONLINE, because that is the fact\ndeciding whether the customer waits a sitting or several weeks — and it names\neach form with what that form is for, so nobody has to already know what an\nSS-4 is to understand why they are signing one.","tags":["company"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/einIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EIN"}}},"description":"ok"}},"x-app":"company","x-public":true,"x-tool":true}},"/v1/company/esign":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/esign/complete":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/founders":{"post":{"operationId":"post_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/company/fundraise/deck":{"post":{"operationId":"post_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","x-public":true}},"/v1/company/fundraise/round":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/fundraise/safe":{"post":{"operationId":"post_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":"[email protected]","name":"Ada"}]},"schema":{"$ref":"#/components/schemas/safeIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/safeOut"}}},"description":"created"}},"x-app":"company","x-public":true,"x-tool":true}},"/v1/company/genesis":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/import/captable":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/import/documents":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/kyc":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/kyc/decision":{"post":{"operationId":"post_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/company/kyc/refresh":{"post":{"operationId":"post_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 —\nso a client cannot force a pass here, and an already-passing founder (e.g. a\nreviewer confirmation) is left untouched.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kycRefreshOut"}}},"description":"ok"}},"x-app":"company","x-public":true,"x-tool":true}},"/v1/company/payment":{"post":{"operationId":"post_company_payment","summary":"Charges the caller's own org the one-time Hanzo Company formation fee.","description":"Charges the caller's own org the one-time Hanzo Company formation fee.\n\nIt is $999 unless the deployment sets another, and the answer is the formation\nrecord carrying its paid flag and the charge reference. It takes no body: the org is the validated tenant and the amount is the\nplatform's, never the caller's to assert.\n\nIDEMPOTENT on the formation rather than on the request: an already-paid\nformation answers 200 with the same record and is not charged again, so a\nretry or a double-clicked button costs nothing. Available only at the\n`payment` stage (409 anywhere else) and only for an org that has begun a\nformation (404 otherwise).\n\nA denial answers the fleet-wide billing contract — 402 insufficient_balance,\n402 spend_cap_exceeded, 503 balance_unavailable — carried by cloud.Denied,\nwhich is the money wire's own {\"error\":{\"code\",\"message\"}} body rather than a\nsecond vocabulary invented for this surface.\n\nThe gate is the LAST thing it does, after the stage check and the paid\nshort-circuit, so a caller the machine is about to refuse is never charged.\nThat ordering is why the gate cannot lift into middleware, where it would run\nfirst. Both facts are pinned: TestPaymentDenialWire, TestPaymentChargesLast.","tags":["company"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/formationView"}}},"description":"ok"}},"x-app":"company","x-public":true,"x-tool":true}},"/v1/company/register":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/company/register/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/company/review":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/company/skip":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/company/structure":{"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/company/tariff":{"post":{"operationId":"post_company_tariff","summary":"Itemises what a formation costs before anyone commits to it.","description":"Itemises what a formation costs before anyone commits to it.\n\nIt answers what is due now and what recurs, as separate figures, and marks the\nstate's filing fee as money we collect and remit rather than keep. A caller can\ntherefore show a payer the whole bill — which is the point of quoting at all,\nand was impossible while the fee was one number in an error string.\n\nA jurisdiction whose filing fee this deployment has not been told REFUSES,\nnaming the setting that fixes it. Quoting our half as though it were the total\nis the one answer that would be worse than no answer.","tags":["company"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/tariffIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tariff"}}},"description":"ok"}},"x-app":"company","x-public":true,"x-tool":true}},"/v1/completions":{"post":{"operationId":"post_completions","summary":"Implements the OpenAI-compatible chat completions API","description":"Implements the OpenAI-compatible chat completions API","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/compliance/accreditation":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/compliance/accreditation/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/accreditation/{id}/decision":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/compliance/audit":{"get":{"operationId":"get_compliance_audit","summary":"AuditRead is the compliance 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 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","x-public":true,"x-tool":true}},"/v1/compliance/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/records":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/subjects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/compliance/subjects/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/verifications":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/compliance/verifications/webhook":{"post":{"operationId":"post_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","x-public":true}},"/v1/compliance/verifications/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/compliance/verifications/{id}/decision":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/compliance/verifications/{id}/refresh":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/content/board":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/content/channels":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/content/generate":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/content/lifecycle":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/content/publish":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/content/{doctype}/{name}/transition":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/crawl":{"post":{"operationId":"read_page","summary":"Fetch one URL and read it back as markdown","description":"Reads one URL and answers with the page as markdown.\n\nIt fetches a single URL from inside the cluster and answers with the address it\nactually landed on, the document's title, its content rendered to MARKDOWN, and\nwhatever the page said about itself. One URL per call: batching would make the\nanswer a partial-failure envelope every caller then has to unpack.\n\nA PAGE THAT COULD NOT BE FETCHED IS A NORMAL ANSWER, not a fault. An\nunreachable host, a refused address and a content type that is not a document\nall answer 200 with `success:false` and the reason in `error`, because the\ncaller sent a well-formed ask and gets a well-formed answer. Non-2xx is reserved\nfor a caller problem — 400 with the same body when there is no url, 401 for a\nbad key, 503 when the surface is unconfigured — so error handling can trust the\nstatus. Check `success` before reading `data`.\n\nAdmission is either a validated principal or the shared service key, presented\nas X-API-Key or a Bearer; neither is refused, and an unset key fails closed\nrather than opening the fetcher to the private network. Pages are archived under\nthe scope of the VERIFIED principal and NEVER a scope named in the body, so a\nURL already read under that scope is answered from the archive without touching\nthe network; a service caller has no org and its pages land in the shared\ncorpus.\n\nThe URL is caller-supplied and dialled from INSIDE the cluster, which makes this\na request-forgery primitive by construction. Only http and https are accepted,\nand every address actually dialled must be public unicast — loopback,\nlink-local, private and multicast are refused. The check lives in the DIALER\nrather than on the hostname, because resolving a name to validate it and then\nletting the transport resolve it again is a gap DNS rebinding walks straight\nthrough; redirects re-enter the same dialer.","tags":["crawl"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crawlRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crawlResult"}}},"description":"ok"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/crawlResult"}}},"description":"bad request"}},"x-app":"crawl","x-public":true,"x-tool":true}},"/v1/crm/applications":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/crm/applications/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/crm/companies":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/crm/companies/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/crm/contacts":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/crm/contacts/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/crm/opportunities":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/crm/opportunities/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/crm/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/dataroom/analytics/dataroom/{dataroomId}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/dataroom/analytics/link/{linkId}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/dataroom/datarooms":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/dataroom/datarooms/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/dataroom/datarooms/{id}/documents":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/dataroom/documents":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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 client, 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","x-public":true}},"/v1/dataroom/documents/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/dataroom/documents/{id}/file":{"get":{"operationId":"get_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","x-public":true}},"/v1/dataroom/health":{"get":{"operationId":"get_dataroom_health","summary":"Health reports that the data room subsystem is up.","description":"Health reports that the data room subsystem is up.\n\nIt answers before the bundle loads, holds no state and touches no store, so it\nstays true in exactly the situation an operator is probing for. It says nothing\nabout whether a room can be OPENED — that is what the room operations answer —\nbecause a liveness probe that fails on a dependency takes a working process out\nof rotation.","tags":["dataroom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dataroomLiveness"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/links":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/dataroom/trust":{"get":{"operationId":"get_dataroom_trust","summary":"Answers the caller org's OWN trust centre: its settings, every item it holds in both tiers, the requests waiting on it, and the grants it has made.","description":"Answers the caller org's OWN trust centre: its settings, every item it\nholds in both tiers, the requests waiting on it, and the grants it has made.\n\nThe org is the caller's, taken from the validated bearer and from nothing else,\nso this op cannot be pointed at another tenant — there is no field for one. An\norg that has never opened a centre reads back an empty one rather than an error,\nbecause having no trust centre is an ordinary state and this is the read that\ntells you so.","tags":["dataroom"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustDesk"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true},"put":{"operationId":"put_dataroom_trust","summary":"SetCenter opens, publishes or withdraws the caller org's trust centre and answers with the centre as it now stands.","description":"SetCenter opens, publishes or withdraws the caller org's trust centre and answers\nwith the centre as it now stands.\n\nPublishing requires a name and an address, and the address must be free: another\norg already answering there is a conflict, never a takeover. Withdrawing closes\nthe public endpoint only — items, grants and the access record are untouched, so\nan org can go quiet and come back without losing anything.\n\nOnly an admin of the org may call it. The org is the caller's own, so there is no\nfield naming one and no way to point this at another tenant.","tags":["dataroom"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustSettings"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustDesk"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/artifacts":{"post":{"operationId":"post_dataroom_trust_artifacts","summary":"Publish puts an item on the caller org's trust centre and answers with it.","description":"Publish puts an item on the caller org's trust centre and answers with it.\n\nThe item is GATED unless it says otherwise, so a kind nobody has thought of yet\narrives private and someone has to release it deliberately — that default is what\nkeeps an auditor's report from becoming readable because a field went unset. An\nitem whose attester is \"auditor\" cannot be public at all: the database refuses the\npair, so no path through this API can publish one.\n\nA file is optional and is uploaded FIRST, through POST /v1/dataroom/documents,\nthen named here — the data room is the one place bytes enter, so a trust centre\ndocument is an ordinary data-room document and inherits its storage, its grants\nand its page-by-page access record. A gated item that has a file is added to the\norg's release room, which is what lets a party be granted the whole gated tier in\none link.\n\nOnly an admin of the org may call it.","tags":["dataroom"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustPublish"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustItemView"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/artifacts/{id}":{"patch":{"operationId":"patch_dataroom_trust_artifacts_by_id","summary":"Amend changes an item on the caller org's trust centre — replace its file with a newer edition, move it between public and gated, rewrite what it says, or retire it — and answers with the item as it now stands.","description":"Amend changes an item on the caller org's trust centre — replace its file with a\nnewer edition, move it between public and gated, rewrite what it says, or retire\nit — and answers with the item as it now stands.\n\nRetiring is the withdrawal: the item leaves the public centre immediately and can\nno longer be granted, while grants already made over it stand, because a release\nthat happened is part of the record and un-happening it in the record would be a\nlie. Restoring is the same call with retired false.\n\nMoving an item an independent auditor signed to the public tier is refused, and\nrefused by the database rather than only here. Only an admin of the org may call\nit, and the item is resolved in that org's own store, so another org's id is not\nfound.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the item to change, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustEdit"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustItemView"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/center/{slug}":{"get":{"operationId":"get_dataroom_trust_center_by_slug","summary":"Answers an org's public trust centre: its name, the text a party must accept to ask for a document, and every item it publishes.","description":"Answers an org's public trust centre: its name, the text a party must\naccept to ask for a document, and every item it publishes.\n\nAn item is either available NOW — the things the org states itself, its policies,\nits filled questionnaires, its subprocessor list, its knowledge base — or\navailable ON REQUEST, which is everything an independent auditor put their name\nto. Both are listed by name and kind, so a reader can see WHAT exists before\nasking for it; only the second withholds the content.\n\nNo principal is involved and none is accepted: the org is resolved from the\naddress, which answers only for a centre its owner has published. An address\nnobody publishes at is not found, the same answer an unpublished one gets.","tags":["dataroom"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the centre's public address. It resolves only for an org that has\npublished; anything else is not found, so this cannot be used to learn which\norgs exist.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustPage"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/center/{slug}/file/{item}":{"get":{"operationId":"get_dataroom_trust_center_by_slug_file_by_item","summary":"Read a public trust-centre item's bytes","description":"Streams the file behind an item a trust centre publishes openly — a policy, a filled questionnaire, a knowledge-base attachment — under its recorded content type.\n\nNo principal and no link: these are the things an org states about itself, so they are served to anyone who asks. The narrowing is in the lookup rather than in a check: the item must be public, must not be retired, and must belong to a centre its owner has published, so an item released only on request is NOT FOUND here rather than refused — the same answer an id that never existed gets, which is what stops this reporting what the released-on-request tier holds.\n\nBytes that cannot be fetched from object storage are 502, never a truncated file.","tags":["dataroom"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"dataroom","x-public":true}},"/v1/dataroom/trust/center/{slug}/requests":{"post":{"operationId":"post_dataroom_trust_center_by_slug_requests","summary":"Records a request to read what an independent auditor signed, and answers with its id.","description":"Records a request to read what an independent auditor signed, and\nanswers with its id.\n\nThe org that owns the centre decides. Nothing is released here and no link is\nminted: this writes the ask down, which is the whole promise the form makes.\nThe write is the answer — a request that could not be stored is an error, never\na receipt, so a form can never appear to have been sent and be gone.\n\n`email` is required and is the ONLY address the eventual grant will admit, so an\naddress the asker cannot read is an ask that cannot be answered. Where the centre\nstates an NDA, `accept` must be true and the text in force is recorded verbatim\nagainst the request.\n\nAsking twice for the same thing from the same address is the SAME ask: the second\nanswers with the first's id rather than opening a second row, which is also what\nkeeps an anonymous endpoint from filling a tenant's store.","tags":["dataroom"],"parameters":[{"name":"slug","in":"path","required":true,"description":"Slug is the centre's public address, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustAsk"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustAsked"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/requests/{id}/grant":{"post":{"operationId":"post_dataroom_trust_requests_by_id_grant","summary":"Grant answers a request by opening access: it mints a share link over what was asked for, addressed to the address that asked and closing at expiry, records the decision, and mails the asker.","description":"Grant answers a request by opening access: it mints a share link over what was\nasked for, addressed to the address that asked and closing at expiry, records the\ndecision, and mails the asker.\n\nThe link is NEVER a public URL. It carries the asker's address on its allow list,\nso forwarding it to somebody else does not open it, and it expires. What the\nparty then does with it — which document, which page, for how long — is recorded\nby the data room's own view tracking, which is where the access record for this\nrelease lives; there is no second log.\n\nA request that was already answered is refused rather than answered twice, so a\nsecond click cannot mint a second link. Only an admin of the org may call it, and\nthe request is resolved in that org's own store, so another org's request id is\nnot found — which is also what stops one org deciding another's queue.\n\nMail is best effort and the grant does not depend on it: a deployment that sends\nno mail still records the grant and says so in `delivery`, so the approver knows\nto pass the address on themselves.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the request to answer, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustDecision"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustGranted"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/trust/requests/{id}/refuse":{"post":{"operationId":"post_dataroom_trust_requests_by_id_refuse","summary":"Refuse answers a request by declining it, recording who declined and why.","description":"Refuse answers a request by declining it, recording who declined and why.\n\nNothing is released and no link is minted. The refusal STAYS on the record beside\nthe ask — a request that was turned down is part of the access record exactly as\none that was granted is, and deleting it would leave a queue that only ever shows\nthe decisions somebody liked.\n\nA request that was already answered is refused rather than answered twice. Only an\nadmin of the org may call it, and the request is resolved in that org's own store,\nso another org's request id is not found.","tags":["dataroom"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the request to answer, taken from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustDecision"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustRefused"}}},"description":"ok"}},"x-app":"dataroom","x-public":true,"x-tool":true}},"/v1/dataroom/view/{linkId}":{"get":{"operationId":"get_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","x-public":true}},"/v1/dataroom/view/{linkId}/authenticate":{"post":{"operationId":"post_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","x-public":true}},"/v1/dataroom/view/{linkId}/document/{documentId}/file":{"get":{"operationId":"get_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","x-public":true}},"/v1/dataroom/view/{linkId}/pageview":{"post":{"operationId":"post_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","x-public":true}},"/v1/dataset":{"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":["dataset"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskDatasetList"}}},"description":"ok"}},"x-app":"dataset","x-public":true,"x-tool":true},"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":["dataset"],"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","x-public":true,"x-tool":true}},"/v1/dataset/{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":["dataset"],"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","x-public":true,"x-tool":true},"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":["dataset"],"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","x-public":true,"x-tool":true}},"/v1/dataset/{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":["dataset"],"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","x-public":true,"x-tool":true}},"/v1/dataset/{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":["dataset"],"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","x-public":true,"x-tool":true}},"/v1/dataset/{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":["dataset"],"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","x-public":true,"x-tool":true}},"/v1/deploy/applications":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/applications/{name}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/applications/{name}/resource-tree":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/applications/{name}/revisions/{revision}/metadata":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/applications/{name}/rollback":{"post":{"operationId":"post_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 client, 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","x-public":true}},"/v1/deploy/applications/{name}/sync":{"post":{"operationId":"post_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","x-public":true}},"/v1/deploy/applications/{name}/syncwindows":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/deploy/clusters":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/gitops":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/health":{"get":{"operationId":"get_deploy_health","summary":"Health reports whether this deployment can observe the delivery plane.","description":"Health reports whether this deployment can observe the delivery plane.\n\n200 only when the Kubernetes API answers AND the App custom resource is served;\n503 with the same shape otherwise, naming which half failed. It reports BOOLEANS\nand never the underlying error, because the route is unauthenticated — liveness\nmust be probe-able without a token — and a raw client error can disclose the\napiserver address or an RBAC detail. That detail is logged server-side instead.","tags":["deploy"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deployHealth"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deployHealth"}}},"description":"service unavailable"}},"x-app":"deploy","x-public":true,"x-tool":true}},"/v1/deploy/login":{"get":{"operationId":"get_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","x-public":true}},"/v1/deploy/logout":{"post":{"operationId":"post_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","x-public":true}},"/v1/deploy/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/reconcile":{"post":{"operationId":"post_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","x-public":true}},"/v1/deploy/session/userinfo":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/settings":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/deploy/stream/applications":{"get":{"operationId":"get_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","x-public":true}},"/v1/deploy/stream/applications/{name}/resource-tree":{"get":{"operationId":"get_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","x-public":true}},"/v1/deploy/version":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/destination":{"get":{"operationId":"get_destination","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":["destination"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationList"}}},"description":"ok"}},"x-app":"destination","x-public":true,"x-tool":true}},"/v1/destination/{platform}":{"delete":{"operationId":"delete_destination_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":["destination"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | insights | analytics.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationDisconnected"}}},"description":"ok"}},"x-app":"destination","x-public":true,"x-tool":true},"get":{"operationId":"get_destination_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":["destination"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | insights | analytics.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationStatus"}}},"description":"ok"}},"x-app":"destination","x-public":true,"x-tool":true},"post":{"operationId":"post_destination_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":["destination"],"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":"destination","x-public":true}},"/v1/destination/{platform}/test":{"post":{"operationId":"post_destination_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":["destination"],"parameters":[{"name":"platform","in":"path","required":true,"description":"Platform is the destination to act on, from the path: ga4 | meta | tiktok |\nlinkedin | x | reddit | insights | analytics.","schema":{"type":"string"},"example":"ga4"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/destinationTest"}}},"description":"ok"}},"x-app":"destination","x-public":true,"x-tool":true}},"/v1/domain/availability":{"get":{"operationId":"get_domain_availability","summary":"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.","description":"Checks exact names rather than searching for them, and answers the\nsame quote shape search does — purchasable, premium, first-term and renewal price\nin cents.\n\nIt requires a validated principal; 403 without one. Nothing is charged and\nnothing is held. A deployment with no registrar credentials answers 503.","tags":["domain"],"parameters":[{"name":"domain","in":"query","required":true,"description":"Domain is one name, or several comma-separated, to check in one call. Names\nare lowercased. It is required.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/quoteList"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/domains":{"get":{"operationId":"get_domain_domains","summary":"Is the domains your org has bought here, 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.","description":"Is the domains your org has bought here, newest registration first, each\ncarrying the name, when it was registered, when it expires, what the org paid,\nthe registrar order id and the nameservers it points at.\n\nScoped to the validated principal's org — 403 without one, and there is no\nparameter that reaches another org's holdings.\n\nThis is the deployment's OWN ownership record, not a query to the registrar: it\nlists what was bought THROUGH this surface, so a domain the org holds elsewhere\nis not here. The default store is in-process, so a deployment that has not\nswapped in a durable store answers from what this process registered.","tags":["domain"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/holdings"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/health":{"get":{"operationId":"get_domain_health","summary":"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.","description":"Reports registrar reachability honestly: ok only when the wholesale\ncredentials are present AND name.com accepted them on a live call made while you\nwaited.\n\nMissing credentials or an unreachable registrar is 503 carrying configured,\nreachable and the reason, so an operator reads the blocker instead of guessing at\nit. It takes no principal, like every subsystem health probe.","tags":["domain"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reachability"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/reachability"}}},"description":"service unavailable"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/register":{"post":{"operationId":"post_domain_register","summary":"Buys a domain for your org and answers the ownership record together with the quote it was bought at.","description":"Buys a domain for your org and answers the ownership record together\nwith the quote it was bought at.\n\nThe order of operations is the product guarantee: quote, refuse anything\nunpurchasable or unpriced, AUTHORIZE the org's prepaid balance, provision the\nauthoritative zone in Hanzo DNS, register at the registrar already pointing at\nHanzo's nameservers, and only then CAPTURE the charge and record ownership. A\nregistrar failure therefore leaves the balance untouched — the org is never\nbilled for a domain it did not get.\n\nIt requires a validated principal; that principal's org owns the domain and is\nthe ledger the charge lands on. Re-buying a name the org already holds is 409,\nnot a second purchase.\n\nRefusals are distinct on purpose: 402 when the prepaid balance cannot cover the\nquoted price, 409 when the name is not available, 503 when the deployment has no\nregistrar credentials, and the registrar's own message with its own 4xx — or 502\nfor its 5xx — when it rejects the purchase. Zone provisioning is best-effort: if\nthe zone service is down the domain is still registered against Hanzo's\nnameservers and the zone reconciles afterwards, rather than the purchase failing.","tags":["domain"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/order"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResult"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/renew":{"post":{"operationId":"post_domain_renew","summary":"Extends a domain your org already owns and answers the updated record with its new expiry alongside what was paid.","description":"Extends a domain your org already owns and answers the updated record with\nits new expiry alongside what was paid.\n\nOwnership is the gate: a name the caller's org does not hold is 404, so a renewal\ncan never reach another tenant's domain.\n\nThe price is re-quoted at the CURRENT renewal rate rather than the one paid at\npurchase. If the registrar returns no renewal price the org's original price is\ncharged instead, so a renewal is never accidentally free. The balance is\nauthorized before the registrar is called and captured after it confirms — 402\nwhen the prepaid balance cannot cover it, 503 when the deployment has no\nregistrar credentials. Requires a validated principal.","tags":["domain"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/renewReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenewResult"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/search":{"get":{"operationId":"get_domain_search","summary":"Finds names built from the keyword q, plus the registrar's 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.","description":"Finds names built from the keyword q, plus the registrar's alternate-TLD\nsuggestions, and answers a quote for each: the name, whether it is purchasable,\nwhether it is premium, the first-term and renewal price in cents, and the TLD.\n\nPrices are RETAIL — this deployment's markup is already applied and the wholesale\ncost is never on the wire.\n\nIt requires a validated principal; 403 without one. Nothing is charged and\nnothing is held — a quote is not a reservation, and the price is re-quoted at\npurchase, so a name quoted here can be gone or dearer by the time you buy it. A\ndeployment with no registrar credentials answers 503.","tags":["domain"],"parameters":[{"name":"q","in":"query","required":true,"description":"Q is the keyword to build names from. It is required.","schema":{"type":"string"}},{"name":"tld","in":"query","required":false,"description":"TLD narrows the search to a comma-separated set of top-level domains.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/quoteList"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/domain/transfer":{"post":{"operationId":"post_domain_transfer","summary":"Moves a domain you own at another registrar onto your org here, using its authCode, and answers the same record-plus-quote a purchase does.","description":"Moves a domain you own at another registrar onto your org here, using\nits authCode, and answers the same record-plus-quote a purchase does.\n\nIt is priced and charged exactly like a registration: authorize the org's prepaid\nbalance, ask the registrar for the transfer, capture only after the registrar\naccepts. A name the registrar will not price is 409, an insufficient balance is\n402, and a deployment with no registrar credentials is 503.\n\nIt requires a validated principal; the ownership record is written under that org\nas soon as the registrar ACCEPTS the request, which is not the same instant the\ntransfer completes at the losing registrar. Unlike a registration this does not\nprovision a zone, so the record carries this deployment's configured nameservers.","tags":["domain"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/transferReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResult"}}},"description":"ok"}},"x-app":"domain","x-public":true,"x-tool":true}},"/v1/embeddings":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/entitlement":{"get":{"operationId":"get_entitlement","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/entitlement/orgs/{org} (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":["entitlement"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectionView"}}},"description":"ok"}},"x-app":"entitlement","x-public":true,"x-tool":true}},"/v1/entitlement/orgs/{org}":{"get":{"operationId":"get_entitlement_orgs_by_org","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/entitlement, resolved from commerce).\n\nA caller may only read its OWN org's row; a platform super admin may read any.","tags":["entitlement"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/entitlementsView"}}},"description":"ok"}},"x-app":"entitlement","x-public":true,"x-tool":true},"post":{"operationId":"post_entitlement_orgs_by_org","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":["entitlement"],"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":"entitlement","x-public":true,"x-tool":true}},"/v1/esign/documents":{"get":{"operationId":"get_esign_documents","summary":"Returns your org's documents, newest first.","description":"Returns your org's documents, newest first.\n\nEach carries its status, recipients and field layout. The listing is capped at\n200 and there is no paging, so treat it as the recent window rather than a\ncomplete export. It reads the caller's own tenant store, so no other org's\ndocuments can appear in it.","tags":["esign"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignDocuments"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true},"post":{"operationId":"post_esign_documents","summary":"Uploads a PDF and opens a draft ready for recipients and fields.","description":"Uploads a PDF and opens a draft ready for recipients and fields.\n\nIt answers 201 with the document in DRAFT — the state where recipients and\nfields may still be added, and the only state they may. The bytes go to object\nstorage rather than into the tenant database, and the original is kept under its\nown key so it survives sealing untouched: a completed document can always be\ncompared against what was uploaded. Creation is recorded on the audit trail.\n\nThis is the sender's surface: a validated principal is required, and the document\nlands in that principal's OWN org. Isolation is physical rather than a filter —\neach tenant has its own store — so another org's document id is simply not\nthere. A body over 32 MiB is refused with 413.","tags":["esign"],"requestBody":{"content":{"application/json":{"example":{"pdfBase64":"JVBERi0xLjQK…","signingOrder":"SEQUENTIAL","title":"Mutual NDA"},"schema":{"$ref":"#/components/schemas/esignUploadIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignDocument"}}},"description":"created"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}":{"get":{"operationId":"get_esign_documents_by_id","summary":"Returns one document with its recipients and field layout.","description":"Returns one document with its recipients and field layout.\n\nIt answers the document, its recipients with each one's read and signing status,\nand every field with its type, page and position — the view a sender's UI\nrenders, and where the field ids come from. The id is resolved in the caller's\nOWN tenant store, so another org's document id is a 404 rather than a refusal\nthat would confirm it exists.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to act on. 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 belonging to another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignDocument"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}/audit":{"get":{"operationId":"get_esign_documents_by_id_audit","summary":"Returns the document's full audit trail, oldest first.","description":"Returns the document's full audit trail, oldest first.\n\nIt answers every recorded event for the document in order — created, recipient\nadded, field created, sent, opened, each field inserted, each recipient\ncompleted or rejected, and completion — with the actor and timestamp on each.\nThis is the evidence record behind a signature, so it is append-only and nothing\nin the surface edits it.\n\nThe id is resolved in the caller's OWN tenant store, so another org's document\nid is a 404.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to act on. 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 belonging to another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignTrail"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}/download":{"get":{"operationId":"get_esign_documents_by_id_download","summary":"Returns the document — the sealed PDF once it is complete.","description":"Returns the document — the sealed PDF once it is complete.\n\nIt answers the document's current PDF as base64 with a sealed flag and a\nfilename. Before completion that is the original upload; once every signer has\nfinished it is the SEALED artifact, with the field values rendered onto the page\nand a real x509 PKCS#7 digital signature applied. There is one pdfBase64 field\neither way, so sealed is what tells you which you are holding.\n\nThe id is resolved in the caller's OWN tenant store, so another org's document\nid is a 404.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to act on. 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 belonging to another tenant is simply not found.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignPDF"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}/fields":{"post":{"operationId":"post_esign_documents_by_id_fields","summary":"Places a field on the page for one recipient to fill.","description":"Places a field on the page for one recipient to fill.\n\nIt adds a signature, date, name, email or text box at a page and position for\nONE named recipient, and answers 201 with its id. The recipient must belong to\nthis document; one from elsewhere is refused.\n\nFields are what make a recipient signable: a document cannot be sent while any\nsigning recipient has none. Only while DRAFT — adding a field to a sent document\nis a 409 — and an unknown document is a 404. The addition is recorded on the\naudit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"page":1,"positionX":72,"positionY":640,"recipientId":"rec_2f…","type":"SIGNATURE"},"schema":{"$ref":"#/components/schemas/esignFieldIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignPlacement"}}},"description":"created"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}/recipients":{"post":{"operationId":"post_esign_documents_by_id_recipients","summary":"Adds someone to a draft and mints their signing token.","description":"Adds someone to a draft and mints their signing token.\n\nIt answers 201 with the recipient's id and their signing TOKEN — the\ncrypto-random capability that is the only credential the signer's surface\naccepts — so this response is where the signing link is built from. A CC\nrecipient is recorded as already complete, because they are never asked to\nsign.\n\nOnly while DRAFT: adding a recipient to a document already sent is a 409,\nbecause the field layout and the turn order were fixed when it went out. An\nunknown document is a 404. The addition is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"email":"[email protected]","name":"Dana Lee","role":"SIGNER"},"schema":{"$ref":"#/components/schemas/esignRecipientIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignInvite"}}},"description":"created"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/documents/{id}/send":{"post":{"operationId":"post_esign_documents_by_id_send","summary":"Sends the document out and answers each signer's link.","description":"Sends the document out and answers each signer's link.\n\nIt moves the document from DRAFT to PENDING and answers the signing tokens — one\nper signing recipient, with the path to hand them — which is how the links reach\nthe people who must sign. Nothing is emailed by this call; delivering the links\nis the caller's.\n\nIt refuses to send an unsignable document: no recipients at all is a 400, and so\nis any signing recipient with no fields to fill, named in the error. Re-sending\nan already-pending document is allowed and re-issues the same links rather than\nrestarting anything; a completed document is a 409, and an unknown one a 404.\nThe send is recorded on the audit trail.","tags":["esign"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the document to send. The URL is the addressing authority.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignLinks"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/health":{"get":{"operationId":"get_esign_health","summary":"Reports whether the e-signature surface is mounted.","description":"Reports whether the e-signature surface is mounted.\n\nIt answers ok whenever the subsystem is mounted, takes no tenant and needs no\nprincipal. It is deliberately shallow: it is registered before the document host\nis built, so it still answers on a deployment that came up WITHOUT object\nstorage and therefore serves nothing else. Read it as reachability, never as a\npromise that documents can be stored.","tags":["esign"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignHealth"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/o/{org}/sign/{token}":{"get":{"operationId":"get_esign_o_by_org_sign_by_token","summary":"Opens a document you were asked to sign, using your signing link.","description":"Opens a document you were asked to sign, using your signing link.\n\nIt answers the document, the recipient the link identifies, the fields THAT\nrecipient must fill, and the PDF to display. The first open also marks the\nrecipient as having opened it and records that on the audit trail, so this read\nhas a side effect by design.\n\nThis surface takes NO account: the signing token is the entire credential, and\nit names the recipient, so a signer sees only their own fields and never the\nother recipients' tokens. The token resolves to its owning tenant FIRST, before\nany per-tenant store is opened, and the org segment is only checked against\nthat answer. An unknown or wrong-org token is one and the same 404, never a\nhint 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"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignSession"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/o/{org}/sign/{token}/complete":{"post":{"operationId":"post_esign_o_by_org_sign_by_token_complete","summary":"Finishes your signing — and seals the document if you were the last.","description":"Finishes your signing — and seals the document if you were the\nlast.\n\nIt marks this recipient as done and answers whether the DOCUMENT sealed with it.\nWhen every signing recipient has completed, sealing happens right here in the\nsame call: the collected values are rendered onto the PDF, a real x509 PKCS#7\nsignature is applied, the sealed bytes are stored beside the untouched original,\nand the document moves to COMPLETED. Until then the answer is the recipient's\nown completion with the document still pending.\n\nIt refuses to complete a half-filled signature: a recipient with any unfilled\nfield is a 400 naming how many remain. A document not out for signature is a\n409, as is a recipient who has already completed, and under SEQUENTIAL order a\nsigner out of turn is a 403. The token is the whole credential — no account, and\na token that does not resolve under the org segment is a 404. Sealing and\ncompletion are one transaction, so a failure anywhere leaves the document\nexactly as it was.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignCompletion"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/o/{org}/sign/{token}/fields/{fieldId}":{"post":{"operationId":"post_esign_o_by_org_sign_by_token_fields_by_fieldid","summary":"Fills in one of your fields.","description":"Fills in one of your fields.\n\nIt records a value for one field and marks it inserted. A signature field takes\na value with isBase64 true for drawn image bytes, or false for a typed\nsignature; a date, name or email field falls back to today, the recipient's name\nor their email when the value is omitted; any other type requires one.\n\nNothing is sealed here — filling every field still leaves the document pending\nuntil the completion call. The token is the whole credential and it bounds what\ncan be written: a field belonging to another recipient is refused with 401 even\nunder a valid token, an unknown field is a 404, and a field already filled is a\n409. A document not out for signature is a 409, as is a recipient who has\nalready completed or rejected. Under SEQUENTIAL order a signer whose turn has\nnot come is refused 403 until every earlier signer has signed. Each insertion is\nrecorded 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"}}],"requestBody":{"content":{"application/json":{"example":{"isBase64":false,"value":"Dana Lee"},"schema":{"$ref":"#/components/schemas/esignValueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignInsertion"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/esign/o/{org}/sign/{token}/reject":{"post":{"operationId":"post_esign_o_by_org_sign_by_token_reject","summary":"Declines to sign, with an optional reason.","description":"Declines to sign, with an optional reason.\n\nIt records this recipient's refusal and moves the WHOLE DOCUMENT to REJECTED —\none declining signer ends it for everyone, and there is no route back: the\ndocument cannot then be signed or completed. An optional reason is stored and\nwritten 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\nsigned or already rejected — a refusal cannot be taken back or repeated. The\ntoken is the whole credential; one that does not resolve under the org segment\nis a 404.","tags":["esign"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"reason":"Terms changed since we agreed them."},"schema":{"$ref":"#/components/schemas/esignRejectIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/esignRejection"}}},"description":"ok"}},"x-app":"esign","x-public":true,"x-tool":true}},"/v1/eval/datasets":{"get":{"operationId":"get_eval_datasets","summary":"Is the datasets your org has, each with its name, description, metadata and timestamps.","description":"Is the datasets your org has, each with its name, description,\nmetadata and timestamps.\n\nIt is the only way to enumerate what an org holds. Requires a validated\nprincipal; 403 without one. Every row is filtered on the validated org, so\nthere is no parameter that reaches another tenant's datasets. The item count is\nNOT populated here — read one dataset to get it.","tags":["eval"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. It defaults to 100 and is capped at 500; a\nnon-positive or unparseable value falls back to the default rather than\nfailing, because a typo about paging is not a reason to refuse a read.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/datasetList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_datasets","summary":"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.","description":"Writes a dataset — the named set of graded examples a run scores\na model against — under the caller's org and answers 201 with it.\n\nThe NAME is the key, not an id: posting a name the org already has updates that\ndataset's description and metadata and keeps its original creation time, so this\nis create-or-edit and never a duplicate. Its items are untouched.\n\nRequires a validated principal; 403 without one. The org comes from the\nvalidated owner claim, never from a client X-Org-Id, so a dataset can only ever\nbe written under the caller's own tenant. A description over 64 KiB is 400.","tags":["eval"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/datasetReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/datasetView"}}},"description":"created"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/datasets/{name}":{"delete":{"operationId":"delete_eval_datasets_by_name","summary":"Removes the named dataset of the caller's org AND all of its examples, in one transaction.","description":"Removes the named dataset of the caller's org AND all of its\nexamples, in one transaction.\n\nThis is not a detach: the examples are gone with the set, so a dataset cannot\nbe resurrected by re-creating the name. A name this org does not have is 404 —\nnever a silent success — and a name belonging to another tenant is the same\n404, because the delete is predicated on the validated org. Requires a\nvalidated principal; 403 without one. Runs and scores already recorded against\nthe dataset are telemetry events and are NOT deleted with it.","tags":["eval"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/none"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"get":{"operationId":"get_eval_datasets_by_name","summary":"Returns one 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.","description":"Returns one dataset of the caller's org by name, together with its\nlive item count — the one read that answers how big the set actually is.\n\nA name this org does not have is 404, which is also what another tenant's\ndataset looks like from here. Requires a validated principal; 403 without one.","tags":["eval"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the dataset the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/datasetView"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/datasets/{name}/items":{"get":{"operationId":"get_eval_datasets_by_name_items","summary":"Is the examples in one of your datasets — the set is named in the path, because this collection only exists inside one.","description":"Is the examples in one of your datasets — the set is named in the\npath, because this collection only exists inside one.\n\nArchived examples are included, so the caller sees the whole set rather than\nonly what a run would use. Requires a validated principal; 403 without one, and\nthe read is filtered on the validated org, so naming another tenant's dataset\nreturns nothing rather than its contents.","tags":["eval"],"parameters":[{"name":"name","in":"path","required":true,"description":"Dataset is the set to read, from the path — this collection only exists\ninside one.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/itemList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_datasets_by_name_items","summary":"Writes one graded example — its input, its expected output, free-form metadata and a status — into the dataset named in the path, and answers 201 with it.","description":"Writes one graded example — its input, its expected output,\nfree-form metadata and a status — into the dataset named in the path, and\nanswers 201 with it.\n\nThat dataset MUST already exist for this org: an unknown one is 404, never a\nsilent create. Requires a validated principal; 403 without one.","tags":["eval"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/itemReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/itemView"}}},"description":"created"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/evaluators":{"get":{"operationId":"get_eval_evaluators","summary":"Is the judges your org has defined, each with its judge model, criteria and the score name it writes under.","description":"Is the judges your org has defined, each with its judge model,\ncriteria and the score name it writes under.\n\nRequires a validated principal; 403 without one, and the listing is filtered on\nthe validated org.","tags":["eval"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. It defaults to 100 and is capped at 500; a\nnon-positive or unparseable value falls back to the default rather than\nfailing, because a typo about paging is not a reason to refuse a read.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/evaluatorList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_evaluators","summary":"Saves a reusable judge for the caller's org — the judge model and the written criteria it grades against — and answers 201 with it.","description":"Saves a reusable judge for the caller's org — the judge model\nand the written criteria it grades against — and answers 201 with it.\n\nLike a dataset, the NAME is the key: re-posting a name edits that judge rather\nthan adding a second one. Requires a validated principal; 403 without one.","tags":["eval"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/evaluatorReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/evaluatorView"}}},"description":"created"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/metrics":{"get":{"operationId":"get_eval_metrics","summary":"Is your org's AI overview board 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.","description":"Is your org's AI overview board over a window: totals\n(generations, prompt and completion tokens, cost in cents, errors, success\nrate, distinct models and users), a gap-filled time series, a per-model\nbreakdown with the long tail folded into \"other\", and latency percentiles read\nfrom the GenAI spans.\n\nThe window the answer was actually computed over is echoed back, so a client\nnever has to infer it. A platform admin sees the board across ALL orgs;\neveryone else sees their own.\n\nThe board is HONEST-EMPTY where it cannot be computed: with no datastore wired,\nor under a named project scope the usage ledger does not yet carry, it answers a\nvalid board with zero totals and a flat series rather than a fabricated number\nor a 500. Requires a validated principal; 403 without one.","tags":["eval"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is 24h (the default), 7d or 30d. Anything else normalises to 24h\nrather than failing, so the board always has a valid window.","schema":{"type":"string"}},{"name":"interval","in":"query","required":false,"description":"Interval overrides the bucket the series is grouped into: \"hour\" or \"day\".\nAny other value leaves the range's own default in place.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Board"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/rubrics":{"get":{"operationId":"get_eval_rubrics","summary":"Is the score shapes your org has declared — each name's data type, its numeric bounds and its allowed categories.","description":"Is the score shapes your org has declared — each name's data\ntype, its numeric bounds and its allowed categories.\n\nRequires a validated principal; 403 without one, and the listing is filtered on\nthe validated org.","tags":["eval"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned. It defaults to 100 and is capped at 500; a\nnon-positive or unparseable value falls back to the default rather than\nfailing, because a typo about paging is not a reason to refuse a read.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreConfigList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_rubrics","summary":"Defines the shape of one score name for the caller's org and answers 201 with it.","description":"Defines the shape of one score name for the caller's org and\nanswers 201 with it.\n\nThis is the integrity contract, not documentation: once a rubric exists for a\nname, every score recorded under that name is checked against it and the\nrubric's data type is AUTHORITATIVE — a caller cannot claim a different one.\nOut-of-range values, unlisted labels and non-finite numbers are refused at\nwrite time.\n\nA CATEGORICAL rubric with no categories is 400, as is a non-finite bound or a\nminValue above maxValue. Requires a validated principal; 403 without one.","tags":["eval"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreConfigReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreConfigView"}}},"description":"created"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/runs":{"get":{"operationId":"get_eval_runs","summary":"Is your past runs and how they scored — the dataset and model, the judge model, how many examples were attempted and how many scored, the average score, and when it happened.","description":"Is your past runs and how they scored — the dataset and model, the\njudge model, how many examples were attempted and how many scored, the average\nscore, and when it happened.\n\nRequires a validated principal; 403 without one, and rows are filtered on the\nvalidated org. These records come from the metastore rather than the datastore,\nso they are readable on a deployment with no telemetry wired — but a run's\ntraces and scores are not.","tags":["eval"],"parameters":[{"name":"datasetName","in":"query","required":false,"description":"Dataset narrows to the runs against one dataset.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runs"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_runs","summary":"Runs a real evaluation and answers the summary when it is finished — this is synchronous work, not a job id.","description":"Runs a real evaluation and answers the summary when it is finished —\nthis is synchronous work, not a job id.\n\nFor each ACTIVE example in the dataset it calls the model under test, records a\ntrace, calls the LLM-as-judge, and records the judge's score with its\nreasoning. The answer carries the per-item results (item id, trace id, score,\noutput or error) alongside items, scored and avgScore.\n\nThe dataset must belong to the caller's org (404 otherwise) and must have at\nleast one ACTIVE example (422 otherwise).\n\nIt runs as YOU: the caller's own Authorization bearer drives the model gateway,\nso a request without one is 401 rather than a run made anonymously or under a\nservice identity. Only a non-reversible hash of that credential is recorded on\nthe traces.\n\nBounded and honest about it: an org may have at most 4 runs in flight and the\nfifth is 429 rather than queued, and the whole run is capped at 10 minutes —\nexamples past the deadline come back with an error instead of a score, and\nscored counts only real successes. A run where NOTHING scored answers 502, not\na 200 that looks like an evaluation. A run must be able to persist what it\nproduces, so a deployment with no datastore wired is 503 up front. Requires a\nvalidated principal; 403 without one.","tags":["eval"],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runSummary"}}},"description":"ok"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/runSummary"}}},"description":"bad gateway"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/scores":{"get":{"operationId":"get_eval_scores","summary":"Is the score events your org has recorded, narrowed by any of name, runName and traceId.","description":"Is the score events your org has recorded, narrowed by any of name,\nrunName and traceId.\n\nThe org is bound as an authoritative predicate on the query, never taken from a\nheader, so a filter can narrow the caller's own scores but can never widen past\nthem. Requires a validated principal; 403 without one. Scores live in the\ndatastore, so a deployment with none wired answers 503 rather than an empty\npage that would read as \"no scores\".","tags":["eval"],"parameters":[{"name":"name","in":"query","required":false,"description":"Name narrows to one score name.","schema":{"type":"string"}},{"name":"runName","in":"query","required":false,"description":"RunName narrows to the scores of one run.","schema":{"type":"string"}},{"name":"traceId","in":"query","required":false,"description":"TraceID narrows to the scores on one model call.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true},"post":{"operationId":"post_eval_scores","summary":"Files one score event for the caller's org and answers 201 with it.","description":"Files one score event for the caller's org and answers 201 with it.\n\nThis is how human review and out-of-band graders land beside the automatic\nones: name the score, give it a value (or a stringValue for a categorical\nlabel), and attach it to a trace, a run, a dataset example, or any combination.\n\nScores are validated fail-closed. A value must be FINITE — NaN and Inf are 400\n— and if the org has declared a rubric for this name, that rubric decides the\ntype and the value must satisfy it: inside the numeric bounds, or one of the\nallowed categories. A caller cannot override the declared type by sending a\ndifferent dataType.\n\nA score is TELEMETRY, not metadata, so it needs the datastore: a deployment\nwith none wired answers 503 rather than accepting a score it cannot persist.\nRequires a validated principal; 403 without one, and the org is stamped from\nthe validated claim rather than read off the body.","tags":["eval"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scoreView"}}},"description":"created"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/eval/traces":{"get":{"operationId":"get_eval_traces","summary":"Is the traces behind your evaluations — one per model call an evaluation made, carrying its input, output, model and timing — narrowed by any of sessionId, runName and datasetName.","description":"Is the traces behind your evaluations — one per model call an\nevaluation made, carrying its input, output, model and timing — narrowed by any\nof sessionId, runName and datasetName.\n\nScoped by org AND by project: the project is the caller's server-minted scope,\nnot a parameter, so it cannot be widened by asking. Requires a validated\nprincipal; 403 without one. Traces live in the datastore, so a deployment with\nnone wired answers 503 rather than an empty page.","tags":["eval"],"parameters":[{"name":"sessionId","in":"query","required":false,"description":"SessionID narrows to one session, which for an evaluation is one run.","schema":{"type":"string"}},{"name":"runName","in":"query","required":false,"description":"RunName narrows to the calls one run made.","schema":{"type":"string"}},{"name":"datasetName","in":"query","required":false,"description":"Dataset narrows to the calls made against one dataset.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/traceList"}}},"description":"ok"}},"x-app":"eval","x-public":true,"x-tool":true}},"/v1/event":{"post":{"operationId":"post_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 endpoint 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 endpoint 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/tag.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":"event","x-public":true}},"/v1/event/errors":{"get":{"operationId":"get_event_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":["event"],"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":"event","x-public":true,"x-tool":true}},"/v1/event/health":{"get":{"operationId":"get_event_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":["event"],"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":"event","x-public":true,"x-tool":true}},"/v1/event/insights/events":{"get":{"operationId":"get_event_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 endpoints\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":["event"],"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":"event","x-public":true,"x-tool":true}},"/v1/event/insights/health":{"get":{"operationId":"get_event_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/event/health.","tags":["event"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/insightsStatus"}}},"description":"ok"}},"x-app":"event","x-public":true,"x-tool":true}},"/v1/event/overview":{"get":{"operationId":"get_event_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":["event"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: a count and a unit — 24h, 7d, 90d, any <N>h or\n<N>d — or day, week, month, all. Default 24h. Ignored when both start and\nend are given. An unknown value, or one past the 730-day horizon, 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":"event","x-public":true,"x-tool":true}},"/v1/event/replay":{"post":{"operationId":"post_event_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":["event"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/replayBody"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptureResult"}}},"description":"Success"}},"x-app":"event","x-public":true}},"/v1/event/tag.js":{"get":{"operationId":"get_event_tag.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 <script defer src=\"https://api.hanzo.ai/v1/event/tag.js\" data-key=\"pk-…\"></script>\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.","tags":["event"],"responses":{"2XX":{"content":{"application/javascript":{"schema":{"format":"binary","type":"string"}}},"description":"Success"}},"x-app":"event","x-public":true}},"/v1/event/timeseries":{"get":{"operationId":"get_event_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":["event"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: a count and a unit — 24h, 7d, 90d, any <N>h or\n<N>d — or day, week, month, all. Default 24h. Ignored when both start and\nend are given. An unknown value, or one past the 730-day horizon, 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":"event","x-public":true,"x-tool":true}},"/v1/event/top":{"get":{"operationId":"get_event_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":["event"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is a relative window: a count and a unit — 24h, 7d, 90d, any <N>h or\n<N>d — or day, week, month, all. Default 24h. Ignored when both start and\nend are given. An unknown value, or one past the 730-day horizon, 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":"event","x-public":true,"x-tool":true}},"/v1/event/{project}/envelope":{"post":{"operationId":"post_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 endpoint 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 todo 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":"event","x-public":true}},"/v1/event/{project}/store":{"post":{"operationId":"post_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 handler, 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 endpoint 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 todo 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":"event","x-public":true}},"/v1/exec":{"post":{"operationId":"post_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/exec/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/exec/download/{session}/{name}.\n\nThe tenant is the caller's, never the body's, at every entry point. A typed op is\nalso an 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 entry points fail closed without a second\ngate to keep 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","x-public":true,"x-tool":true}},"/v1/exec/files/{sid}":{"get":{"operationId":"get_exec_files_by_sid","summary":"Files lists what a session holds.","description":"Files lists what a session holds.\n\nOne recursive `find`, the same traversal the artifact sweep makes. It used to be\n`ls -1A` — top level only — while the sweep collected with `find`, so a run that\nwrote a nested artifact reported it in its reply and then omitted it here, and\nthe client's prefix match read the file as expired. Two traversals of one\ndirectory is two answers about what a session holds; there is one now.","tags":["exec"],"parameters":[{"name":"sid","in":"path","required":true,"description":"SID is the session identifier — the sandbox this listing is of. The URL is\nthe addressing authority: a path segment binds after the body and after the\nquery, so the address decides which session is read whatever else is sent.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/listing"},"type":"array"}}},"description":"ok"}},"x-app":"exec","x-public":true,"x-tool":true}},"/v1/exec/programmatic":{"post":{"operationId":"post_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","x-public":true}},"/v1/exec/upload":{"post":{"operationId":"post_exec_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":["exec"],"x-app":"exec","x-public":true}},"/v1/experiment":{"get":{"operationId":"get_experiment","summary":"Is every experiment in the caller's org, with its variants, status and decision, ordered by project then id.","description":"Is every experiment in the caller's org, with its variants, status and\ndecision, ordered by project then id.\n\nScoped to the org resolved from the validated principal — a distinct org is a\ndistinct physical store, so no query here can reach another tenant's rows — and\nfurther narrowed to the caller's project scope when the credential carries one.\nA principal with NO project scope sees the org's experiments across all of its\nprojects, which is the answer a reader most often expects to be filtered and is\nnot.\n\nRequires a validated principal; refuses without one rather than answering an\nempty list.","tags":["experiment"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/experimentList"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true},"post":{"operationId":"post_experiment","summary":"Registers a controlled experiment AND puts its assignment flag live, 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.","description":"Registers a controlled experiment AND puts its assignment flag live, in\nthat order, so the arms start bucketing subjects the moment this returns 201 —\nthe flag is created active at 100% rollout, with each variant weighted as\ndeclared. There is no separate start call; creating IS starting.\n\nA variant carries an opaque payload this primitive never interprets: a feature\nconfig, an ad-creative id, a subject line, a model id.\n\nRequires a validated principal, and refuses without one. The org and project are\ntaken from that principal and the creator is stamped from the credential — none\nof the three is a body field, so an experiment cannot be filed against another\ntenant. An id already used in this project is a conflict, never a silent\noverwrite: 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\nexist would assign nobody, so if that write fails nothing is registered.","tags":["experiment"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/createBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trial"}}},"description":"created"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/experiment/health":{"get":{"operationId":"get_experiment_health","summary":"Is whether the experiments subsystem is mounted and serving in this process.","description":"Is whether the experiments subsystem is mounted and serving in this\nprocess.\n\nIt answers unconditionally. It proves exactly one thing — that this binary\nregistered the experiments routes and is dispatching them — and deliberately no\nmore: it reads no principal, opens no per-org registry, and touches neither the\nflags engine nor the analytics plane, so a 200 here says nothing about whether a\ngiven tenant's store will open or whether an analysis can run. It is the only\nroute on this surface that needs no org.\n\nThe static path is registered ahead of the /:id read, so it always wins the\nfirst-match scan. \"health\" is a legal experiment id, which means an experiment\ncreated under that id can never be fetched by id — pick another.","tags":["experiment"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/health"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/experiment/{id}":{"get":{"operationId":"get_experiment_by_id","summary":"Is one experiment's definition and lifecycle: variants, weights, control arm, status and winner.","description":"Is one experiment's definition and lifecycle: variants, weights, control\narm, status and winner.\n\nIt reads the registry row only — the definition and the decision, never live\nmeasurements. Assignment lives in the flags plane and outcomes in analytics;\nthis is the value that names both.\n\nScoped to the caller's org and project from the validated principal, so another\ntenant's experiment of the same id is simply not found. An id that is not a\nlegal slug is answered the same way, without a store read — the shape check and\nthe existence check are one answer, so neither leaks the other.","tags":["experiment"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the experiment the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trial"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/experiment/{id}/analyze":{"post":{"operationId":"post_experiment_by_id_analyze","summary":"Is per-variant conversion, lift and statistical significance against the control arm.","description":"Is per-variant conversion, lift and statistical significance against\nthe control arm.\n\nIt reads per-subject outcomes from the analytics plane over a window, folds them\ninto per-variant samples, and returns each arm's exposed count, conversions,\nrate, lift versus control, two-proportion z, two-tailed p-value and whether it\nclears alpha. Arms with no data still appear with zero exposed, so the read is\ncomplete over the experiment's declared arms; the control arm sorts first. The\npooled-variance estimator is used and the p-value is exact; a degenerate\ncomparison (an empty arm, no variance) answers z 0 and p 1 — not significant,\nnever an error.\n\nOnly EXPOSED subjects are counted, and each is joined to its arm by re-evaluating\nthe assignment flag AT ANALYSIS TIME — not from what was in force during the\nwindow. That is the one rule to get right: analyzing an experiment after its\nwinner has been promoted re-buckets every subject into the promoted arm,\ncollapsing the control to zero exposed and making the result meaningless. Read\nthe analysis before deciding. A subject the flag cannot place is dropped rather\nthan allowed to poison the fold.\n\nThe winner in the response is ADVISORY — the significant, control-beating arm\nwith the highest rate, or empty when inconclusive. It promotes nothing; the\ndecision is a separate, explicit act.\n\nEvery plane read is scoped to the caller's org. Per-variant samples are also\nwritten to the research evidence plane as immutable ab rows, best-effort: the\nanalysis is still returned if that write fails, because the samples are\nrecomputable, and the failure is logged rather than swallowed.","tags":["experiment"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the experiment the URL names.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/analyzeQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Analysis"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/experiment/{id}/assign":{"get":{"operationId":"get_experiment_by_id_assign","summary":"Is the variant one subject is bucketed into, and the payload that variant carries.","description":"Is the variant one subject is bucketed into, and the payload that\nvariant carries.\n\nThe bucketing is a deterministic hash of the subject, so the same subject gets\nthe same arm on every call for as long as the flag definition is unchanged —\nand this is a pure READ: it records nothing. In particular it does NOT record an\nexposure. The caller's SDK must emit the experiment's exposure event itself, or\nthe analysis has an empty denominator and every arm measures zero.\n\nAn empty variant with on false is not an error — it means the flag returned\nnothing for this subject, so the subject is not enrolled. A flags engine that is\nunavailable refuses rather than defaulting to an arm. Requires a validated\nprincipal, and the experiment must exist in the caller's org and project.","tags":["experiment"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the experiment the URL names.","schema":{"type":"string"}},{"name":"subject","in":"query","required":true,"description":"Subject is the unit to bucket — a user, org, session or audience key,\nmatching the experiment's subjectKind.","schema":{"type":"string"}},{"name":"props","in":"query","required":false,"description":"Props is a JSON object of person properties for targeting. A value that is\nnot valid JSON is dropped rather than refused, so a malformed one changes\nthe bucketing without saying so.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/assignment"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/experiment/{id}/decide":{"post":{"operationId":"post_experiment_by_id_decide","summary":"Promotes one variant to the whole rollout and records who decided.","description":"Promotes one variant to the whole rollout and records who decided.\n\nIt rewrites the assignment flag so the named winner serves 100% of the rollout\nand every other arm 0%, preserving the flag's targeting groups and payloads,\nthen stamps the experiment decided with the winner, the deciding credential and\nthe time. This is a production behaviour change that takes effect immediately\nfor every subject the flag evaluates.\n\nIt requires an ORG ADMIN of the caller's own org — a stricter gate than the rest\nof this surface, matching the flags write plane, because promoting is a flag\nwrite. The admin check runs AFTER the experiment is found, so a caller from\nanother tenant is answered not-found rather than forbidden and learns nothing\nabout what exists.\n\nAn experiment whose assignment flag has gone missing is a conflict rather than a\nsilent no-op — there is nothing to promote.\n\nDeciding is NOT terminal. A second call re-promotes a different variant and\nre-stamps the row; the status stays decided and the previous winner is\noverwritten with no record that it was ever chosen. Nothing here reverts the\nflag to its original weights either, so an experiment cannot be un-decided\nthrough this route — restoring a split means writing the flag definition back\nthrough the flags plane.","tags":["experiment"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/decideBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trial"}}},"description":"ok"}},"x-app":"experiment","x-public":true,"x-tool":true}},"/v1/explorer/indexers":{"get":{"operationId":"get_explorer_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":["explorer"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexersOut"}}},"description":"ok"}},"x-app":"explorer","x-public":true,"x-tool":true}},"/v1/explorer/oracles":{"get":{"operationId":"get_explorer_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":["explorer"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/oraclesOut"}}},"description":"ok"}},"x-app":"explorer","x-public":true,"x-tool":true}},"/v1/flags":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/flags/activity":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/flags/decide":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/flags/defs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/flags/defs/{key}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/flags/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/flow/runs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/flow/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/flow/workflows":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/flow/workflows/{workflow}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/framework/doctypes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/framework/doctypes/{name}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/framework/modules":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/framework/modules/{module}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/framework/modules/{module}/install":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/framework/roles":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/framework/roles/{user}/{role}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/framework/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/framework/{doctype}":{"get":{"operationId":"get_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 \"<field> [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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/framework/{doctype}/{name}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true}},"/v1/framework/{doctype}/{name}/cancel":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/framework/{doctype}/{name}/submit":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/functions":{"get":{"operationId":"get_functions","summary":"Is every serverless function the caller's org has published, each with its real 7-day rollup.","description":"Is every serverless function the caller's org has published, each with its\nreal 7-day rollup.\n\nA row carries the function's runtime, resource limits, deployment target and its\ninvoke endpoint, plus envCount — how many secrets it mounts. The rollup fields\nare ABSENT rather than zero when the function has not run in the window, so a\nconsole renders \"—\" instead of a fabricated 0.\n\nRequires a validated principal; the listing is scoped to its org.","tags":["functions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/fnList"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true},"post":{"operationId":"post_functions","summary":"Publishes a serverless function under the caller's org and answers 201 with it.","description":"Publishes a serverless function under the caller's org and answers 201\nwith it.\n\nThe name is the key and is claimed once; the names that would shadow a\ncollection route are reserved. runtime and environment are the same field —\neither spelling is accepted — and default to node.\n\nBounds are clamped rather than refused where a clamp is honest: a timeout above\nthe 900-second ceiling becomes the ceiling instead of silently reverting to the\n30-second default, and an omitted memory limit becomes 256Mi. target=fleet runs\non the org's own GPU fleet and supports runtime=python only.\n\nRequires a validated principal; the function is owned by that principal's org.","tags":["functions"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/definition"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/functionView"}}},"description":"created"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/deployments":{"get":{"operationId":"get_functions_deployments","summary":"Is what is live right now — each function's current record IS its live deployment, so this is the deployment inventory.","description":"Is what is live right now — each function's current record IS its\nlive deployment, so this is the deployment inventory.\n\nThere is no deployment history behind it: a function has one record, and\npublishing replaces it. The 7-day rollup is deliberately absent here, because\nthis read is about what is deployed rather than about how it has performed.","tags":["functions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/fnList"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/metrics":{"get":{"operationId":"get_functions_metrics","summary":"Is the org's serverless dashboard over a window: a per-function invocation costLine and how those invocations ended.","description":"Is the org's serverless dashboard over a window: a per-function\ninvocation costLine and how those invocations ended.\n\nEvery point is a REAL count of rows that fell in that bucket — nothing is\ninterpolated or invented, so an empty window draws a flat line rather than a\nfabricated one.\n\ncostCents is null and stays null: there is no per-invocation cost source to read,\nand reporting a number computed some other way would be a guess presented as a\nmeasurement. Requires a validated principal; the read is scoped to its org.","tags":["functions"],"parameters":[{"name":"range","in":"query","required":false,"description":"Range is 1H, 6H, 24H (the default), 7D or 30D. Anything else falls back to\n24H rather than failing.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/usage"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/secrets":{"get":{"operationId":"get_functions_secrets","summary":"Is the NAMES of the secrets the caller org's functions mount.","description":"Is the NAMES of the secrets the caller org's functions mount.\n\nValues are NEVER read or returned — this surface knows which names a function\nasks for and nothing about what is behind them, which is what makes it safe to\nlist at all. One row per distinct (namespace, name).","tags":["functions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/secretList"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/triggers":{"get":{"operationId":"get_functions_triggers","summary":"Is what calls the caller org's functions — one row per function.","description":"Is what calls the caller org's functions — one row per function.\n\nEvery function has exactly one trigger today, its HTTP invoke endpoint, so this\nis the function list read as \"how is each of these reached\".","tags":["functions"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/triggerList"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/{name}":{"delete":{"operationId":"delete_functions_by_name","summary":"Removes one of the caller org's functions and answers 204.","description":"Removes one of the caller org's functions and answers 204.\n\nA name this org does not hold is 404 — never a silent success — and a name\nbelonging to another tenant is the same 404, because the delete is predicated on\nthe validated org.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the function the URL names.","schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/none"}}},"description":"no content"}},"x-app":"functions","x-public":true,"x-tool":true},"get":{"operationId":"get_functions_by_name","summary":"Is one function with everything a detail page needs in one round-trip: its definition, its 7-day rollup, its trigger, its twenty most recent invocations and the NAMES of the secrets it mounts.","description":"Is one function with everything a detail page needs in one round-trip: its\ndefinition, its 7-day rollup, its trigger, its twenty most recent invocations\nand the NAMES of the secrets it mounts.\n\nSecret values are never read or returned. A name the caller's org does not hold\nis 404, which is also what another tenant's function looks like from here.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the function the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/functionDetail"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/{name}/invocations":{"get":{"operationId":"get_functions_by_name_invocations","summary":"Is one function's past runs, newest first — each with its status, HTTP code, method, time and duration.","description":"Is one function's past runs, newest first — each with its status,\nHTTP code, method, time and duration.\n\nThese are real recorded rows, not a projection: an invocation appears here only\nonce it actually ran. Requires a validated principal; the read is scoped to its\norg.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the function the URL names.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the page, defaulting to 100.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/invocationList"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/{name}/invoke":{"post":{"operationId":"post_functions_by_name_invoke","summary":"Runs a function and records a REAL invocation.","description":"Runs a function and records a REAL invocation.\n\nThe answer is the invocation record whatever happened to it: 200 when the org's\ncode ran clean, 502 when it ran and failed, 503 when this deployment has no\nsandbox to run code in. The record IS the evidence, so it rides the failure\nrather than being replaced by an error envelope.\n\nBilling is two-part and both parts are prepaid-then-metered on the one shared\nmeter: a flat per-invocation request fee, gated BEFORE any sandbox compute runs\nso an unfunded org gets 402 and nothing executes, and a usage-native\nGB-seconds compute debit taken after the run. Either is independently free when\nits fee is zero, so an operator can bill by request alone, by compute alone, or\nby both — and a zero request fee removes the balance gate with it.\n\nA TRANSPORT failure is not charged: the sandbox being unreachable ran no\nbillable compute. Code that ran and exited non-zero IS charged — that is a\nsuccessful invocation of a failing program, not a billing failure.\n\nWhen the sandbox is not configured on this deployment, a non-fleet function\nfails closed before anything is recorded — no execution and no fabricated\noutput. Scoped to the caller's org; requires a validated principal.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/invokeReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/invocationView"}}},"description":"ok"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/invocationView"}}},"description":"bad gateway"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/invocationView"}}},"description":"service unavailable"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/functions/{name}/logs":{"get":{"operationId":"get_functions_by_name_logs","summary":"Is the output of a function's most recent run — its error text when that run failed, else what it printed.","description":"Is the output of a function's most recent run — its error text when that\nrun failed, else what it printed.\n\nIt is the LAST run only, and it is empty when the function has never run. There\nis no log retention behind this beyond the recorded invocation itself.","tags":["functions"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the function the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/logLines"}}},"description":"ok"}},"x-app":"functions","x-public":true,"x-tool":true}},"/v1/gateway/config":{"get":{"operationId":"get_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=<slug>.","tags":["gateway"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}},"description":"ok"}},"x-app":"gateway","x-public":true,"x-tool":true},"put":{"operationId":"put_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=<slug>. 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","x-public":true,"x-tool":true}},"/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=<slug>, 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","x-public":true,"x-tool":true}},"/v1/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/repos. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML.","tags":["git"],"x-app":"git","x-public":true}},"/v1/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/repos. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML.","tags":["git"],"x-app":"git","x-public":true}},"/v1/git/keys":{"get":{"operationId":"get_git_keys","summary":"Returns the SSH public keys registered to the caller's org — the keys that authenticate `git clone git@<host>:<org>/<repo>.git`.","description":"Returns the SSH public keys registered to the caller's org — the keys\nthat authenticate `git clone git@<host>:<org>/<repo>.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","x-public":true,"x-tool":true},"post":{"operationId":"post_git_keys","summary":"Registers an SSH public key so it can authenticate `git clone git@<host>:<org>/<repo>.git` for the caller's org.","description":"Registers an SSH public key so it can authenticate `git clone\ngit@<host>:<org>/<repo>.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… [email protected]","title":"laptop"},"schema":{"$ref":"#/components/schemas/registerKeyReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/keyView"}}},"description":"created"}},"x-app":"git","x-public":true,"x-tool":true}},"/v1/git/keys/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/git/repos":{"get":{"operationId":"get_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":"[email protected]:acme/widgets.git"}]},"schema":{"$ref":"#/components/schemas/repoList"}}},"description":"ok"}},"x-app":"git","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/blob":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/commits":{"get":{"operationId":"get_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/files":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/gc":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/mirror":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/mirrors":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/mirrors/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/pulls":{"get":{"operationId":"get_git_repos_by_name_pulls","summary":"Returns a repo's pull requests, newest number first — what is waiting to be reviewed, and what has already landed.","description":"Returns a repo's pull requests, newest number first — what is\nwaiting to be reviewed, and what has already landed. Narrow it with\n?state=open or ?state=merged; omit state for every proposal.","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":"state","in":"query","required":false,"description":"State narrows the list to \"open\" or \"merged\". Omit it for every proposal.","schema":{"type":"string"},"example":"open"}],"responses":{"200":{"content":{"application/json":{"example":{"data":[{"base":"main","createdAt":"2026-08-07T10:00:00Z","head":"agent/cache-catalog","number":4,"repo":"widgets","state":"open","title":"cache the catalog read","updatedAt":"2026-08-07T10:00:00Z"}]},"schema":{"$ref":"#/components/schemas/pullList"}}},"description":"ok"}},"x-app":"git","x-public":true,"x-tool":true},"post":{"operationId":"post_git_repos_by_name_pulls","summary":"Proposes a branch for merging and returns it with its number.","description":"Proposes a branch for merging and returns it with its number. Answers\n201. Both branches must already exist — a proposal naming a branch nobody\npushed is a typo, not a plan — and base defaults to the repo's default branch.\n\nProposing the same head into the same base twice is a 409 while the first\nproposal is still open, so a retried agent run leaves ONE thing to review\nrather than a pile of identical ones. A repo outside the caller's scope is a\n404, exactly as reading it is.","tags":["git"],"parameters":[{"name":"name","in":"path","required":true,"description":"Name is the repo the proposal belongs to, from the :name path segment.","schema":{"type":"string"},"example":"widgets"}],"requestBody":{"content":{"application/json":{"example":{"base":"main","head":"agent/cache-catalog","name":"widgets","title":"cache the catalog read"},"schema":{"$ref":"#/components/schemas/openReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pullView"}}},"description":"created"}},"x-app":"git","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/pulls/{number}":{"get":{"operationId":"get_git_repos_by_name_pulls_by_number","summary":"Returns one pull request by its per-repo number.","description":"Returns one pull request by its per-repo number. A number belonging to\nanother tenant's repo is not found, exactly as the repo itself is not.","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":"number","in":"path","required":true,"description":"Number is the proposal's per-repo number, from the :number path segment.","schema":{"type":"integer"},"example":4}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pullView"}}},"description":"ok"}},"x-app":"git","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/pulls/{number}/merge":{"post":{"operationId":"post_git_repos_by_name_pulls_by_number_merge","summary":"Merges an open pull request by FAST-FORWARDING base to head, and answers the proposal in its merged state with the revision base now points at.","description":"Merges an open pull request by FAST-FORWARDING base to head, and\nanswers the proposal in its merged state with the revision base now points at.\n\nIt merges only when base is already an ancestor of head — the case where head\ncontains every commit base has, so moving the branch loses nothing and invents\nnothing. When base has moved on independently, this REFUSES with 409 and says\nso: a real three-way merge is not implemented here, and reporting one would\nclaim a result these bytes do not produce. Rebase head onto base and merge\nagain.\n\nThe move is judged by the same ref policy a `git push` of it would face, and\nfires the same build and notify reactions, so merging is not a way around\neither. Merging an already-merged proposal is a 409.","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":"number","in":"path","required":true,"description":"Number is the proposal's per-repo number, from the :number path segment.","schema":{"type":"integer"},"example":4}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pullView"}}},"description":"ok"}},"x-app":"git","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/push":{"post":{"operationId":"post_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":"<h1>hi</h1>","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":"[email protected]:acme/widgets.git"},"schema":{"$ref":"#/components/schemas/pushResp"}}},"description":"ok"}},"x-app":"git","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/readme":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/refs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/subscriptions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/subscriptions/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/git/repos/{name}/tree":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/usage":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/git/webhook":{"post":{"operationId":"post_git_webhook","summary":"Retired — forge pushes build via platform.hanzo.ai","description":"GONE (410). This was the canonical forge's push-to-deploy endpoint, and it never dispatched a build in its life.\n\nIt handed each verified push to cloud.OnGitPush, a single-registrant client 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","x-public":true}},"/v1/git/zap/createRepo":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/zap/deleteRepo":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/zap/getRepo":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/zap/listRepos":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/zap/usage":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/{org}/{project}/{repo}/git-receive-pack":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/{org}/{project}/{repo}/git-upload-pack":{"post":{"operationId":"post_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","x-public":true}},"/v1/git/{org}/{project}/{repo}/info/refs":{"get":{"operationId":"get_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","x-public":true}},"/v1/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/repos. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git","x-public":true}},"/v1/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/repos. Repository names, paths and file contents all render through auto-escaping templates rather than being concatenated into HTML.","tags":["git"],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"git","x-public":true}},"/v1/git/{org}/{repo}/git-receive-pack":{"post":{"operationId":"post_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://<host>/v1/git/<org>/<repo>.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","x-public":true}},"/v1/git/{org}/{repo}/git-upload-pack":{"post":{"operationId":"post_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://<host>/v1/git/<org>/<repo>.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","x-public":true}},"/v1/git/{org}/{repo}/info/refs":{"get":{"operationId":"get_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://<host>/v1/git/<org>/<repo>.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","x-public":true}},"/v1/graph":{"get":{"operationId":"graphRead","summary":"Read the assertions this organization has recorded","tags":["graph"],"parameters":[{"name":"entity","in":"query","required":false,"description":"Entity narrows to what was asserted ABOUT one entity. Absent matches every\nentity.","schema":{"type":"string"}},{"name":"relation","in":"query","required":false,"description":"Relation narrows to one relation. Absent matches every relation.","schema":{"type":"string"}},{"name":"value","in":"query","required":false,"description":"Value narrows to assertions pointing AT one value, which is how the edges\ninto an entity are read.","schema":{"type":"string"}},{"name":"as_of","in":"query","required":false,"description":"AsOf bounds the read to what was knowable at an instant, RFC 3339. Absent\nreads everything this plane holds.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many assertions come back. Absent, zero, or anything above\nthe walk ceiling is the ceiling.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphReadOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true},"post":{"operationId":"graphAssert","summary":"Assert what is true of an entity","tags":["graph"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphAssertIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphAssertOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true}},"/v1/graph/graphql":{"post":{"operationId":"post_graph_graphql","summary":"Ask the graph in one request, traversing.","description":"Runs a GraphQL query against this organization's assertions.\n\nIt is the one endpoint here a caller can TRAVERSE: the REST ops each answer a single question, so composing them — the entities this one points at, and what each of those resolves to — costs a request per hop with the intermediate keys held by the caller. Here that is one query and the nesting is the answer's shape.\n\nIt adds no way to ask anything new. Every field runs the SAME operation the matching REST route runs, so the tenancy, the as-of bound, the traversal bounds and the conflict rule are the ones already in force; the schema is served by introspection.\n\nA query that cannot run answers 200 with an `errors` list, which is the wire every GraphQL client parses.","tags":["graph"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphQLIn"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphQLOut"}}},"description":"Success"}},"x-app":"graph","x-public":true}},"/v1/graph/neighbors":{"post":{"operationId":"graphNeighbors","summary":"Walk the edges from a seed set, bounded","tags":["graph"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphNeighborsIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphNeighborsOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true}},"/v1/graph/resolve":{"post":{"operationId":"graphResolve","summary":"What is in force about an entity as of an instant, and what disagreed","tags":["graph"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphResolveIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphResolveOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true}},"/v1/graph/search":{"get":{"operationId":"graphSearch","summary":"Find assertions by their text rather than by an entity key","description":"Finds assertions by their text where read finds them by their keys.\n\nIt is the READ with one more term, not a second way to leave the store: same\norder, same ceiling, same tenancy, and searching composes with narrowing by\nrelation and by instant because all of them are terms of one filter.\n\nIt resolves nothing. What matches is what was asserted, including claims that\nwere later corrected — which is the honest answer to \"where is this mentioned\"\nand the reason the caller then asks resolve about what it found.","tags":["graph"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q is what to look for: words, matched as prefixes, all of them required.\nPunctuation is text here rather than syntax, so an entity key searches as\nitself.","schema":{"type":"string"}},{"name":"relation","in":"query","required":false,"description":"Relation narrows to one relation. Absent matches every relation.","schema":{"type":"string"}},{"name":"as_of","in":"query","required":false,"description":"AsOf bounds the search to what was knowable at an instant, RFC 3339. Absent\nsearches everything this plane holds.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps how many assertions come back. Absent, zero, or anything above\nthe walk ceiling is the ceiling.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphReadOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true}},"/v1/graph/vocabulary":{"get":{"operationId":"graphVocabulary","summary":"The relations in use, and the rule that resolves a conflict","tags":["graph"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/graphVocabularyOut"}}},"description":"ok"}},"x-app":"graph","x-public":true,"x-tool":true}},"/v1/guide":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/actions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/analytics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/blueprint":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true}},"/v1/guide/blueprint/versions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/blueprint/{collection}/{id}":{"patch":{"operationId":"patch_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","x-public":true}},"/v1/guide/chat":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/guide/curriculum":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true}},"/v1/guide/profile":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/steps/{id}/do":{"post":{"operationId":"post_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","x-public":true}},"/v1/guide/steps/{id}/done":{"post":{"operationId":"post_guide_steps_by_id_done","summary":"Marks one step of the caller org's journey complete and returns the refreshed journey.","description":"Marks one step of the caller org's journey complete and returns the\nrefreshed journey.\n\nDependency-GATED, exactly as start is: a step whose prerequisites are unfinished\nis refused 409 carrying {error, step, blockedBy} naming what is in the way.","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","x-public":true,"x-tool":true}},"/v1/guide/steps/{id}/reset":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/guide/steps/{id}/skip":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/guide/steps/{id}/start":{"post":{"operationId":"post_guide_steps_by_id_start","summary":"Marks one step of the caller org's journey in progress and returns the refreshed journey.","description":"Marks one step of the caller org's journey in progress and returns the\nrefreshed journey.\n\nDependency-GATED: a step whose prerequisites are unfinished is refused 409\ncarrying {error, step, blockedBy}, where blockedBy names the exact steps in the\nway — enough to render the reason without asking again.","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","x-public":true,"x-tool":true}},"/v1/guide/strategies":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/guide/suggest":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/help/articles":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/help/articles/{slug}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/help/categories":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/help/tickets":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/.well-known/jwks":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/.well-known/oauth-authorization-server":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/.well-known/openid-configuration":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/account":{"get":{"operationId":"get_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","x-public":true},"put":{"operationId":"put_iam_account","summary":"Saves the calling person's own profile — the name they are shown by, their picture, a line about themselves and a link.","description":"Saves the calling person's own profile — the name they are\nshown by, their picture, a line about themselves and a link.\n\nOnly their own: the request names nobody, so it cannot reach another account.\nSend only what you are changing; a field you leave out keeps the value it had,\nand a field you send empty is cleared.\n\nA picture is an https link or an inline image up to 96 KiB, the same value an\norganization's mark is (schema.AvatarRef) — one rule for how a subject appears,\nwhether the subject is a person or an organization.","tags":["iam"],"parameters":[{"name":"Cookie","in":"header","required":false,"schema":{"type":"string"}},{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.accountBody"}}},"required":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/iam/admin/provision":{"post":{"operationId":"post_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","x-public":true}},"/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\nIt DESCRIBES an account it meets and GRANTS only to one it creates: org-admin is\nnever raised on a row that already exists, and a machine identity is answered by\nname rather than adopted. Both are properties of the update itself, so a\nsteady-state reconcile — which changes neither — is unaffected.\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; send the same one\nagain and it is kept too, so a steady-state re-run is not a rotation.","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","x-public":true,"x-tool":true}},"/v1/iam/applications":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/applications/{owner}/{name}":{"delete":{"operationId":"delete_iam_applications_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_applications_by_owner_by_name","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":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Application"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_applications_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/assume":{"post":{"operationId":"post_iam_assume","summary":"Steps a platform operator into an organization: it returns their own access token re-scoped to that tenant, so they see what the tenant sees.","description":"Steps a platform operator into an organization: it returns their\nown access token re-scoped to that tenant, so they see what the tenant sees.\n\nThe token still names the operator — stepping in is not becoming somebody\nelse — and records the organization it was scoped to, so everything done with\nit is attributed to the person who did it. Only a platform operator may, and\nthe attempt is recorded whether or not it succeeds.","tags":["iam"],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Forwarded-For","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.assumeBody"}}},"required":true},"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"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"not found"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/audit-logs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/audit-logs/{owner}/{name}":{"delete":{"operationId":"delete_iam_audit-logs_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_audit-logs_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_audit-logs_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.AuditLog"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/auth/application":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/auth/methods":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/certs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_iam_certs","summary":"Adds a signing certificate your applications can verify tokens against — the call you make 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 stage the next one before a rotation. A name already\nused in your organization is refused.\n\nIt registers the certificate's IDENTITY: its name (which is the JWKS `kid`),\nits algorithm, its expiry. Key material does not travel this way and cannot:\nthe private key is not part of the Cert's JSON, so it is neither served here\nnor accepted here. It is supplied to the process by the deployment, under the\nname registered here (internal/keyring). Staging a rotation is therefore two\nhalves — this call names the key, and the deployment provides it.","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","x-public":true,"x-tool":true}},"/v1/iam/certs/{owner}/{name}":{"delete":{"operationId":"delete_iam_certs_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.certs.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_certs_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Cert"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_certs_by_owner_by_name","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.\n\nA PUT here is a METADATA edit — display name, expiry, provider. It overlays\nonly the fields the request actually SET onto the loaded row: a field the JSON\nomits (or leaves at its zero value) keeps what the row holds, rather than\nblanking it. That is load-bearing, not a nicety. A read serves the public\nCertificate (Mask hides only PrivateKey and AccessSecret), so a client that\nreads a cert, changes one field, and writes it back sends the masked halves\nempty and every other field it did not touch at its zero value — and the old\nfull-struct overlay wrote all of those blanks back. Blanking CryptoAlgorithm\nalone drops the cert from the JWKS (oidc.Publishes turns false), so every\ntoken under its `kid` stops verifying; blanking Provider/Account/ExpireTime\nbreaks ACME renewal and expiry — all from a request that only meant to rename\nit. Absent-or-zero means \"unchanged\", so the deployment (key) and a rotation\n(cert) remain the only way key or published material changes; the metadata API\ncannot clear it.\n\nThe overlay is generic — it copies every set field, so a field nobody has added\nyet is carried without a line here — and leaves three things the request may\nnot move: the bound Model (id, createdAt, key, snapshot), the natural key\n(owner/name address the row, they do not mutate it), and the creation stamp.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/consent":{"get":{"operationId":"get_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/iam/delete-membership":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/invitations":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/invitations/{owner}/{name}":{"delete":{"operationId":"delete_iam_invitations_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.invitations.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_invitations_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Invitation"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_invitations_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/keys/org":{"get":{"operationId":"get_iam_keys_org","summary":"Resolve a PUBLISHABLE key to the organization that owns it","description":"Answers which organization a publishable key belongs to — what a service calls to attribute a request that arrived carrying a key shipped in a browser. This is the noun spelling of `/v1/iam/resolve-key`, the same handler at the address that replaces it; both answer while callers migrate.\n\nIt names an ORGANIZATION and never a person. No path through it loads or returns a user, so a key placed in client code cannot become a way to learn who anyone is — which is the whole reason this is a separate endpoint from the one below.\n\nA key that is expired, secret rather than publishable, or simply unknown all answer with the same sentence and a `code` saying which it was. Only a confidential service that has already proved it may resolve keys reads that code — there is no anonymous caller here to probe which keys exist — and telling those apart is what lets a holder be told to re-mint an expired key instead of hunting a configuration error.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/keys/principal":{"get":{"operationId":"get_iam_keys_principal","summary":"Resolve a SECRET key to the principal it authenticates","description":"Answers who a secret key belongs to — the owner and name a gateway needs to attribute and bill a request that arrived carrying an `sk-`. This is the noun spelling of `/v1/iam/get-user?accessKey=`, the same handler at the address that replaces it; both answer while callers migrate.\n\nIt resolves a KEY and nothing else. The verb it replaces also reads a user by `?id=`, and carrying that here would make this a second address for the user read — the exact thing being retired. Ask for a person by name at the user read; ask here only what a credential resolves to.\n\nRequires a confidential caller: the resolver authenticates as an app, so a request without that credential resolves nothing rather than falling back to an anonymous lookup. An unresolvable key answers with a `code` distinguishing expired from wrong-endpoint from unknown, so the holder can be told which one cure applies.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/keys/{owner}/{name}":{"delete":{"operationId":"delete_iam_keys_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteResponse"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_keys_by_owner_by_name","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":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Key"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_keys_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"description":"Owner is the tenant that holds the key; Name is unique within Owner.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/link":{"post":{"operationId":"post_iam_link","summary":"Starts connecting another sign-in identity to the account you are already signed in as.","description":"Starts connecting another sign-in identity to the account you are already\nsigned in as. It answers with the provider's URL for the browser to follow; when\nthe provider returns, that identity is attached and you come back to returnUri.\n\nYour account is fixed here, from the credential you are already holding, and is\ncarried server-side for the rest of the round-trip — so nothing that happens at\nthe provider can point the link at somebody else.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/linked-accounts":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/login":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/memberships":{"get":{"operationId":"get_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 \"<homeOrg>/<username>\" — 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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/mfa":{"delete":{"operationId":"delete_iam_mfa","summary":"Turns a factor off, so sign-in stops asking for it.","description":"Turns a factor off, so sign-in stops asking for it. Naming no factor turns\noff ALL of them — the reset path. People may do this for themselves; doing it for\nsomebody else takes an administrator, which is what makes it the way back in when a\nphone is lost.\n\nThe recovery codes go with the last factor: they are the way past a challenge, so\nkeeping them alive for an account with nothing to challenge would leave a standing\ncredential behind.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/mfa/preferred":{"post":{"operationId":"post_iam_mfa_preferred","summary":"Picks which second factor an account is asked for first when it has more than one.","description":"Picks which second factor an account is asked for first when it has\nmore than one. Only a factor the account actually holds: storing an unheld one told\nthe login gate \"MFA is on\" — factor.Enabled reads that column — while leaving it\nnothing to ask for, so the sign-in required the password alone.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/mfa/setup/enable":{"post":{"operationId":"post_iam_mfa_setup_enable","summary":"Finishes the enrolment: from here the account's sign-ins ask for this factor.","description":"Finishes the enrolment: from here the account's sign-ins ask for this factor.\nIt requires the proof initiate handed out — a passcode from the authenticator, or the\ncode that was sent — and verifies it BEFORE writing anything.\n\nVerifying BEFORE writing is what keeps a client that never completed the proof —\na skipped verify step, a QR scanned into the wrong app, a bug — from switching on\na factor no code can satisfy. That would lock the account out with no\nself-service way back: the gate holds the sign-in before minting, so the person\ncould not obtain the bearer that disable requires.\n\nThe recovery codes are minted here and returned ONCE, on the first factor the\naccount adds. Answering with them is the way back in when no factor can be\nproduced, so they are the same value the row's digests were made from — by\nconstruction, not by a client echoing them back.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/mfa/setup/initiate":{"post":{"operationId":"post_iam_mfa_setup_initiate","summary":"Starts enrolling a factor and hands over whatever the person needs to prove they hold it: app a fresh secret and the otpauth:// URL to render as a QR code sms a code texted to the number on the account email a code mailed to the address on the account Nothing is switched on yet, so abandoning this step leaves the account exactly as it was.","description":"Starts enrolling a factor and hands over whatever the person needs to prove\nthey hold it:\n\n\tapp a fresh secret and the otpauth:// URL to render as a QR code\n\tsms a code texted to the number on the account\n\temail a code mailed to the address on the account\n\nNothing is switched on yet, so abandoning this step leaves the account exactly as\nit was. Response: {status:\"ok\", data:{mfaType, secret, url}} — secret and url only\nfor the authenticator.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/oauth/authorize":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/oauth/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/oauth/device":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/oauth/device/info":{"post":{"operationId":"post_iam_oauth_device_info","summary":"Answers \"what am I approving?\" for a pending device code.","description":"Answers \"what am I approving?\" for a pending device code.\n\nThe approval page exists to tell a human WHICH application they are authorizing;\na page that names any other one defeats the control it implements. The client is\na property of the CODE, not of the page or of whatever app the browser happens\nto be signed in to, so it is read from the code's row here and nowhere else.\n\nRequires a signed-in session, and answers with the same ONE opaque refusal\napproveDevice uses. That is deliberate: the user_code is only 40 bits and is the\none secret in this flow, so an unauthenticated lookup — or one that\ndistinguished unknown from expired from already-approved — would be an oracle\nfor hunting live codes. Gated and opaque, it reveals strictly less than the\napproval the same caller could already attempt.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/oauth/federation/mfa":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/oauth/introspect":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/oauth/logout":{"get":{"operationId":"get_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 — worth stating, because a logout that computes a\nredirect and answers {\"status\":\"ok\"} while ending no session and revoking no\ntoken is worse than none: the person on the shared machine believes it worked.\nThree things happen here, 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","x-public":true},"post":{"operationId":"post_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 — worth stating, because a logout that computes a\nredirect and answers {\"status\":\"ok\"} while ending no session and revoking no\ntoken is worse than none: the person on the shared machine believes it worked.\nThree things happen here, 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","x-public":true}},"/v1/iam/oauth/revoke":{"post":{"operationId":"post_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. A native app or CLI is a public PKCE client and holds no\nsecret, so requiring one here would leave signing out as a local delete —\nforgetting a credential that stays spendable for the rest of its lifetime.\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","x-public":true}},"/v1/iam/oauth/token":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/oauth/userinfo":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/onboard":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/organizations":{"get":{"operationId":"listOrganizations","summary":"Returns the organizations you can act in, the ones you belong to first and the rest after, newest first, narrowed by an optional query against the name or the display name.","description":"Returns the organizations you can act in, the ones you belong to first\nand the rest after, newest first, narrowed by an optional query against the\nname or the display name.\n\nPlatform operators see every organization; everyone else sees their own. Pass\nthe cursor from the previous page to continue; an empty cursor in the answer\nmeans there is nothing more.\n\nTHE SCOPE IS THE HANDLER'S OWN, so it holds at every endpoint. The Guard refuses\na bearerless request before this runs, but the MCP server carries a typed op to\nits handler with no middleware in front of it — a handler that read no\nprincipal would answer such a caller with the whole registry. Reading the\nprincipal here is what makes the answer the same one over both.","tags":["iam"],"parameters":[{"name":"X-Forwarded-For","in":"header","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListOrganizationsOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/iam/organizations/avatar":{"post":{"operationId":"setOrganizationAvatar","summary":"Changes how an organization appears across Hanzo: the square mark beside its name, as an uploaded image or as a single emoji.","description":"Changes how an organization appears across Hanzo: the square mark\nbeside its name, as an uploaded image or as a single emoji. Sending an image\nclears the emoji and sending an emoji clears the image — an organization has\none mark, not a preference order — and sending neither clears both, which is\nhow it goes back to being drawn as its initial.\n\nAn image is an https link or the bytes inline as a data URL, up to 96 KiB.\nAnyone who administers the organization may set this; it is not reserved to\nthe platform.\n\nIt writes the two fields onto the stored row and touches nothing else, which\nupdate cannot do: update replaces the whole record, and a record read back\nfirst arrives masked, so a read-modify-write through it would persist the mask\nover the organization's own credential settings.","tags":["iam"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.SetAvatarInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Organization"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/organizations/{owner}/{name}":{"delete":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteOrganizationOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"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":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Organization"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/password":{"put":{"operationId":"put_iam_password","summary":"Replaces the calling person's password.","description":"Replaces the calling person's password. Only their own —\nthere is no shape of this request that writes somebody else's.\n\nProve who you are with the password you are replacing, or — when you cannot\nsign in at all — with a code sent to the address the account already holds.\nExactly one of the two: a request carrying both proves nothing more than\neither, and answering it would mean deciding which one mattered.\n\nA reset also clears the account lockout, in the SAME transaction as the\ndigest. Replacing a credential retires the run of guesses against the old one,\nand without this a person who reset a forgotten password was still refused for\nup to fifteen more minutes — with the brand-new password they had just chosen.","tags":["iam"],"parameters":[{"name":"Cookie","in":"header","required":false,"schema":{"type":"string"}},{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.passwordBody"}}},"required":true},"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","x-public":true,"x-tool":true}},"/v1/iam/permissions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/permissions/{owner}/{name}":{"delete":{"operationId":"delete_iam_permissions_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.permission.DeleteResponse"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_permissions_by_owner_by_name","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":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Permission"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_permissions_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"description":"Identity — the (owner, name) natural key.","schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/preferences":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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.projects.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/projects/{owner}/{name}":{"delete":{"operationId":"delete_iam_projects_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_projects_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_projects_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.projects.Input"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Project"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/iam/providers/{owner}/{name}":{"delete":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.mutationResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.providerResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/registry/jwks":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/registry/token":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/release":{"post":{"operationId":"post_iam_release","summary":"Steps a platform operator back out: it returns their own access token with no organization assumed, which is the credential they had before they stepped in.","description":"Steps a platform operator back out: it returns their own access\ntoken with no organization assumed, which is the credential they had before\nthey stepped in. Recorded like the step in.","tags":["iam"],"parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Forwarded-For","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.assumeBody"}}},"required":true},"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"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Answer"}}},"description":"unauthorized"}},"x-app":"iam","x-public":true,"x-tool":true}},"/v1/iam/roles":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/roles/{owner}/{name}":{"delete":{"operationId":"delete_iam_roles_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.roles.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_roles_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Role"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_roles_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/ResourceTypes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/ResourceTypes/{name}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/Schemas":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/Schemas/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/ServiceProviderConfig":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/iam/scim/v2/Users":{"get":{"operationId":"get_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","x-public":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/scim/v2/Users/{owner}/{name}":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/iam/service-accounts":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true}},"/v1/iam/service-accounts/{name}":{"delete":{"operationId":"delete_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","x-public":true}},"/v1/iam/service-accounts/{name}/keys":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/sessions":{"get":{"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"],"parameters":[{"name":"owner","in":"query","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","required":false,"schema":{"type":"string"}},{"name":"application","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.ListSessionsOut"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/iam/sessions/{owner}/{name}/{application}":{"delete":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"application","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.DeleteSessionOut"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"application","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Session"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"application","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/signin":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/signup":{"post":{"operationId":"post_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","x-public":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/iam/tokens/issue":{"post":{"operationId":"post_iam_tokens_issue","summary":"Mints an access token for the `?id=<owner>/<name>` target user (optional `?aud=` resource, RFC 8707), issued by the authenticated + allow-listed confidential client.","description":"Mints an access token for the `?id=<owner>/<name>` 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","x-public":true}},"/v1/iam/tokens/{owner}/{name}":{"delete":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenMutation"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.tokenResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/unlink":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/users":{"get":{"operationId":"get_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":"email","in":"query","required":false,"description":"Email narrows the page to the accounts carrying one address. Looking a\nperson up by their address is a QUERY over the collection, not an item\nread: an address is not the natural key, two rows in one org can carry\none, and a caller that gets a page SEES both — where a single-item read\nwould have to choose, and choosing is how somebody joins a team under a\ncolleague's identity.","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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/users/{owner}/{name}":{"delete":{"operationId":"delete_iam_users_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.users.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_users_by_owner_by_name","summary":"Returns one person in your organization, addressed by their username or by their email address.","description":"Returns one person in your organization, addressed by their username or by\ntheir email address. Passwords, API secrets and MFA material are stripped from\nthe response.\n\nAn address that names two accounts names none: the read refuses rather than\npicking one, and says so instead of reporting \"no such user\". Handing back an\narbitrary one of two rows is how somebody gets added to a team under a\ncolleague's identity.","tags":["iam"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}},{"name":"email","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.User"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_users_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/users/{owner}/{name}/keys":{"delete":{"operationId":"delete_iam_users_by_owner_by_name_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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam","x-public":true},"post":{"operationId":"post_iam_users_by_owner_by_name_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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"iam","x-public":true}},"/v1/iam/verification-codes":{"post":{"operationId":"post_iam_verification-codes","summary":"Validates the request and asks otp to get a code to the person.","description":"Validates the request and asks otp to get a code to the\nperson. The request fields are read via fiber's FormValue — the escape hatch zip\nexposes for form bodies (multipart or urlencoded) — since the typed JSON Bind does\nnot apply here. v1 also accepts countryCode/method/checkUser/captchaType; iam\nignores them (the captcha/forget/MFA flows those drive are not ported), and\nCAPTCHA verification is likewise not enforced — iam models no captcha provider —\nso the code is issued once the destination and application validate.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/web3/nonce":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/web3/verify":{"post":{"operationId":"post_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","x-public":true}},"/v1/iam/webauthn-credentials":{"get":{"operationId":"listWebauthnCredentials","summary":"Returns the passkeys and security keys registered to one person, newest first — which device each lives on and when it was registered.","description":"Returns the passkeys and security keys registered to\none person, newest first — which device each lives on and when it was\nregistered.\n\nYours by default. Name somebody else and you get them only if you already\nadminister their account, which is the same authority that governs reading\ntheir user record — so this list can never show more people than the surface\nbeside it already does.\n\nThere is no organization-wide list, by design. Scoping to the ORG would hand an\norg admin every member's credential rows in one answer and a SuperAdmin every\ntenant's, while a plain member could not read even their own (an unnamed target\nfails the Guard's tenant rule). One scope answers both halves cleanly: the\nanswer is a person's, and the caller is that person unless they say otherwise\nand may.","tags":["iam"],"parameters":[{"name":"user","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.listWebauthnCredentialsOut"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/iam/webauthn-credentials/{owner}/{name}":{"delete":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialMutationResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.webauthnCredentialResult"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/iam/webauthn/signin/begin":{"get":{"operationId":"get_iam_webauthn_signin_begin","summary":"Starts a passkey sign-in: it returns the challenge the person's authenticator signs.","description":"Starts a passkey sign-in: it returns the challenge the person's\nauthenticator signs.\n\nThe account is named in the query, and the challenge is bound to it, so what may\nanswer is decided here — by the server, from the row — and the finish checks the\nanswer against that decision rather than recomputing it.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/webauthn/signin/finish":{"post":{"operationId":"post_iam_webauthn_signin_finish","summary":"Verifies the signed challenge and signs the person in.","description":"Verifies the signed challenge and signs the person in.\n\nIt answers exactly as a password sign-in does — the same envelope, through the\nsame grant — so nothing downstream branches on how somebody arrived.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/webauthn/signup/begin":{"get":{"operationId":"get_iam_webauthn_signup_begin","summary":"Starts enrolling a passkey for the signed-in person: it returns the options their browser hands to the authenticator.","description":"Starts enrolling a passkey for the signed-in person: it returns the\noptions their browser hands to the authenticator.\n\nPasskeys already on the account are EXCLUDED, so a second enrollment on a device\nthat already holds one is refused by the authenticator itself rather than\nsilently producing a duplicate the person cannot tell apart.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/webauthn/signup/finish":{"post":{"operationId":"post_iam_webauthn_signup_finish","summary":"Verifies the newly created passkey and stores it, so the person can sign in with their device from then on.","description":"Verifies the newly created passkey and stores it, so the person\ncan sign in with their device from then on.","tags":["iam"],"x-app":"iam","x-public":true}},"/v1/iam/whoami":{"get":{"operationId":"get_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","x-public":true}},"/v1/iam/workspaces":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/iam/workspaces/{owner}/{name}":{"delete":{"operationId":"delete_iam_workspaces_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.workspaces.DeleteOutput"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"get":{"operationId":"get_iam_workspaces_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/iam.Workspace"}}},"description":"ok"}},"x-app":"iam","x-public":true,"x-tool":true},"put":{"operationId":"put_iam_workspaces_by_owner_by_name","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"],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"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","x-public":true,"x-tool":true}},"/v1/images/generations":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/index/health":{"get":{"operationId":"get_index_health","summary":"Reports whether the search plane can serve.","description":"Reports whether the search plane can serve.\n\nAnswers the dialect's `{\"status\":\"available\"}` when the index store is\nreadable. It FAILS CLOSED — an unreadable store answers 503 with\n`{\"status\":\"unavailable\"}` rather than an empty result set, because a\nMeilisearch client probes this before it will use a server at all and a\ncheerful 200 over a broken volume turns \"search is down\" into \"nothing\nmatched\". It requires no principal and reads no tenant data.","tags":["index"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexHealth"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexHealth"}}},"description":"service unavailable"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/indexes":{"get":{"operationId":"get_index_indexes","summary":"Lists the indexes your org holds.","description":"Lists the indexes your org holds.\n\nAnswers every index in the caller's own org with its primary key and\ntimestamps. Without it an index whose uid a caller has forgotten is\nunreachable — there is no other way to enumerate what an org holds. The page\nis the whole set: an org's index count is small by construction, so `limit`\nand `total` both report it.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a\nclient-supplied header, and two orgs may both hold an index named \"messages\"\nwithout either seeing the other. Without a validated principal the answer is\n403 carrying the dialect's `invalid_api_key` body.","tags":["index"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexList"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true},"post":{"operationId":"post_index_indexes","summary":"Creates an index.","description":"Creates an index.\n\nRegisters a named index in the caller's own org and answers the dialect's\nEnqueuedTask. It is idempotent: creating an index that already exists returns\nthe same receipt and changes nothing, which is what lets a client create on\nstartup without checking first.\n\n`primaryKey` is optional — the first write establishes one when it is omitted.\nAn index is a ROW here rather than a table, so an unusual uid is stored\nverbatim instead of being sanitised into a schema name.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of\nlater work: the write is already applied when this answers. A client that\npolls waitForTask resolves immediately.","tags":["index"],"requestBody":{"content":{"application/json":{"example":{"primaryKey":"id","uid":"messages"},"schema":{"$ref":"#/components/schemas/indexNew"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"accepted"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/indexes/{uid}":{"delete":{"operationId":"delete_index_indexes_by_uid","summary":"Deletes an index and everything in it.","description":"Deletes an index and everything in it.\n\nDrops the index and every document in it from the caller's own org, and\nanswers the dialect's EnqueuedTask. This is the only way to retire an index;\nwithout it a mistaken uid is permanent. Deleting an index that is not there\nsucceeds, so a cleanup pass is safe to re-run.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of\nlater work: the documents are already gone when this answers.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"accepted"}},"x-app":"index","x-public":true,"x-tool":true},"get":{"operationId":"get_index_indexes_by_uid","summary":"Reads one index's definition.","description":"Reads one index's definition.\n\nAnswers the index's uid, primary key and timestamps. An index this org does\nnot hold answers 404 carrying the dialect's `index_not_found` — the code a\nMeilisearch client reads as permission to create it, which is why this is a\nrefusal rather than an empty object.\n\nThe uid is scoped to the caller's own org, so another tenant's index is\nindistinguishable from one that never existed: this surface is not an\nexistence oracle.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexView"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/indexes/{uid}/documents":{"get":{"operationId":"get_index_indexes_by_uid_documents","summary":"Pages through the documents in an index.","description":"Pages through the documents in an index.\n\nAnswers the org's stored documents in insertion order, whole, with the page's\nbounds and the index's total. It is the enumeration surface — search ranks by\nrelevance and cannot walk a corpus — so a caller reconciling what it has\nwritten reads it here.\n\nAn index this org does not hold answers 404 carrying the dialect's\n`index_not_found`.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexDocuments"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true},"post":{"operationId":"post_index_indexes_by_uid_documents","summary":"Add or replace documents in an index","description":"Writes documents into the caller's own index, keyed by the index's primary key: a document whose key is already present is REPLACED whole. The body is the dialect's own — an array of documents, or a single document — and each is stored verbatim, so a read gives back exactly what was written.\n\nThe index is CREATED when it is missing rather than refused, because a Meilisearch client writes before it configures.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header, 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 the dialect's `invalid_api_key` body.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the documents are searchable when this answers, and a client that polls waitForTask resolves immediately.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"items":{},"type":"array"},{}]}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"Success"}},"x-app":"index","x-public":true},"put":{"operationId":"put_index_indexes_by_uid_documents","summary":"Add or update documents in an index","description":"The dialect's update spelling of the write above, and the same act: an upsert keyed by the index's primary key. The JS client's addDocuments and updateDocuments both reduce to this for whole documents, so both spellings are served and both behave identically.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header. Without a validated principal the answer is 403 carrying the dialect's `invalid_api_key` body.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the write is already applied when this answers.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"items":{},"type":"array"},{}]}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"Success"}},"x-app":"index","x-public":true}},"/v1/index/indexes/{uid}/documents/delete-batch":{"post":{"operationId":"post_index_indexes_by_uid_documents_delete-batch","summary":"Delete many documents by primary key in one call","description":"Removes every named document from the caller's own index. The body is the dialect's own: a bare array of primary keys, which may be strings or numbers. A key that is not there is not an error, so a client reconciling its own corpus can send one list rather than checking each key first.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a client-supplied header. Without a validated principal the answer is 403 carrying the dialect's `invalid_api_key` body.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of later work: the documents are already gone when this answers.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"items":{"type":"string"},"type":"array"},{"items":{"type":"number"},"type":"array"}]}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"Success"}},"x-app":"index","x-public":true}},"/v1/index/indexes/{uid}/documents/{id}":{"delete":{"operationId":"delete_index_indexes_by_uid_documents_by_id","summary":"Deletes one document by its primary key.","description":"Deletes one document by its primary key.\n\nRemoves the document from the caller's own org and answers the dialect's\nEnqueuedTask. Deleting a key that is not there succeeds, so a client\nreconciling its own corpus can delete without checking first.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of\nlater work: the document is already gone when this answers.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"accepted"}},"x-app":"index","x-public":true,"x-tool":true},"get":{"operationId":"get_index_indexes_by_uid_documents_by_id","summary":"Reads one document by its primary key.","description":"Reads one document by its primary key.\n\nAnswers the stored document exactly as it was written — this surface keeps\ndocuments whole rather than projecting them, so what comes back is what went\nin. A primary key this index does not hold answers 404 carrying the dialect's\n`document_not_found`; an index this org does not hold answers\n`index_not_found`, and the two are different facts a client acts on\ndifferently.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/indexes/{uid}/search":{"post":{"operationId":"post_index_indexes_by_uid_search","summary":"Searches an index, forgiving typos.","description":"Searches an index, forgiving typos.\n\nRanks the org's documents in one index against `q` and answers the matching\ndocuments whole, most relevant first. A prefix matches, so a partial word\nfinds the documents containing it, and `filter` narrows the result to\ndocuments whose filterable attributes match — which is how a caller scopes\nresults to one end user within its own org.\n\n`estimatedTotalHits` is the dialect's name for the count; every hit is\nmaterialised here, so for this page it is exact. An index this org does not\nhold answers 404 carrying the dialect's `index_not_found`.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"filter":"user = alice","limit":20,"q":"roadmap"},"schema":{"$ref":"#/components/schemas/indexQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexHits"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/indexes/{uid}/settings":{"get":{"operationId":"get_index_indexes_by_uid_settings","summary":"Reads an index's filterable attributes.","description":"Reads an index's filterable attributes.\n\nAnswers the settings subset this surface implements: the attributes a search\n`filter` may constrain. An index this org does not hold answers 404 carrying\nthe dialect's `index_not_found`.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexSettings"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true},"patch":{"operationId":"patch_index_indexes_by_uid_settings","summary":"Sets which attributes an index can be filtered on.","description":"Sets which attributes an index can be filtered on.\n\nReplaces the whole filterable set. An attribute not listed here cannot be\nused in a search `filter`, so this is what makes a per-user or per-tag\nnarrowing possible at all.\n\nIt CREATES the index when it is missing rather than answering 404, because a\nMeilisearch client configures settings on an index it has just asked for and\na refusal there leaves the client with no index at all.\n\nThe 202 and its `enqueued` task are DIALECT COMPATIBILITY, not a promise of\nlater work: the setting is already applied when this answers.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"filterableAttributes":["user","conversationId"]},"schema":{"$ref":"#/components/schemas/indexFilter"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexEnqueued"}}},"description":"accepted"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/stats":{"get":{"operationId":"get_index_stats","summary":"Counts the documents in each of your indexes.","description":"Counts the documents in each of your indexes.\n\nReports every index the caller's own org holds with its document count, plus\nthe org's total. `isIndexing` is always false because writes here are applied\nbefore their response — there is never a background pass to wait on.\n\nThe tenant is the org minted from the VALIDATED bearer's owner claim, never a\nclient-supplied header, so this counts the caller's own documents and no\nother tenant's. Without a validated principal the answer is 403 carrying the\ndialect's `invalid_api_key` body.","tags":["index"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexStats"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/tasks/{uid}":{"get":{"operationId":"get_index_tasks_by_uid","summary":"Checks a write task, which has already finished.","description":"Checks a write task, which has already finished.\n\nAlways reports `succeeded`. Writes here are applied to SQLite before their\nEnqueuedTask is returned, so a client polling waitForTask resolves on its\nfirst call rather than waiting for a queue that was never there. The three\ntimestamps are the same instant for the same reason.\n\nIt requires a validated principal but reads no tenant data: the task id it\nechoes was minted by this process and names nothing about any org.","tags":["index"],"parameters":[{"name":"uid","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexTask"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/index/version":{"get":{"operationId":"get_index_version","summary":"Identifies the search implementation answering.","description":"Identifies the search implementation answering.\n\nReports the dialect's version shape with `commitSha` naming this\nimplementation rather than a Meilisearch build, so a client that logs the\nversion records which server answered instead of implying a release of\nsoftware this is not. It requires no principal and reads no tenant data.","tags":["index"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/indexVersion"}}},"description":"ok"}},"x-app":"index","x-public":true,"x-tool":true}},"/v1/ingress/middlewares":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/ingress/middlewares/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/ingress/routes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/ingress/routes/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/ingress/services":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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/Upstream"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upstream"}}},"description":"ok"}},"x-app":"ingress","x-public":true,"x-tool":true}},"/v1/ingress/services/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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/Upstream"}}},"description":"ok"}},"x-app":"ingress","x-public":true,"x-tool":true},"put":{"operationId":"put_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/Upstream"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upstream"}}},"description":"ok"}},"x-app":"ingress","x-public":true,"x-tool":true}},"/v1/ingress/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/ingress/tls":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/integrations":{"get":{"operationId":"get_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/integrations/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","x-public":true,"x-tool":true}},"/v1/integrations/connectors":{"get":{"operationId":"get_integrations_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":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"connectors":[{"account":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/providers":{"get":{"operationId":"get_integrations_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":["integrations"],"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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{id}":{"delete":{"operationId":"delete_integrations_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":["integrations"],"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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{id}/refresh":{"post":{"operationId":"post_integrations_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":["integrations"],"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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{id}/token":{"get":{"operationId":"get_integrations_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":["integrations"],"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":"<the access token>"},"schema":{"$ref":"#/components/schemas/connectorTokenOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{provider}/credential":{"post":{"operationId":"post_integrations_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":["integrations"],"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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{provider}/device":{"post":{"operationId":"post_integrations_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":["integrations"],"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","x-public":true,"x-tool":true}},"/v1/integrations/connectors/{provider}/device/{flow}/poll":{"post":{"operationId":"post_integrations_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":["integrations"],"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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/integrations/discord/interactions":{"post":{"operationId":"post_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 channel 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","x-public":true}},"/v1/integrations/discord/link":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/discord/link/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/discord/link/discord":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/github/claim":{"post":{"operationId":"post_integrations_github_claim","summary":"Binds installations the App ALREADY holds to the org the caller is acting in — the reconciliation for a grant that happened outside our connect flow.","description":"Binds installations the App ALREADY holds to the org the caller is\nacting in — the reconciliation for a grant that happened outside our connect\nflow.\n\nAn installation IS the grant: GitHub recorded the consent when the App was\ninstalled, and our connection row is bookkeeping that never got written because\nnobody came through our callback. This writes that row from the App's own view,\nso 23 accounts granted straight from GitHub stop reading as nothing.\n\nThe org is taken from the VALIDATED PRINCIPAL and never from the body, because\nit is the one part GitHub cannot tell us. An installation carries an account\nlogin, a type and a repository selection — nothing that names a Hanzo org. So\nthe binding cannot be DERIVED, only asserted, and the only unforgeable assertion\navailable is the org the caller is already acting in. Inferring one from the\naccount name would be a guess the store cannot catch: its key is\n(org,provider,owner), so a wrong org is a valid row, and a valid row is a\nmirror pointed at the wrong tenant.\n\nSUPER ADMIN only, for that same reason. A tenant's proof that an account is\ntheirs is GitHub's own consent screen — the connect flow — and without it any\norg could claim any account the App holds. Platform sudo is already the scope\nthat reads the whole install list, so it is the scope that may bind from it;\ngiving a tenant this verb would hand it every other tenant's repositories.\n\nIdempotent: the row is keyed (org,provider,owner) and connected_at survives an\nupsert, so claiming twice rebinds the same account to the same org and reports\nit under `already`. Re-claiming also REFRESHES the installation id, so an\naccount reinstalled on GitHub — new id, same login — self-heals instead of\nminting tokens against a dead installation.\n\nClaiming an account another org holds ADDS this org's row and leaves theirs\nstanding, so no org loses an integration it is using.","tags":["integrations"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/githubClaimIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"already":["zooai"],"claimed":["hanzoai","luxfi"]},"schema":{"$ref":"#/components/schemas/githubClaimOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/github/fork":{"post":{"operationId":"post_integrations_github_fork","summary":"Forks a granted repository.","description":"Forks a granted repository.\n\nGitHub's fork is ASYNCHRONOUS: it answers 202 with the target repo and\npopulates it in the background, and it answers the same 202 when the fork\nalready exists. So this reports what GitHub said rather than waiting — a call\nthat blocked until the clone finished would time out on a large repository and\ntell the caller nothing it does not already know.","tags":["integrations"],"requestBody":{"content":{"application/json":{"example":{"repo":"widgets"},"schema":{"$ref":"#/components/schemas/githubForkReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"clone_url":"https://github.com/acme/widgets.git","default_branch":"main","existing":false,"full_name":"acme/widgets","html_url":"https://github.com/acme/widgets"},"schema":{"$ref":"#/components/schemas/githubForkOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/github/installations":{"get":{"operationId":"get_integrations_github_installations","summary":"Lists the GitHub accounts the caller may see the App installed on, each confirmed against the App's own list, plus where to add another.","description":"Lists the GitHub accounts the caller may see the App\ninstalled on, each confirmed against the App's own list, plus where to add\nanother.\n\nThe confirmation is the point. A connection row holds an installation id, and\nan id whose installation was since removed on GitHub is a row that mints\nnothing — every list and import against it fails with a token error, which\nreads as \"our git integration is broken\" rather than \"that install is gone\".\nChecking the App's view turns that into a fact the caller can act on.\n\nORG-SCOPED for a tenant, deliberately. The App is installed across every\ncustomer, so the raw list is the customer list; a tenant sees only accounts its\nown org has bound. It discovers a NEW account by installing it (InstallURL),\nwhich is GitHub's own consent screen — not by reading ours.\n\nA SUPER ADMIN sees the App's whole install list, because that list is the\nplatform's own inventory rather than any one tenant's data, and platform sudo\nis the single cross-tenant scope this house has. Without it an App installed\nout-of-band — granted straight from GitHub, so no connect flow ever ran and no\nconnection row exists — is invisible to everyone: the console card reads \"not\nconnected\" and an operator asked \"which GitHub orgs do you see\" can only\nanswer for accounts already bound, which is precisely the accounts that were\nnever the question.","tags":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"installUrl":"https://github.com/apps/hanzo/installations/new","installations":[{"connected":true,"grant":"all","htmlUrl":"https://github.com/hanzoai","login":"hanzoai","type":"Organization"}]},"schema":{"$ref":"#/components/schemas/githubInstallationsOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/github/issues/backfill":{"post":{"operationId":"post_integrations_github_issues_backfill","summary":"Seeds the native todo with the EXISTING issues across the org's granted repos (default state=open); the webhook keeps them live thereafter.","description":"Seeds the native todo 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","x-public":true,"x-tool":true}},"/v1/integrations/github/repos":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/integrations/github/repos/import":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/integrations/github/repos/{repo}/pages":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/integrations/github/repos/{repo}/pages/builds":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/integrations/github/search":{"post":{"operationId":"post_integrations_github_search","summary":"Finds repositories on GitHub.","description":"Finds repositories on GitHub.\n\nThis reads the PUBLIC index and returns nothing an installation unlocks: it is\nhow you find a repository to fork, not a way to see inside one. The org's own\ntoken is used only so the query is rate-limited against the installation\nrather than anonymously — the results are the same ones anyone would get.","tags":["integrations"],"requestBody":{"content":{"application/json":{"example":{"limit":5,"q":"language:go raft"},"schema":{"$ref":"#/components/schemas/githubSearchReq"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"count":1,"repos":[{"full_name":"hashicorp/raft","language":"Go","stars":8000}]},"schema":{"$ref":"#/components/schemas/githubSearchOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/github/webhook":{"post":{"operationId":"post_integrations_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 todo — 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":["integrations"],"x-app":"integrations","x-public":true}},"/v1/integrations/gitlab/projects":{"get":{"operationId":"get_integrations_gitlab_projects","summary":"Lists the projects the org's GitLab connection can reach — membership projects, most recently active first.","description":"Lists the projects the org's GitLab connection can reach —\nmembership projects, most recently active first.","tags":["integrations"],"responses":{"200":{"content":{"application/json":{"example":{"account":"acme","projects":[{"cloneUrl":"https://gitlab.com/acme/widgets.git","defaultBranch":"main","fullName":"acme/widgets","htmlUrl":"https://gitlab.com/acme/widgets","name":"widgets","private":true,"pushedAt":"2026-07-01T10:00:00Z"}]},"schema":{"$ref":"#/components/schemas/gitlabProjectsOut"}}},"description":"ok"}},"x-app":"integrations","x-public":true,"x-tool":true}},"/v1/integrations/openrouter/webhook":{"post":{"operationId":"post_integrations_openrouter_webhook","summary":"Receive OpenRouter Broadcast traces as usage rows","description":"OpenRouter's spend is invisible to every Hanzo money lens because those lenses read hanzo.cloud_usage and OpenRouter meters keys of its own. Point a Broadcast destination (Settings ▸ Observability ▸ Webhook) at this endpoint and each generation span becomes ONE row in that same ledger with provider `openrouter`, so one query answers what we spend everywhere. Enable the Cost and Identity field categories: cost is the money and identity carries `openrouter.api_key_name`, which is what says WHICH key spent it — it lands in `account` as openrouter/<key name>.\n\nAUTHENTICATION IS A HANZO KEY. Broadcast signs nothing; its only authentication is the destination's Headers map, so send a key as `Authorization: Bearer pk-…` and it is admitted exactly as /v1/event admits a beacon's: a project key resolves through the project that minted it, an IAM-issued key through IAM. That key names the org every row is filed under; it can write and cannot read. No key, or a key that names no org, is 401 and nothing is stored.\n\nThe body is OTLP/JSON — `{resourceSpans:[{scopeSpans:[{spans:[…]}]}]}` — exactly as OpenTelemetry defines it; the model, tokens and cost are read from each span's `gen_ai.*` attributes and the key name from `openrouter.api_key_name`. The answer is `{stored, dropped}`: how many generations became rows, and how many spans named no model. Those are OpenRouter's trace and span parents — they carry no cost to meter. An empty payload stores nothing and answers 200, which is what makes Test Connection pass. A warehouse that cannot take the rows answers 503 so the delivery shows red and can be replayed: a row is keyed by its span id, so a redelivery collapses rather than double-counting.","tags":["integrations"],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"Success"}},"x-app":"integrations","x-public":true}},"/v1/integrations/slack/commands":{"post":{"operationId":"post_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","x-public":true}},"/v1/integrations/slack/events":{"post":{"operationId":"post_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. The turn holds the product's own tools, so a request to change code starts a sandbox run because the model chose to — there is no prefix and no second path.\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","x-public":true}},"/v1/integrations/slack/install":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/slack/link":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/slack/link/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/slack/link/slack":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/teams/events":{"post":{"operationId":"post_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 Connection; 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","x-public":true}},"/v1/integrations/teams/link":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/teams/link/aad":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/teams/link/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/telegram/connect":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/integrations/telegram/link":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/telegram/link/auth":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/telegram/link/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/telegram/webhook":{"post":{"operationId":"post_integrations_telegram_webhook","summary":"Telegram Bot API webhook","description":"The update webhook for the Telegram bot. It does two jobs: `/start <code>` or `/connect <code>` 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","x-public":true}},"/v1/integrations/{provider}":{"get":{"operationId":"get_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/integrations/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","x-public":true,"x-tool":true}},"/v1/integrations/{provider}/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/integrations/{provider}/connect":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/integrations/{provider}/disconnect":{"post":{"operationId":"post_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/integrations/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","x-public":true,"x-tool":true}},"/v1/integrations/{provider}/verify":{"post":{"operationId":"post_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/integrations/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","x-public":true,"x-tool":true}},"/v1/kms/auth/login":{"post":{"operationId":"post_kms_auth_login","summary":"Exchanges a machine credential for an IAM bearer token.","description":"Exchanges a machine credential for an IAM bearer token.\n\nTakes a tenant's machine credential — a client id and client secret — and\nreturns an owner-scoped IAM access token with its lifetime, which is the\nbearer the caller then carries on the org-scoped secret operations.\n\nIt is deliberately public and unauthenticated, because it IS the credential\nexchange and runs before any principal exists. That makes it the one route in\nthis subsystem rate-limited PER SOURCE IP, keyed on the real TCP peer rather\nthan on any caller-supplied header, and body-capped in the same place.\n\nThe submitted secret is never logged and never echoed, and failures collapse\nto one clean status with no upstream detail: 401 when the credential does not\nauthenticate, 502 when the identity provider is unreachable, 503 when no\nissuer is configured. That is on purpose — a richer error would be a validity\noracle for guessed credentials.","tags":["kms"],"requestBody":{"content":{"application/json":{"example":{"clientId":"kms-operator","clientSecret":"…"},"schema":{"$ref":"#/components/schemas/kmsLogin"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsToken"}}},"description":"ok"}},"x-app":"kms","x-public":true,"x-tool":true}},"/v1/kms/config":{"get":{"operationId":"get_kms_config","summary":"Returns the runtime configuration for the KMS console.","description":"Returns the runtime configuration for the KMS console.\n\nWhat the console needs before anyone has signed in: the brand, the OIDC issuer\nit authenticates against, the API base for this subsystem and the path of the\nlogin exchange.\n\nPublic on purpose, and it holds nothing sensitive — it is deliberately kept\nunder this subsystem's own namespace rather than under an admin prefix, so a\ngateway that admin-gates the admin routes cannot break the console's\nlegitimate pre-login fetch.","tags":["kms"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsConfig"}}},"description":"ok"}},"x-app":"kms","x-public":true,"x-tool":true}},"/v1/kms/health":{"get":{"operationId":"get_kms_health","summary":"Reports whether this broker can actually serve secrets.","description":"Reports whether this broker can actually serve secrets.\n\nA real readiness probe, not a liveness stub: 200 only when the store is open\nAND a master key is configured, with `signing` reporting whether signing keys\nare set up too. Anything less answers 503 with `ready:false` and the reason —\nno in-process store, or no master key — which are exactly the two states in\nwhich the secret operations refuse.\n\nNot token-gated, because the platform must be able to probe it without a\ncredential. It reports the broker's configuration state only; no secret, no\nkey material and no tenant name appears in it.","tags":["kms"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsHealth"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsHealth"}}},"description":"service unavailable"}},"x-app":"kms","x-public":true,"x-tool":true}},"/v1/kms/secrets":{"get":{"operationId":"get_kms_secrets","summary":"Lists the secrets your org holds, without their values.","description":"Lists the secrets your org holds, without their values.\n\nReturns the METADATA of the caller's own secrets: each one's name, path,\nenvironment and sealing scheme. No value and no ciphertext is included — this\noperation exists to enumerate what is held, and reading a value is a separate,\nper-secret call.\n\nScoped to the caller's own org and nothing else, structurally: there is no org\nin the path, the store root is derived from the validated org claim, and a\ncaller therefore has no way to name another tenant's namespace. `path` narrows\nto a subpath and `env` selects the environment; both are also accepted under\nthe operator's spellings, `secretPath` and `environment`. An omitted `env`\nmeans every environment and an omitted `path` means the whole org, because a\ndefault here reported a populated store as empty.\n\nAdmission is fail-closed and in order: a validated member, an org that is a\nDNS-1123 label, and a store holding a master key — 403, 400 and 503\nrespectively, all decided before any record is touched.","tags":["kms"],"parameters":[{"name":"env","in":"query","required":false,"schema":{"type":"string"}},{"name":"environment","in":"query","required":false,"schema":{"type":"string"}},{"name":"path","in":"query","required":false,"schema":{"type":"string"}},{"name":"secretPath","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsSecrets"}}},"description":"ok"}},"x-app":"kms","x-public":true,"x-tool":true},"post":{"operationId":"post_kms_secrets","summary":"Stores or replaces one secret in your org.","description":"Stores or replaces one secret in your org.\n\nUpserts one secret under the caller's own org. The value is sealed before it\nis written — a fresh per-secret data key, itself wrapped by the master key —\nso plaintext never reaches disk. The receipt confirms the name and environment\nthat 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\ngot wrong here: reads and deletes still fall back to the default environment\nfor older callers, but a write must not, because the environment is part of\nthe storage key. A silently defaulted write lands in a bucket the readers that\nresolve project, environment and path never look in, and the stale value keeps\nbeing served — so the write fails loudly instead.\n\n`name` is required, `path` is an optional subpath beneath the org root, and\nthe org is taken from the validated claim rather than the body.\n\nRequires ADMIN authority over the org — a member reads, an admin writes. A\nmachine credential holds no membership and so is never an org admin: it can\nread the secrets it was issued for and cannot replace one. Fail-closed\nadmission, in order: admin of the org, well-formed org, master key present —\n403, 400 and 503, all decided before any record is touched.","tags":["kms"],"requestBody":{"content":{"application/json":{"example":{"env":"prod","name":"deploy-token","path":"/ci","value":"s3cr3t"},"schema":{"$ref":"#/components/schemas/kmsPut"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kmsStored"}}},"description":"ok"}},"x-app":"kms","x-public":true,"x-tool":true}},"/v1/knowledge/connectors":{"get":{"operationId":"get_knowledge_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":["knowledge"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kbConnectorsOut"}}},"description":"ok"}},"x-app":"knowledge","x-public":true,"x-tool":true}},"/v1/knowledge/connectors/catalog":{"get":{"operationId":"get_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/connectors/{provider}":{"delete":{"operationId":"delete_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/connectors/{provider}/callback":{"get":{"operationId":"get_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/connectors/{provider}/connect":{"get":{"operationId":"get_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/connectors/{provider}/sync":{"post":{"operationId":"post_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/graph":{"get":{"operationId":"get_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/knowledge/import":{"post":{"operationId":"post_knowledge_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":["knowledge"],"x-app":"knowledge","x-public":true}},"/v1/knowledge/search":{"post":{"operationId":"post_knowledge_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":["knowledge"],"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","x-public":true,"x-tool":true}},"/v1/kv/{bucket}":{"delete":{"operationId":"delete_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":["kv"],"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":"kv","x-public":true,"x-tool":true},"post":{"operationId":"post_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":["kv"],"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":"kv","x-public":true,"x-tool":true}},"/v1/kv/{bucket}/{key}":{"delete":{"operationId":"delete_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":["kv"],"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":"kv","x-public":true,"x-tool":true},"get":{"operationId":"get_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":["kv"],"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":"kv","x-public":true,"x-tool":true},"put":{"operationId":"put_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":["kv"],"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":"kv","x-public":true,"x-tool":true}},"/v1/kv/{bucket}/{key}/history":{"get":{"operationId":"get_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":["kv"],"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":"kv","x-public":true,"x-tool":true}},"/v1/label":{"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":["label"],"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","x-public":true,"x-tool":true},"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":["label"],"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","x-public":true,"x-tool":true}},"/v1/label/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":["label"],"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","x-public":true,"x-tool":true}},"/v1/label/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":["label"],"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","x-public":true,"x-tool":true}},"/v1/label/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":["label"],"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","x-public":true,"x-tool":true}},"/v1/label/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":["label"],"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","x-public":true,"x-tool":true}},"/v1/label/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":["label"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/riskLabelVocabulary"}}},"description":"ok"}},"x-app":"label","x-public":true,"x-tool":true}},"/v1/leaderboard":{"get":{"operationId":"get_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":["leaderboard"],"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","x-public":true,"x-tool":true}},"/v1/leaderboard/activity":{"get":{"operationId":"get_leaderboard_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":["leaderboard"],"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","x-public":true,"x-tool":true}},"/v1/leaderboard/optin":{"get":{"operationId":"get_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":["leaderboard"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/optinView"}}},"description":"ok"}},"x-app":"leaderboard","x-public":true,"x-tool":true},"put":{"operationId":"put_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":["leaderboard"],"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","x-public":true,"x-tool":true}},"/v1/leaderboard/optin/org":{"put":{"operationId":"put_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":["leaderboard"],"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","x-public":true,"x-tool":true}},"/v1/legal/documents":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/legal/documents/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/legal/documents/{id}/sign":{"post":{"operationId":"post_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":"[email protected]","name":"Ada"}]},"schema":{"$ref":"#/components/schemas/signRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/signReply"}}},"description":"ok"}},"x-app":"legal","x-public":true,"x-tool":true}},"/v1/legal/documents/{id}/sign/complete":{"post":{"operationId":"post_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","x-public":true}},"/v1/legal/filings":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/legal/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/legal/templates":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/legal/templates/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/licensing/download/{release}":{"get":{"operationId":"get_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":"X-License-Token","in":"header","required":false,"schema":{"type":"string"}},{"name":"token","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.ReleaseAsset"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/fingerprint":{"post":{"operationId":"post_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/licensing.FingerprintRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.FingerprintResponse"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/healthz":{"get":{"operationId":"get_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/licensing.HealthView"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/issue":{"post":{"operationId":"post_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/licensing.IssueRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.IssueResponse"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/jwks":{"get":{"operationId":"get_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/licensing.PubkeyView"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/pubkey":{"get":{"operationId":"get_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/licensing.PubkeyView"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/releases":{"get":{"operationId":"get_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/licensing.ReleaseList"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true},"post":{"operationId":"post_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/licensing.Release"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.Release"}}},"description":"created"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/releases/{release}":{"get":{"operationId":"get_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/licensing.Release"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/revoke":{"post":{"operationId":"post_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/licensing.RevokeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.RevokeResponse"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/licensing/verify":{"post":{"operationId":"post_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/licensing.VerifyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/licensing.VerifyResponse"}}},"description":"ok"}},"x-app":"licensing","x-public":true,"x-tool":true}},"/v1/link":{"get":{"operationId":"get_link","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":["link"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/linkList"}}},"description":"ok"}},"x-app":"link","x-public":true,"x-tool":true},"post":{"operationId":"post_link","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":["link"],"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","x-public":true,"x-tool":true}},"/v1/link/devices/{machine}":{"get":{"operationId":"get_link_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":["link"],"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","x-public":true,"x-tool":true}},"/v1/link/devices/{machine}/revoke":{"post":{"operationId":"post_link_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":["link"],"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","x-public":true,"x-tool":true}},"/v1/link/route":{"get":{"operationId":"get_link_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":["link"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutePlan"}}},"description":"ok"}},"x-app":"link","x-public":true,"x-tool":true}},"/v1/link/usage":{"get":{"operationId":"get_link_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":["link"],"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","x-public":true,"x-tool":true},"post":{"operationId":"post_link_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":["link"],"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","x-public":true,"x-tool":true}},"/v1/link/usage/accounts":{"get":{"operationId":"get_link_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":["link"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountsUsage"}}},"description":"ok"}},"x-app":"link","x-public":true,"x-tool":true}},"/v1/link/usage/summary":{"get":{"operationId":"get_link_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":["link"],"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","x-public":true,"x-tool":true}},"/v1/link/{id}":{"delete":{"operationId":"delete_link_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":["link"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_link_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":["link"],"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","x-public":true,"x-tool":true}},"/v1/lsp/complete":{"post":{"operationId":"post_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":["lsp"],"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","x-public":true,"x-tool":true}},"/v1/lsp/diagnostics":{"post":{"operationId":"post_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":["lsp"],"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","x-public":true,"x-tool":true}},"/v1/lsp/hover":{"post":{"operationId":"post_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":["lsp"],"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","x-public":true,"x-tool":true}},"/v1/lsp/locate":{"post":{"operationId":"post_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":["lsp"],"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","x-public":true,"x-tool":true}},"/v1/lsp/symbols":{"post":{"operationId":"post_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":["lsp"],"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","x-public":true,"x-tool":true}},"/v1/marketing/audiences":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/audiences/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/audiences/{id}/preview":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/calendar":{"get":{"operationId":"get_marketing_calendar","summary":"Returns the org's calendar, latest scheduled first, optionally narrowed to one status.","description":"Returns the org's calendar, latest 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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/calendar/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/marketing/calendar/{id}/publish":{"post":{"operationId":"post_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 client 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","x-public":true,"x-tool":true}},"/v1/marketing/campaigns":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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 >= 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","x-public":true,"x-tool":true}},"/v1/marketing/campaigns/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/marketing/campaigns/{id}/schedule":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/promos":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/promos/{code}/eligibility":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/promos/{code}/redeem":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/promos/{code}/redemption":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}/enroll":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}/enrollments":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}/enrollments/{eid}/cancel":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}/status":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketing/sequences/{id}/steps":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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 >= 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","x-public":true,"x-tool":true}},"/v1/marketing/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketing/suppressions":{"delete":{"operationId":"delete_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":"[email protected]"},{"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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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":"[email protected]","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","x-public":true,"x-tool":true}},"/v1/marketing/unsubscribe":{"get":{"operationId":"get_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":"[email protected]"},{"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":"[email protected]","channel":"email","unsubscribed":true},"schema":{"$ref":"#/components/schemas/Unsubscribed"}}},"description":"ok"}},"x-app":"marketing","x-public":true,"x-tool":true}},"/v1/marketplace":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/marketplace/install":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/marketplace/listings":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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 client 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","x-public":true,"x-tool":true}},"/v1/marketplace/listings/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/marketplace/uninstall":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/mcp":{"post":{"operationId":"post_mcp","summary":"The agent endpoint: every subsystem's operations as MCP tools","description":"Model Context Protocol over JSON-RPC 2.0 — one POST per message, stateless, protocol revision 2026-07-28. tools/list answers without a credential with one tool per subsystem (its operations in the \"op\" enum) plus \"describe\", which returns one operation's input schema. tools/call names a subsystem tool and carries {\"op\": <operation>, \"input\": <its arguments>}; it takes the same bearer the REST API does, and a call that carries none is answered 401 with a WWW-Authenticate header naming the resource metadata at /.well-known/oauth-protected-resource, which names the authorization server to sign in at. The tool surface is the public contract: the operator's admin product is not offered, and a name that would disclose a secret or mutate an identity is withheld — the list says how many, under _meta.","tags":["openapi"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPRequest"}}}},"responses":{"2XX":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPResponse"}}}}},"x-app":"openapi","x-public":true}},"/v1/meet/getToken":{"post":{"operationId":"post_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 `<workspace>_<room>_<id>`, 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","x-public":true}},"/v1/meet/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/meet/record":{"delete":{"operationId":"meetRecordStop","summary":"Stop a room's recording","description":"Ends a room's recording — EVERY one of them.\n\nWhoever the room admits may stop it, including someone who did not start it:\na person being recorded has to be able to end it, and a rule that only the\nstarter may stop would deny exactly that. Stopping is free — a caller made to\npay to stop being recorded would be paying for the wrong thing.\n\n200 MEANS THE ROOM IS NOT BEING RECORDED, and that is why this ends all of them\nrather than the first. \"At most one per room\" is an invariant this surface wants\nand cannot impose: reading the list and starting are two calls, and two replicas\nracing through that window both start. When the list comes back holding two, two\nis the truth — and ending one while answering 200 tells the person withdrawing\nconsent that it stopped while a second worker keeps writing. A stop that cannot\nfinish the job says so instead.\n\nStopping a room that is not being recorded is not an error. The answer names the\nroom with no recording on it, which is the state the caller asked for.","tags":["meet"],"parameters":[{"name":"room","in":"query","required":true,"description":"Room is the LiveKit room, named the way the office client names one\n(`<workspace>_<name>_<id>`). Its leading segment is what binds the room to a\ntenant, and it is the segment the caller's membership is checked against.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/recording"}}},"description":"ok"}},"x-app":"meet","x-public":true,"x-tool":true},"get":{"operationId":"meetRecordRead","summary":"What is being recorded in a room, and where the file goes","description":"Answers what is being recorded in a room, and where the file went.\n\nIt reports the recording that is RUNNING, and once none is, the most recent one\nthe media server still holds — with its final status and its object. That second\ncase is the one that matters for finding a file: the answer to a start is the\nonly other place the location appears, and a client that lost it, or a colleague\nwho was not the one to press record, has nowhere else to look.\n\nIt is behind the same check as starting one: where a recording of a private\nconversation is kept is a fact about that conversation, so it is told to the\npeople the room admits and to nobody else.","tags":["meet"],"parameters":[{"name":"room","in":"query","required":true,"description":"Room is the LiveKit room, named the way the office client names one\n(`<workspace>_<name>_<id>`). Its leading segment is what binds the room to a\ntenant, and it is the segment the caller's membership is checked against.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/recording"}}},"description":"ok"}},"x-app":"meet","x-public":true,"x-tool":true},"post":{"operationId":"meetRecordStart","summary":"Start recording a room, or return the recording already running","description":"Begins recording a room, or hands back the recording already running.\n\nA recording is a durable artifact of a conversation, so only someone this room\nwould admit may make one: the caller is authorized by the SAME decision\n/v1/meet/getToken makes about the same room, and refused with the same 401.\n\nA SECOND START RETURNS THE FIRST rather than refusing it. There is at most one\nrecording per room and this operation's job is to establish that there is one —\nwhich is already true when a colleague, or the caller's own double-click,\nstarted it a moment ago. The answer is the same shape either way, naming the\nrecording that is actually running, so a client never has to tell the two cases\napart to find the id.\n\nA deployment with no media server address or no object store answers 503 naming\nwhich, because a recording that silently does not happen is worse than one that\nis refused. The reason reaches only a caller this room already admits.","tags":["meet"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/recordIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/recording"}}},"description":"ok"}},"x-app":"meet","x-public":true,"x-tool":true}},"/v1/meet/session":{"get":{"operationId":"get_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","x-public":true}},"/v1/messages":{"post":{"operationId":"post_messages","summary":"Implements the Anthropic Messages API.","description":"Implements the Anthropic Messages API.","tags":["ai"],"x-app":"ai","x-public":true}},"/v1/messages/count_tokens":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/metrics/batch":{"post":{"operationId":"post_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 endpoint 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 `<name>_sum` and `<name>_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","x-public":true}},"/v1/metrics/health":{"get":{"operationId":"get_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","x-public":true}},"/v1/metrics/logs/health":{"get":{"operationId":"get_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/logs/query":{"get":{"operationId":"get_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/logs/write":{"post":{"operationId":"post_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/query":{"get":{"operationId":"get_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","x-public":true}},"/v1/metrics/traces/health":{"get":{"operationId":"get_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/traces/query":{"get":{"operationId":"get_metrics_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/metrics/traces/trace. The tenant is the gateway-minted `X-Org-Id` header, falling back to the deployment brand and then `default`.","tags":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/traces/trace":{"get":{"operationId":"get_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/traces/write":{"post":{"operationId":"post_metrics_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":["metrics"],"x-app":"metrics","x-public":true}},"/v1/metrics/write":{"post":{"operationId":"post_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","x-public":true}},"/v1/ml/health":{"get":{"operationId":"get_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","x-public":true}},"/v1/ml/models":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_ml_models","summary":"Deploys one inference model for the caller's org, and answers 201 with the model as Kubernetes admitted it.","description":"Deploys one inference model for the caller's org, and answers 201\nwith the model as Kubernetes admitted it.\n\nThe `spec` is a kserve InferenceService spec, passed through unchanged — this\nplane owns the tenancy, the billing and the namespace, and kserve owns what a\nmodel IS. An unfunded org is refused BEFORE anything is created, so nobody runs\nfree GPU compute and nobody is charged for a resource that was never made.","tags":["ml"],"requestBody":{"content":{"application/json":{"example":{"name":"sentiment","spec":{"predictor":{"model":{"modelFormat":{"name":"sklearn"},"storageUri":"s3://models/sentiment"}}}},"schema":{"$ref":"#/components/schemas/mlCreate"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mlResource"}}},"description":"created"}},"x-app":"ml","x-public":true,"x-tool":true}},"/v1/ml/models/{name}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true}},"/v1/ml/models/{name}/predict":{"post":{"operationId":"post_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","x-public":true}},"/v1/models":{"get":{"operationId":"get_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":["ai"],"x-app":"ai","x-public":true}},"/v1/models/providers":{"get":{"operationId":"get_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":["ai"],"x-app":"ai","x-public":true}},"/v1/models/{model}/access":{"get":{"operationId":"get_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":["ai"],"parameters":[{"name":"model","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai","x-public":true},"post":{"operationId":"post_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":["ai"],"parameters":[{"name":"model","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai","x-public":true}},"/v1/mq/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/mq/info":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/mq/stream":{"get":{"operationId":"get_mq_stream","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","x-public":true,"x-tool":true},"post":{"operationId":"post_mq_stream","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","x-public":true,"x-tool":true}},"/v1/mq/stream/{name}":{"delete":{"operationId":"delete_mq_stream_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","x-public":true,"x-tool":true},"get":{"operationId":"get_mq_stream_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","x-public":true,"x-tool":true},"put":{"operationId":"put_mq_stream_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","x-public":true,"x-tool":true}},"/v1/mq/stream/{name}/message":{"get":{"operationId":"get_mq_stream_by_name_message","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","x-public":true,"x-tool":true}},"/v1/mq/stream/{name}/message/{seq}":{"delete":{"operationId":"delete_mq_stream_by_name_message_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","x-public":true,"x-tool":true}},"/v1/mq/stream/{name}/purge":{"post":{"operationId":"post_mq_stream_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","x-public":true,"x-tool":true}},"/v1/mq/stream/{stream}/consumer":{"get":{"operationId":"get_mq_stream_by_stream_consumer","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","x-public":true,"x-tool":true},"post":{"operationId":"post_mq_stream_by_stream_consumer","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","x-public":true,"x-tool":true}},"/v1/mq/stream/{stream}/consumer/{name}":{"delete":{"operationId":"delete_mq_stream_by_stream_consumer_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","x-public":true,"x-tool":true},"get":{"operationId":"get_mq_stream_by_stream_consumer_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","x-public":true,"x-tool":true}},"/v1/mq/stream/{stream}/consumer/{name}/next":{"post":{"operationId":"post_mq_stream_by_stream_consumer_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","x-public":true,"x-tool":true}},"/v1/network":{"get":{"operationId":"get_network","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-<org>\" 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":["network"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/networkList"}}},"description":"ok"}},"x-app":"network","x-public":true,"x-tool":true}},"/v1/network/routers":{"get":{"operationId":"get_network_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-<org>\" 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-<slug>\" 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":["network"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/routerList"}}},"description":"ok"}},"x-app":"network","x-public":true,"x-tool":true}},"/v1/network/services":{"get":{"operationId":"get_network_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-<org>\" 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":["network"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/meshServiceList"}}},"description":"ok"}},"x-app":"network","x-public":true,"x-tool":true}},"/v1/network/{id}":{"get":{"operationId":"get_network_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":["network"],"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":"network","x-public":true,"x-tool":true}},"/v1/node":{"get":{"operationId":"get_node","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":["node"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/nodesView"}}},"description":"ok"}},"x-app":"node","x-public":true,"x-tool":true}},"/v1/node/connect":{"get":{"operationId":"get_node_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":["node"],"x-app":"node","x-public":true}},"/v1/node/peer/invoke":{"post":{"operationId":"post_node_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":["node"],"x-app":"node","x-public":true}},"/v1/node/{id}/invoke":{"post":{"operationId":"post_node_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":["node"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"node","x-public":true}},"/v1/notify/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/notify/send":{"post":{"operationId":"post_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/<org>/notify/<service>/<key>, 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","x-public":true,"x-tool":true}},"/v1/notify/send/email":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/notify/send/sms":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/alerts/last":{"get":{"operationId":"get_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","x-public":true}},"/v1/o11y/alerts/{receiver}":{"post":{"operationId":"post_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","x-public":true}},"/v1/o11y/api/{project_id}/envelope/":{"post":{"operationId":"post_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/<project>/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/event/{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","x-public":true}},"/v1/o11y/api/{project_id}/store/":{"post":{"operationId":"post_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","x-public":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/auto_complete/attribute_values":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/autocomplete/aggregate_attributes":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/autocomplete/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/autocomplete/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/availability":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/clusters/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/clusters/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/clusters/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/complete/google":{"get":{"operationId":"get_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 endpoint — the code it carries is single-use and verified against the provider.","tags":["o11y"],"x-app":"o11y","x-public":true}},"/v1/o11y/complete/oidc":{"get":{"operationId":"get_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","x-public":true}},"/v1/o11y/complete/saml":{"post":{"operationId":"post_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","x-public":true}},"/v1/o11y/countErrors":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/daemonsets/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/daemonsets/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/daemonsets/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/o11y/dependency_graph":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/deployments/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/deployments/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/deployments/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/disks":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/o11y/errorFromErrorID":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/errorFromGroupID":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/errortracking/issues":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/errortracking/issues/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/event":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/explorer/views":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/explorer/views/{viewId}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/o11y/export_raw_data":{"post":{"operationId":"post_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","x-public":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/features":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/fields/keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/fields/values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/filter_suggestions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/global/config":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/healthz":{"get":{"operationId":"get_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","x-public":true}},"/v1/o11y/hosts/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/hosts/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/hosts/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/checks":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/clusters":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/daemonsets":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/deployments":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/hosts":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/jobs":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/namespaces":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/nodes":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/pods":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/pvcs":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_monitoring/statefulsets":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/infra_onboarding/k8s/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/jobs/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/jobs/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/jobs/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/licenses":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/licenses/active":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/listErrors":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/livez":{"get":{"operationId":"get_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","x-public":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/o11y/login":{"get":{"operationId":"get_o11y_login","summary":"Report why an observability sign-in did not complete","description":"Where a failed sign-in callback lands. The module builds that redirect with a path and no host, so it can only be same-origin, and its assumption is that the console lives beside the API. Here the console is a separate host, so this answers as the API it belongs to rather than redirecting onward — a hostname baked into a shared surface is how one brand's identity ends up in front of another brand's customer.\n\n401 with the provider's own reason, carried from the query the module already populated. Unauthenticated by necessity: it exists precisely for the case where no principal was established.","tags":["o11y"],"x-app":"o11y","x-public":true}},"/v1/o11y/logs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/aggregate":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/fields":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/livetail":{"get":{"operationId":"get_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","x-public":true}},"/v1/o11y/logs/pipelines":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/pipelines/preview":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/pipelines/{version}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/logs/promote_paths":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/consumer-lag/consumer-details":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/consumer-lag/network-latency":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/consumer-lag/producer-details":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/onboarding/consumers":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/onboarding/kafka":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/onboarding/producers":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/partition-latency/consumer":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/partition-latency/overview":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/span/evaluation":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/topic-throughput/consumer":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/topic-throughput/consumer-details":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/topic-throughput/producer":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/kafka/topic-throughput/producer-details":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/messaging-queues/queue-overview":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/metric/metric_metadata":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/namespaces/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/namespaces/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/namespaces/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/nextPrevErrorIDs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/nodes/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/nodes/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/nodes/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/pods/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/pods/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/pods/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/processes/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/processes/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/processes/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/product/metrics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/pvcs/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/pvcs/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/pvcs/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/query":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/query_filter/analyze":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/query_progress":{"get":{"operationId":"get_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\nONE ADDRESS, TWO PROTOCOLS. Send an Upgrade and this is a websocket carrying the same progress; send an ordinary GET and it is a long poll. The Upgrade is a property of the request, not of the address, so the read that used to answer at /ws/query_progress answers here.\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, and an upgraded connection has no JSON response to declare.\n\nA validated, org-scoped principal is required; a query id belonging to another tenant is simply not found.","tags":["o11y"],"x-app":"o11y","x-public":true}},"/v1/o11y/query_range":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/query_range/format":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/query_range/preview":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/readyz":{"get":{"operationId":"get_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","x-public":true}},"/v1/o11y/register":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/reviews":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/reviews/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/o11y/reviews/{id}/items":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/reviews/{id}/items/{itemId}":{"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/discover":{"post":{"operationId":"post_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/events/{id}":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/issues":{"get":{"operationId":"get_o11y_sentinel_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":["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"}},{"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/issues/{id}":{"get":{"operationId":"get_o11y_sentinel_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":["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","x-public":true,"x-tool":true},"put":{"operationId":"put_o11y_sentinel_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.O11ySentryUpdateIssueIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11yErrorIssueOut"}}},"description":"ok"}},"x-app":"o11y","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/issues/{id}/events":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/logs":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/projects":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.O11ySentryProjectsOut"}}},"description":"ok"}},"x-app":"o11y","x-public":true,"x-tool":true},"post":{"operationId":"post_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/projects/{id}":{"delete":{"operationId":"delete_o11y_sentinel_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":["o11y"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the project id.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"o11y","x-public":true,"x-tool":true},"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/projects/{id}/keys/rotate":{"post":{"operationId":"post_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/stats":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/traces":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/sentinel/traces/{id}":{"get":{"operationId":"get_o11y_sentinel_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":["o11y"],"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","x-public":true,"x-tool":true}},"/v1/o11y/service/entry_point_operations":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/service/top_level_operations":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/service/top_operations":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/services":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/services/list":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/settings/apdex":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/settings/ttl":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/o11y/span_percentile":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/statefulsets/attribute_keys":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/statefulsets/attribute_values":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/statefulsets/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/stats":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/o11y/substitute_vars":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/summary":{"get":{"operationId":"get_o11y_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":["o11y"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/o11y.StatusSummary"}}},"description":"ok"}},"x-app":"o11y","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/third-party-apis/overview/domain":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/third-party-apis/overview/list":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/traces":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/usage":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/o11y/variables/query":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/o11y/version":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/v1/openapi.json":{"get":{"operationId":"get_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 public endpoint it is the composed 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.","tags":["openapi"],"security":[],"x-app":"openapi","x-public":true}},"/v1/openapi/commands":{"get":{"operationId":"get_openapi_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":["openapi"],"security":[],"x-app":"openapi","x-public":true}},"/v1/plan":{"get":{"operationId":"get_plan","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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/blockchain":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/dns":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/entitlements/{id}":{"get":{"operationId":"get_plan_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":["plan"],"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","x-public":true,"x-tool":true}},"/v1/plan/gpu":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planTierList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/health":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"example":{"service":"plans","status":"ok"},"schema":{"$ref":"#/components/schemas/planHealth"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/policy":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/regions":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planRegionList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/resolve/{id}":{"get":{"operationId":"get_plan_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":["plan"],"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","x-public":true,"x-tool":true}},"/v1/plan/schema":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planSchemas"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/storage":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/subscriptions":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/tools":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planToolList"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/plan/vocab":{"get":{"operationId":"get_plan_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":["plan"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/planVocab"}}},"description":"ok"}},"x-app":"plan","x-public":true,"x-tool":true}},"/v1/platform/apps":{"get":{"operationId":"get_platform_apps","summary":"Answers what this organisation has declared, joined with what the delivery plane has done about it.","description":"Answers what this organisation has declared, joined with what\nthe delivery plane has done about it.\n\nThe join is best-effort BY DESIGN and says so when it is missing: the\ndeclarations ARE the answer to \"what have I deployed\", so refusing the whole\nboard because the cluster is unreadable would lose the half that is readable.\nWhat must never happen is a silent null — an unreadable plane is reported as\n`cd.unavailable` carrying the reason, never as an app with no reconciliation.","tags":["platform"],"parameters":[{"name":"org","in":"query","required":false,"description":"Org names the organisation whose declarations to read, defaulting to the\ncaller's own. Only a SuperAdmin may name one that is not theirs; anyone\nelse naming a foreign org is refused, so this widens nothing by itself.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/declaredResp"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true},"post":{"operationId":"post_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/<namespace>/<name>.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/<namespace>/<name>/<tag>` 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 `<org>`, and the image is `<registry>/<org>/<app>`. 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","x-public":true}},"/v1/platform/apps/{app}":{"get":{"operationId":"get_platform_apps_by_app","summary":"Answers ONE declaration — what git says this app is, before the delivery plane has had any say in it.","description":"Answers ONE declaration — what git says this app is, before the\ndelivery plane has had any say in it.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"description":"App is the DNS-1123 label of the declaration. The URL is the addressing\nauthority — a path segment binds after the body and after the query — so\nthe address decides which app is read whatever else is sent.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org names the organisation the declaration lives in, defaulting to the\ncaller's own and subject to the same SuperAdmin rule as the listing.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Declaration"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/apps/{app}/cd":{"get":{"operationId":"get_platform_apps_by_app_cd","summary":"Answers ONE app's reconciliation alone — the poll a deploy console makes while it waits, without re-reading the whole inventory each time.","description":"Answers ONE app's reconciliation alone — the poll a deploy\nconsole makes while it waits, without re-reading the whole inventory each time.","tags":["platform"],"parameters":[{"name":"app","in":"path","required":true,"description":"App is the DNS-1123 label of the declaration. The URL is the addressing\nauthority — a path segment binds after the body and after the query — so\nthe address decides which app is read whatever else is sent.","schema":{"type":"string"}},{"name":"org","in":"query","required":false,"description":"Org names the organisation the declaration lives in, defaulting to the\ncaller's own and subject to the same SuperAdmin rule as the listing.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CDApp"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/builds":{"get":{"operationId":"get_platform_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":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildBoard"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/cd":{"get":{"operationId":"get_platform_cd","summary":"Answers every Application the delivery plane holds.","description":"Answers every Application the delivery plane holds.\n\nScoped to the namespaces the caller's own validated org owns: the ROLE admits\nthe caller and the tenant boundary is applied inside, so an admin of one org\nnever observes another's.","tags":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/cdResp"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/ci":{"get":{"operationId":"get_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","x-public":true}},"/v1/platform/environments":{"get":{"operationId":"get_platform_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":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/environmentBoard"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/fleet":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/fleet/{app}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/fleet/{app}/deploy":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/hook":{"post":{"operationId":"post_platform_hook","summary":"Receive a push from the forge and trigger its build","description":"The forge's push-to-deploy endpoint. git.hanzo.ai runs as a separate server, so its pushes never reach this fleet's own receive-pack; without this a push to the host we call canonical builds nothing. A verified push is handed to the SAME two clients a native push travels — the single-registrant deploy trigger, and the many-subscriber lifecycle stream that notifies and indexes — and the build decision itself stays downstream in the one place that knows what a push means.\n\nPUBLIC at the JWT layer, because the forge carries no Hanzo session: AUTHENTICATION IS THE SIGNATURE. The HMAC covers the raw bytes and is verified BEFORE the payload is parsed, so an unauthenticated body is never decoded. The secret is read from KMS; a deployment that cannot read it answers 503 and processes nothing, rather than trusting a delivery it could not check. The body is read UNCOMPRESSED — a request declaring a Content-Encoding is refused 415 before it is touched, because decoding one is unbounded work bought with a few bytes and no credential. A bad signature is 401, a payload over 8 MiB is 413, and a malformed one 400.\n\nA verified push that reaches both clients answers 200 with fired true and the NUMBER OF BUILDS it launched — zero is ordinary, since most pushes track no application, and it is the answer 'fired' cannot give. A push that could not be dispatched answers 500: the delivery page shows it red, and the Replay that prompts reaches a fresh attempt rather than being declined as already landed.\n\nThe deliveries deliberately ignored answer 200 with a reason and nothing else: a payload that is not a push, a ref DELETE (a zero `after` has no commit to build), a BOT-authored push (release automation pushes as the forge's own Actions user, and a release must never rebuild itself), a push from a forge namespace that maps to no org, and a redelivery of a push already fired. Branches and tags both reach the build trigger, because releases are cut by tag and filtering here would silently stop publishing.","tags":["platform"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/push"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/verdict"}}},"description":"Success"}},"x-app":"platform","x-public":true}},"/v1/platform/pipelines":{"get":{"operationId":"get_platform_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":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pipelineBoard"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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-<org>` 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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/deploy":{"post":{"operationId":"post_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-<org>`\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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/deployments":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/deployments/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/deployments/{id}/logs":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/domains":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/domains/{host}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/domains/{host}/verify":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/env":{"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/preview":{"post":{"operationId":"post_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 `<app>-<branch>` 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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/promote":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/rollback":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/start":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/projects/{project}/apps/{app}/stop":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/platform/releases":{"get":{"operationId":"get_platform_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":["platform"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/releaseBoard"}}},"description":"ok"}},"x-app":"platform","x-public":true,"x-tool":true}},"/v1/platform/run":{"post":{"operationId":"post_platform_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":["platform"],"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","x-public":true,"x-tool":true}},"/v1/platform/runner":{"post":{"operationId":"post_platform_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` and git-push-to-deploy\ncall. It answers 202 with the build job id: a queued build, not a pushed\nartifact.\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.\n\nPRIVILEGED, and A BUILD BELONGS TO THE ORGANIZATION ITS CREDENTIAL NAMES. Two\ncredentials, never a third:\n\n - one that NAMES an organization — a person who administers it (the `hanzo\n build` path, so one IAM login authorizes a build with no separate build\n token), or that organization's own machine identity (the pipeline path). The\n build is attributed to that org and confined to what it owns.\n - the shared build-callback token, compared in constant time. It names NO\n organization, which is both why the fabric's own release can publish across\n brands with it and why anything that CAN name one is read first.\n\nBoth are bounded by the owned-registry allowlist. The org path is bounded again,\nby the org: the image's registry namespace must be one that organization owns, so\nit publishes into its own brand and can never overwrite another's through the\nshared push credential. The same confinement applies to the artifact lane's repo\nowner. There is no request field naming an organization — the attribution is read\noff the credential, so there is nothing for a caller to write it with.\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":["platform"],"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","x-public":true,"x-tool":true}},"/v1/pref":{"get":{"operationId":"get_pref","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":["pref"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/prefsView"}}},"description":"ok"}},"x-app":"pref","x-public":true,"x-tool":true},"patch":{"operationId":"patch_pref","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 `<owner>/<name>` 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":["pref"],"x-app":"pref","x-public":true}},"/v1/pricing":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/base":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/blockchain":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/cloud":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/cloud/plans":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/cloud/regions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/cloud/storage":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/compute":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/compute/presets":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/datastore":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/enablement":{"get":{"operationId":"get_pricing_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":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/enablementBoard"}}},"description":"ok"}},"x-app":"pricing","x-public":true,"x-tool":true}},"/v1/pricing/enablement/optin":{"post":{"operationId":"post_pricing_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":["pricing"],"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","x-public":true,"x-tool":true}},"/v1/pricing/enablement/optout":{"post":{"operationId":"post_pricing_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":["pricing"],"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","x-public":true,"x-tool":true}},"/v1/pricing/featured":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/free":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/gpu":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/iam":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/model/{name}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/models":{"get":{"operationId":"get_pricing_models","summary":"Returns the whole model catalog — every model the gateway serves, Zen and third-party alike — filtered to what the caller's org may see.","description":"Returns the whole model catalog — every model the gateway serves,\nZen and third-party alike — filtered to what the caller's org may see. A\nmodel an admin has disabled is absent; one in beta appears only for an org\ngranted it. A SuperAdmin sees every model, each annotated with its\nenablement state.","tags":["pricing"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/pricingModelList"}}},"description":"ok"}},"x-app":"pricing","x-public":true,"x-tool":true}},"/v1/pricing/paas":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/policy":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/providers":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/services":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/subscriptions":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/summary":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/pricing/sync":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/pricing/tools":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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`<slug>.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","x-public":true,"x-tool":true}},"/v1/projects/edge":{"get":{"operationId":"get_projects_edge","summary":"health reports whether a publish reaches readers, rather than whether it was accepted.","description":"health reports whether a publish reaches readers, rather than whether it was\naccepted. Those are different questions and only the second one was ever\nvisible.\n\nIt asks the edge and nothing else. There is no live call to the provider here:\nConfigured is a local fact, it is the fact that was missing, and a health check\nthat spends a third-party API call is one an operator learns not to run.","tags":["projects"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/edgeState"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/edgeState"}}},"description":"service unavailable"}},"x-app":"projects","x-public":true,"x-tool":true}},"/v1/projects/fork":{"post":{"operationId":"post_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 <slug>.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 <slug>.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 <slug>.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","x-public":true,"x-tool":true}},"/v1/projects/sites":{"get":{"operationId":"get_projects_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":["projects"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/projectsSite"},"type":"array"}}},"description":"ok"}},"x-app":"projects","x-public":true,"x-tool":true},"post":{"operationId":"post_projects_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 entry point to 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":["projects"],"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","x-public":true,"x-tool":true}},"/v1/projects/sites/deploy":{"post":{"operationId":"post_projects_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":["projects"],"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","x-public":true,"x-tool":true}},"/v1/projects/sites/{slug}":{"get":{"operationId":"get_projects_sites_by_slug","summary":"Returns one site — the same row ListSites carries, for one slug.","description":"Returns one site — the same row ListSites carries, for one slug.\n\nEvery sub-resource under a site already answered: deployments, releases,\npublish. The site itself did not, and a route that is never registered\nanswers 404 for a LIVE site exactly as it does for one that was never\ncreated. So the one call a client makes to ask \"is it there yet?\" could only\never say no, and a CI lane watching for its own publish would wait forever on\na success it had already achieved.\n\nThe org is the caller's, never a path segment. A slug is unique within an org\nand two orgs may both own `tel`; taking the org from the validated principal\ninstead of the URL means a caller cannot read another org's site by editing a\npath, and it is the same scope ListProjects and ListSites already use.\n\nA site that exists but is not live is NOT found here, matching ListSites,\nwhich keeps only `live` rows so a draft or a failed build is never advertised\nas a site. One definition of \"is a site\", used by both.","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/projectsSite"}}},"description":"ok"}},"x-app":"projects","x-public":true,"x-tool":true}},"/v1/projects/tags":{"get":{"operationId":"get_projects_tags","summary":"The site's browser tag set for the hosted tag — which pixels to inject, by publishable key","description":"Returns the client-side pixels the SITE has connected (GA4, Google Ads, LinkedIn, Meta, Pinterest, Reddit, TikTok, X) with their NON-SECRET ids, so the hosted tag injects them first-party and stamps each browser event with the same event_id the server-side Conversions API uses — deduping the two. Resolved per site: by the publishable key on ?key= when it names a project, else by the request host, so hanzo.ai and hanzo.chat carry different tags under one org. WITHOUT a resolvable site it answers an empty set at 200 — a page never breaks on its tag config.","tags":["projects"],"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/tagConfig"}}},"description":"Success"}},"x-app":"projects","x-public":true}},"/v1/projects/{slug}":{"delete":{"operationId":"delete_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 `<slug>` 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 git source is retired on\nevery copy it has so a reclaimed slug never adopts a repository left behind\n(visibility.go), the S3 origin is purged under BOTH `<org>/<slug>/` and the\nsite's sibling release space, and the edge cache-tag is flushed. A failure in\nany of those is logged and the delete still answers 204 — resurrecting a\nproject because a purge missed would be worse than 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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/deploy":{"post":{"operationId":"post_projects_by_slug_deploy","summary":"Upload a built site as one archive and serve it","description":"Takes a built site live at `https://<slug>.hanzo.app` in one call. The body is the site itself — a `zip` or `tar.gz` holding `index.html` at its root (or a single wrapper directory that does), sent raw or as a multipart file part. It is unpacked to the site's own storage prefix and served immediately, answering the finished deployment.\n\nIt is bounded by the edge body limit (16 MiB by default), and that bound is the whole reason the other path exists: an oversized POST is refused by the server BEFORE any handler runs and surfaces as an opaque `400 Error when parsing request` that reads like a malformed payload rather than a size cap. A site too large for one archive opens a deployment with `POST /v1/projects/{slug}/deployments` instead and writes its files straight to storage against the scoped grant that answers with — no body limit, and no bytes through this API at all.\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 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":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}}},"responses":{"2XX":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployment"}}},"description":"Success"}},"x-app":"projects","x-public":true}},"/v1/projects/{slug}/deployments":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_projects_by_slug_deployments","summary":"Opens a deployment and hands back a short-lived, prefix-scoped grant to write its bytes straight to object storage.","description":"Opens a deployment and hands back a short-lived, prefix-scoped\ngrant to write its bytes straight to object storage. Answers 202.\n\nThis is the path for a site too large to send as one archive: a real export is\nhundreds of megabytes against a 16 MiB body limit, so the bytes deliberately do\nNOT pass through the API. The answer carries `bucket`, `prefix` and `upload` —\na presigned POST policy that S3 itself confines to this site's prefix\n(starts-with `<org>/<slug>/`), expires in 30 minutes and bounds each object.\nSo a build writes its own files and holds no standing bucket credential; there\nis nothing to rotate and nothing that leaks between tenants. Never guess the\nprefix — it is server-derived, and a guessed one lands where nothing is served.\n\nThe deployment is `queued` until POST .../deployments/{id}/complete flips it\nlive (or error). That completion is also where DELETION happens: the grant\nauthorizes writes only, so a build cannot remove a file, and cloud reconciles\nthe prefix against the `keys` manifest the completion carries. A build that\ndies before completing leaves the deployment queued rather than a half-live\nsite.\n\nThe grant is on the 202 and NOWHERE else — it is never stored and never\nreplayed on a later read, so it cannot outlive the build it was minted for. A\ndeployment whose grant could not be minted is still created and still\ncompletable; it simply carries no `upload`, and a caller with no other way to\nwrite should treat that as the failure it is.\n\nBilling: the hosting gate runs BEFORE anything is created (402 unfunded, 503\ncommerce unreachable), and the debit lands on the completion that goes live —\nnever on a queued or failed build.\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 to deploy, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployStart"}}},"required":true},"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/projectsDeployment"}}},"description":"accepted"}},"x-app":"projects","x-public":true,"x-tool":true}},"/v1/projects/{slug}/deployments/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/deployments/{id}/complete":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/domains":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/domains/{host}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/domains/{host}/verify":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/publish":{"post":{"operationId":"post_projects_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":["projects"],"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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/purge":{"post":{"operationId":"post_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-<org>-<slug>` 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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/releases":{"get":{"operationId":"get_projects_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":["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/projectsRelease"},"type":"array"}}},"description":"ok"}},"x-app":"projects","x-public":true,"x-tool":true},"post":{"operationId":"post_projects_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`<org>/.releases/<slug>/<id>/` 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":["projects"],"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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/releases/{release}/activate":{"post":{"operationId":"post_projects_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":["projects"],"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","x-public":true,"x-tool":true}},"/v1/projects/{slug}/shot":{"get":{"operationId":"get_projects_by_slug_shot","summary":"Get a PNG of the project's live site","description":"Returns a screenshot of what this project currently serves, as image/png. The capture is keyed by the deployment, so a redeploy invalidates it by construction rather than by anyone remembering to clear a cache. A project with nothing deployed answers 404 — that is a 404 about the PICTURE and not about the project, which is still right there in the list. Scoped to the caller's org: a validated principal is required, and a slug belonging to another org is not found rather than forbidden.","tags":["projects"],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"projects","x-public":true}},"/v1/projects/{slug}/star":{"delete":{"operationId":"delete_projects_by_slug_star","summary":"Removes the caller's own bookmark from a project, and answers whether it is starred afterwards.","description":"Removes the caller's own bookmark from a project, and answers whether\nit is starred afterwards.\n\nIt removes only YOUR star — the same one star wrote — so a project other\npeople have starred stays on their lists. Unstarring one you had not starred\nis not an error; it leaves it unstarred.","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/projectsStar"}}},"description":"ok"}},"x-app":"projects","x-public":true,"x-tool":true},"put":{"operationId":"put_projects_by_slug_star","summary":"Bookmarks a project for the person calling, and answers whether it is starred afterwards.","description":"Bookmarks a project for the person calling, and answers whether it is\nstarred afterwards.\n\nThe star is YOURS: it is keyed by you as well as by the project, so two people\nsee two answers for the same one and starring it says nothing about anybody\nelse's list. Starring a project you have already starred leaves it starred.","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/projectsStar"}}},"description":"ok"}},"x-app":"projects","x-public":true,"x-tool":true}},"/v1/prompt":{"get":{"operationId":"get_prompt","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":["prompt"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/promptList"}}},"description":"ok"}},"x-app":"prompt","x-public":true,"x-tool":true},"post":{"operationId":"post_prompt","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":["prompt"],"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":"prompt","x-public":true,"x-tool":true}},"/v1/prompt/catalog":{"get":{"operationId":"get_prompt_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/prompt. Entries that\nwould fail the create guards are dropped, so everything offered here can actually\nbe imported.","tags":["prompt"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/catalogList"}}},"description":"ok"}},"x-app":"prompt","x-public":true,"x-tool":true}},"/v1/prompt/metrics":{"get":{"operationId":"get_prompt_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":["prompt"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/metricList"}}},"description":"ok"}},"x-app":"prompt","x-public":true,"x-tool":true}},"/v1/prompt/{name}":{"delete":{"operationId":"delete_prompt_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":["prompt"],"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":"prompt","x-public":true,"x-tool":true},"get":{"operationId":"get_prompt_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":["prompt"],"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":"prompt","x-public":true,"x-tool":true}},"/v1/provisioning/datastore":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_datastore","summary":"Launches your org's OWN Hanzo Datastore instance and answers with its `datastore://` connection string.","description":"Launches your org's OWN Hanzo Datastore instance and answers\nwith its `datastore://` connection string.\n\nThe instance is yours alone — a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to you and no other tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing back a\nshared one.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/datastore/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/docdb":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_docdb","summary":"CreateDocDB launches your org's OWN document-database instance and answers with its `mongodb://` connection string.","description":"CreateDocDB launches your org's OWN document-database instance and answers with\nits `mongodb://` connection string. It speaks the MongoDB wire protocol, so\nexisting MongoDB drivers connect unchanged.\n\nThe instance is yours alone — a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to you and no other tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing back a\nshared one.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/docdb/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/kv":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_kv","summary":"CreateKV launches your org's OWN key-value instance and answers with its `kv://` connection string.","description":"CreateKV launches your org's OWN key-value instance and answers with its `kv://`\nconnection string.\n\nThe instance is yours alone — a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to you and no other tenant shares the\nprocess. Off-cluster this fails closed with 503 rather than handing back a\nshared one.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/kv/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/s3":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_s3","summary":"Creates an S3-compatible bucket inside the already-running shared object store and answers with the endpoint that reaches it.","description":"Creates an S3-compatible bucket inside the already-running shared\nobject store and answers with the endpoint that reaches it.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/s3/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/search":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_search","summary":"Creates a search index inside the already-running shared search backend and answers with the endpoint that reaches it.","description":"Creates a search index inside the already-running shared search\nbackend and answers with the endpoint that reaches it.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/search/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/sql":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_sql","summary":"CreateSQL launches your org's OWN PostgreSQL instance and answers with its `postgres://` connection string.","description":"CreateSQL launches your org's OWN PostgreSQL instance and answers with its\n`postgres://` connection string.\n\nThe instance is yours alone — a deployment in your own tenant namespace, so its\nadmin credential is naturally scoped to you and no other tenant shares the\nprocess. Off-cluster, where there is no orchestrator to launch one, this fails\nclosed with 503 rather than handing back a shared one.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/sql/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/provisioning/vector":{"get":{"operationId":"get_provisioning_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":["provisioning"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/provisionedSummary"},"type":"array"}}},"description":"ok"}},"x-app":"provisioning","x-public":true,"x-tool":true},"post":{"operationId":"post_provisioning_vector","summary":"Creates a vector collection inside the already-running shared vector backend and answers with the endpoint that reaches it.","description":"Creates a vector collection inside the already-running shared\nvector backend and answers with the endpoint that reaches it.","tags":["provisioning"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/provisionResult"}}},"description":"created"}},"x-app":"provisioning","x-public":true,"x-tool":true}},"/v1/provisioning/vector/{name}":{"delete":{"operationId":"delete_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true},"get":{"operationId":"get_provisioning_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":["provisioning"],"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","x-public":true,"x-tool":true}},"/v1/pubsub/publish":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/pubsub/request":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/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":["reference"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceSetsOut"}}},"description":"ok"}},"x-app":"reference","x-public":true,"x-tool":true}},"/v1/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":["reference"],"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","x-public":true,"x-tool":true}},"/v1/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":["reference"],"requestBody":{"content":{"application/json":{"example":{"keys":["[email protected]","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","x-public":true,"x-tool":true}},"/v1/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":["reference"],"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","x-public":true,"x-tool":true},"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":["reference"],"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","x-public":true,"x-tool":true},"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":["reference"],"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","x-public":true,"x-tool":true}},"/v1/referral":{"get":{"operationId":"get_referral","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/referral/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":["referral"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/myReferrals"}}},"description":"ok"}},"x-app":"referral","x-public":true,"x-tool":true}},"/v1/referral/claim":{"post":{"operationId":"post_referral_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/referral/sweep), and that is the end of it. No credit is ever\nissued from this package.","tags":["referral"],"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":"referral","x-public":true,"x-tool":true}},"/v1/registry/images":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/registry/packages":{"get":{"operationId":"get_registry_packages","summary":"Packages lists the org's npm packages — `<org>` and `@<org>/…` — from the npm registry's search index, optionally narrowed by a query within that scope.","description":"Packages lists the org's npm packages — `<org>` and `@<org>/…` — 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","x-public":true,"x-tool":true}},"/v1/registry/projects":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/registry/status":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/registry/tags":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/registry/token":{"post":{"operationId":"post_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 `<org>/<image>` 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","x-public":true,"x-tool":true}},"/v1/rerank":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/responses":{"post":{"operationId":"post_responses","summary":"Implements POST /v1/responses.","description":"Implements POST /v1/responses. The converted request is completed by\nthe chat path, which is handed a sink saying where the answer goes: a stream is\ntranslated as it is produced, a whole body is translated entire.","tags":["ai"],"x-app":"ai","x-public":true}},"/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","x-public":true,"x-tool":true}},"/v1/risk/health":{"get":{"operationId":"get_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","x-public":true}},"/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 entry point to a verdict. They were one call, which meant you could\nnot record 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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true}},"/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","x-public":true,"x-tool":true},"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","x-public":true,"x-tool":true}},"/v1/s3/buckets":{"get":{"operationId":"get_s3_buckets","summary":"Lists the caller org's own buckets.","description":"Lists the caller org's own buckets.\n\nOnly the caller's: every bucket is physically named under a per-org prefix and\nthe listing strips that prefix, so a tenant sees friendly names and another\ntenant's buckets are not in the answer at all.","tags":["s3"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bucketList"}}},"description":"ok"}},"x-app":"s3","x-public":true,"x-tool":true},"post":{"operationId":"post_s3_buckets","summary":"Makes a new bucket for the caller's org and answers 201 with it.","description":"Makes a new bucket for the caller's org and answers 201 with it.\n\nThe physical name is derived from the caller's validated org, so a tenant can\nonly ever create inside its own namespace and no request field can redirect\nthat. A name already taken in the org is 409.","tags":["s3"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bucketIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/bucketItem"}}},"description":"created"}},"x-app":"s3","x-public":true,"x-tool":true}},"/v1/s3/buckets/{bucket}":{"delete":{"operationId":"delete_s3_buckets_by_bucket","summary":"Removes an EMPTY bucket and answers 204.","description":"Removes an EMPTY bucket and answers 204.\n\nA non-empty bucket is 409 rather than a cascade: deleting a tenant's objects\nbehind a single bucket call is not a thing this surface will do silently. A\nbucket the caller's org does not own is the same 404 an unknown name gives.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket's friendly name, from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"s3","x-public":true,"x-tool":true}},"/v1/s3/buckets/{bucket}/objects":{"get":{"operationId":"get_s3_buckets_by_bucket_objects","summary":"Lists one folder level of a bucket.","description":"Lists one folder level of a bucket.\n\nFolder-style by default: sub-prefixes come back as directory entries, which is\nthe file-manager view. `?recursive=true` lists every key flat under the prefix\ninstead. Keys are RELATIVE to `?prefix=`, and the listing is bounded so a huge\nbucket cannot exhaust memory — Total is what came back, not what the bucket\nholds.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket to list, from the path.","schema":{"type":"string"}},{"name":"prefix","in":"query","required":false,"schema":{"type":"string"}},{"name":"recursive","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/objectList"}}},"description":"ok"}},"x-app":"s3","x-public":true,"x-tool":true},"post":{"operationId":"post_s3_buckets_by_bucket_objects","summary":"Mints a presigned PUT URL the caller uploads to DIRECTLY.","description":"Mints a presigned PUT URL the caller uploads to DIRECTLY.\n\nThe bytes never pass through this binary and the admin credential never leaves\nthe server: the URL is signed against the PUBLIC host, scoped to exactly this\nbucket and key, and expires. A deployment with no public endpoint configured\ncannot mint one and answers 503 rather than a URL that will not work.","tags":["s3"],"parameters":[{"name":"bucket","in":"path","required":true,"description":"Bucket is the bucket to upload into, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/uploadIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/presignResponse"}}},"description":"ok"}},"x-app":"s3","x-public":true,"x-tool":true}},"/v1/s3/health":{"get":{"operationId":"get_s3_health","summary":"Health reports whether this deployment can serve object storage.","description":"Health reports whether this deployment can serve object storage.\n\nIt is a REAL probe rather than a constant: 200 when admin credentials are\npresent, so the store is reachable in principle, and 503 with the reason when\nthey are not. It is deliberately NOT gated — liveness has to be probe-able\nwithout a token — so it is the one operation here that names no bucket and\nbills nothing.","tags":["s3"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/s3Health"}}},"description":"ok"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/s3Health"}}},"description":"service unavailable"}},"x-app":"s3","x-public":true,"x-tool":true}},"/v1/sandbox":{"get":{"operationId":"get_sandbox","summary":"Lists the caller org's sandboxes, newest first.","description":"Lists the caller org's sandboxes, newest first.\n\n`?project=` and `?status=` narrow it. Only the caller's org's: the store is\nkeyed on the validated org, so another tenant's sandbox is not something this\noperation can return.","tags":["sandbox"],"parameters":[{"name":"project","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/sandboxList"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true},"post":{"operationId":"post_sandbox","summary":"Leases a sandbox — a real computer — for the caller's org.","description":"Leases a sandbox — a real computer — for the caller's org.\n\nThe class decides what it is for and therefore its image, working directory\nand isolation. A dev or desktop sandbox is SINGLE-ATTACH per project, so\nasking twice for one project resumes the one that exists rather than paying\nfor a second; an exec sandbox carries no project and is bounded per org\ninstead, refused 429 past the ceiling because the caller's correct response is\nto wait.\n\nAnswers 201 with the sandbox as leased, which names the runtime it GOT — not\nthe one that was asked for.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/leaseIn"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}},"description":"created"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/end":{"post":{"operationId":"end_sandbox","summary":"End a sandbox and release it","description":"Ends the caller's sandbox lease: the pod goes, and the volume goes only\nwhen the caller asked for that too.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndIn"}}},"required":true},"responses":{"204":{"description":"no content"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/lease":{"post":{"operationId":"lease_sandbox","summary":"Lease a sandbox — a real computer — or resume one you hold","description":"Leases the caller's sandbox, or returns the one it named if that\nlease is still running.\n\nWhat comes back is a real computer: a pod under a runtime boundary with a\ntoolchain already in it, its own filesystem, and a lease that ends it. Every\nother op here acts on the one this returns.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeaseIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Leased"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/read":{"post":{"operationId":"read_sandbox_file","summary":"Read a file from a sandbox you hold","description":"Reads one path in the caller's sandbox: a file's bytes, or a\ndirectory's entries when the path names one.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Blob"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/run":{"post":{"operationId":"run_in_sandbox","summary":"Run a command in a sandbox you hold and read its output","description":"Runs one command inside the caller's sandbox and answers its exit code,\nstdout and stderr. A non-zero exit is a successful call carrying a failed\nprogram, so it comes back as data and not as an error.\n\nName a `session` and the command NARRATES INTO IT: its output is appended to\nthat session's live log as the program produces it, so anything watching the\nsession — GET /v1/agents/sessions/stream, scoped to one run with ?root= —\nwatches the work happen rather than waiting for the verdict. Without it the\ncall is what it always was: silent until it returns, which for an agentic run\nis twenty-five minutes of blank screen.\n\nThe session is named; the TENANT is not. It is the org the caller already\nproved, so a session belonging to somebody else is absent from the org this\ncall acts for and the append is refused there.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ran"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/stop":{"post":{"operationId":"stop_run","summary":"Stop what a sandbox is running, and keep the sandbox","description":"Interrupts whatever the caller's sandbox is running and answers how\nmany commands it ended. The sandbox stays leased — stop ends the WORK, end ends\nthe RESOURCE — so whoever stopped a run can still read what it left behind.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stopped"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/write":{"post":{"operationId":"write_sandbox_file","summary":"Write a file into a sandbox you hold","description":"Writes bytes to one path in the caller's sandbox, creating parents,\nand answers the resolved path.","tags":["sandbox"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WriteIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wrote"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/{id}":{"delete":{"operationId":"delete_sandbox_by_id","summary":"Ends a sandbox and releases the compute behind it.","description":"Ends a sandbox and releases the compute behind it. Answers 204.\n\nENDING IS NOT STOPPING. This releases the resource: the pod goes and anything\nonly inside it goes with it. To end what a sandbox is RUNNING while keeping\nthe sandbox — the checkout, the logs, the half-written file — the verb is\nPOST /v1/sandbox/stop.\n\n`?purge=1` additionally removes the record, so the sandbox stops being listed\nat all rather than being listed as ended.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sandbox to end, from the path.","schema":{"type":"string"}},{"name":"purge","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"sandbox","x-public":true,"x-tool":true},"get":{"operationId":"get_sandbox_by_id","summary":"Returns one sandbox: its class, project, image, the runtime it was given, its status and when its lease ends.","description":"Returns one sandbox: its class, project, image, the runtime it was\ngiven, its status and when its lease ends.\n\nAn id the caller's org does not hold is the same 404 an unknown id gives — the\nstore is keyed on the org, so a cross-tenant id simply is not there.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sandbox to address, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Sandbox"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/{id}/exec":{"post":{"operationId":"post_sandbox_by_id_exec","summary":"Runs one command in a sandbox the caller holds and answers with its exit code, stdout and stderr.","description":"Runs one command in a sandbox the caller holds and answers with\nits exit code, stdout and stderr.\n\nSend `argv` — an argument vector cannot be word-split by accident — or\n`command` for a shell line, which is the only input here that ever reaches a\nshell. A non-zero exit is a SUCCESSFUL call carrying a failed command: the\nstatus is 200 and the exit code is in the answer, because \"the command failed\"\nand \"the call failed\" are different facts.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sandbox to run in, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/execRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecResult"}}},"description":"ok"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/{id}/fs":{"get":{"operationId":"get_sandbox_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":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true},"post":{"operationId":"post_sandbox_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":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true}},"/v1/sandbox/{id}/screen":{"get":{"operationId":"get_sandbox_by_id_screen","summary":"The screen, as a page","description":"A complete, self-contained desktop — noVNC inline, no other origin — that opens its own socket and draws this sandbox's display. Embed it in an iframe and there is nothing else to build.\n\n`ticket` is the credential from the POST above, carried through to the socket. The page is NOT gated: it is inert markup and does not redeem the ticket, because a ticket is spent once and a page that spent it would hold a credential that no longer opens anything. `frame-ancestors` admits our own brands' hosts and nothing further.\n\nIt is served for every class, not only for `desktop`. The class is a fact about the image, and a sandbox with no VNC server already fails exactly — the connection is refused and the page says so — where a check here would be a second opinion about what is running inside a pod, formed from a label rather than from the pod.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true}},"/v1/sandbox/{id}/screen/ticket":{"post":{"operationId":"post_sandbox_by_id_screen_ticket","summary":"Mints a short-lived grant to open the screen of a desktop sandbox.","description":"Mints a short-lived grant to open the screen of a desktop\nsandbox. Same properties as the terminal ticket, for the other endpoint.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sandbox to address, from the path.","schema":{"type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ticketGrant"}}},"description":"created"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/{id}/screen/ws":{"get":{"operationId":"get_sandbox_by_id_screen_ws","summary":"The screen, as a socket","description":"Upgrades to a WebSocket carrying RFB — the VNC wire protocol — from the sandbox's display, for a host that brings its own client. Requires `ticket`; a missing, expired or already-spent one answers 401 without upgrading.\n\nTHE WIRE IS RFB, in BINARY frames both ways, and it is not interpreted here: this is a pipe between the caller's client and the server inside the pod.\n\nTHE PIXELS COME OUT THROUGH THE EXEC CHANNEL. The display binds 127.0.0.1 only and deliberately nothing else, so there is no address to dial — `socat` joins the stream to that loopback port over the same Kubernetes exec subresource every other call into a sandbox uses. One way in, one thing to authorize, nothing further exposed.\n\nThe window size is ignored. A browser pane is not the X server's geometry, and the client scales what it is given rather than asking a server with no RandR to resize itself.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true}},"/v1/sandbox/{id}/terminal":{"get":{"operationId":"get_sandbox_by_id_terminal","summary":"The terminal, as a page","description":"A complete, self-contained terminal — xterm inline, no other origin — that opens its own socket and runs a shell in this sandbox. Embed it in an iframe and there is nothing else to build.\n\n`ticket` is the credential from the POST above and `arg` names the session (see the socket below); both are simply carried through to the socket. The page is NOT gated — it is inert markup and does not redeem the ticket, because a ticket is spent once and a page that spent it would hold a credential that no longer opens anything.\n\nWhen the terminal is up it posts `{source:\"hanzo-term\", ready:true}` to its parent frame, so a host can tell a live terminal from a page that failed into something else. `frame-ancestors` admits our own brands' hosts and nothing further.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true}},"/v1/sandbox/{id}/terminal/ticket":{"post":{"operationId":"post_sandbox_by_id_terminal_ticket","summary":"Mints a short-lived grant to open a terminal on a sandbox.","description":"Mints a short-lived grant to open a terminal on a sandbox.\n\nThe ticket travels in the query string of the URL it answers with, because a\nbrowser cannot set an Authorization header on a WebSocket handshake. It is\nsingle-purpose and short-lived for exactly that reason. A sandbox that is not\nrunning is 409 rather than a ticket that cannot be used.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the sandbox to address, from the path.","schema":{"type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ticketGrant"}}},"description":"created"}},"x-app":"sandbox","x-public":true,"x-tool":true}},"/v1/sandbox/{id}/terminal/ws":{"get":{"operationId":"get_sandbox_by_id_terminal_ws","summary":"The terminal, as a socket","description":"Upgrades to a WebSocket carrying a login shell on a pseudo-terminal inside the sandbox — for a host that brings its own emulator. Requires `ticket`; a missing, expired or already-spent one answers 401 without upgrading.\n\nTHE WIRE. A text frame is stdin, unless it is the one control object `{\"resize\":{\"cols\":N,\"rows\":M}}`; a binary frame is always stdin. Output comes back as BINARY frames, because a pty emits arbitrary bytes cut at arbitrary offsets and a text frame carrying half a rune is one the browser closes the connection over.\n\n`arg` names a SESSION: the shell runs under `tmux new -A -s <arg>`, which attaches to that session if it exists and creates it if it does not — so one sandbox holds as many terminals as a caller has names for. It is 1-64 characters of letters, digits, `-` or `_` and may not begin with `-`; anything else is 400. Without `arg` the shell is unnamed and unmultiplexed.\n\nThe shell is `zsh -l`, falling back to `bash -l` and then to `sh -l`, and to the plain shell again when the image has no tmux. Every step is a preference and none is a requirement: whatever else the image carries — the hanzo CLI included — is a command to type, never a condition for getting a prompt.","tags":["sandbox"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"sandbox","x-public":true}},"/v1/sbom":{"post":{"operationId":"post_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":"oci.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","x-public":true,"x-tool":true}},"/v1/sbom/health":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/search":{"post":{"operationId":"search","summary":"Hybrid search over the org's own corpora","description":"Is the typed op behind POST /v1/search. It does exactly two things the\nin-process entry point must not do: resolve the tenant from the validated\nprincipal, and refuse when there is none. Everything else is ForOrg.","tags":["search"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Request"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Response"}}},"description":"ok"}},"x-app":"search","x-public":true,"x-tool":true}},"/v1/security/findings":{"get":{"operationId":"get_security_findings","summary":"Is the org's findings — rule, severity, path, line, masked preview and fingerprint — newest first, across scans or within one.","description":"Is the org's findings — rule, severity, path, line, masked preview\nand fingerprint — newest first, across scans or within one.\n\nA minSeverity outside critical|high|medium|low is refused rather than quietly\nignored, so a filter typo cannot read as \"no findings\". Strictly org-scoped, and\na caller with no validated org is refused.","tags":["security"],"parameters":[{"name":"scanId","in":"query","required":false,"description":"ScanID narrows to a single scan.","schema":{"type":"string"}},{"name":"minSeverity","in":"query","required":false,"description":"MinSeverity drops everything below that rank: critical, high, medium or low.\nA value outside that set is refused rather than quietly ignored, so a filter\ntypo cannot read as \"no findings\".","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the page.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/findingList"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/security/findings/{id}":{"get":{"operationId":"get_security_findings_by_id","summary":"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.","description":"Returns a single finding: which rule fired, where (path and line),\nthe masked preview and the SHA-256 fingerprint of the secret — the raw secret is\nnot stored and cannot be read back.\n\nScoped to the caller's org, and a finding belonging to another org is the same\n404 as one that never existed.","tags":["security"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the finding the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/findingView"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/security/health":{"get":{"operationId":"get_security_health","summary":"Reports that the scanning subsystem is serving and how many secret-detection rules the engine holds.","description":"Reports that the scanning subsystem is serving and how many\nsecret-detection rules the engine holds.\n\nIt has no external dependency — the answer is ok whenever the findings store\nopened — so it measures this process rather than anything downstream. It reads\nno tenant: a prober that sends no principal is answered, not refused.","tags":["security"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ruleset"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/security/rules":{"get":{"operationId":"get_security_rules","summary":"Is the secret-detection catalog the engine scans with.","description":"Is the secret-detection catalog the engine scans with.\n\nIt returns every rule a scan can fire — the id, name and severity a finding\ncites — so a caller can render or triage results without hard-coding the\ncatalog. It is the same for everyone and discloses nothing tenant-specific, so\nit carries no org scope.","tags":["security"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ruleList"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/security/scans":{"get":{"operationId":"get_security_scans","summary":"Is the org's scan history, newest first, each as the same summary the submission answered — files read, findings fired, tally by severity.","description":"Is the org's scan history, newest first, each as the same summary the\nsubmission answered — files read, findings fired, tally by severity.\n\nStrictly org-scoped: a caller only ever sees its own scans, and one with no\nvalidated org is refused.","tags":["security"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Limit caps the page.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scanList"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true},"post":{"operationId":"post_security_scans","summary":"Runs the detection engine over a batch of files and answers 201 with the scan summary: how many files were read, how many findings fired, and the tally by severity.","description":"Runs the detection engine over a batch of files and answers 201 with\nthe scan summary: how many files were read, how many findings fired, and the\ntally by severity.\n\nTHE SUBMITTED CONTENT IS NEVER STORED. It is scanned in memory; what persists is\nthe finding — its rule, its path and line, a MASKED preview (first and last\ncharacters kept, the middle starred) and the SHA-256 fingerprint of the raw\nsecret. The fingerprint is what makes the same secret recognisable across scans\nand after rotation without the secret ever being written down.\n\nIt requires a validated org, which scopes the stored scan and every finding on\nit; a caller with no org is refused. Bounded at 500 files and 8 MiB of total\ncontent per submission — split a larger tree across scans. One scan is one\nmetered unit, and the scan is recorded in the audit log with its tally, never\nwith its findings.","tags":["security"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/submitReq"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scanView"}}},"description":"created"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/security/scans/{id}":{"get":{"operationId":"get_security_scans_by_id","summary":"Returns one scan together with every finding on it, so the detail view is one round-trip rather than a list call per scan.","description":"Returns one scan together with every finding on it, so the detail view\nis one round-trip rather than a list call per scan. The findings carry masked\npreviews and fingerprints, never secrets.\n\nScoped to the caller's org: a scan id belonging to another org is the same 404\nas an id that never existed, so a ruleset learns nothing about what exists\nelsewhere. No validated org is refused.","tags":["security"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the scan the URL names.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/scanDetail"}}},"description":"ok"}},"x-app":"security","x-public":true,"x-tool":true}},"/v1/seo/audit":{"post":{"operationId":"seoAudit","summary":"Fetch one page and report what it gets wrong","description":"Fetches one page and reports what it gets wrong.\n\nIt returns the page's on-page score, its title and description, how much readable\ntext it carries, and the full set of named checks — is it https, does it have one\nh1, is the title duplicated, is it slow, is it a redirect, is anything on it\nbroken. It is the technical half of search visibility, and it is the half a\ndeveloper can act on this afternoon.\n\nONE PAGE, LIVE, IN THIS REQUEST. It is deliberately not a site crawl: a crawl is\na job with a lifecycle, and this answers the same questions about the page\nsomebody is actually looking at, now, with no task id to poll. Point it at the\npages that matter one at a time.\n\nIt is priced per page fetched, which is one.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoAuditIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoAuditOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/backlinks":{"post":{"operationId":"seoBacklink","summary":"Who links to a target, and how much of it is broken or spam","description":"Summarises who links to a target.\n\nIt returns the authority score, how many links point at it and from how many\ndistinct sites, how many of those are broken, and how much of the profile reads\nas spam. Distinct sites is the number to read: a thousand links from one domain\nis one endorsement, and a profile that grew fast in links and not in domains is\nusually a profile somebody bought.\n\nThe target can be a whole domain, a subdomain, or one page URL — the summary is\nscoped to whatever is named. It is priced per request, so a domain with ten\nmillion links costs the same as one with ten.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoBacklinkIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoBacklinkOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/competitors":{"post":{"operationId":"seoCompetitor","summary":"The domains that place for the same phrases","description":"Names the domains that place for the same phrases.\n\nGiven a set of phrases it returns the sites that appear across them, with each\none's average position, how many of the phrases it places for, its share of the\navailable attention and the visits that earns. It answers \"who am I actually up\nagainst here\", which is a different question from \"who do I think my competitors\nare\" and frequently a different answer.\n\nPair it with seoRank: this says who is in the race, seoRank says where any one of\nthem finishes. It is priced per row, so Limit decides the cost.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoCompetitorIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoCompetitorOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/ideas":{"post":{"operationId":"seoIdea","summary":"Grow a seed phrase into the phrases nobody named yet","description":"Grows a seed phrase into the phrases nobody named yet.\n\nIt takes phrases you have and returns phrases in the same category that you do\nnot — relevant rather than merely containing the seed — each with its search\nvolume, click cost, competition and how hard its first page is to reach. This is\nwhere a keyword list comes FROM; seoKeyword is where a list you already have gets\nmeasured.\n\nIt is priced per row, so Limit is the knob that decides what the call costs.\nTotal says how many more there were.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoIdeaIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoIdeaOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/keywords":{"post":{"operationId":"seoKeyword","summary":"How often named phrases are searched, and what a click costs","description":"Measures phrases the caller already has.\n\nIt answers, for each phrase named, how many people search it in a month, what an\nadvertising click on it costs, and how contested that advertising is. This is the\nground fact of search: everything else on this surface is a question about\nphrases, and this is the one that says whether a phrase is worth having.\n\nGive it phrases you already suspect. To find phrases you have not thought of,\nuse seoIdea; to find the ones a site already places for, use seoRank.\n\nThe market defaults to the United States in English. It is priced per request\nrather than per phrase, so asking about fifty phrases costs what asking about\none does.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoKeywordIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoKeywordOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/rankings":{"post":{"operationId":"seoRank","summary":"Every phrase a domain already places for, with its position","description":"Reports every phrase a domain already places for.\n\nFor each one it gives the phrase, the position on the results page, the page of\nthe site that placed, that result's headline, the phrase's monthly searches and\nthe visits the placement is estimated to earn. It is the single most direct\nquestion about a site's search visibility — yours or a competitor's, since it\ntakes any domain.\n\nPosition is the ABSOLUTE rank, counting every element on the page — the ads, the\nanswer boxes, the map — because that is what a person scrolling actually passes.\nAn organic-only rank flatters a result that sits below half a screen of other\nthings.\n\nIt is priced per row, so Limit decides what the call costs, and Total says how\nmany more there were.","tags":["seo"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoRankIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoRankOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/seo/rates":{"get":{"operationId":"seoRate","summary":"What each call on this surface costs, from the vendor's own list","description":"Publishes what every call on this surface costs.\n\nThe numbers are read from the upstream's own published price list, not from a\ntable kept here, so a price change on their side moves this card within the hour\nand moves what is debited with it. That is the whole of the pricing model: this\nsurface resells at cost, and the cost is theirs to state.\n\nA row has two numbers because a call has two costs: a flat charge for asking, and\na charge per row returned. An op priced per request reports zero for the second,\nand for one priced per row the total is `request + result x limit` — which is the\namount your balance is authorized against before the call, and roughly what you\nwill be debited after it.\n\nIt is a read and it is free: asking what something costs must not require the\nbalance that would pay for it. If the upstream cannot be reached the card comes\nback empty rather than stale — a price nobody can confirm is not a price.","tags":["seo"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/seoRateOut"}}},"description":"ok"}},"x-app":"seo","x-public":true,"x-tool":true}},"/v1/settings/{product}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/share":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/share/enable":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/social/accounts":{"get":{"operationId":"get_social_accounts","summary":"Returns the org's connected accounts — each one's id, network, handle, status and timestamps, most-recently-updated first.","description":"Returns the org's connected accounts — each one's id, network,\nhandle, status and timestamps, most-recently-updated first.\n\nAn account's provider access token is NEVER included in any response on this\nsurface. Only the publisher reads it.","tags":["social"],"parameters":[{"name":"provider","in":"query","required":false,"description":"Provider keeps only accounts on one network — x, facebook, instagram,\nlinkedin, tiktok, youtube or threads. Omit it for every network. It is\nlower-cased and trimmed before it is matched, and a value that names no\nnetwork simply matches nothing rather than being refused.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page, defaulting to 200 and capped at 1000. It is a string\nrather than an integer on purpose: the route parses it with a leading trim\nand falls back to the default on anything it cannot read, so `?limit=%2050`\nis a page of fifty today. An integer field would refuse the space and read\nan unparseable value as zero, which is a different page.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccounts"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true},"post":{"operationId":"post_social_accounts","summary":"Records a social account for the org and answers 201 with the stored row, including the generated id later calls address it by.","description":"Records a social account for the org and answers 201 with the\nstored row, including the generated id later calls address it by.","tags":["social"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccountBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccount"}}},"description":"created"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/accounts/{id}":{"delete":{"operationId":"delete_social_accounts_by_id","summary":"Removes one connected account from the org and answers 204 with no body; an id that is not there is 404.","description":"Removes one connected account from the org and answers 204 with no\nbody; an id that is not there is 404.\n\nIt removes the account record only. Posts that already published through it keep\ntheir published state and their recorded external ids — this does not retract\nanything from the network.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the account or post to act on, taken from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"social","x-public":true,"x-tool":true},"get":{"operationId":"get_social_accounts_by_id","summary":"Returns one of the org's connected accounts by id — its network, handle, status and timestamps — or 404.","description":"Returns one of the org's connected accounts by id — its network,\nhandle, status and timestamps — or 404. The provider access token is not part of\nthe response.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the account or post to act on, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccount"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true},"put":{"operationId":"put_social_accounts_by_id","summary":"Replaces the account's network, handle and status with what the body carries, and answers with the stored row.","description":"Replaces the account's network, handle and status with what the\nbody carries, and answers with the stored row.\n\nThis is a REPLACEMENT, not a merge, which is the rule most easily got wrong: a\nfield the body omits is written as its default, so leaving out the handle blanks\nit and leaving out the status resets it to connected. Send the whole record. The\nsame vocabularies as create apply, and an unknown network or status is refused\nrather than coerced.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccountWrite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialAccount"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/posts":{"get":{"operationId":"get_social_posts","summary":"Returns the org's posts — content, channel, status, scheduled time, media and timestamps — most-recently-updated first.","description":"Returns the org's posts — content, channel, status, scheduled time,\nmedia and timestamps — most-recently-updated first.","tags":["social"],"parameters":[{"name":"status","in":"query","required":false,"description":"Status keeps only posts in one state — draft, scheduled, published or\nfailed. Omit it for every state. The transient publishing claim is not a\nuser-visible state and matching it is not useful.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit bounds the page, defaulting to 200 and capped at 1000. A string for\nthe same reason accountFilter.Limit is.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPosts"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true},"post":{"operationId":"post_social_posts","summary":"Stores a post for the org and answers 201 with the stored row.","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\nIMMEDIATELY, and the row returned carries that outcome — this is the one behaviour\na reader would otherwise miss. A future-scheduled post is left for the scheduler,\nand a draft is left alone. Publishing never fails the creation: the post is stored\neither way, and a publish that could not run leaves the row for the scheduler to\nretry.","tags":["social"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPostBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPost"}}},"description":"created"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/posts/{id}":{"delete":{"operationId":"delete_social_posts_by_id","summary":"Removes one post from the org and answers 204 with no body; an id that is not there is 404.","description":"Removes one post from the org and answers 204 with no body; an id that\nis not there is 404.\n\nIt deletes the record here only. A post that has already published is not\nretracted from the network by deleting it.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the account or post to act on, taken from the path.","schema":{"type":"string"}}],"responses":{"204":{"description":"no content"}},"x-app":"social","x-public":true,"x-tool":true},"get":{"operationId":"get_social_posts_by_id","summary":"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.","description":"Returns one of the org's posts by id, with its current status, scheduled\ntime, media and — once it has published — the account and external id it published\nunder. 404 when there is no such post for this org.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the account or post to act on, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPost"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true},"put":{"operationId":"put_social_posts_by_id","summary":"Replaces the post's content, channel, status, scheduled time and media with what the body carries, and answers with the stored row.","description":"Replaces the post's content, channel, status, scheduled time and media\nwith 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\nomitting media clears it and omitting the status resets the post to draft.\n`content` is required on every update. Unlike create, this never triggers a\npublish — moving a post's scheduled time into the past here leaves it for the\nscheduler; publish now is its own operation.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPostWrite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPost"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/posts/{id}/publish":{"post":{"operationId":"post_social_posts_by_id_publish","summary":"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.","description":"Publishes the post immediately to the connected accounts on its channel\nand answers with the updated row, carrying the account and external id it\npublished under.\n\nIt is IDEMPOTENT: a post that has already published, or that another caller is\npublishing right now, comes back unchanged rather than being posted twice. That\nclaim 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\nchannel is the caller's to fix, so it is recorded ON the post as failed with the\nreason and answers normally. A deployment that lacks the network's own credentials\ncannot publish for anyone, so that is a 503 naming exactly what is missing.","tags":["social"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the account or post to act on, taken from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialPost"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/providers":{"get":{"operationId":"get_social_providers","summary":"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.","description":"Reports each supported network's publish-readiness: whether this\ndeployment holds the OAuth application credentials for it and, when it does not,\nexactly which environment variables are missing.\n\nThis is a live read of the deployment's own configuration, not a static list of\nnetworks — it answers \"can I connect this today\", which is what a connect\naffordance and a pre-cutover checklist both need. It says nothing about whether\nthe caller has connected an account; that is the accounts listing.","tags":["social"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialProviders"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/social/summary":{"get":{"operationId":"get_social_summary","summary":"Returns four counts for the caller's org: total posts, how many are scheduled, how many have published, and how many accounts are connected.","description":"Returns four counts for the caller's org: total posts, how many are\nscheduled, how many have published, and how many accounts are connected. It is\nthe dashboard roll-up, computed over the org's own rows in one read.","tags":["social"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/socialSummary"}}},"description":"ok"}},"x-app":"social","x-public":true,"x-tool":true}},"/v1/standing/upkeep":{"post":{"operationId":"post_standing_upkeep","summary":"Reports what keeping this entity costs every year, itemised.","description":"Reports what keeping this entity costs every year, itemised.\n\nThis is the figure that decides where to incorporate, and the one a formation\nprice cannot show: Delaware is cheaper to form than Wyoming for a corporation\nand dearer to keep, so a founder shown only the formation fee is shown the half\nthat reverses. Each state line carries the authority that publishes it and the\ndate it was checked, and a franchise tax that scales is marked a minimum rather\nthan quoted as final.","tags":["standing"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/upkeepIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upkeep"}}},"description":"ok"}},"x-app":"standing","x-public":true,"x-tool":true}},"/v1/sync":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/sync/{id}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/sync/{id}/run":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/tasks":{"delete":{"operationId":"delete_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","x-public":true},"get":{"operationId":"get_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","x-public":true},"patch":{"operationId":"patch_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","x-public":true},"post":{"operationId":"post_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","x-public":true},"put":{"operationId":"put_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","x-public":true}},"/v1/taxonomy":{"get":{"operationId":"get_taxonomy","summary":"Read returns the product catalogue as this caller sees it: the PLATFORM catalogue — Hanzo's own products, the part that is true for everyone — plus the caller's own org's rows, every category in display order and each carrying the products filed under it in theirs.","description":"Read returns the product catalogue as this caller sees it: the PLATFORM\ncatalogue — Hanzo's own products, the part that is true for everyone — plus the\ncaller's own org's rows, every category in display order and each carrying the\nproducts filed under it in theirs. Another customer's rows are never in it. It\nis readable signed out, and a signed-out visitor gets the platform catalogue\nalone, which is what the marketing landing renders from.\n\nWhere the caller's org and the platform hold the same id, the caller's own row\nis the one served. That rule exists because ids are unique per ORG and not\nglobally — two customers may each have a \"crm\", and refusing the second would\ntell one of them the other exists — so a collision with the platform is possible\nby construction and something has to win deterministically. Yours does: your own\ncatalogue is the one you edited.\n\n`?brand=` narrows it the way a brand's own console does: only the categories\nthat brand admits, and within them only the taxa scoped to it. An unpublished\nrow is served only to whoever may edit it — a SuperAdmin for the platform's, an\norg admin for their own — so a product can be staged before anyone sees it\nwithout becoming invisible to the person staging it.","tags":["taxonomy"],"parameters":[{"name":"brand","in":"query","required":false,"description":"Brand returns only what that brand's console shows — the categories it\nadmits, and within them the taxa scoped to it. Empty returns everything.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Taxonomy"}}},"description":"ok"}},"x-app":"taxonomy","x-public":true,"x-tool":true}},"/v1/taxonomy/categories/{id}":{"delete":{"operationId":"delete_taxonomy_categories_by_id","summary":"Removes one empty category.","description":"Removes one empty category. A category that still has taxa\nfiled under it is refused with 409 and a count: deleting the label off a group\nmust never silently take the products wearing it, and the alternative — orphan\nrows naming a category that no longer exists — is a catalogue that cannot be\nrendered. Move or delete its taxa first. An id no category holds is a 404.","tags":["taxonomy"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the slug to act on, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deleted"}}},"description":"ok"}},"x-app":"taxonomy","x-public":true,"x-tool":true},"put":{"operationId":"put_taxonomy_categories_by_id","summary":"Creates or replaces one category and returns it as stored.","description":"Creates or replaces one category and returns it as stored. The id in\nthe URL is the one it is filed under whatever the body says, so a category can\nnever be written under a name it was not addressed by — which also makes create\nand replace the same act, and is why there is no POST beside this.\n\nPlatform SuperAdmin only: one catalogue serves every tenant, so an org admin who\ncould rename a category would rename it for all of them.","tags":["taxonomy"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the category slug to write, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"label":"Observe","order":6,"summary":"Traces, metrics, logs and alerts."},"schema":{"$ref":"#/components/schemas/categoryIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Category"}}},"description":"ok"}},"x-app":"taxonomy","x-public":true,"x-tool":true}},"/v1/taxonomy/taxa/{id}":{"delete":{"operationId":"delete_taxonomy_taxa_by_id","summary":"Removes one product from the catalogue.","description":"Removes one product from the catalogue. An id no taxon holds is a\n404. To take a product out of view without losing what was written about it, set\n`published` to false instead.","tags":["taxonomy"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the slug to act on, from the path.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/deleted"}}},"description":"ok"}},"x-app":"taxonomy","x-public":true,"x-tool":true},"put":{"operationId":"put_taxonomy_taxa_by_id","summary":"Creates or replaces one product and returns it as stored.","description":"Creates or replaces one product and returns it as stored. The id in the\nURL is the one it is filed under whatever the body says. The category must\nalready exist — a taxon naming a category that does not is refused with 400\nrather than stored where nothing can render it.\n\nA taxon opens exactly one way: `route` for a product the console renders\nitself, or `href` for one that genuinely lives at its own domain. Giving both,\nor neither, is refused.\n\nPlatform SuperAdmin only.","tags":["taxonomy"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the taxon slug to write, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"category":"data","description":"Managed vector search.","icon":"Database","name":"Vector","order":3,"route":"/vector","tags":["search"]},"schema":{"$ref":"#/components/schemas/taxonIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Taxon"}}},"description":"ok"}},"x-app":"taxonomy","x-public":true,"x-tool":true}},"/v1/team/account":{"post":{"operationId":"post_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 refusal 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","x-public":true}},"/v1/team/account/auth/{provider}":{"get":{"operationId":"get_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 endpoint here.","tags":["team"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"team","x-public":true}},"/v1/team/account/auth/{provider}/callback":{"get":{"operationId":"get_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","x-public":true}},"/v1/team/account/cookie":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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 hole, 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","x-public":true}},"/v1/team/account/providers":{"get":{"operationId":"get_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 provider 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","x-public":true,"x-tool":true}},"/v1/team/billing/plan":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/team/billing/ui":{"get":{"operationId":"get_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","x-public":true}},"/v1/team/bots":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/team/bots/sync":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/team/collaborator":{"get":{"operationId":"get_team_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\nBOTH LANES SHARE ONE ROOT. The client derives them from one configured URL — this socket at its root, the markup-snapshot RPC one segment in — so pointing the editor at this service is one value, and the two lanes cannot drift apart.\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.","tags":["team"],"x-app":"team","x-public":true}},"/v1/team/collaborator/rpc/{documentId}":{"post":{"operationId":"post_team_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.","tags":["team"],"parameters":[{"name":"documentId","in":"path","required":true,"description":"DocumentID addresses the document field, as\n\"<workspaceUuid>|<objectClass>|<objectId>|<objectAttr>\" — 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","x-public":true,"x-tool":true}},"/v1/team/files/{workspace}":{"post":{"operationId":"post_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","x-public":true}},"/v1/team/files/{workspace}/{filename}":{"delete":{"operationId":"delete_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","x-public":true,"x-tool":true},"get":{"operationId":"get_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","x-public":true}},"/v1/team/transactor/api/v1/statistics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/team/transactor/statistics":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/team/transactor/{token}":{"get":{"operationId":"get_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","x-public":true}},"/v1/tel/calls":{"get":{"operationId":"get_tel_calls","summary":"Lists the calls this org has placed or received, newest first.","description":"Lists the calls this org has placed or received, newest first. Like the\nmessage list beside it, these are our own records rather than the carrier's.","tags":["tel"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/callList"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true},"post":{"operationId":"post_tel_calls","summary":"Dials.","description":"Dials. An `agent` names a Hanzo assistant to answer it; the call is\nrefused up front when no assistant plane is configured, because a call that\nconnects to silence has already cost the person who answered it.","tags":["tel"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/callInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Call"}}},"description":"created"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/calls/{id}":{"delete":{"operationId":"delete_tel_calls_by_id","summary":"Ends a call this org placed.","description":"Ends a call this org placed. The holding is read for THIS org before the\ncarrier is asked, for the reason releaseNumber gives one surface up: an id\nbelonging to another tenant would otherwise be hung up by whoever guessed it.","tags":["tel"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/noInput"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/messages":{"get":{"operationId":"get_tel_messages","summary":"Lists the messages this org has sent or received, newest first.","description":"Lists the messages this org has sent or received, newest first. Records from\nour own store, not the carrier's — so it is what this platform did on the\norg's behalf, which is the set an audit or a bill has to agree with.","tags":["tel"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/messageList"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true},"post":{"operationId":"post_tel_messages","summary":"Sends a message from one of this org's own numbers.","description":"Sends a message from one of this org's own numbers.\n\n`from` must be a number the org HOLDS, checked against the store rather than\ntaken on trust — a caller that could send from any number could impersonate\none, and the carrier would deliver it. `to` is required, and the body needs\ntext or media, because a message with neither is delivered as nothing and\nbilled as something.","tags":["tel"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/messageInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMS"}}},"description":"created"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/numbers":{"get":{"operationId":"get_tel_numbers","summary":"Lists the phone numbers this org HOLDS — the ones it has bought and not released.","description":"Lists the phone numbers this org HOLDS — the ones it has bought and not\nreleased. Distinct from the availability search one path down\n(`/numbers/available`), which asks the carrier what could be bought: this\nanswers only from our own store, so it is what an org owns rather than what\nit could own.","tags":["tel"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/numberList"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true},"post":{"operationId":"post_tel_numbers","summary":"Provisions with the carrier FIRST and records second.","description":"Provisions with the carrier FIRST and records second. The other order\nrecords a holding that may not exist, and a number the platform believes it owns\nbut cannot use is worse than one it failed to buy.","tags":["tel"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buyInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Number"}}},"description":"created"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/numbers/available":{"get":{"operationId":"get_tel_numbers_available","summary":"Asks the carrier what is available to buy.","description":"Asks the carrier what is available to buy. Nothing is recorded —\na search is not a holding, and treating it as one is how inventory leaks.","tags":["tel"],"parameters":[{"name":"Country","in":"query","required":false,"schema":{"type":"string"}},{"name":"Area","in":"query","required":false,"schema":{"type":"string"}},{"name":"Type","in":"query","required":false,"schema":{"type":"string"}},{"name":"Limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/numberList"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/numbers/{id}":{"delete":{"operationId":"delete_tel_numbers_by_id","summary":"Checks the holding is THIS org's before it reaches the carrier.","description":"Checks the holding is THIS org's before it reaches the carrier.\nWithout that read, an id belonging to another tenant would be released by\nwhoever guessed it.","tags":["tel"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/noInput"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/tel/summary":{"get":{"operationId":"get_tel_summary","summary":"Counts what this org holds on the telephony plane: its numbers, its calls and its messages.","description":"Counts what this org holds on the telephony plane: its numbers, its calls and\nits messages. The one read a dashboard makes before it asks for any list, so\nit answers three totals and no rows.","tags":["tel"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/summary"}}},"description":"ok"}},"x-app":"tel","x-public":true,"x-tool":true}},"/v1/template":{"get":{"operationId":"get_template","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":["template"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/kitList"}}},"description":"ok"}},"x-app":"template","x-public":true,"x-tool":true},"post":{"operationId":"post_template","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":["template"],"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":"template","x-public":true,"x-tool":true}},"/v1/template/{slug}":{"delete":{"operationId":"delete_template_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":["template"],"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":"template","x-public":true,"x-tool":true},"get":{"operationId":"get_template_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":["template"],"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":"template","x-public":true,"x-tool":true},"put":{"operationId":"put_template_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":["template"],"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":"template","x-public":true,"x-tool":true}},"/v1/todo/board":{"get":{"operationId":"get_todo_board","summary":"Returns a board's issues — work items with their column, priority, assignee, labels and schedule.","description":"Returns a board's issues — work items with their column, priority,\nassignee, labels and schedule.\n\nWHICH board is a filter, not an address. Bound to a repository (the key from\nthe path) it is that project's board; left unbound it is the org's whole\nboard; narrowed by label it is a board smaller than any repository — which is\nthe only way an app that lives as a directory inside a shared repository can\nhave one. Every combination is the same rows through the same projection, so\nno two boards can disagree about what a column means.\n\nThe column is a LABEL on the forge, so the board and the forge web UI are the\nsame object seen twice: relabelling in either moves the card in both. A closed\nissue reads as done whatever its labels say.","tags":["todo"],"parameters":[{"name":"key","in":"query","required":false,"description":"Key is the project whose issues to list, from the path. EMPTY means every\nproject in the org — the global board. It is a filter like the rest of\nthis struct rather than an address, which is what lets one op answer both\n\"this board\" and \"all the work\" without a second surface disagreeing with\nthe first about what a column is.","schema":{"type":"string"}},{"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"}},{"name":"repo","in":"query","required":false,"description":"Repo keeps only issues bound to that git repository.","schema":{"type":"string"}},{"name":"label","in":"query","required":false,"description":"Label keeps only issues carrying that label, compared case-insensitively.\n\nThis is how a board narrows to something SMALLER than a repository — the\none mechanism for it. An estate whose apps are directories inside one\nrepository (hanzoai/cloud carries ~140 of them) has no repository per app\nto address, so the app is a label: `label=app/meet` is the meet board.\nNothing is provisioned to make one exist; a board is the query.","schema":{"type":"string"}},{"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":"todo","x-public":true,"x-tool":true}},"/v1/todo/issues":{"get":{"operationId":"get_todo_issues","summary":"Answers across every project in the org.","description":"Answers across every project in the org.\n\nThe org comes from the validated principal and never from the request: a\ncaller able to name the org could read another tenant's backlog, and a search\nis exactly the shape that would quietly return it.","tags":["todo"],"parameters":[{"name":"q","in":"query","required":false,"description":"Q matches an issue's title or description. A word from the issue, which is\nwhat someone remembers — not its number, which is what they are looking up.","schema":{"type":"string"}},{"name":"project","in":"query","required":false,"description":"Project narrows to one team key; \"\" searches every project in the org,\nwhich is the point of this op.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Status keeps one board column: backlog, todo, in_progress, done, canceled.","schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"description":"Kind keeps one shape: issue, pr, epic.","schema":{"type":"string"}},{"name":"repo","in":"query","required":false,"description":"Repo keeps issues bound to one git repository.","schema":{"type":"string"}},{"name":"source","in":"query","required":false,"description":"Source keeps one origin: team, git, crm, helpdesk, cms, agent. \"git\" is\nhow you ask for the mirrored GitHub issues specifically.","schema":{"type":"string"}},{"name":"assignee","in":"query","required":false,"description":"Assignee keeps issues held by one person. Pass \"me\" for yourself.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the answer; 0 means the default, and anything above the ceiling\nis clamped rather than refused — a search that errors on being too broad\nteaches people to guess.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueHits"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true}},"/v1/todo/projects":{"get":{"operationId":"get_todo_projects","summary":"Returns the boards of your org — the places your work actually is.","description":"Returns the boards of your org — the places your work actually\nis. The key addresses the board's issues.\n\nA BOARD IS A PLACE WORK IS, not an object somebody provisioned. So the list is\nassembled from the work itself: the repositories your org has filed issues on,\nplus the boards the index holds. A repository with nothing on it is not in the\nlist and is still perfectly addressable — GET /projects/<name> reads it and a\ncreate files into it — so nothing is lost by leaving it out.\n\nMeasured, which is why: reading the forge's whole repository inventory put 745\nboards here, of which all but a handful were vendored forks and mirrors\n(.github, .profile, DOMPurify, BoatAttack) that will never carry this org's\nwork. A list that long is not a list — the estate's real roadmap was in it\nsomewhere and no one could see it.\n\nThe forge half is the FORGE's answer for your own account, so two people in\none org can legitimately see different boards.","tags":["todo"],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/todoProject"},"type":"array"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true},"post":{"operationId":"post_todo_projects","summary":"Refused — a board is a repository on the forge","description":"Answers 405. A todo board IS a repository on this deployment's forge, so creating, renaming and deleting one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on the same object, guarded by this surface instead of by the forge — a weaker guard on the same thing. So the route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts, and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule a milestone is. Those are the routes beside this one.","tags":["todo"],"x-app":"todo","x-public":true}},"/v1/todo/projects/{key}":{"delete":{"operationId":"delete_todo_projects_by_key","summary":"Refused — a board is a repository on the forge","description":"Answers 405. A todo board IS a repository on this deployment's forge, so creating, renaming and deleting one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on the same object, guarded by this surface instead of by the forge — a weaker guard on the same thing. So the route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts, and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule a milestone is. Those are the routes beside this one.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"todo","x-public":true},"get":{"operationId":"get_todo_projects_by_key","summary":"Returns one board of your org by its key — the repository name.","description":"Returns one board of your org by its key — the repository name.\n404 when your org has no repository under that key, or when your own forge\naccount cannot see it.","tags":["todo"],"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/todoProject"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true},"patch":{"operationId":"patch_todo_projects_by_key","summary":"Refused — a board is a repository on the forge","description":"Answers 405. A todo board IS a repository on this deployment's forge, so creating, renaming and deleting one is a FORGE operation carried out with FORGE permissions.\n\nOffering it here would put a second endpoint on the same object, guarded by this surface instead of by the forge — a weaker guard on the same thing. So the route exists and refuses, rather than 404ing: \"not this service's job\" and \"no such thing\" are different facts, and the body names the forge so a caller knows where the job IS done.\n\nWhat this surface DOES own is the work on a board: list the boards you can see, read and file their issues, move a card between columns, and read the schedule a milestone is. Those are the routes beside this one.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"todo","x-public":true}},"/v1/todo/projects/{key}/issues":{"get":{"operationId":"get_todo_projects_by_key_issues","summary":"Returns a board's issues — work items with their column, priority, assignee, labels and schedule.","description":"Returns a board's issues — work items with their column, priority,\nassignee, labels and schedule.\n\nWHICH board is a filter, not an address. Bound to a repository (the key from\nthe path) it is that project's board; left unbound it is the org's whole\nboard; narrowed by label it is a board smaller than any repository — which is\nthe only way an app that lives as a directory inside a shared repository can\nhave one. Every combination is the same rows through the same projection, so\nno two boards can disagree about what a column means.\n\nThe column is a LABEL on the forge, so the board and the forge web UI are the\nsame object seen twice: relabelling in either moves the card in both. A closed\nissue reads as done whatever its labels say.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the project whose issues to list, from the path. EMPTY means every\nproject in the org — the global board. It is a filter like the rest of\nthis struct rather than an address, which is what lets one op answer both\n\"this board\" and \"all the work\" without a second surface disagreeing with\nthe first about what a column is.","schema":{"type":"string"}},{"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"}},{"name":"repo","in":"query","required":false,"description":"Repo keeps only issues bound to that git repository.","schema":{"type":"string"}},{"name":"label","in":"query","required":false,"description":"Label keeps only issues carrying that label, compared case-insensitively.\n\nThis is how a board narrows to something SMALLER than a repository — the\none mechanism for it. An estate whose apps are directories inside one\nrepository (hanzoai/cloud carries ~140 of them) has no repository per app\nto address, so the app is a label: `label=app/meet` is the meet board.\nNothing is provisioned to make one exist; a board is the query.","schema":{"type":"string"}},{"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":"todo","x-public":true,"x-tool":true},"post":{"operationId":"post_todo_projects_by_key_issues","summary":"Opens a work item on the board — an issue on that repository on the deployment's forge, filed as YOU.","description":"Opens a work item on the board — an issue on that repository on\nthe deployment's forge, filed as YOU.\n\nThe column and priority are written as LABELS, which is what makes the card\nand the forge issue the same object: someone relabelling in the forge web UI\nhas moved your card.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the board — the repository name, from the path.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/newIssue"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueView"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true}},"/v1/todo/projects/{key}/issues/{num}":{"get":{"operationId":"get_todo_projects_by_key_issues_by_num","summary":"Returns ONE work item in full — its description included.","description":"Returns ONE work item in full — its description included.\n\nThe list reads answer a board, and a board is a summary: the description is\nwhere the actual content of a work item lives — what an issue asks for, what\nan epic's acceptance criteria are — and no read on this surface returned it.\nThe address is the one PATCH already accepts, so an item you can move is now\nan item you can read.\n\nIt reads the forge directly rather than filtering the org fan-out, then falls\nback to the index for a board the forge has never heard of — the same order,\nand the same reason, as GetProject: a row is one kind of thing however it came\nto exist, so a caller does not have to know which store it is in to fetch it.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the board — the repository name, or an index board's key.","schema":{"type":"string"}},{"name":"num","in":"path","required":true,"description":"Num is the issue's number on that board.","schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueView"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true},"patch":{"operationId":"patch_todo_projects_by_key_issues_by_num","summary":"Edits a work item — rename it, rewrite it, move it to another column, or re-prioritise it.","description":"Edits a work item — rename it, rewrite it, move it to another\ncolumn, or re-prioritise it. Absent fields are left alone.\n\nMOVING A CARD IS A RELABEL. The column lives in the forge's label set, so the\nmove replaces that set rather than writing a status column here that a\nforge-side change could contradict. Moving to `done` also CLOSES the issue on\nthe forge, because a done card and an open issue are a contradiction.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"description":"Key is the board — the repository name, from the path.","schema":{"type":"string"}},{"name":"num","in":"path","required":true,"description":"Num is the issue number on that repository, from the path.","schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueEdit"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueView"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true}},"/v1/todo/projects/{key}/issues/{num}/claim":{"post":{"operationId":"post_todo_projects_by_key_issues_by_num_claim","summary":"Takes an issue: it becomes yours and it moves to in_progress.","description":"Takes an issue: it becomes yours and it moves to in_progress.\n\nThe holder is the CALLER, never an argument. \"Assign this to someone else\" is\na different act with different authority, and it already exists as a PATCH;\nconflating them would let anyone hand work to anyone by naming them.\n\nClaiming something already held by someone else is refused rather than\nsilently taken — two agents on one issue is the failure this prevents, and a\nclaim that quietly wins a race is worse than one that says no.","tags":["todo"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}},{"name":"num","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/issueHit"}}},"description":"ok"}},"x-app":"todo","x-public":true,"x-tool":true}},"/v1/tools":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/tools/activation":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/tools/call":{"post":{"operationId":"post_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 endpoint 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 client 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","x-public":true,"x-tool":true}},"/v1/tools/catalog":{"get":{"operationId":"get_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/tools/mcp/servers with its id — and its tools then\njoin the org's tool plane and the fleet's MCP server. 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","x-public":true,"x-tool":true}},"/v1/tools/catalog/sync":{"post":{"operationId":"post_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","x-public":true,"x-tool":true}},"/v1/tools/catalog/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"patch":{"operationId":"patch_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","x-public":true,"x-tool":true}},"/v1/tools/mcp/servers":{"get":{"operationId":"get_tools_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":["tools"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/mcpServerList"}}},"description":"ok"}},"x-app":"tools","x-public":true,"x-tool":true},"post":{"operationId":"post_tools_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 server.","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 server. 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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/tools/mcp/servers/{id}":{"delete":{"operationId":"delete_tools_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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/tools/plugins":{"get":{"operationId":"get_tools_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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/tools/plugins/authored":{"get":{"operationId":"get_tools_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/tools/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":["tools"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authoredPluginList"}}},"description":"ok"}},"x-app":"tools","x-public":true,"x-tool":true}},"/v1/tools/plugins/authored/{id}":{"delete":{"operationId":"delete_tools_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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/tools/plugins/build":{"post":{"operationId":"post_tools_plugins_build","summary":"Builds and stores one plugin for the caller's org.","description":"Builds and stores one plugin for the caller's org. The 201 carries\nthe bundle's size, whether a model wrote the source, and the plugin as stored.\n\nPost `source` to build TypeScript as-is, or `spec` — an OpenAPI document or\nplain prose describing the endpoints — to have one generated; the generated\nsource comes back in the answer, so a caller reads what will run before it\nruns. Exactly one of the two, and `name` must be one lowercase path segment;\nboth or neither is 400.\n\nCOMPILING IS THE GATE. The source goes through the same pipeline the committed\nconnectors do — esbuild to one CommonJS program, then compiled in the goja\nruntime that will actually execute it — and anything that fails is rejected and\nNEVER stored. So a plugin in the store is one this deployment has already\nloaded once, not one a model claimed was fine. A failed build answers 422\ncarrying the diagnostics a caller needs to fix it: the bundler's error\n(`detail`), the source that failed, and whether the model wrote it.\n\nCREDENTIALS ARE NOT PART OF A PLUGIN. A plugin names the connectors `provider`\nit needs and reads that credential from `ctx.auth` at run time, under KMS\ncustody. Source that carries something key-shaped is REFUSED rather than\nsilently persisted — a scrubbed key looks like it worked.","tags":["tools"],"requestBody":{"content":{"application/json":{"example":{"name":"acme","provider":"acme","spec":"POST /v1/things creates a thing"},"schema":{"$ref":"#/components/schemas/buildRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/buildOut"}}},"description":"created"}},"x-app":"tools","x-public":true,"x-tool":true}},"/v1/tools/skills":{"get":{"operationId":"get_tools_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":["tools"],"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","x-public":true,"x-tool":true},"post":{"operationId":"post_tools_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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/tools/skills/authored":{"get":{"operationId":"get_tools_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/tools/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":["tools"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/authoredSkillList"}}},"description":"ok"}},"x-app":"tools","x-public":true,"x-tool":true}},"/v1/tools/skills/{id}":{"delete":{"operationId":"delete_tools_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":["tools"],"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","x-public":true,"x-tool":true}},"/v1/translate":{"post":{"operationId":"post_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","x-public":true}},"/v1/translate/memory":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/treasury":{"get":{"operationId":"get_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":["treasury"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreasuryReport"}}},"description":"ok"}},"x-app":"treasury","x-public":true,"x-tool":true}},"/v1/treasury/accounts":{"get":{"operationId":"get_treasury_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:<tenant>:\" 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=<tenant> — 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":["treasury"],"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","x-public":true,"x-tool":true}},"/v1/trust":{"get":{"operationId":"get_trust","summary":"Reads YOUR organization's whole trust centre, including the addresses of your own gated documents.","description":"Reads YOUR organization's whole trust centre, including the addresses of your\nown gated documents. Same shape as the published endpoint; the difference is that\nthis one is resolved from your validated bearer and shows you your own\nartifacts.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/centre"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/controls":{"get":{"operationId":"get_trust_controls","summary":"Lists every control your organization publishes, with the counts.","description":"Lists every control your organization publishes, with the counts.\n\nA control names what it asserts, the mechanism behind it, the repository and\nfile where that mechanism is enforced, how it is verified, and the framework\nclauses it maps to. Status is automated, partial or absent — and an absent one\nstill names the clause it would satisfy, which is a roadmap, while never\nmoving a coverage number.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/controlList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/controls/{id}":{"get":{"operationId":"get_trust_controls_by_id","summary":"Reads one control by id.","description":"Reads one control by id.","tags":["trust"],"parameters":[{"name":"id","in":"path","required":true,"description":"ID is the control's id, dotted lowercase — \"iam.pkce.s256\".","schema":{"type":"string"},"example":"iam.pkce.s256"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/coverage":{"get":{"operationId":"get_trust_coverage","summary":"Reads coverage: per framework, how many clauses have an automated control behind them, how many are partial, and how many have none — each carrying the unit it is counted in, because \"12 of 20\" is not a fact until you know what the 20 are.","description":"Reads coverage: per framework, how many clauses have an automated control\nbehind them, how many are partial, and how many have none — each carrying the\nunit it is counted in, because \"12 of 20\" is not a fact until you know what\nthe 20 are.\n\nNothing here is a verdict. There is no boolean, and a control that only a\nperson has read counts one rung weaker than it claims to be, because only a\ncheck that can FAIL is evidence.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustCoverage"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/coverage/{framework}":{"get":{"operationId":"get_trust_coverage_by_framework","summary":"Reads one framework clause by clause: every clause the standard publishes, what covers it, and which controls stand behind it — so a coverage number can be checked line by line rather than taken on trust.","description":"Reads one framework clause by clause: every clause the standard publishes,\nwhat covers it, and which controls stand behind it — so a coverage number can\nbe checked line by line rather than taken on trust.","tags":["trust"],"parameters":[{"name":"framework","in":"path","required":true,"description":"Framework is the framework id — \"soc2\", \"iso27001\", \"nist80053\".","schema":{"type":"string"},"example":"soc2"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/clauseCoverage"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/documents":{"get":{"operationId":"get_trust_documents","summary":"Lists your organization's documents.","description":"Lists your organization's documents. Because this is your own centre, a gated\nartifact carries its address here; through the published endpoint it does not.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/trustDocuments"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/evidence":{"get":{"operationId":"get_trust_evidence","summary":"Reads the audit rows that stand behind one control, over a window.","description":"Reads the audit rows that stand behind one control, over a window.\n\nThe inventory decides what evidences what: a control names the audit actions\nthat are its trail, and this resolves the control id to those actions and\nreads them. So evidence cannot drift from the inventory, and it is scoped to\nyour own organization — the query carries no organization field for a caller\nto fill in.\n\nA control that nothing in the trail evidences says so plainly rather than\nanswering an empty page, because an empty page reads like a clean quarter. A\ndeployment with no audit store answers 501 and says the trail was not read,\nfor the same reason.","tags":["trust"],"parameters":[{"name":"control","in":"query","required":false,"description":"Control is the control id whose trail to read. Required.","schema":{"type":"string"},"example":"iam.refresh.rotation"},{"name":"from","in":"query","required":false,"description":"From is the inclusive lower bound, an RFC 3339 date or instant\n(\"2026-01-01\" or \"2026-01-01T00:00:00Z\"). Empty leaves it unbounded. A\nmalformed bound is refused rather than silently widening the window.","schema":{"type":"string"},"example":"2026-01-01"},{"name":"to","in":"query","required":false,"description":"To is the upper bound, same form and same tolerance.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Limit caps the rows returned, 1..1000, default 100. It is a string because\nan unparseable value is refused rather than read as zero.","schema":{"type":"string"},"example":"50"}],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/faq":{"get":{"operationId":"get_trust_faq","summary":"Lists your knowledge base — the questions a reviewer asks, answered once.","description":"Lists your knowledge base — the questions a reviewer asks, answered once.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/faqList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/frameworks":{"get":{"operationId":"get_trust_frameworks","summary":"Lists the frameworks coverage is computed against, and how many clauses each publishes.","description":"Lists the frameworks coverage is computed against, and how many clauses each\npublishes. That count is the denominator of every coverage number, which is\nwhat keeps an uncovered clause visible instead of dropping out of the\nfraction.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/frameworkList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/policies":{"get":{"operationId":"get_trust_policies","summary":"Lists your organization's published policies.","description":"Lists your organization's published policies.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/policyList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/profile":{"get":{"operationId":"get_trust_profile","summary":"Reads your organization's trust-centre profile — the name, tagline and summary a visitor sees, whether the centre is published, and where to send somebody who wants a gated document.","description":"Reads your organization's trust-centre profile — the name, tagline and\nsummary a visitor sees, whether the centre is published, and where to send\nsomebody who wants a gated document.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/published/{org}":{"get":{"operationId":"get_trust_published_by_org","summary":"Reads a published trust centre — the whole thing in one answer: the organization's profile, its control inventory, coverage computed against each framework's whole published clause list, its documents, subprocessors, policies, knowledge base, updates and risk profile.","description":"Reads a published trust centre — the whole thing in one answer: the\norganization's profile, its control inventory, coverage computed against each\nframework's whole published clause list, its documents, subprocessors,\npolicies, knowledge base, updates and risk profile.\n\nThis is the PUBLIC endpoint and needs no credential, because a published trust\ncentre is a public document. It answers only for an organization that has\npublished one — an organization that has not is not found rather than empty,\nsince an empty centre and a centre nobody meant to show read the same and are\nnot the same thing.\n\nA gated document appears here with its title, its type and its date and NO\naddress: the listing says the artifact exists and that reading it takes a\ngrant. Nothing an independent auditor signed is ever released through this\nendpoint.","tags":["trust"],"parameters":[{"name":"org","in":"path","required":true,"description":"Org is the organization's slug — the name in its address.","schema":{"type":"string"},"example":"hanzo"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/centre"}}},"description":"ok"}},"security":[],"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/risk":{"get":{"operationId":"get_trust_risk","summary":"Reads your risk profile — the label and value pairs describing what your organization handles and how.","description":"Reads your risk profile — the label and value pairs describing what your\norganization handles and how.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/subprocessors":{"get":{"operationId":"get_trust_subprocessors","summary":"Lists the third parties your organization sends data to, each naming what it is for.","description":"Lists the third parties your organization sends data to, each naming what it\nis for.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/subprocessorList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/updates":{"get":{"operationId":"get_trust_updates","summary":"Lists your trust-centre updates, newest as you ordered them.","description":"Lists your trust-centre updates, newest as you ordered them.","tags":["trust"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/updateList"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/trust/{kind}/{id}":{"delete":{"operationId":"delete_trust_by_kind_by_id","summary":"Removes one record from a section of your organization's trust centre.","description":"Removes one record from a section of your organization's trust centre. A\nrecord that is not there is a 404, never a silent success. A control that\nbelongs to the deployment's own inventory is removed by a commit, not by a\nrequest.","tags":["trust"],"parameters":[{"name":"kind","in":"path","required":true,"description":"Kind is the section — profile, control, document, subprocessor, policy,\nfaq, update or risk. Anything else is not found. The URL is the authority:\na value here is bound from the path, which zip binds last.","schema":{"type":"string"},"example":"faq"},{"name":"id","in":"path","required":true,"description":"ID is the record's id within that section. The single-valued sections\n(profile, risk) hold one record whatever id is named.","schema":{"type":"string"},"example":"where-is-data-held"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/dropped"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true},"put":{"operationId":"put_trust_by_kind_by_id","summary":"Writes one record into a section of YOUR organization's trust centre — profile, control, document, subprocessor, policy, faq, update or risk.","description":"Writes one record into a section of YOUR organization's trust centre —\nprofile, control, document, subprocessor, policy, faq, update or risk.\n\nA control written here is held to exactly the rule a control committed to the\ndeployment's own inventory is held to, by the same validator: its prose may\nnot claim a certificate and may not name a framework (a framework belongs in\nthe mappings, where it arrives attached to a number), anything short of\nautomated must say what is missing, and a mapping to a clause no framework\ndeclares is refused rather than scored as nothing.\n\nA document defaults to GATED. An artifact an independent auditor signed — a\nSOC 2 report, an ISO certificate, a penetration test, an auditor letter —\ncannot be made public at all; it is released through a grant. A\nself-assessment can, because the organization is the one attesting it.\n\nThe deployment's OWN control inventory is governed in git and is not writable\nhere: naming one of its ids is a conflict, not an overwrite.","tags":["trust"],"parameters":[{"name":"kind","in":"path","required":true,"description":"Kind is the section being written. The URL is the authority.","schema":{"type":"string"},"example":"subprocessor"},{"name":"id","in":"path","required":true,"description":"ID is the record's id. Omit it on a create and one is minted; the\nsingle-valued sections (profile, risk) hold one record whatever is named.","schema":{"type":"string"},"example":"acme-cloud"}],"requestBody":{"content":{"application/json":{"example":{"data":{"name":"A cloud","purpose":"Compute"},"id":"acme-cloud","kind":"subprocessor"},"schema":{"$ref":"#/components/schemas/sectionWrite"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/written"}}},"description":"ok"}},"x-app":"trust","x-public":true,"x-tool":true}},"/v1/usage":{"post":{"operationId":"post_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/link, 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","x-public":true,"x-tool":true}},"/v1/usage/analytics":{"get":{"operationId":"get_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: a count and a unit — 24h, 7d, 90d, any <N>h or <N>d —\nor day, week, month, all, custom. Empty means 24h. The window is then\nclamped forward to the plan's retention entitlement.","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","x-public":true,"x-tool":true}},"/v1/usage/analytics/access":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/usage/samples":{"get":{"operationId":"get_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: a count and a unit — 1h, 24h, 90d, any <N>h or\n<N>d — or day, week, month, all. Empty means 24h. A label that is not a\ncount, or one reaching past the 730-day horizon, is refused rather than\nsilently 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","x-public":true,"x-tool":true}},"/v1/usage/summary":{"get":{"operationId":"get_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: a count and a unit — 24h, 7d, 90d, any <N>h or <N>d —\nor day, week, month, all, custom. Empty means 24h. A label this surface\ndoes not know, or one reaching past the 730-day horizon, is refused rather\nthan 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","x-public":true,"x-tool":true}},"/v1/validator":{"get":{"operationId":"get_validator","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":["validator"],"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":"validator","x-public":true,"x-tool":true},"post":{"operationId":"post_validator","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":["validator"],"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":"validator","x-public":true,"x-tool":true}},"/v1/validator/challenge":{"get":{"operationId":"get_validator_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/validator verifies is rebuilt from those same server\nfacts rather than trusted from the caller. Redeem it with\nPOST /v1/validator before it expires; it can be redeemed once.\n\nA tokenId outside the Validator tier is refused here rather than after signing.","tags":["validator"],"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":"validator","x-public":true,"x-tool":true}},"/v1/validator/{tokenId}":{"get":{"operationId":"get_validator_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":["validator"],"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":"validator","x-public":true,"x-tool":true}},"/v1/videos/generations":{"post":{"operationId":"post_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":["ai"],"x-app":"ai","x-public":true}},"/v1/videos/{id}":{"get":{"operationId":"get_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":["ai"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai","x-public":true}},"/v1/videos/{id}/content":{"get":{"operationId":"get_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":["ai"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"ai","x-public":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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/visor/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/visor/clusters. Billed the nominal management fee: the\ncustomer brings the compute, Hanzo meters the management plane.","tags":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"parameters":[{"name":"clusterId","in":"path","required":true,"description":"ClusterID is the cluster holding the pool, from the URL path.","schema":{"type":"string"}},{"name":"poolId","in":"path","required":true,"description":"PoolID is the pool to resize, from the URL path — the `poolId` a cluster read\nreports for it. Required.","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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/compute/bots/launch":{"post":{"operationId":"post_visor_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":["visor"],"x-app":"visor","x-public":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/compute/bots/{id}/{action}":{"post":{"operationId":"post_visor_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":["visor"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"action","in":"path","required":true,"schema":{"type":"string"}}],"x-app":"visor","x-public":true}},"/v1/visor/compute/regions":{"get":{"operationId":"get_visor_compute_regions","summary":"Regions lists the regions a machine can be launched in.","description":"Regions lists the regions a machine can be launched in.\n\nThe catalog is GLOBAL — identical for every tenant — so no owner is forwarded\nupstream. It is still org-gated, because a catalog is a map of what this\ndeployment can spend money in and an anonymous caller has no business reading it.","tags":["visor"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"visor","x-public":true,"x-tool":true}},"/v1/visor/compute/sizes":{"get":{"operationId":"get_visor_compute_sizes","summary":"Sizes lists the machine sizes available to launch, with their specifications.","description":"Sizes lists the machine sizes available to launch, with their specifications.\n\nGlobal and org-gated, exactly as the region catalog is, and for the same reasons.","tags":["visor"],"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"ok"}},"x-app":"visor","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"parameters":[{"name":"gpu","in":"query","required":false,"description":"GPU selects one node's lane: jobs TARGETED at it (gpu:<node>) 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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"responses":{"200":{"content":{"application/json":{"example":{"alerts":[]},"schema":{"$ref":"#/components/schemas/gpuAlertList"}}},"description":"ok"}},"x-app":"visor","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"post":{"operationId":"post_visor_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":["visor"],"x-app":"visor","x-public":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/visor/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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true},"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":["visor"],"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","x-public":true,"x-tool":true}},"/v1/wallet":{"get":{"operationId":"get_wallet","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":["wallet"],"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":"wallet","x-public":true,"x-tool":true},"post":{"operationId":"post_wallet","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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/wallet/accounts":{"get":{"operationId":"get_wallet_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":["wallet"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/accountList"}}},"description":"ok"}},"x-app":"wallet","x-public":true,"x-tool":true},"post":{"operationId":"post_wallet_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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/wallet/{id}":{"get":{"operationId":"get_wallet_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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/wallet/{id}/keys":{"post":{"operationId":"post_wallet_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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/wallet/{id}/sign":{"post":{"operationId":"post_wallet_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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/wallet/{id}/transactions":{"post":{"operationId":"post_wallet_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":["wallet"],"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":"wallet","x-public":true,"x-tool":true}},"/v1/web3/chains":{"get":{"operationId":"get_web3_chains","summary":"Reports the chains this deployment can reach.","description":"Reports the chains this deployment can reach. The list is the\ndeclared registry, so it is exactly what /v1/web3/rpc will accept — a chain that\nappears here is one this deployment actually has an upstream for.","tags":["web3"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/chainList"}}},"description":"ok"}},"x-app":"web3","x-public":true,"x-tool":true}},"/v1/web3/chains/{chain}":{"get":{"operationId":"get_web3_chains_by_chain","summary":"Reports one chain and whether its upstream is answering.","description":"Reports one chain and whether its upstream is answering. An\nunreachable chain is still a 200 with live:false — the chain is configured,\nwhich is a different fact from the chain being up, and a 502 here would make\na console page error rather than show the outage.","tags":["web3"],"parameters":[{"name":"chain","in":"path","required":true,"description":"Chain is the registry id, as in /v1/web3/chains/lux.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/chainStatus"}}},"description":"ok"}},"x-app":"web3","x-public":true,"x-tool":true}},"/v1/web3/rpc/{chain}":{"post":{"operationId":"post_web3_rpc_by_chain","summary":"Forwards a JSON-RPC call to the named chain and returns its answer unchanged.","description":"Forwards a JSON-RPC call to the named chain and returns its answer\nunchanged. Only declared chains are reachable, and only to a caller with a\nvalidated principal — this is the deployment's upstream, not an open relay.","tags":["web3"],"parameters":[{"name":"chain","in":"path","required":true,"description":"Chain is the registry id, from the URL.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/rpcIn"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/rpcOut"}}},"description":"ok"}},"x-app":"web3","x-public":true,"x-tool":true}},"/v1/web3/tokens/{chain}/{address}":{"get":{"operationId":"get_web3_tokens_by_chain_by_address","summary":"Reads an address's native balance on a chain.","description":"Reads an address's native balance on a chain.\n\nERC-20 positions are NOT enumerated here: eth_getBalance answers the native\none, but \"every token this address holds\" is an indexer question — there is no\nRPC call that answers it, and walking a token list would return a number that\nsilently omits whatever the list missed. explorer owns the indexer\nrelationship; this returns the balance the chain itself can prove.","tags":["web3"],"parameters":[{"name":"chain","in":"path","required":true,"description":"Chain is the registry id.","schema":{"type":"string"}},{"name":"address","in":"path","required":true,"description":"Address is the account, 0x-prefixed.","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/balances"}}},"description":"ok"}},"x-app":"web3","x-public":true,"x-tool":true}},"/v1/webhook":{"get":{"operationId":"get_webhook","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":["webhook"],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/endpointList"}}},"description":"ok"}},"x-app":"webhook","x-public":true,"x-tool":true},"post":{"operationId":"post_webhook","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":["webhook"],"requestBody":{"content":{"application/json":{"example":{"description":"order pipeline","events":["commerce.order.>"],"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":"webhook","x-public":true,"x-tool":true}},"/v1/webhook/{id}":{"delete":{"operationId":"delete_webhook_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":["webhook"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"wh_9f8c1d2e"}],"responses":{"204":{"description":"no content"}},"x-app":"webhook","x-public":true,"x-tool":true},"get":{"operationId":"get_webhook_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":["webhook"],"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":"webhook","x-public":true,"x-tool":true},"put":{"operationId":"put_webhook_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/webhook/{id}/secret.","tags":["webhook"],"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":"webhook","x-public":true,"x-tool":true}},"/v1/webhook/{id}/deliveries":{"get":{"operationId":"get_webhook_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":["webhook"],"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":"webhook","x-public":true,"x-tool":true}},"/v1/webhook/{id}/secret":{"post":{"operationId":"post_webhook_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":["webhook"],"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":"webhook","x-public":true,"x-tool":true}},"/v1/webhook/{id}/test":{"post":{"operationId":"post_webhook_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":["webhook"],"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":"webhook","x-public":true,"x-tool":true}},"/v1/websearch":{"post":{"operationId":"search_web","summary":"Search the live web","description":"Searches the live web and answers with ranked results.\n\nThis is the fleet's path to what is happening RIGHT NOW — today's weather, an\noutage, a release that postdates any model's training. `q` is the query and\n`language` narrows it to a locale. The answer is `{query, number_of_results,\nresults:[{url, title, content, engine}]}`, where `content` is the ENGINE's\nsnippet and not the page: read a page with POST /v1/crawl.\n\nIt is served in-process by a Go meta-search over keyless public engines — never\na third-party search API and never a search key. The enabled engines run\nconcurrently and their hits are merged, deduplicated by normalised URL (host\nand path, trailing slash and fragment dropped, query kept, so distinct queries\nstay distinct results) and capped at 30. Ranking is deterministic rather than\nscored: the first configured engine's hits lead.\n\nIt fails SOFT on the engines. One that errors, times out or is served a\nbot-challenge page contributes zero results and never fails the call, so an\nempty `results` is a real answer — nothing was found — and not an outage. The\narray is always present, never null.\n\nA VALIDATED PRINCIPAL IS REQUIRED, and there is no tenant beyond that: the\nresults are public web pages, identical for every caller, so nothing here is\nscoped and nothing here can leak across orgs. A typed op is also an MCP tool\nand a CLI command, and tools/call invokes it with no route and therefore no\nmiddleware — so the gate is in the handler, where every caller reaches it,\nrather than in a middleware only one caller passes through.","tags":["websearch"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webSearchQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/webSearchResults"}}},"description":"ok"}},"x-app":"websearch","x-public":true,"x-tool":true}},"/v1/websearch/scrape":{"post":{"operationId":"post_websearch_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 endpoint.\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":["websearch"],"x-app":"websearch","x-public":true}},"/v1/websearch/search":{"delete":{"operationId":"delete_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 30. 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","x-public":true},"get":{"operationId":"get_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 30. 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","x-public":true},"patch":{"operationId":"patch_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 30. 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","x-public":true},"post":{"operationId":"post_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 30. 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","x-public":true},"put":{"operationId":"put_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 30. 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","x-public":true}},"/v1/world":{"get":{"operationId":"get_world","summary":"Answers GET /v1/world — the product's public endpoint, naming every wire this surface answers on.","description":"Answers GET /v1/world — the product's public endpoint, naming every wire\nthis surface 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","x-public":true,"x-tool":true}},"/v1/world/limits":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/world/news":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/world/pipeline":{"get":{"operationId":"get_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","x-public":true,"x-tool":true},"put":{"operationId":"put_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","x-public":true,"x-tool":true}},"/v1/world/stream":{"get":{"operationId":"get_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","x-public":true}},"/v1/x402/settlements/{id}":{"get":{"operationId":"get_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","x-public":true,"x-tool":true}},"/v1/{name}":{"get":{"operationId":"get_capability","summary":"One capability's operations, with the address and method to call each","description":"What the capability named in the path answers: every published operation, its operationId, its method and its address, and the sentence lifted from the handler that serves it — plus the way back to /v1.\n\nIt answers where the capability serves nothing at its own root. Where it does, that operation is the answer and is described at its own address; a client following the root index reaches the capability either way.\n\nUnauthenticated, and scoped to the same customer surface the root is. A name that is not a published capability is answered exactly as any other unrouted address, so this cannot be asked whether something exists that it would not have listed.","tags":["openapi"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"2XX":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Index"}}}}},"security":[],"x-app":"openapi","x-public":true}}},"components":{"schemas":{"Account":{"properties":{"name":{"description":"Name is the account's human name, for a statement's line label.","type":"string"},"number":{"description":"Number is the posting key every voucher leg, rule and report references —\nstable, and the reason the chart is a fixed value rather than a table anybody\ncan edit. It looks numeric and is a string: \"1000\" sorts and compares as text.","type":"string"},"party":{"description":"Party marks the account as carrying a SUBLEDGER — receivable is money owed to\nus, payable money we owe — so a leg posted here also writes a payment-ledger\nrow against a counterparty. Absent means no subledger: a bank, wallet, revenue\nor cost account tracks no counterparty at all.","type":"string"},"type":{"description":"Type is the account's fundamental class, which is also its NORMAL balance\nside: asset and expense are debit-normal, liability, income and equity\ncredit-normal.","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"},"ActionRecord":{"properties":{"args":{"description":"Args is the JSON the tool was called with, recorded as TEXT exactly as sent —\nincluding whatever the AI drafted into it — so a run can be read back and\nreproduced. It is a string, not an object.","type":"string"},"createdAt":{"description":"CreatedAt is when the run was recorded, as Unix seconds. The ledger is read\nnewest-first on this column.","type":"integer"},"err":{"description":"Err is why the run failed, when it did. Empty on a successful run.","type":"string"},"id":{"description":"ID identifies this one execution. The ledger is append-only, so an id is\nnever reused and never updated.","type":"string"},"ok":{"description":"OK is whether the tool ran to completion. It is the ledger's own verdict, not\nthe tool's opinion of the outcome — a tool that succeeded at reporting bad\nnews is ok.","type":"boolean"},"result":{"description":"Result is the tool's own answer, likewise recorded as JSON text. Present on a\nfailed run too, where the tool answered but the answer was a refusal.","type":"string"},"stepId":{"description":"StepID is the checklist step the Business AI was acting on.","type":"string"},"tool":{"description":"Tool is the MCP tool that was dispatched, by name.","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":{"description":"Action is one of created, updated, deleted.","type":"string"},"actor":{"description":"Actor is the email of the principal who made the change. Empty for a write\nby an in-process composer; a project key can never appear here, because\nevaluating flags is all a key may do.","type":"string"},"at":{"description":"At is when the change was made, RFC 3339 UTC.","type":"string"},"detail":{"description":"Detail is free-form context about the change. Nothing writes it today, so\nit is absent from every row the store serves.","type":"string"},"id":{"description":"ID is the log's own sequence number, rising with each entry. The log is\nserved newest-first, which is this descending.","type":"integer"},"key":{"description":"Key is the flag that changed. It survives a delete, so the log still names\nflags the definition store no longer holds.","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":"Account is the provider ad-account the campaign runs under, in Meta's\nact_<id> form. Empty until the org supplies one or a launch resolves it.","type":"string"},"budget":{"description":"Budget is the campaign's authorized spend in MINOR units (cents). Negative\nclamps to 0. It is the org's stored plan: a Meta launch creates the campaign\nobject only, and the delivering budget lives on the ad set.","type":"integer"},"createdAt":{"description":"CreatedAt is when the campaign was first stored, in unix seconds. It never\nchanges, including across a full-replace update.","type":"integer"},"externalId":{"description":"ExternalID is the ad network's own campaign id, written by a successful\nlaunch and by nothing else — an update never touches it. Empty means this\ncampaign has never reached its network.","type":"string"},"id":{"description":"ID is the campaign's server-minted handle, \"camp_\" + 32 hex. A create body\ncannot choose it, and it is the id every other route addresses.","type":"string"},"name":{"description":"Name is the campaign's display label, and the name Meta creates the campaign\nobject under at launch. Required; trimmed and bounded to 1024 bytes.","type":"string"},"objective":{"description":"Objective is the campaign goal spelled as the provider names it\n(\"conversions\", \"OUTCOME_TRAFFIC\"), passed through to the network verbatim at\nlaunch — Meta defaults an empty one to OUTCOME_TRAFFIC. Free text, bounded to\n1024 bytes; no vocabulary is enforced here.","type":"string"},"platform":{"description":"Platform is the ad network: meta, google, tiktok or x, and nothing else — a\nwrite naming another is 400. Empty stores as meta. Only meta executes today;\nlaunching any of the other three is 501.","type":"string"},"spend":{"description":"Spend is spend-to-date in MINOR units (cents), as last written through create\nor update. Negative clamps to 0. It is NOT read back from the network — that\nis a separate insights call — so 0 means nothing was recorded here, not that\nnothing was spent.","type":"integer"},"status":{"description":"Status is the lifecycle: draft, active, paused or completed, and nothing else\n— a write naming another is 400. Empty stores as draft; a successful launch\nsets active. It records what this deployment did, not what the ad network\ncurrently reports.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the row was last written, in unix seconds — set by create,\nupdate and launch. Listings are ordered by it, newest first.","type":"integer"}},"type":"object"},"Alert":{"properties":{"createdAt":{"type":"string"},"currency":{"type":"string"},"enforce":{"type":"boolean"},"id":{"type":"string"},"over":{"type":"boolean"},"period":{"type":"string"},"periodSpentCents":{"type":"integer"},"project":{"type":"string"},"rateLimitRpm":{"type":"integer"},"resetsAt":{"type":"string"},"service":{"type":"string"},"softPct":{"type":"integer"},"threshold":{"type":"integer"},"title":{"type":"string"},"triggeredAt":{"type":"string"},"updatedAt":{"type":"string"},"userId":{"type":"string"},"warn":{"type":"boolean"}},"type":"object"},"AlertPatch":{"properties":{"enforce":{"type":"boolean"},"id":{"type":"string"},"project":{"type":"string"},"rateLimitRpm":{"type":"integer"},"service":{"type":"string"},"softPct":{"type":"integer"},"subject":{"type":"string"},"threshold":{"type":"integer"},"title":{"type":"string"}},"type":"object"},"AlertSpec":{"properties":{"currency":{"type":"string"},"enforce":{"type":"boolean"},"project":{"type":"string"},"rateLimitRpm":{"type":"integer"},"service":{"type":"string"},"softPct":{"type":"integer"},"subject":{"type":"string"},"threshold":{"type":"integer"},"title":{"type":"string"}},"type":"object"},"Allowance":{"properties":{"limit":{"description":"calls the plan allows per period; 0 = unbounded","type":"integer"},"plan":{"description":"the tier the limit came from","type":"string"},"resets":{"description":"unix seconds; when the count starts again","type":"integer"},"spent":{"description":"the subject is at the limit","type":"boolean"},"used":{"description":"Used is how many zero-priced calls this subject has been SERVED in the period\nending at Resets — the UTC calendar day. Only a served call counts, so an\nadmission check, a refusal, or a vendor that never answered leaves it where it\nstood. It stops AT Limit rather than climbing past it, so Limit-Used is what\nremains and never goes negative.","type":"integer"}},"type":"object"},"Analysis":{"properties":{"alpha":{"description":"the two-tailed threshold significance was judged at","type":"number"},"experiment":{"description":"the experiment that was analysed","type":"string"},"exposedTotal":{"description":"subjects enrolled across every arm","type":"integer"},"metric":{"description":"the event a conversion is counted from","type":"string"},"results":{"description":"one row per declared arm, control first","items":{"$ref":"#/components/schemas/Outcome"},"type":"array"},"winner":{"description":"ADVISORY: the significant, control-beating arm with the highest rate, else empty","type":"string"}},"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":{"description":"Completions is complete's answer: the candidates at the position, typed and\nresolved through the repository's dependencies rather than guessed from text.","items":{"$ref":"#/components/schemas/Completion"},"type":"array"},"diagnostics":{"description":"Diagnostics is diagnostics' answer: every problem the server finds in the\nwhole file, position ignored. Empty means it found none.","items":{"$ref":"#/components/schemas/Diagnostic"},"type":"array"},"hover":{"description":"Hover is hover's answer: the type and documentation as the language server\nitself renders them, so it is prose meant to be shown, not parsed.","type":"string"},"lang":{"description":"Lang is the language the server that answered speaks (\"go\"), as the daemon\nreports it. Empty when the daemon named none.","type":"string"},"locations":{"description":"Locations is locate's answer: where the symbol is defined, referenced, typed\nor implemented, per the relation asked for. Empty means the server resolved\nnothing there, which is an answer.","items":{"$ref":"#/components/schemas/Location"},"type":"array"},"op":{"description":"Op is the question that was asked: hover, locate, symbols, diagnostics or\ncomplete. It names which result field below is the populated one.","type":"string"},"path":{"description":"Path is the repo-relative file the question was about, echoed back.","type":"string"},"repo":{"description":"Repo is the repository the question was about, echoed back.","type":"string"},"rev":{"description":"Rev is the RESOLVED commit sha, never the branch or tag that was asked for.\nIt is what makes an answer re-askable: a branch moves, this does not.","type":"string"},"symbols":{"description":"Symbols is symbols' answer: the file's whole outline, position ignored.","items":{"$ref":"#/components/schemas/Symbol"},"type":"array"}},"type":"object"},"AppView":{"properties":{"app":{"description":"service / CR name, e.g. iam","type":"string"},"cluster":{"description":"always \"hanzo-k8s\"; cross-cluster federation is a follow-up phase","type":"string"},"declaredTag":{"description":"spec.image.tag — the tag the CR SAYS to run; anything but vX.Y.Z is red drift","type":"string"},"drift":{"$ref":"#/components/schemas/Verdict","description":"declared vs running vs latest as flags, plus their rolled-up severity"},"endpoints":{"description":"status.endpoints the operator published for this service; empty until it reconciles","items":{"type":"string"},"type":"array"},"env":{"description":"main|test|dev","type":"string"},"health":{"description":"green|yellow|red|\"\" (unknown)","type":"string"},"id":{"description":"<org>/<app>/<env>, e.g. hanzoai/iam/main","type":"string"},"latestTag":{"description":"newest released tag; \"\" until the GH release reader lands, so \"stale\" cannot fire yet","type":"string"},"namespace":{"description":"namespace the row was scanned from: a platform one (hanzo, hanzo-testnet, …) or a tenant-<org>","type":"string"},"org":{"description":"image namespace, e.g. hanzoai","type":"string"},"phase":{"description":"operator status.phase (Running/…)","type":"string"},"registry":{"description":"spec.image.repository verbatim (ghcr.io/hanzoai/iam); Org and Repo are read off it","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":{"description":"the tag the live Deployment actually runs; \"\" when unreadable — unknown, not a guess","type":"string"}},"type":"object"},"Arm":{"properties":{"control":{"description":"true on the baseline arm every other arm is compared to","type":"boolean"},"key":{"description":"the arm's slug, unique within the experiment","type":"string"},"payload":{"description":"opaque JSON the arm carries, which the consumer interprets"},"weight":{"description":"its share of the rollout; the arms sum to 100","type":"number"}},"type":"object"},"AskAnswer":{"properties":{"answer":{"description":"Answer is the synthesized prose. EMPTY is a real answer here: nothing in the\nindex matched, or synthesis was unavailable — read `degraded` and `citations`\nto tell those apart. It is never written without grounding.","type":"string"},"citations":{"description":"Citations are the exact regions the answer was grounded on, and they are the\npoint: an answer is checkable only because every claim in it can be read back\nat a file and line. Present even when Answer is empty.","items":{"$ref":"#/components/schemas/Citation"},"type":"array"},"degraded":{"description":"Degraded is true when retrieval worked but no synthesizer was reachable. The\ncitations are still real code, so a caller can answer from them itself; a\ncaller that treats this like an error throws away a usable result.","type":"boolean"},"question":{"description":"Question is the ask, echoed back.","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"},"Audience":{"properties":{"createdAt":{"description":"CreatedAt is unix seconds when the filter was saved, 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":{"description":"UpdatedAt is unix seconds of the last write, server-assigned, and the key\nthe audience list is ordered by (newest first). A saved audience has no\nupdate route, so in practice it stays equal to CreatedAt: to change a\nfilter you save another one.","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 mailboxes a send would reach. Two\ncustomers sharing an address count once, so it is <= Count.","type":"integer"},"reason":{"description":"Reason is the error text of the read that failed: the org's roster could\nnot be loaded (\"identity store unavailable…\"), or the cohort query had no\nwarehouse to run against (\"analytics warehouse not configured\"). Absent\nwhen the evaluation succeeded, so its presence and Available=false are one\nfact seen twice.","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:<org>\" for the whole-org one.","type":"string"},"unmatched":{"description":"Unmatched is how many cohort identifiers named nobody on the org's roster\nand so have no address to mail. It is reported rather than hidden: it is\nthe honest explanation for a cohort of 500 that mails 3. Always 0 for an\nevent-less (whole-org) audience, which starts from the roster and has\nnothing to match.","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 >= 0.","type":"integer"}},"type":"object"},"BackendStatus":{"properties":{"error":{"description":"Error is the failure text from a leg whose status is degraded — the reason a\nconfigured backend could not answer. Absent otherwise.","type":"string"},"hits":{"description":"Hits is how many results this leg returned, counted BEFORE fusion, so it is\nnot the number that survived into Response.Hits — fusion merges what both\nlegs found and the caller's limit and offset then page it. 0 for a leg that\ndid not run.","type":"integer"},"name":{"description":"Name is which leg this reports: \"index\", the lexical store, \"vector\", the\nsemantic one, or \"code\", the org's own repositories. Match.Backend uses the\nsame three names.","type":"string"},"status":{"description":"Status is one of ok, degraded, disabled, skipped — four distinct operational\nfacts that are never collapsed. It ran and answered; it is configured and\nFAILED (Error says how, and only this one is a fault); this deployment never\nprovisioned it; or the request's mode excluded it.","type":"string"},"took_ms":{"description":"TookMS is how long this leg took, in milliseconds, timed around its own call\nand excluding fusion. 0 for a leg that was skipped or is disabled, since\nnothing was called.","type":"integer"}},"type":"object"},"BalanceLine":{"properties":{"account":{"description":"Account is the chart-of-accounts number this line reports on. ABSENT marks a\nDERIVED line that no account holds — retained earnings is the one such line,\ncomputed from cumulative income minus expense.","type":"string"},"amount":{"description":"Amount is the balance as of the statement date, in whole cents, in its NATURAL\nsign: positive when the account behaved normally, on all three sides. Assets\nare debit-normal and shown as stored; liabilities and equity are credit-normal\nand flipped once here for display. A negative asset is a real overdraft, not a\nsign convention.","type":"integer"},"name":{"description":"Name is the account's human name, or the derived line's own name.","type":"string"},"type":{"description":"Type is the account's fundamental class. Absent on a derived line, which\nbelongs to no account and therefore has none.","type":"string"}},"type":"object"},"BalanceSheet":{"properties":{"asOf":{"description":"AsOf is the posting time the statement is taken at, inclusive. A balance sheet\nis a snapshot, not a window, so there is no From. Absent means as of now.","type":"string"},"assets":{"description":"Assets are what the org OWNS at that instant, one line per account that has a\nbalance. Cash, receivables, funds captured but not yet settled.","items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"balanced":{"description":"Balanced is whether assets equal liabilities plus equity — the accounting\nequation, computed from the totals above rather than assumed. False means the\nledger is broken, not that the statement is.","type":"boolean"},"equity":{"description":"Equity is what is left over for the owners. It carries a DERIVED retained\nearnings line holding cumulative income minus expense, because this ledger has\nno period close that sweeps the P&L into equity — without that line the\nequation would not close.","items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"liabilities":{"description":"Liabilities are what the org OWES — including customers' unspent prepaid\ncredit, which is their money until it is consumed and so is carried here\nrather than counted as revenue.","items":{"$ref":"#/components/schemas/BalanceLine"},"type":"array"},"totalAssets":{"description":"TotalAssets is the sum of the asset lines, in cents.","type":"integer"},"totalEquity":{"description":"TotalEquity is the sum of the equity lines including retained earnings, in cents.","type":"integer"},"totalLiabilities":{"description":"TotalLiabilities is the sum of the liability lines, in cents.","type":"integer"}},"type":"object"},"BankQuestion":{"properties":{"connector":{"description":"Connector names the feed the unplaceable line arrived on. With externalId it\nidentifies both the question and the bank line it is about, so re-syncing the\nsame deposit never asks twice.","type":"string"},"createdAt":{"description":"CreatedAt is when the question was raised.","type":"string"},"externalId":{"description":"ExternalID is the bank's own id for the line in question.","type":"string"},"prompt":{"description":"Prompt is the question put to the founder in plain language — what this money\nwas, since the books cannot place it on their own.","type":"string"},"status":{"description":"Status is whether the question is still open or has been answered.","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&L)","type":"integer"}},"type":"object"},"BankTxnRow":{"properties":{"amountCents":{"description":"AmountCents is the size of the movement in whole cents, always POSITIVE —\ndirection carries the sign, so a caller must read both to know which way money\nwent.","type":"integer"},"connector":{"description":"Connector names the feed this row arrived on — which bank or processor\nconnection it was synced from. With externalId it is the row's identity, so\nre-syncing the same statement never books a second copy.","type":"string"},"currency":{"description":"Currency is the ISO code the bank reported the line in.","type":"string"},"description":{"description":"Description is the statement memo as the bank wrote it.","type":"string"},"direction":{"description":"Direction is which way the money moved: an inflow into the account or an\noutflow from it, from the org's point of view.","type":"string"},"externalId":{"description":"ExternalID is the bank's OWN id for the line, carried verbatim. It is unique\nonly within its connector.","type":"string"},"matchedVoucher":{"description":"MatchedVoucher names the ledger voucher this line was reconciled against —\nthe bill it paid, or the settlement it cleared. Absent when nothing matched,\nwhich for an inflow is what raises a question.","type":"string"},"merchant":{"description":"Merchant is the counterparty the feed identified, where it did.","type":"string"},"postedAt":{"description":"PostedAt is the bank's posting date for the line, not when we synced it.","type":"string"},"status":{"description":"Status is where the line got to: posted (an outflow booked straight to an\nexpense), settled (an outflow that paid down a scanned bill), reconciled (an\ninflow that cleared a pending settlement), transfer (a move between the org's\nown accounts, recorded but with no effect on the books), or unmatched (an\ninflow nobody could place, which is waiting on a human answer).","type":"string"}},"type":"object"},"Benchmark":{"properties":{"axis":{"description":"what capability it measures","type":"string"},"id":{"description":"the id every other op on this surface takes","type":"string"},"items":{"description":"how many items it holds, when the set is fixed","type":"integer"},"native":{"description":"whether the standardized harness runs it today","type":"boolean"},"source":{"description":"where the items come from","type":"string"},"title":{"description":"the benchmark's published name","type":"string"}},"type":"object"},"BillingAccount":{"properties":{"createdAt":{"type":"string"},"currency":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"orgId":{"type":"string"},"orgName":{"type":"string"},"role":{"description":"Role is the caller's standing. Absent when nobody was named, so an\nanonymous read returns an account rather than an implied membership.","type":"string"}},"type":"object"},"BillingInvoice":{"properties":{"amountDue":{"type":"integer"},"amountPaid":{"type":"integer"},"attemptCount":{"type":"integer"},"createdAt":{"type":"string"},"creditApplied":{"type":"integer"},"currency":{"type":"string"},"customerEmail":{"type":"string"},"discount":{"type":"integer"},"dueDate":{"type":"string"},"id":{"type":"string"},"lineItems":{"description":"LineItems carries no omitempty and is never allocated empty, because the\nwire it reproduces sends `null` for an invoice with no lines. An empty\narray there would be a different answer to \"were there lines\".","items":{"$ref":"#/components/schemas/InvoiceLineItem"},"type":"array"},"number":{"type":"integer"},"numberStr":{"type":"string"},"paidAt":{"type":"string"},"paymentMethod":{"type":"string"},"paymentRef":{"type":"string"},"periodEnd":{"type":"string"},"periodStart":{"type":"string"},"status":{"type":"string"},"subscriptionId":{"type":"string"},"subtotal":{"type":"integer"},"tax":{"type":"integer"},"updatedAt":{"type":"string"},"userId":{"type":"string"},"voidedAt":{"type":"string"}},"type":"object"},"Blob":{"properties":{"data":{"contentEncoding":"base64","description":"Data is the file's bytes, verbatim, base64 on the wire. Empty for a directory\nand for an empty file alike; Dir is what tells those apart.","type":"string"},"dir":{"description":"Dir says which of the two answers this is: true and the path is a directory,\nso read Entries; false and it is a file, so read Data. Nothing else\ndistinguishes them — an empty file and an empty directory look alike here.","type":"boolean"},"entries":{"description":"Entries is a directory's contents as bare NAMES, not paths — one level, no\nrecursion, dotfiles included, \".\" and \"..\" excluded (`ls -1A`). Empty for a\nfile, and for an empty directory.","items":{"type":"string"},"type":"array"},"path":{"description":"Path is the RESOLVED absolute path that was read — the caller's relative path\njoined onto the sandbox's working directory (Leased.Workdir), so it names the\nsame file for a reader who does not know the class.","type":"string"}},"type":"object"},"Blog":{"properties":{"caseStudy":{"description":"CaseStudy is one worked instance — somebody who ran it, and what happened.","type":"string"},"how":{"description":"How is the run book: the steps to execute the tactic.","type":"string"},"slug":{"description":"Slug is the post's address — the last path segment it is published at.","type":"string"},"title":{"description":"Title is the post's headline.","type":"string"},"why":{"description":"Why is the mechanism: the reason the tactic works, stated as a principle\nrather than as instructions.","type":"string"}},"type":"object"},"Blueprint":{"properties":{"brand":{"description":"Brand is the white-label key this playbook serves. Empty is the shared\ndefault every unbranded deployment falls back to.","type":"string"},"enabled":{"description":"Enabled is the whole-playbook lever. Absent reads as ON; an explicit false\nmakes resolution skip this playbook entirely and fall through to the next\ntier, rather than serving an empty journey.","type":"boolean"},"principles":{"description":"Principles are the fixed 64-archetype spine a tactic files under. It is\nauthored data an operator organises the corpus by; nothing in the checklist\nengine reads it.","items":{"$ref":"#/components/schemas/Principle"},"type":"array"},"sections":{"description":"Sections are the journey's ordered phases.","items":{"$ref":"#/components/schemas/Section"},"type":"array"},"steps":{"description":"Steps are every checklist item, disabled ones included — this is the authored\ndocument, not the projection an org runs.","items":{"$ref":"#/components/schemas/JourneyStep"},"type":"array"},"strategies":{"description":"Strategies are the tactics corpus the recommendation reads narrow.","items":{"$ref":"#/components/schemas/Strategy"},"type":"array"},"templates":{"description":"Templates are the reusable prompts and snippets steps reference by id.","items":{"$ref":"#/components/schemas/Page"},"type":"array"},"title":{"description":"Title is the playbook's name as a person reads it.","type":"string"},"version":{"description":"Version is the playbook's own name for this edition of its content, chosen by\nwhoever authored it. It travels onto every journey projected from it. The\nstore's numeric revision is a separate value and lives beside it.","type":"string"}},"type":"object"},"Board":{"properties":{"byModel":{"description":"the top models by spend","items":{"$ref":"#/components/schemas/ModelStat"},"type":"array"},"latency":{"$ref":"#/components/schemas/LatencyStat","description":"overall latency percentiles from the GenAI spans"},"other":{"$ref":"#/components/schemas/ModelStat","description":"the long tail beyond the top models, folded into one row"},"range":{"$ref":"#/components/schemas/BoardRange","description":"the window they were computed over, echoed back"},"scope":{"$ref":"#/components/schemas/BoardScope","description":"whose numbers these are"},"series":{"description":"one gap-filled bucket per interval, so a chart never breaks","items":{"$ref":"#/components/schemas/BoardPoint"},"type":"array"},"totals":{"$ref":"#/components/schemas/BoardTotals","description":"the window's headline numbers"}},"type":"object"},"BoardPoint":{"properties":{"costCents":{"description":"what this bucket cost, in cents","type":"integer"},"errors":{"description":"calls in this bucket that did not succeed","type":"integer"},"generations":{"description":"model calls in this bucket","type":"integer"},"t":{"description":"RFC3339 (UTC) bucket start","type":"string"},"totalTokens":{"description":"tokens in this bucket","type":"integer"}},"type":"object"},"BoardRange":{"properties":{"end":{"description":"RFC3339 (UTC)","type":"string"},"interval":{"description":"hour | day","type":"string"},"range":{"description":"echoed label (24h | 7d | 30d | custom)","type":"string"},"start":{"description":"RFC3339 (UTC)","type":"string"}},"type":"object"},"BoardScope":{"properties":{"allOrgs":{"description":"true when a platform admin is seeing every org at once","type":"boolean"},"org":{"description":"the org the board covers; \"\" when it covers all of them","type":"string"},"project":{"description":"the sub-scope within the org; \"\" is the whole org","type":"string"}},"type":"object"},"BoardTotals":{"properties":{"completionTokens":{"description":"tokens the models answered with","type":"integer"},"costCents":{"description":"what the window cost, in cents","type":"integer"},"errors":{"description":"calls that did not succeed","type":"integer"},"generations":{"description":"how many model calls the window holds","type":"integer"},"models":{"description":"how many distinct models were called","type":"integer"},"promptTokens":{"description":"tokens sent to the models","type":"integer"},"successRate":{"description":"share of calls that succeeded, 0..1","type":"number"},"totalTokens":{"description":"prompt plus completion","type":"integer"},"users":{"description":"how many distinct users called them","type":"integer"}},"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"},"CDApp":{"properties":{"automated":{"description":"Automated is whether CD applies git without being asked. It is\ncd.automated in the values file, rendered by the ApplicationSet's\ntemplatePatch — false means the Application reports drift and nothing moves.","type":"boolean"},"health":{"description":"Health is the workload's verdict: Healthy, Progressing, Degraded, Missing.","type":"string"},"message":{"description":"Message is why, when Health is not Healthy.","type":"string"},"name":{"description":"Name is the Application name the generator mints: <namespace>-<app>. It is\nthe join key against a Declaration.","type":"string"},"namespace":{"description":"Namespace is the DESTINATION namespace as the CR declares it — where the\nworkload lands. For a fleet Application that is the org, but this is the\nOBSERVED field and not our model of it: the two can disagree, and a board\nwhose whole job is drift must be able to show that they do.","type":"string"},"operationMessage":{"type":"string"},"path":{"description":"Path is the values file CD renders against, relative to the chart source.","type":"string"},"phase":{"description":"Phase is the last sync operation's phase (Running, Succeeded, Failed) and\nOperationMessage is its message. A Failed phase with a Synced verdict is\nthe shape a stuck Application takes.","type":"string"},"project":{"description":"Project is the AppProject fence the sync is admitted under.","type":"string"},"reconciledAt":{"description":"ReconciledAt is when CD last compared this Application.","type":"string"},"revision":{"description":"Revision is the universe commit CD last applied. Empty means it has not\napplied one — never assume it means main.","type":"string"},"selfHeal":{"description":"SelfHeal is whether CD also corrects drift the cluster introduced.","type":"boolean"},"sync":{"description":"Sync is CD's verdict on git-versus-cluster: Synced, OutOfSync, or Unknown.","type":"string"}},"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 is unix seconds when the post was added, server-assigned and\nnever rewritten.","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":{"description":"UpdatedAt is unix seconds of the last write, server-assigned. The durable\nsweep writes too — claiming a due post, publishing it and recording a\nfailure each bump it — so this moves without anyone editing the post.","type":"integer"}},"type":"object"},"Call":{"properties":{"agent":{"description":"Agent names the Hanzo assistant handling the call. Set means the call was\nanswered by that assistant rather than connected to a person.","type":"string"},"from":{"description":"From is the calling number in E.164. It must be one this org holds: a carrier\nrefuses an origination from a number nobody proved they own.","type":"string"},"id":{"description":"ID is the carrier's handle for the call — what a hangup or a lookup names.","type":"string"},"org":{"description":"Org is the tenant the call was placed for or received by.","type":"string"},"status":{"description":"Status is where the call is: \"queued\", \"ringing\", \"answered\", \"completed\" or\n\"failed\". Only the last two are terminal.","type":"string"},"to":{"description":"To is the called number in E.164.","type":"string"}},"type":"object"},"Campaign":{"properties":{"budget":{"description":"Budget is what the campaign is allowed to cost, in USD cents. A negative\nvalue is clamped to 0; nothing enforces the ceiling here.","type":"integer"},"channel":{"description":"Channel is the delivery surface: email, sms, social, meta, google or\ntiktok. Empty means email.","type":"string"},"createdAt":{"description":"CreatedAt is unix seconds when the campaign was registered. Server-assigned\nand never rewritten — an update leaves it as it was.","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":{"description":"Spend is what the campaign has cost so far, in USD cents, clamped to >= 0.\nThe CALLER owns it: no send, ad buy or invoice moves it, so it changes only\nwhen create or update carries a new value. It is summed across the org's\ncampaigns into GET /v1/marketing/summary.","type":"integer"},"status":{"description":"Status is the lifecycle: draft, scheduled, active, paused or completed.\nEmpty means draft.","type":"string"},"updatedAt":{"description":"UpdatedAt is unix seconds of the last write. Server-assigned on create and\non every update or schedule change, and the campaign list is ordered by it,\nnewest first.","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"},"CapVerdict":{"properties":{"allow":{"type":"boolean"},"capCents":{"type":"integer"},"reason":{"type":"string"},"spentCents":{"type":"integer"},"warnPct":{"type":"integer"}},"type":"object"},"Capability":{"type":"object","properties":{"description":{"type":"string"},"href":{"type":"string"},"name":{"type":"string"},"stage":{"type":"string"}}},"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"},"Category":{"properties":{"brands":{"description":"Brands are the brands whose console shows this category. Absent means every\nbrand.","items":{"type":"string"},"type":"array"},"id":{"description":"ID is the stable slug this category is addressed by, e.g. \"observe\".","type":"string"},"label":{"description":"Label is the display name, e.g. \"Observe\".","type":"string"},"order":{"description":"Order is where the category sits among its siblings, ascending.","type":"integer"},"owner":{"description":"Owner is the org this category belongs to: the platform's own org for a\ncategory every tenant sees, or your org for one you added. It tells a console\nwhich rows it may offer to edit.","type":"string"},"summary":{"description":"Summary is the one line describing what the category groups, shown as the\nheader copy on its landing page.","type":"string"},"taxa":{"description":"Taxa are the products filed under this category, in display order.","items":{"$ref":"#/components/schemas/Taxon"},"type":"array"}},"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"},"Chain":{"properties":{"chainId":{"description":"ChainID is the EIP-155 id, so a caller can check it matches the wallet\nthey are about to sign with.","type":"integer"},"id":{"description":"ID is the URL name: the value of :chain.","type":"string"},"name":{"description":"Name is for humans.","type":"string"}},"type":"object"},"Channel":{"properties":{"disabled":{"description":"Disabled is true for a channel the org switched off at the social edge. It\nis still listed — this is what the org has CONNECTED, not what it can post\nto — but a publish never targets it, neither by name nor as part of the\n\"every channel\" default.","type":"boolean"},"id":{"description":"ID is the social integration id a post targets. It is the exact value to put\nin a content item's `channels` list to reach this one connected account.","type":"string"},"name":{"description":"Name is the account label as the org connected it — the handle a human\nrecognises. It is never an address: a publish resolves channels by ID or by\nProvider and never by this.","type":"string"},"provider":{"description":"Provider is the network behind the integration: \"x\", \"instagram\", \"tiktok\"\nand the rest of what the org connected. Naming a provider in a publish\ntargets EVERY connected account of it, so it is the coarse handle where ID\nis the precise one.","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"},"Charge":{"properties":{"amountCents":{"description":"AmountCents is what this line costs.","type":"integer"},"asOf":{"description":"AsOf is when a pass-through amount was last checked against its source.","type":"string"},"code":{"description":"Code names the line so a caller can branch on it without reading prose.","type":"string"},"label":{"description":"Label is what the payer sees on the invoice.","type":"string"},"passThrough":{"description":"PassThrough marks money we collect and remit rather than keep — the state's\nfee is not our revenue, and a quote that hides that is a quote that reads\nas a bigger margin than it is.","type":"boolean"},"recurring":{"description":"Recurring marks a line that repeats. An agent of record is billed every\nyear for as long as the entity stands, and a payer agreeing to a one-time\ntotal is not agreeing to that.","type":"string"},"source":{"description":"Source names who publishes this amount, for a line we merely pass through.\nEmpty for a price of ours, which needs no external authority.","type":"string"},"stale":{"description":"Stale reports that AsOf is older than the review window — the figure may\nhave moved and nobody has looked. It does not block; it tells.","type":"boolean"}},"type":"object"},"Charged":{"properties":{"balanceCents":{"description":"BalanceCents is the subject's balance AFTER the charge settled, in cents, so\na caller does not have to re-read to show the new number.","type":"integer"},"processorRef":{"description":"ProcessorRef is the payment processor's own reference. It is the only field\nthat proves money moved at the GATEWAY rather than merely in our ledger,\nwhich is why it is answered and not only logged. Absent where the processor\nreturned none.","type":"string"},"status":{"description":"Status is how the charge ended. Read it rather than inferring success from\nthe HTTP status: the call succeeded whenever this field is present, and what\nthe PROCESSOR did is what this says.","type":"string"},"test":{"description":"Test states which bucket was credited — sandbox money or real money — so no\nreader has to guess whether a receipt is real. Sandbox and live funds are\nphysically separate ledgers, and a reader that conflates them restates the\ncompany's revenue.","type":"boolean"},"transactionId":{"description":"TransactionID is the ledger entry this charge created. It is the handle a\nlater read or a refund names, and it is minted by the ledger rather than by\nthe caller.","type":"string"}},"type":"object"},"Citation":{"properties":{"endLine":{"description":"EndLine is its last line, inclusive.","type":"integer"},"file":{"description":"File is the path inside the repo, relative to its root.","type":"string"},"line":{"description":"Line is the first line of the cited region, 1-based.","type":"integer"},"repo":{"description":"Repo is the repository the cited code lives in (\"owner/name\"), absent when the\nask was already scoped to one.","type":"string"},"symbol":{"description":"Symbol is the declaration the region belongs to, when it belongs to one.","type":"string"}},"type":"object"},"ClaimRow":{"properties":{"at":{"description":"At is when a stored row was recorded. Zero for a seed row.","format":"date-time","type":"string"},"benchmark":{"description":"Benchmark is the canonical test id the claim is about, from /catalog.","type":"string"},"by":{"description":"By is who recorded it, when the caller said.","type":"string"},"model":{"description":"Model is the system the score is claimed for.","type":"string"},"origin":{"description":"Origin is \"seed\" for a compiled row and \"stored\" for one written through\nthis surface. It is the difference between what we shipped and what an\noperator has since corrected.","type":"string"},"protocol":{"description":"Protocol records HOW it was scored — provider-reported, agentic,\nthird-party-leaderboard — so a provider card is never read as a measurement.","type":"string"},"provider":{"description":"Provider is who the claim belongs to — the lab or leaderboard whose number\nthis is.","type":"string"},"score":{"description":"Score is the reported aggregate, as a percentage.","type":"number"},"source":{"description":"Source is the citation the row was read from.","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"},"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/exec/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/exec/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":{"after":{"description":"After names a previous run's session, and starts this one from where that\none stopped instead of from the repository's default. It is how a follow-up\ninstruction — \"now add tests for it\" — builds on work already done rather\nthan beginning again on a fresh clone.\n\nIt sets the base and nothing else, so this run still writes its OWN branch.\nOne run, one branch: a run that wrote back onto an earlier run's branch\nwould break the rule the forge's ref policy is built on, and would leave\ntwo turns of work with one name to review.\n\nA caller who already knows the branch may pass Base directly; this exists\nbecause the branch is derived from a session id and nobody should have to\nknow how. Base wins if both are given.","type":"string"},"agentRef":{"description":"AgentRef names a configured agent to run as, which is how an org pins a\nharness, a model and a prompt to a name. Empty runs the default agent.","type":"string"},"base":{"description":"Base is the branch to start from. Empty takes the repository's default. The\nrun never writes here — it writes the agent branch it answers with.","type":"string"},"desktop":{"description":"Desktop asks for a run with a SCREEN — an image carrying an X server — for a\ntask that has to drive a browser or another windowed program. False, the\ndefault, is a headless checkout, which is what writing code needs.","type":"boolean"},"project":{"description":"Project scopes the run to one board's work when the org keeps more than one.\nEmpty is the org's default.","type":"string"},"prompt":{"description":"Prompt is the task, in the words you would use with a colleague who has the\ncheckout open. It is the whole instruction: there is no second field for\ncontext, and a prompt that names files and the outcome it wants gets a run\nthat does not have to guess either.","type":"string"},"replyChannel":{"description":"ReplyChannel / ReplyThread are WHERE THE RUN NARRATES ITSELF, when the\nsurface that started it has somewhere for it to talk. Empty means nobody is\nlistening and the run simply does not narrate — which is the app surface's\ncase, because /v1/agents/coding hands back a session id and the session stream is\na better progress feed than any message could be.\n\nIt is an ADDRESS and not a token: the engine says \"put this text there\",\nand the process that owns the workspace's bot credential is the one that\nactually posts. So a run reports into a Slack thread without the engine\never holding the token that could post anywhere else in that workspace.","type":"string"},"replyThread":{"description":"ReplyThread narrows that address to one THREAD inside the channel: on Slack\nit is the parent message's ts, the same value a reply carries as thread_ts.\nEmpty puts the run's status line at the top level of the channel instead.\n\nThe channel is what decides whether a run narrates at all, so this on its own\naddresses nothing — a thread with no ReplyChannel is a run nobody hears.","type":"string"},"repo":{"description":"Repo is what to work on, as `owner/name` in the caller's own org. The engine\nresolves the clone URL and the push credential from the org itself, so this\nsays WHICH repository and never how to reach it.","type":"string"},"targetId":{"description":"TargetID routes the run to a registered machine the org has claimed instead\nof to a sandbox in our cluster. Empty runs it here, which is the usual case.","type":"string"},"timeoutSeconds":{"description":"TimeoutSeconds bounds the whole run. Unset takes the default budget; a run\nthat hits the bound is stopped and reports what it had done by then.","type":"integer"},"tool":{"description":"Tool is which harness runs the prompt — dev | claude | codex | python |\nnode — and Desktop is whether the run needs a screen. Both are empty by\ndefault, which is `dev` with no screen, and that default is what every\ncaller gets until it says otherwise.\n\nThey are two fields because they are two questions. The harness decides\nwhat argv starts; the screen decides which image carries an X server. A\ncaller may want claude WITH a browser it can see, and a single enum would\nhave made that combination unsayable.","type":"string"}},"type":"object"},"CodingStarted":{"properties":{"branch":{"description":"Branch is the ref the run will push its work to, and the ONLY ref it is\npermitted to write. It exists before the work does, so it is safe to tell\nsomebody where to look while the run is still going.","type":"string"},"repo":{"description":"Repo is the repository the run was admitted against, echoed back as the\nengine resolved it.","type":"string"},"routed":{"description":"Routed says the run went to one of the org's own registered machines rather\nthan to a sandbox in our cluster. False is the ordinary case.","type":"boolean"},"sessionId":{"description":"SessionID is the run's handle: its durable record, and the id its live\nprogress streams under at /v1/agents/sessions/{sessionId}/stream. Every\nlater question about this run is asked with it.","type":"string"},"targetId":{"description":"TargetID names that machine when Routed is true, and is empty otherwise.","type":"string"}},"type":"object"},"Collected":{"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/Invoice","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":{"description":"Detail is the server's short elaboration, typically the type or signature.","type":"string"},"kind":{"description":"Kind is the LSP CompletionItemKind number (2 method, 3 function, 5 field, 6\nvariable, …), passed through as the protocol spells it.","type":"integer"},"label":{"description":"Label is the text a client would insert, and what an editor lists.","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"},"Contacts":{"properties":{"admin":{"$ref":"#/components/schemas/Registrant","description":"who administers it"},"billing":{"$ref":"#/components/schemas/Registrant","description":"who is reached about payment"},"registrant":{"$ref":"#/components/schemas/Registrant","description":"who owns the domain"},"tech":{"$ref":"#/components/schemas/Registrant","description":"who is reached about technical matters"}},"type":"object"},"ContextBundle":{"properties":{"budgetTokens":{"description":"BudgetTokens is the ceiling the caller asked for. Packing stops under it, so\nthis is a bound and not a target.","type":"integer"},"query":{"description":"Query is the ask this bundle was packed for, echoed back so a cached or\nforwarded bundle still says what it answers.","type":"string"},"repo":{"description":"Repo narrows the retrieval to one repository. Absent means every indexed repo\nwas searched.","type":"string"},"spans":{"description":"Spans are the packed chunks, most relevant first, each expanded with the\ndefinitions it calls and its notable callers. The top match is always present\neven if it had to be truncated to fit, so a matched query never comes back\nwith nothing.","items":{"$ref":"#/components/schemas/Span"},"type":"array"},"usedTokens":{"description":"UsedTokens is what the returned spans actually cost, by the same estimate the\npacker used (roughly one token per four characters — an estimate, not a\ntokenizer's count, so size a real window with headroom).","type":"integer"}},"type":"object"},"CreditBalance":{"properties":{"balances":{"items":{"$ref":"#/components/schemas/CreditEntry"},"type":"array"},"userId":{"type":"string"}},"type":"object"},"CreditEntry":{"properties":{"available":{"type":"integer"},"currency":{"type":"string"}},"type":"object"},"CreditGrant":{"properties":{"active":{"type":"boolean"},"amountCents":{"type":"integer"},"createdAt":{"type":"string"},"currency":{"type":"string"},"effectiveAt":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"priority":{"type":"integer"},"remainingCents":{"type":"integer"},"tags":{"type":"string"},"userId":{"type":"string"},"voided":{"type":"boolean"}},"type":"object"},"CreditGrants":{"properties":{"count":{"type":"integer"},"grants":{"items":{"$ref":"#/components/schemas/CreditGrant"},"type":"array"}},"type":"object"},"CryptoDeposit":{"properties":{"addressTag":{"type":"string"},"chain":{"type":"string"},"depositAddress":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"status":{"type":"string"},"token":{"type":"string"}},"type":"object"},"CryptoOptions":{"properties":{"chains":{"items":{"type":"string"},"type":"array"},"tokens":{"items":{"type":"string"},"type":"array"}},"type":"object"},"Curriculum":{"properties":{"steps":{"description":"Steps are the enabled steps in authoring order. Order is the tiebreak the\nnext-step logic walks, so it is part of the contract rather than cosmetic.","items":{"$ref":"#/components/schemas/JourneyStep"},"type":"array"},"title":{"description":"Title is the playbook's name as it heads the checklist.","type":"string"},"version":{"description":"Version identifies the authored playbook this journey was projected from, so\ntwo orgs on different playbooks can be told apart. It is the blueprint's own\n`version` string, not the store's numeric revision.","type":"string"}},"type":"object"},"D1Query":{"properties":{"params":{"items":{},"type":"array"},"sql":{"type":"string"}},"type":"object"},"Declaration":{"properties":{"application":{"description":"Application is the CD Application name the generator mints: <org>-<name>.\nIt is the join key against /v1/platform/cd.","type":"string"},"automated":{"description":"Automated is cd.automated: false means the Application reports drift and\nNOTHING moves. It is off by default for a new file on purpose.","type":"boolean"},"digest":{"description":"image.digest — wins over tag","type":"string"},"env":{"description":"Env is the declared container environment, as the chart's list of\n{name,value}. It is read back so a re-declare of an identical body is a\nno-op rather than a refusal — idempotency is what makes a retry safe.","items":{"$ref":"#/components/schemas/declareEnv"},"type":"array"},"hosts":{"description":"ingress.hosts, both shapes flattened","items":{"type":"string"},"type":"array"},"name":{"description":"the Helm release name — the file's basename","type":"string"},"org":{"description":"Org is the owner. It is ALSO the values directory and the destination\nnamespace, because those are one value under one name — see the header.","type":"string"},"path":{"description":"Path is the file, relative to the repository root.","type":"string"},"project":{"description":"Project is the AppProject the sync is admitted under, derived from the\ndirectory exactly as the ApplicationSet derives it. It differs from Org\nfor a reserved directory, which syncs under the platform fence.","type":"string"},"replicas":{"type":"integer"},"repository":{"description":"image.repository","type":"string"},"tag":{"description":"image.tag","type":"string"}},"type":"object"},"DefRow":{"properties":{"definition":{"description":"Definition is the flag-definition document the evaluator consumes, kept\nBYTE-FOR-BYTE as it was written. It is the engine's format rather than this\npackage's, so it carries fields no Go type here names — targeting groups,\nrollout percentages, variants, payloads — and a caller must round-trip it\nwhole rather than rebuilding it from the parts it recognizes."},"key":{"description":"Key is the flag's primary key in the caller's (org, project) store, and the\nname evaluation looks it up by. On a write it is taken from the URL, never\nfrom the body: the stored document's own \"key\" is forced to match.","type":"string"},"updated_at":{"description":"UpdatedAt is when the definition was last written, RFC 3339 UTC.","type":"string"},"updated_by":{"description":"UpdatedBy is the email of the principal who last wrote it. Empty when the\nwrite came from an in-process composer (an experiment registering its own\nassignment flag) rather than from a signed-in person.","type":"string"},"version":{"description":"Version is 1 when the key was created and rises by one on every overwrite.\nIt counts writes, not content changes: re-storing an identical document\nbumps it.","type":"integer"}},"type":"object"},"Delivery":{"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"},"DeliveryRow":{"properties":{"attempt":{"description":"Attempt is which try this row is, starting at 1. The ladder waits 1s, then 5s,\nthen 25s before the next one.","type":"integer"},"created":{"description":"Created is when the attempt was made, RFC3339 in UTC.","type":"string"},"delivery":{"description":"DeliveryID groups the attempts for ONE event to ONE endpoint. Rows sharing it\nare the same delivery being retried, not separate events.","type":"string"},"durationMs":{"description":"DurationMs is how long this attempt took end to end, in MILLISECONDS.","type":"integer"},"endpoint":{"description":"EndpointID is which subscriber this attempt was for.","type":"string"},"error":{"description":"Error says what went wrong on a non-ok attempt. Empty on success.","type":"string"},"httpStatus":{"description":"HTTPStatus is what the subscriber answered. ZERO means it never answered — a\nrefused connection, a DNS failure or a timeout — which is why a zero here is\nnot a 200.","type":"integer"},"status":{"description":"Status is \"ok\" when the subscriber accepted it, \"retrying\" while a further\nattempt will follow, and \"failed\" when none will. Exactly one row of a delivery\nis terminal.","type":"string"},"subject":{"description":"Subject is the event that was delivered (\"commerce.order.created\"). A manual\ntest send carries \"webhook.test\".","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 Analytics). False on a\nconnected destination whose secret has gone missing — Connected && !Live is\nexactly the \"reconnect me\" state.","type":"boolean"},"name":{"description":"the platform's display name (\"Google Analytics 4\")","type":"string"},"pixel":{"description":"Pixel is whether the hosted tag can inject a browser pixel for this platform,\nso a console offers a per-SITE pixel input for exactly these. False means the\nplatform receives conversions server-side only, and an input would promise an\ninjection that never happens. Derived from the tag's own map (event.BrowserTags),\nnever restated — a second list is how a console offers a pixel nothing fires.","type":"boolean"},"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"},"Detachment":{"properties":{"deleted":{"description":"Deleted is whether the method was actually removed. False with no error\nmeans it was already gone, which is a successful detach rather than a\nfailure — a retry must not be an error.","type":"boolean"},"id":{"description":"ID is the method that was detached, echoed so a caller batching several can\ntell the answers apart.","type":"string"}},"type":"object"},"Diagnostic":{"properties":{"code":{"description":"Code is the checker's own identifier for the rule, a string or a number\ndepending on the server. Absent when it published none.","type":"object"},"message":{"description":"Message is the problem in the server's own words, meant to be shown.","type":"string"},"range":{"$ref":"#/components/schemas/Range","description":"Range is the span the problem is about."},"severity":{"description":"Severity is the LSP's: 1 error, 2 warning, 3 information, 4 hint. A file with\nonly 3s and 4s still compiles.","type":"integer"},"source":{"description":"Source is which checker reported it (\"compiler\", \"go vet\", a linter's name),\nwhich is what separates a build error from a style opinion.","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"},"DriftFlag":{"properties":{"kind":{"description":"Kind is which finding this is, one of stale, un-rolled, floating-declared,\nfloating-running, no-release or zero-assets. It is what code matches on, and\nthe kinds are independent — one row can carry several at once.","type":"string"},"message":{"description":"Message is the finding in words, naming the tags that produced it (\"running\nv1.2.3 has not rolled to declared v1.2.4\"). For display: match on Kind.","type":"string"},"severity":{"description":"Severity is this ONE finding's weight — yellow for stale and un-rolled, red\nfor the other four. It is a constant of the kind (severityOf), never a\njudgement about the row, so the same kind always weighs the same.","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"},"EIN":{"properties":{"expedited":{"description":"Expedited reports that prioritised handling was asked for.","type":"boolean"},"forms":{"description":"Forms are the forms this application owes, with what each is for.","items":{"$ref":"#/components/schemas/Form"},"type":"array"},"naics":{"description":"NAICS is the six-digit code for what the business does. The SS-4 asks it\nand the IRS will not process an application without one.","type":"string"},"number":{"description":"Number is the issued EIN, absent until the IRS issues it.","type":"string"},"online":{"description":"Online reports that this application can be filed with the IRS online and\nissued in a sitting, rather than signed and posted. It is the single fact\nthat decides how long a customer waits, so it is answered rather than\nimplied by the absence of forms.","type":"boolean"},"responsible":{"$ref":"#/components/schemas/Responsible","description":"Responsible is the person the IRS holds answerable."},"status":{"description":"Status is how far it has got.","type":"string"}},"type":"object"},"EndIn":{"properties":{"id":{"description":"ID is the sandbox whose lease ends, from an earlier lease.","type":"string"},"purge":{"description":"Purge deletes the project's DISK as well. It is opt-in because the disk holds\nthe only copy of the checkout: ending a lease is cheap and reversible,\ndeleting someone's uncommitted work is neither.","type":"boolean"}},"type":"object"},"Endpoint":{"properties":{"created":{"description":"CreatedAt is when the endpoint was registered, RFC3339 in UTC — stored in that\nspelling because it sorts as a string.","type":"string"},"deliveries7d":{"description":"Deliveries7d is how many deliveries SETTLED in the trailing 7 days — the\nattempts that ended ok or failed, so a delivery still retrying is in neither\ncounter yet. It is counted from the log at read time rather than stored, and\nit is filled only on a list or a get; a create answers 0 because there is no\nhistory, which is why it is never omitted.","type":"integer"},"description":{"description":"Description is the operator's own label for the endpoint. Never sent anywhere.","type":"string"},"events":{"description":"Events are the subject patterns this endpoint subscribes to\n(\"commerce.order.>\"). An EMPTY list means every event, not none.","items":{"type":"string"},"type":"array"},"failures7d":{"description":"Failures7d is how many of those settled as failed — the subscriber never\naccepted it and no further attempt will be made. It is the numerator to\nDeliveries7d, over the same window.","type":"integer"},"id":{"description":"ID is the endpoint's handle, server-minted and stable for its life. It is what\nevery other route here addresses.","type":"string"},"org":{"description":"Org is the tenant that owns the endpoint, taken from the validated principal\nrather than from any request field.","type":"string"},"secret":{"description":"Secret is the HMAC-SHA256 signing key a subscriber recomputes the signature\nwith. It is returned exactly ONCE, on create: a later read of the endpoint\nomits it, so a lost secret is replaced rather than recovered.","type":"string"},"status":{"description":"Status is \"active\" or \"disabled\" — nothing else is accepted. A disabled\nendpoint keeps its subscription and receives no deliveries, except a manual\ntest send, which goes out anyway.","type":"string"},"updated":{"description":"UpdatedAt is when its url, events, status or description last changed.","type":"string"},"url":{"description":"URL is where the POST goes. Changing it is the one edit that redirects an\norg's events, which is why it is never bindable from a query string.","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 is unix seconds when the contact joined the walk, and orders the\nenrollment list (newest first).","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":{"description":"UpdatedAt is unix seconds of the last move: the drip engine writes it each\ntime it advances the walk a step, completes it or cancels it. Together with\nStatus it says when the walk last did anything, which is how a stalled\nenrollment is told from a finished one.","type":"integer"}},"type":"object"},"EnrollmentList":{"properties":{"data":{"description":"Data is the page: every contact walking this ONE sequence, in any state —\nactive, completed and canceled walks all appear, since the history of who\nwas reached is the point. An empty array when nobody has been enrolled.","items":{"$ref":"#/components/schemas/Enrollment"},"type":"array"}},"type":"object"},"Entry":{"properties":{"archetype":{"description":"Archetype is WHAT KIND OF THING this is, from a closed and ordered list —\nmodel | contract | chain | sdk | template | infra | site | app — derived from\nthe repository's own topics, name and description, first match winning, and\nalways `site` for a deployed site. It is DERIVED, never guessed by a model,\nbecause a wrong archetype hides a row from the browse rail more thoroughly\nthan a missing one does. Empty when no topic matched: unclassified, not\nuncategorisable.","type":"string"},"description":{"description":"Description is the repository's own one-line GitHub description, carried\nverbatim. It comes from the SOURCE half of a row, so a site that was never\nmatched to a repository has none, and nothing here is written by us.","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":{"description":"ID is \"<org>/<name>\" and is the corpus's primary key: a re-published entry\nupdates in place under it rather than accumulating duplicates, so it is the\none handle stable enough to link to or to name in a `template` filter. Two\norgs can spell the same id, and `canonical` picks which one keeps it.","type":"string"},"kind":{"description":"repo | site","type":"string"},"language":{"description":"Language is the repository's primary implementation language as GitHub\ncomputes it (\"Go\", \"TypeScript\"), and the case is GitHub's. Empty for a site\nwith no source half and for a repository GitHub could not classify.","type":"string"},"license":{"description":"License is the terms that upstream work carries, in whichever form the half\nthat credited it had: an SPDX id (\"MIT\", \"Apache-2.0\") on a GitHub fork,\nfree text on a site whose publisher declared it. GitHub's NOASSERTION — \"we\ncould not identify it\" — reads as none rather than as a licence by that name.\nSo empty means UNDECLARED and never unencumbered, and Upstream is what says\nwhether the question applies at all.","type":"string"},"name":{"description":"Name is the short identifier inside the org — the repository's name, or the\nsite's slug — and is the half of ID after the slash. Not a display name;\nTitle is.","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":{"description":"Stars is GitHub's stargazer count for the source repository, read at the last\nsync and never accumulated here. It is not a ranking — the page sorts on\nUpdated — but it is the tiebreak when two orgs claim one ID. Absent for a\nsite with no repository behind it, and for a repository nobody has starred.","type":"integer"},"template":{"description":"lineage, if forked from one","type":"string"},"title":{"description":"Title is what to SHOW. A site's human name wins where it has one; a repo row\nfalls back to the repository name, so on a repo this usually just repeats\nName. Absent only for a site whose project was never named — render Name.","type":"string"},"updated":{"description":"Updated is when the thing last MOVED, as RFC 3339 in UTC: a repository's last\npush, or a site's last deploy. The page is ordered on it, most recent first,\nby comparing these strings — so the format is load-bearing and not cosmetic.\nAbsent means the source reported no timestamp, and such a row sorts last.","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":{"description":"Key is the variable's name in the container, which must match\n`^[A-Za-z_][A-Za-z0-9_]*$`. For a sealed value it is also the last segment\nof the KMS ref, so it is what identifies the value across a round trip.","type":"string"},"secret":{"description":"Secret says the value lives in KMS and never in the database. A caller may\nonly ADD secrecy: the server seals a value whose key or shape looks like a\ncredential anyway (secretshape.go), so an entry can come back secret that\nwas not sent that way.","type":"boolean"},"value":{"description":"Value is the plaintext, and it is WRITE-ONLY once the entry is secret: a\nsealed value reads back as \"\", and sending \"\" again KEEPS what is sealed\nrather than wiping it. Only a non-empty value seals a new one.","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"},"ExecResult":{"properties":{"exitCode":{"description":"ExitCode is the command's own exit status. A non-zero one is a SUCCESSFUL\ncall carrying a failed command — the HTTP status stays 200, because \"the\ncommand failed\" and \"the call failed\" are different facts and a caller has\nto be able to tell them apart.","type":"integer"},"stderr":{"description":"Stderr is everything it wrote to standard error. It is populated on a\nsuccessful run too — plenty of tools report progress there — so it is not a\nsignal that anything went wrong; ExitCode is.","type":"string"},"stdout":{"description":"Stdout is everything the command wrote to standard output, as text.","type":"string"}},"type":"object"},"Extracted":{"properties":{"category":{"description":"Category is the expense bucket the SCANNER guessed, as a slug — a hint only.\nVendor rules override it whenever they know better, so this is the model's\nreading and not the account the entry will land on.","type":"string"},"currency":{"description":"Currency is the ISO code the document is denominated in.","type":"string"},"issuedAt":{"description":"IssuedAt is the document's OWN date as YYYY-MM-DD — when the bill was issued,\nwhich is not when it was uploaded or when it will post.","type":"string"},"lineItems":{"description":"LineItems are the individual lines read off the document, where it had any.\nThey need not sum to totalCents: a document may carry lines the scanner could\nnot read, and the total is taken from the total.","items":{"$ref":"#/components/schemas/LineItem"},"type":"array"},"merchant":{"description":"Merchant is the supplier as printed on the document.","type":"string"},"note":{"description":"Note is anything else worth carrying from the document that has no field of\nits own.","type":"string"},"taxCents":{"description":"TaxCents is how much of that total is tax, in cents. It is part of totalCents,\nnot additional to it.","type":"integer"},"totalCents":{"description":"TotalCents is the document total in whole cents, tax INCLUDED.","type":"integer"}},"type":"object"},"Field":{"properties":{"key":{"description":"Key is the identifier the body substitutes ({{.key}}) and the key a\ngeneration's data map must carry. snake_case by convention across the\nbuilt-ins — effective_date, company_name, governing_law. An override whose\nbody references a key no Field declares is refused on save.","type":"string"},"label":{"description":"Label is the human prompt for whoever fills the value in — \"Governing law\n(state)\". It never reaches the rendered document; only Key does.","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"},"FinancialPackage":{"properties":{"balanceSheet":{"$ref":"#/components/schemas/BalanceSheet","description":"BalanceSheet is struck as of the period END, not the start."},"from":{"description":"From opens the reporting period. Absent means from the beginning of the ledger.","type":"string"},"generatedAt":{"description":"GeneratedAt is when the bundle was assembled — the moment the statements were\nstruck, which is what makes two exports of the same period comparable.","type":"string"},"gl":{"description":"GL is the newest slice of ledger detail, as the audit trail behind the\nstatements. It is CAPPED, so on a busy ledger it is a sample rather than the\nfull support for the figures above.","items":{"$ref":"#/components/schemas/GLRow"},"type":"array"},"org":{"description":"Org is the organisation whose books these are — the validated caller's own,\nstamped so a downloaded bundle still says whose it is.","type":"string"},"pnl":{"$ref":"#/components/schemas/PnL","description":"PnL is the income statement for the period, on an accrual basis."},"to":{"description":"To closes it. Absent means up to now.","type":"string"},"trialBalance":{"$ref":"#/components/schemas/TrialBalance","description":"TrialBalance is the proof the ledger balances over the period."}},"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"},"Form":{"properties":{"code":{"description":"Code is the IRS designation, e.g. \"SS-4\".","type":"string"},"name":{"description":"Name is the form's own title, so a reader need not already know the code.","type":"string"},"signed":{"description":"Signed reports whether we hold the signature.","type":"boolean"},"why":{"description":"Why states what this form is for in this application — the same form is\nowed for different reasons on different paths.","type":"string"}},"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":{"description":"Available separates \"this org has no traffic\" from \"we could not ask\". False\nmeans the warehouse was unreachable or the org has emitted nothing at all, and\nevery count below is then a placeholder zero rather than a measurement — a\ncaller must read this before reading any of them.","type":"boolean"},"orders":{"description":"Orders counts completed orders in the window — purchases, not carts started.","type":"integer"},"pageviews":{"description":"Pageviews counts page events in the window, one per view rather than per\nperson, so a single visitor reading ten pages counts ten.","type":"integer"},"revenue":{"description":"Revenue is the sum of the amounts those orders reported, in whatever currency\nthe beacon stamped on them (major units, e.g. 49.5 for $49.50) — NOT cents,\nand not converted to a single currency. Contrast revenueCents on the profile,\nwhich is the money of record.","type":"number"},"signups":{"description":"Signups counts completed signups in the window, the step where an anonymous\nvisitor becomes somebody with an account.","type":"integer"},"visitors":{"description":"Visitors is the number of DISTINCT people seen in the window, counted by the\nbeacon's distinct id — so it is unique visitors, not sessions and not views.","type":"integer"},"windowDays":{"description":"WindowDays is the length of the trailing window every count covers, so a\nreader knows whether 40 signups is a month or a day.","type":"integer"}},"type":"object"},"GLRow":{"properties":{"account":{"description":"Account is the chart-of-accounts number this leg posts to.","type":"string"},"against":{"description":"Against names the OTHER accounts in the same voucher — the contra side of this\nleg — so a single row reads as an entry rather than as half of one.","type":"string"},"credit":{"description":"Credit is the amount credited to that account, in whole cents.","type":"integer"},"debit":{"description":"Debit is the amount debited to that account, in whole cents. Exactly one of\ndebit and credit is non-zero on a leg; a negative amount is never used to mean\nthe other side.","type":"integer"},"id":{"description":"ID is the entry's position in the ledger. The ledger is append-only, so ids\nascend with posting order and a higher id is a later entry.","type":"integer"},"postingAt":{"description":"PostingAt is the accounting date this entry belongs to — what the reports\nwindow on, which need not be when the row was written.","type":"string"},"remarks":{"description":"Remarks is the memo carried onto the entry, for a human reading the ledger.","type":"string"},"sourceId":{"description":"SourceID identifies that originating record within its kind.","type":"string"},"sourceKind":{"description":"SourceKind is what caused the posting: a bank line, a scanned document, a\ncommerce sale. With sourceId it traces the entry back to the thing that\nproduced it.","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":{"description":"Automated is whether CD applies new commits without being asked. It reads the\nPRESENCE of spec.syncPolicy.automated, which is a block rather than a\nboolean; false means drift is reported and nothing moves.","type":"boolean"},"health":{"description":"Health is CD's verdict on the objects it manages, verbatim: Healthy,\nProgressing, Degraded, Suspended, Missing or Unknown.","type":"string"},"history":{"description":"History is the recent deploy log, NEWEST FIRST and capped at ten. CD appends\noldest-first and bounds the list itself; the reversal happens here so a\ncaller never has to know the storage order to show what shipped last. Empty\n(never null) for an Application that has deployed nothing.","items":{"$ref":"#/components/schemas/GitOpsDeploy"},"type":"array"},"name":{"description":"Name is what CD calls this tracked source, not the workload it deploys —\nthe Application CR's own metadata.name. The fleet ApplicationSet mints these\nas <namespace>-<app>.","type":"string"},"namespace":{"description":"Namespace is where the Application OBJECT lives: CD's own controller\nnamespace, which is the same one for every row here. It is NOT the\ndestination the workloads land in — this endpoint lists cluster-wide and\nnever reads spec.destination.","type":"string"},"operation":{"$ref":"#/components/schemas/GitOpsOperation","description":"Operation is the last sync attempt and how it ended. Absent when CD has run\nnone, which is the honest gap between \"never tried\" and \"tried and failed\"."},"path":{"description":"Path is the directory inside that repository CD renders, relative to its root.","type":"string"},"project":{"description":"Project is the AppProject fence the sync is admitted under: which repos this\nApplication may pull from and which destinations it may write to. Empty when\nthe CR declares none.","type":"string"},"reconciledAt":{"description":"ReconciledAt is when CD last COMPARED this Application against git, RFC 3339.\nIt moves on every comparison, including ones that applied nothing.","type":"string"},"repoURL":{"description":"RepoURL is the git repository CD polls for this Application's desired state.","type":"string"},"resources":{"description":"Resources is how MANY objects CD manages for this Application\n(len(status.resources)) — a count, not the objects. Zero for an Application\nCD has not reconciled.","type":"integer"},"revision":{"description":"Revision is the commit CD last APPLIED (status.sync.revision). Empty means it\nhas applied none — never read that as the head of TargetRevision.","type":"string"},"selfHeal":{"description":"SelfHeal is whether CD also reverts changes made directly in the cluster\n(syncPolicy.automated.selfHeal). Meaningless unless Automated.","type":"boolean"},"sync":{"description":"Sync is CD's verdict on git versus cluster, verbatim: Synced, OutOfSync or\nUnknown. It is about the applied REVISION, so an Application can be Synced to\na commit that is several behind the branch it tracks.","type":"string"},"targetRevision":{"description":"TargetRevision is the git ref CD TRACKS — usually a branch such as \"main\".\nIt is what CD aims at; Revision is what it has reached.","type":"string"}},"type":"object"},"GitOpsDeploy":{"properties":{"automated":{"description":"Automated is whether CD started this deploy itself, from its own polling of\nthe tracked git ref (initiatedBy.automated), rather than someone asking for it.","type":"boolean"},"deployedAt":{"description":"DeployedAt is when the apply finished, RFC 3339. Absent when CD recorded none.","type":"string"},"id":{"description":"ID is CD's own sequence number for this deploy (status.history[].id). It\nincreases with every applied revision, so the largest id in `history` is the\nmost recent deploy — which is the first entry, since the list is reversed.","type":"integer"},"revision":{"description":"Revision is the git commit this deploy applied, as CD recorded it.","type":"string"},"startedAt":{"description":"StartedAt is when CD began applying the revision (deployStartedAt), RFC 3339.\nAbsent when CD recorded none.","type":"string"}},"type":"object"},"GitOpsOperation":{"properties":{"finishedAt":{"description":"FinishedAt is when it ended, RFC 3339. Absent while the phase is Running.","type":"string"},"message":{"description":"Message is CD's account of the phase — \"successfully synced (all tasks run)\"\nfor a Succeeded operation, the reason it stopped for a Failed one.","type":"string"},"phase":{"description":"Phase is how the last sync operation ended, in CD's own vocabulary: Running,\nSucceeded or Failed. It is never empty — an Application whose phase is empty\nhas no operation at all and omits this whole object.","type":"string"},"revision":{"description":"Revision is the commit this operation ATTEMPTED (operationState.syncResult).\nIt differs from the Application's own revision exactly when the attempt did\nnot land: revision is the last commit CD got applied, this is the last one it\ntried.","type":"string"},"startedAt":{"description":"StartedAt is when the operation began, RFC 3339.","type":"string"}},"type":"object"},"GitOpsPlane":{"properties":{"applications":{"description":"Applications is every CD Application in the cluster, ordered by namespace\nthen name. Empty (never null) when the plane is not installed, and equally\nempty when it is installed and tracks nothing — Installed is what separates\nthose two.","items":{"$ref":"#/components/schemas/GitOpsApp"},"type":"array"},"installed":{"description":"Installed is whether this cluster serves the CD Application CRD at all. False\nis a fact about the cluster, not a failure of the request: the caller says\n\"no CD plane here\" rather than rendering an error it cannot act on.","type":"boolean"},"reason":{"description":"Reason says why the plane is absent, in words a caller can show. Empty when\nInstalled.","type":"string"}},"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"},"Hit":{"properties":{"corpus":{"description":"Corpus is which store the document lives in: \"kb\" for a document either\nknowledge leg returned, \"code\" for a span out of one of the org's own\nrepositories. It is PROVENANCE — read it to say where a hit came from, not\nto branch on: the fused ranking is what decides order, and a caller that\nfilters by corpus wants the backend's own endpoint instead.","type":"string"},"doctype":{"description":"DocType is the knowledge doctype: kb-page, kb-memory or kb-source from the\nsemantic leg, and a lexical row's own doctype/type field otherwise. Absent\nwhen the row carried neither.","type":"string"},"id":{"description":"ID is the document's identity inside its corpus — the KB document name from\nthe semantic leg, or a lexical row's own name/id/_id (falling back to\n\"row-<n>\" when the row carries none). It is unique with DocType, not alone:\nthe pair is the key the two legs are fused on.","type":"string"},"matched":{"description":"Matched is one entry per leg that returned this document, with that leg's\nrank and native score. More than one entry means the legs AGREED, which is\nexactly why the hit outranks one a single leg found. Never empty on a\nreturned hit.","items":{"$ref":"#/components/schemas/Match"},"type":"array"},"project":{"description":"Project is the project scope the document was indexed under. Absent for a\ndocument saved with none; Request.Project filters the semantic leg on it.","type":"string"},"score":{"description":"Score is the FUSED score, not a relevance or a similarity: Reciprocal Rank\nFusion sums 1/(60+rank) over each leg that returned the document, so it is\nbounded by roughly 1/61 per leg (about 0.033 for a document two legs put\nfirst) and hits are ordered by it, descending. Being built from ranks, it is\ncomparable only WITHIN one response — never across queries, and never against\na backend's own score, which stays in Matched.","type":"number"},"title":{"description":"Title is the document's display title — the indexed title from the semantic\nleg, the row's title (falling back to its name) from the lexical one. Absent\nwhen the document has none.","type":"string"},"url":{"description":"URL is where the document can be opened, carried from the indexed payload —\nthe link back to the app a connector ingested it from. Absent for anything\nwritten in the product, which has no external address.","type":"string"}},"type":"object"},"Holder":{"properties":{"addedAt":{"type":"string"},"email":{"type":"string"},"id":{"type":"string"},"role":{"type":"string"},"userId":{"type":"string"}},"type":"object"},"Holding":{"properties":{"costCents":{"description":"wholesale cost","type":"integer"},"domain":{"description":"the name owned","type":"string"},"expiresAt":{"description":"when the registration lapses, RFC3339","type":"string"},"nameservers":{"description":"the authoritative nameservers the name points at","items":{"type":"string"},"type":"array"},"order":{"description":"registrar order id","type":"integer"},"org":{"description":"the org that owns the domain","type":"string"},"priceCents":{"description":"what the customer paid (sell)","type":"integer"},"registeredAt":{"description":"unix seconds","type":"integer"}},"type":"object"},"InboxItem":{"properties":{"category":{"description":"Category is the expense account the scanner proposed, as a chart number — a\nPROPOSAL, not a posting: nothing is booked until it is accepted.","type":"string"},"confidence":{"description":"Confidence is how sure the scanner is of that reading, and is the signal for\nwhether a person needs to check it before it is booked.","type":"string"},"createdAt":{"description":"CreatedAt is when the document was uploaded.","type":"string"},"extracted":{"$ref":"#/components/schemas/Extracted","description":"Extracted is what the scanner read off the document. Absent until it has been\nscanned, so its absence is \"not read yet\", never \"nothing on it\"."},"filename":{"description":"Filename is the name the document was uploaded under, for a person to\nrecognise it by. It is not part of the item's identity.","type":"string"},"id":{"description":"ID is the CONTENT HASH of the uploaded bytes, which is what makes the queue\nidempotent: re-uploading the same document returns this item rather than\nadding a second one. It is also the id the scan of this document carries.","type":"string"},"status":{"description":"Status is where the document is in the queue — unsorted until the scanner has\nread it, and thereafter whether it is waiting on a person or has been booked.","type":"string"},"vendor":{"description":"Vendor is the supplier the scanner identified, surfaced beside the item so a\nqueue renders without opening each document.","type":"string"}},"type":"object"},"Index":{"type":"object","properties":{"_links":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Link"}},"description":{"type":"string"},"name":{"type":"string"},"operations":{"type":"array","items":{"$ref":"#/components/schemas/Op"}},"stage":{"type":"string"}}},"Install":{"properties":{"created":{"items":{"type":"string"},"type":"array"},"existing":{"items":{"type":"string"},"type":"array"},"module":{"type":"string"}},"type":"object"},"Invoice":{"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/InvoiceLine"},"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"},"InvoiceLine":{"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"},"InvoiceLineItem":{"properties":{"amount":{"type":"integer"},"currency":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"meterId":{"type":"string"},"periodEnd":{"type":"string"},"periodStart":{"description":"The billed period. Both carry omitempty and neither is ever empty, which\nlooks contradictory and is not: the shape they reproduce is a time value,\nand omitempty does nothing to a struct — so those keys render even for the\nzero instant. The adapter formats the zero instant rather than skipping it,\nwhich is what keeps the two wires the same.","type":"string"},"planId":{"type":"string"},"planName":{"type":"string"},"quantity":{"type":"integer"},"type":{"type":"string"},"unitPrice":{"type":"integer"}},"type":"object"},"Invoices":{"properties":{"count":{"type":"integer"},"invoices":{"items":{"$ref":"#/components/schemas/BillingInvoice"},"type":"array"}},"type":"object"},"JourneyStep":{"properties":{"args":{"additionalProperties":{"type":"object"},"description":"Args are the tool's default arguments, merged under whatever the caller\npasses at run time, so a step ships with the arguments that make it work.","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":"Detail is the juncture — what the Guide explains, or asks for, at this step.","type":"string"},"draft":{"description":"Draft, when set, is the prompt the embedded AI answers first; its output is\nfolded into one of Args before the tool runs, so the model writes the\ncontent and the tool only delivers it.","type":"string"},"draftInto":{"description":"DraftInto names the argument the drafted text lands in. Empty means \"brief\".","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":{"description":"ID is the stable slug the whole plane addresses this step by — the value in\n`deps`, in `next`, in the progress rows, and in the URL of every step route.\nRenaming it orphans an org's recorded progress for this step.","type":"string"},"section":{"description":"Section is the id of the phase this step groups under. A disabled section\ntakes its steps out of the journey with it.","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":{"description":"Title is the one-line quest as a person reads it in the checklist.","type":"string"},"tool":{"description":"Tool, when set, names the MCP tool the Business AI runs for \"do it for me\".\nA step with no tool can only be completed by a person; it is the field the\n`automatable` flag on every projection of this step is derived from.","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"},"LatencyStat":{"properties":{"available":{"description":"false when no GenAI spans carry timing; the percentiles are then null","type":"boolean"},"p50Ms":{"description":"median latency over the window","type":"number"},"p95Ms":{"description":"95th-percentile latency","type":"number"},"p99Ms":{"description":"99th-percentile latency","type":"number"}},"type":"object"},"LeaderRow":{"properties":{"ciHigh":{"description":"CIHigh is the upper bound of that interval. Wilson rather than the normal\napproximation because the normal one produces bounds past 100 exactly where\nbenchmark scores live — at 194/198 that is the top of the board, not a\ncorner case.","type":"number"},"ciLow":{"description":"CILow and CIHigh are the 95% Wilson interval on Measured, in percent. They\nare what makes the score comparable: at n=198 a 98% carries roughly ±2\npoints, so most differences at the top of a board are not distinguishable\nand a bare number implies a precision it does not have. Absent when there\nis no measurement.","type":"number"},"claims":{"description":"Claims is how many independent claims exist for this model on this\nbenchmark. More than one means several sources reported it.","type":"integer"},"gap":{"description":"published − measured (the arena signal)","type":"number"},"mean":{"description":"Mean is the unweighted average of every claim, which answers a different\nquestion from Published: what the field says on average, rather than what\nthe vendor says about itself. With one claim the two are equal.","type":"number"},"measured":{"description":"hanzo-measured accuracy % (nil if unrun)","type":"number"},"measuredAt":{"description":"MeasuredAt is when the run behind Measured was recorded.","format":"date-time","type":"string"},"model":{"description":"the model this row scores","type":"string"},"n":{"description":"coverage — NEVER compare across different n","type":"integer"},"protocol":{"description":"how the vendor scored their claim: single-attempt, pass@k or agentic","type":"string"},"published":{"description":"provider-claimed % (nil if none)","type":"number"},"run":{"description":"Run names the measurement Measured came from, and MeasuredAt is when it\nran. A score with no date is not a fact about a model, it is a fact about\na model on a day — and models change, so the date is what makes the number\ncheckable rather than merely quoted.","type":"string"},"spread":{"description":"Spread is the distance between the highest and lowest of them, nil when\nthere is only one. It is the disagreement AMONG sources, which a single\nPublished number cannot show — signal in the same way the\npublished-minus-measured gap is.","type":"number"}},"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"},"LeaseIn":{"properties":{"class":{"description":"Class is what KIND of computer to lease, and the set is closed:\n\n\texec a throwaway one that keeps nothing. Seconds to minutes.\n\tdev a coding one, with the project's own disk attached. Hours.\n\tdesktop a dev one that also has a screen.\n\tandroid a desktop with a phone running on that screen.\n\nEmpty leases an `exec`, which is the right answer for running a program and\nthe wrong one for working on a repository, because it keeps nothing.\n\nAn `android` needs a node that can virtualise a CPU, so it is the one class\na deployment may not be able to place. Where the fleet has none, the lease\nsucceeds and the pod stays Pending naming the device it is waiting for —\nwhich is the honest answer, because the alternative is an emulator running\non an interpreted CPU and never finishing its boot.","type":"string"},"id":{"description":"ID names a sandbox to RESUME, and is the id an earlier lease answered with.\nEmpty asks for a new one. A caller that holds an id and omits it does not get\na second view of the same computer, it gets a second computer.","type":"string"},"project":{"description":"Project names the disk to attach, and is REQUIRED for every class but `exec`.\n\nOne live sandbox per project: the disk attaches to one computer at a time, so\na second lease over a project that already has one is refused by name rather\nthan handed a silently empty disk.","type":"string"},"runtime":{"description":"Runtime is the isolation boundary asked for: `gvisor` shares a filesystem\nand holds a project volume, `kata-fc` is a microVM that boots slower and\nreads files faster but has no shared filesystem at all. Empty asks for the\nfleet's default, which is the right answer unless you are measuring.\n\nIt is a REQUEST. The owner decides, and refuses a combination it cannot\nhonour — a volume under a runtime with no shared filesystem would write\ninto a tmpfs and lose the bytes at exit. Read Leased.Runtime for what the\nsandbox actually got.","type":"string"},"ttlSec":{"description":"TTLSec bounds the lease in seconds. Unset takes the class default. Nothing\nruns forever, because a sandbox is somebody else's code on our nodes.","type":"integer"}},"type":"object"},"Leased":{"properties":{"class":{"description":"Class is what was actually leased, from the closed set LeaseIn.Class names:\nexec | dev | desktop | android. A request that named none leased an `exec`,\nso this is where a caller learns which kind of computer it is holding, and it\nis what Workdir below follows from.","type":"string"},"id":{"description":"ID names this computer for every later call — run, read, write, stop and end\nall take it, and a LeaseIn carrying it resumes THIS sandbox instead of leasing\na second one. Minted here; a caller cannot choose it, and a resumed lease that\nhad expired comes back under a new one.","type":"string"},"runtime":{"description":"Runtime is the boundary this sandbox GOT, which need not be the one asked\nfor — carried for the same reason Workdir is, that it is a fact only the\nowner knows and a caller assuming it would be holding a second copy. Empty\nis the node's default runtime, and a real answer.","type":"string"},"status":{"description":"Status is where the pod stands, from the store's three: pending | running |\nerror. A lease that ANSWERS has already waited for the pod, so this reads\n`running` — a start that failed is a 503 and no sandbox at all. Read it\nanyway: exec refuses a sandbox that is not running, so anything else here is\nthe reason the next call will not work.","type":"string"},"workdir":{"description":"Workdir is the absolute directory this sandbox keeps files in, and what a\nrelative path in a later read, write or run resolves against — /work for dev,\ndesktop and android (the project volume's mount point), /mnt/data for exec\n(the artifact directory the code tool tells the model to write to). A path\nthat climbs above it is refused rather than rewritten.","type":"string"}},"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":{"description":"AmountCents is that line's amount in whole cents. The scanner is instructed to\nreturn integer cents rather than a decimal, so no float rounding can enter the\nledger through here.","type":"integer"},"description":{"description":"Description is the line as it appears on the document.","type":"string"}},"type":"object"},"Link":{"type":"object","properties":{"href":{"type":"string"}}},"Listing":{"properties":{"category":{"description":"Category groups the listing in the shop window. Free text — no vocabulary,\nnothing validates it — and unlike Description it is silently cut to 4096\nbytes rather than refused. Empty means ungrouped.","type":"string"},"createdAt":{"description":"CreatedAt is when the listing was published, in Unix SECONDS, minted at\ninsert. Every listing read orders by it descending, so it is the shop's\nordering key as well as its age.","type":"integer"},"currency":{"description":"Currency is the ISO 4217 code Price is quoted in; Create defaults it to \"USD\"\nwhen the publisher names none. It is a LABEL that travels to the shop window:\npublish parses Price with money.ParseUSD and the x402 terms carry no\ncurrency, so another code here changes what is displayed, not what is charged.","type":"string"},"description":{"description":"Description is the long copy. Publish REFUSES one past 4096 bytes rather\nthan truncating it, so what is stored is what was sent; empty is allowed.","type":"string"},"id":{"description":"ID is the listing's id, minted here as \"lst_\" + 16 hex characters. A\npublisher cannot choose it: Create overwrites whatever arrives. It is unique\nwithin PublisherOrg (the primary key is the pair), and it is the path segment\nDELETE /v1/marketplace/listings/:id takes.","type":"string"},"price":{"description":"exact per-call price; 0 is free."},"public":{"description":"Public is whether other orgs can discover the listing. It also decides\nENFORCEMENT: only public rows reach the price table, so a private listing\nwith a price charges nobody. False leaves the row visible to its publisher\nalone.","type":"boolean"},"publisherOrg":{"description":"PublisherOrg is the org that published the listing, taken from the validated\nprincipal and never off the wire. It is also the PAYEE org — Recipient is\nresolved inside it — and the isolation key: a publisher reads and deletes\nonly rows carrying its own org.","type":"string"},"recipient":{"description":"seller payout WALLET ID, in PublisherOrg.","type":"string"},"title":{"description":"Title is the shop-window name, required and refused past 200 bytes. It is\nwhat discovery paints over the tool's registry name.","type":"string"},"tool":{"description":"Tool is the registry name of the offered capability, in the flat fleet-wide\ntool namespace. It resolved in the publisher's own scope at publish time, so\nno listing advertises a capability that did not exist; it is also the key the\nprice table looks a dispatch up by.","type":"string"}},"type":"object"},"Location":{"properties":{"external":{"description":"External is true when the answer left the repository — the case a static index\ncannot answer, and the reason this service resolves through dependencies.","type":"boolean"},"path":{"description":"Path is repo-relative while External is false, and the module coordinate\n(\"golang.org/x/[email protected]/semver/semver.go\") once it is true.","type":"string"},"range":{"$ref":"#/components/schemas/Range","description":"Range is the span inside that file, in LSP positions."}},"type":"object"},"LogBody":{"properties":{"body":{"type":"string"},"number":{"type":"integer"},"severity":{"type":"string"}},"type":"object"},"MCPError":{"type":"object","properties":{"code":{"type":"integer"},"data":{},"message":{"type":"string"}}},"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"},"MCPRequest":{"type":"object","properties":{"id":{},"jsonrpc":{"type":"string"},"method":{"type":"string"},"params":{"type":"object","additionalProperties":{}}}},"MCPResponse":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/MCPError"},"id":{},"jsonrpc":{"type":"string"},"result":{"type":"object","additionalProperties":{}}}},"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"},"Match":{"properties":{"backend":{"description":"Backend is the leg that contributed this match: \"index\" (lexical), \"vector\"\n(semantic) or \"code\" (the org's repositories). It is the same name that leg\nreports itself under in Response.Backends, so a hit can be traced to a\nstatus.","type":"string"},"rank":{"description":"Rank is this document's 1-based position in THAT leg's own result list,\nbefore fusion — 1 is the leg's best hit. It is the only input to the fused\nscore: RRF adds 1/(60+rank) per leg, which is why a document two legs ranked\nsecond beats one a single leg ranked first.","type":"integer"},"score":{"description":"Score is the leg's NATIVE score, on that leg's own scale, reported for\nexplanation and never used in ranking — the scales are incomparable (a cosine\nsimilarity against a term-match count), which is why fusion works on ranks.\nThe vector leg reports Qdrant's cosine similarity; the lexical leg exposes no\nper-row score and reports 0, meaning \"unscored\", not \"scored zero\".","type":"number"}},"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"},"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":{"description":"Load1 is the machine's own one-minute load average — a count of runnable and\nuninterruptible tasks, NOT a percentage and NOT already divided by core\ncount, so it is read against Spec.CPUs: 8.0 is idle on 16 cores and swamped\non 4. Coerced finite and non-negative on write, so 0 means either genuinely\nidle or nothing reported.","type":"number"},"load15":{"description":"Load15 is the same figure over fifteen. The three together are what separate\na machine that is busy right now from one that has been busy all along —\nwhich is the question a dispatcher is really asking.","type":"number"},"load5":{"description":"Load5 is the same figure averaged over five minutes.","type":"number"},"memFree":{"description":"bytes","type":"integer"},"memUsed":{"description":"bytes","type":"integer"}},"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"},"Mode":{"properties":{"live":{"type":"boolean"},"orgId":{"type":"string"},"orgName":{"type":"string"},"testMode":{"type":"boolean"}},"type":"object"},"ModeIn":{"properties":{"testMode":{"type":"boolean"}},"type":"object"},"ModelHistory":{"properties":{"model":{"description":"Model is the system these runs measured.","type":"string"},"points":{"description":"Points is every run, oldest first.","items":{"$ref":"#/components/schemas/RunPoint"},"type":"array"},"trend":{"description":"Trend is the change from the first run to the last, absent when there has\nonly been one. It answers the question a list of points makes you compute.","type":"number"}},"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"},"ModelStat":{"properties":{"completionTokens":{"description":"tokens it answered with","type":"integer"},"costCents":{"description":"what this model cost, in cents","type":"integer"},"costPct":{"description":"share of total spend, 0..100","type":"number"},"errorRate":{"description":"share of its calls that failed, 0..1","type":"number"},"errors":{"description":"calls to it that did not succeed","type":"integer"},"model":{"description":"the model this row is about, or \"other\" for the fold","type":"string"},"modelCount":{"description":">0 only on the \"other\" fold","type":"integer"},"p50Ms":{"description":"median latency, null when no spans carry it","type":"number"},"p95Ms":{"description":"95th-percentile latency, null when unknown","type":"number"},"p99Ms":{"description":"99th-percentile latency, null when unknown","type":"number"},"promptTokens":{"description":"tokens sent to it","type":"integer"},"provider":{"description":"who serves it","type":"string"},"requests":{"description":"calls to this model in the window","type":"integer"},"totalTokens":{"description":"prompt plus completion","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"},"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"},"Number":{"properties":{"capable":{"description":"Capable is what the number can carry: any of \"voice\", \"sms\", \"mms\", \"fax\". A\nnumber missing \"sms\" cannot send one no matter what this platform does.","items":{"type":"string"},"type":"array"},"country":{"description":"Country is the ISO 3166-1 alpha-2 code the number is issued under. Numbering is\nnational, so this is what makes a search answerable at all.","type":"string"},"currency":{"description":"Currency is the ISO 4217 code Monthly is denominated in. Without it the number\nbeside it means nothing, so the two are always read together.","type":"string"},"e164":{"description":"E164 is the number in E.164: a leading + and digits only, no spaces or dashes.\nThat is what a carrier accepts and what a search result must be bought by.","type":"string"},"id":{"description":"ID is the carrier's handle for the number, and the id every route here\naddresses it by. It is not the number itself — see E164.","type":"string"},"monthly":{"description":"Monthly is the recurring rental in the MINOR unit of Currency (cents for USD),\nexactly as the carrier quoted it. It is a price, not a charge: nothing is billed\nby this field.","type":"integer"},"org":{"description":"Org is the tenant holding the number. A search result carries none — nobody\nholds it yet — which is how an available number is told from a held one.","type":"string"},"type":{"description":"Type is what kind of number it is: \"local\", \"national\", \"tollfree\" or \"mobile\".\nIt decides both price and what a carrier will let it originate.","type":"string"}},"type":"object"},"Obligation":{"properties":{"amountCents":{"description":"AmountCents is what it costs each period.","type":"integer"},"asOf":{"description":"AsOf is when that amount was last checked against its source, RFC 3339 date.","type":"string"},"code":{"description":"Code names the obligation so a caller can branch without reading prose.","type":"string"},"every":{"description":"Every is how often it falls due — \"yearly\" for every obligation known here.","type":"string"},"label":{"description":"Label is what the payer sees.","type":"string"},"minimum":{"description":"Minimum marks a floor rather than a fixed price — a franchise tax that\nscales with shares or assets is quoted at its minimum, and an entity past\nthe threshold owes more. Saying so is the difference between a quote and a\nnumber someone later disputes.","type":"boolean"},"passThrough":{"description":"PassThrough marks money we collect and remit to the state rather than keep.","type":"boolean"},"source":{"description":"Source names the authority that publishes a pass-through amount, so the\nfigure can be checked without first working out who would know.","type":"string"},"stale":{"description":"Stale reports that AsOf is older than the review window. It tells; it does\nnot block.","type":"boolean"}},"type":"object"},"Offer":{"properties":{"available":{"description":"whether it can be bought right now","type":"boolean"},"currency":{"description":"the currency both prices are in","type":"string"},"domain":{"description":"the name this quote prices","type":"string"},"premium":{"description":"whether the registry prices it above the standard rate","type":"boolean"},"priceCents":{"description":"sell (first-term registration)","type":"integer"},"renewalPriceCents":{"description":"sell (renewal)","type":"integer"},"tld":{"description":"the top-level domain the name sits under","type":"string"}},"type":"object"},"Op":{"type":"object","properties":{"href":{"type":"string"},"method":{"type":"string"},"operationId":{"type":"string"},"summary":{"type":"string"}}},"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"},"Outcome":{"properties":{"control":{"description":"true on the baseline arm; its own lift and stats are zero","type":"boolean"},"converted":{"description":"of those, how many fired the metric event","type":"integer"},"exposed":{"description":"subjects the arm enrolled — the denominator","type":"integer"},"lift":{"description":"relative to control: (rate-ctrl)/ctrl","type":"number"},"pValue":{"description":"two-tailed p vs control","type":"number"},"rate":{"description":"converted over exposed","type":"number"},"significant":{"description":"pValue < alpha","type":"boolean"},"variant":{"description":"the arm this row measures","type":"string"},"z":{"description":"two-proportion z vs control","type":"number"}},"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"},"Page":{"properties":{"body":{"description":"Body is the reusable prompt or snippet itself. It may carry {placeholder}\ntokens for the client-specific bits — {client_name}, {domain}, {product} —\nwhich are substituted where the template is used, not here.","type":"string"},"enabled":{"description":"Enabled is the admin lever. Absent reads as ON; an explicit false withdraws\nthe template from org-facing reads.","type":"boolean"},"id":{"description":"ID is the slug a step references to pull this template in.","type":"string"},"title":{"description":"Title names the template in the authoring plane and in a picker.","type":"string"}},"type":"object"},"PagesBuildConfig":{"properties":{"build_command":{"description":"BuildCommand is what Cloudflare runs to build the site (\"npm run build\").\nOmitted means no build step: the repository is published as it stands.","type":"string"},"destination_dir":{"description":"DestinationDir is the directory the build leaves the site in (\"dist\"),\nrelative to RootDir. It is what gets served.","type":"string"},"root_dir":{"description":"RootDir is where in the repository the build runs, for a project that is not at\nthe repository root. Omitted means the root.","type":"string"}},"type":"object"},"PagesD1Binding":{"properties":{"id":{"description":"ID is the D1 database this binding points at, by Cloudflare's uuid. The binding\nname the Worker code reads it as is the map key, not a field here.","type":"string"}},"type":"object"},"PagesDeploy":{"properties":{"branch":{"type":"string"}},"type":"object"},"PagesDeploymentConfig":{"properties":{"compatibility_date":{"description":"CompatibilityDate pins which Workers runtime behaviour the functions run under,\nas a date (\"2024-01-01\"). It is a pin, not a version: the runtime keeps that\ndate's semantics for code deployed against it.","type":"string"},"compatibility_flags":{"description":"CompatibilityFlags turn individual runtime behaviours on or off ahead of, or\nbehind, the date above (\"nodejs_compat\").","items":{"type":"string"},"type":"array"},"d1_databases":{"additionalProperties":{"$ref":"#/components/schemas/PagesD1Binding"},"description":"D1Databases binds D1 databases in, keyed by binding name.","type":"object"},"env_vars":{"additionalProperties":{"$ref":"#/components/schemas/PagesEnvVar"},"description":"EnvVars are the environment variables the functions see, KEYED BY VARIABLE NAME.\nThe key is the name; the value carries the value and whether it is a secret.","type":"object"},"kv_namespaces":{"additionalProperties":{"$ref":"#/components/schemas/PagesKVBinding"},"description":"KVNamespaces binds KV namespaces into the functions, KEYED BY THE BINDING NAME\nthe code reads (`env.SESSIONS`). Same shape for the two below.","type":"object"},"r2_buckets":{"additionalProperties":{"$ref":"#/components/schemas/PagesR2Binding"},"description":"R2Buckets binds R2 buckets in, keyed by binding name.","type":"object"}},"type":"object"},"PagesDeploymentConfigs":{"properties":{"preview":{"$ref":"#/components/schemas/PagesDeploymentConfig","description":"Preview is the config every branch build other than the production branch runs\nunder. It is a SEPARATE set of bindings and variables, which is what lets a\npreview point at test data."},"production":{"$ref":"#/components/schemas/PagesDeploymentConfig","description":"Production is the config the production branch builds under."}},"type":"object"},"PagesEnvVar":{"properties":{"type":{"description":"Type is \"plain_text\" or \"secret_text\" and decides that: plain text is readable\nafterwards, secret text is write-only. Empty is Cloudflare's default,\nplain_text — so a secret with no type set is stored in the clear.","type":"string"},"value":{"description":"Value is the variable's value. Under type \"secret_text\" Cloudflare encrypts it\non arrival and never reads it back, so a later read of the project shows the\nvariable without this.","type":"string"}},"type":"object"},"PagesKVBinding":{"properties":{"namespace_id":{"description":"NamespaceID is the KV namespace this binding points at, by Cloudflare's id\nrather than its title. The BINDING NAME — what the Worker code reads it as — is\nthe map key this value sits under, not a field here.","type":"string"}},"type":"object"},"PagesProjectCreate":{"properties":{"build_config":{"$ref":"#/components/schemas/PagesBuildConfig","description":"BuildConfig says how to build the site. Omitted means no build step."},"deployment_configs":{"$ref":"#/components/schemas/PagesDeploymentConfigs","description":"DeploymentConfigs carries the preview and production runtime configs — the\nbindings and variables the built site's functions run with."},"name":{"description":"Name is the project name, and it is also the address: the site answers at\n<name>.pages.dev. Cloudflare will not rename a project afterwards.","type":"string"},"production_branch":{"description":"ProductionBranch is which git branch builds to production; every other branch\nbuilds a preview. Omitted leaves Cloudflare's own default.","type":"string"}},"type":"object"},"PagesR2Binding":{"properties":{"name":{"description":"Name is the R2 bucket this binding points at, by bucket name — R2 addresses\nbuckets by name where KV and D1 use ids. The binding name the Worker code reads\nit as is the map key.","type":"string"}},"type":"object"},"PathIn":{"properties":{"id":{"description":"ID is the sandbox to read from, from an earlier lease.","type":"string"},"path":{"description":"Path is read relative to the sandbox's working directory unless it is\nabsolute, and a path that climbs out of it is refused rather than rewritten.\nEmpty names the working directory itself, which lists it.","type":"string"}},"type":"object"},"PaymentConfig":{"properties":{"applicationId":{"type":"string"},"environment":{"type":"string"},"live":{"type":"boolean"},"locationId":{"type":"string"},"provider":{"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"},"Payout":{"properties":{"amount":{"type":"integer"},"arrivalDate":{"type":"string"},"created":{"type":"string"},"currency":{"type":"string"},"description":{"type":"string"},"destinationId":{"type":"string"},"destinationType":{"type":"string"},"failureCode":{"type":"string"},"failureMessage":{"type":"string"},"id":{"type":"string"},"metadata":{},"providerRef":{"type":"string"},"status":{"type":"string"}},"type":"object"},"PnL":{"properties":{"expense":{"description":"Expense is the cost lines that moved in the period, one per account.","items":{"$ref":"#/components/schemas/PnLLine"},"type":"array"},"from":{"description":"From opens the period and is EXCLUSIVE — movement strictly after it, matching\nthe trial balance's opening boundary so the two reports agree on what belongs\nto a period. Absent means from the beginning of the ledger.","type":"string"},"income":{"description":"Income is the revenue lines that moved in the period, one per account.\nAccounts that did not move are omitted rather than listed at zero.","items":{"$ref":"#/components/schemas/PnLLine"},"type":"array"},"netIncome":{"description":"NetIncome is totalIncome minus totalExpense, in cents. Negative is a loss.","type":"integer"},"to":{"description":"To closes the period and is inclusive. Absent means up to now.","type":"string"},"totalExpense":{"description":"TotalExpense is cost MATCHED to that revenue, in cents, including accrued\ninfrastructure that has not been billed yet.","type":"integer"},"totalIncome":{"description":"TotalIncome is revenue RECOGNIZED in the period, in cents — accrual, not cash,\nso a prepaid top-up is not in it until the credit is consumed.","type":"integer"}},"type":"object"},"PnLLine":{"properties":{"account":{"description":"Account is the chart-of-accounts number this line reports on.","type":"string"},"amount":{"description":"Amount is the account's movement over the period in whole cents, in its\nNATURAL sign: positive when the account behaved normally, for income and\nexpense alike. Income is credit-normal so its stored net is flipped once here\nfor display; the ledger underneath is never sign-flipped. A negative amount\ntherefore means the account ran backwards — a refunded sale, a reversed cost.","type":"integer"},"name":{"description":"Name is that account's human name from the fixed chart.","type":"string"},"type":{"description":"Type is the account's fundamental class, which on this statement is always\nincome or expense — it tells a reader which half of the statement the line\ncame from without re-deriving it from the array it arrived in.","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":{"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. An emoji before the\ncursor counts as one here and as two in Go's arithmetic.","type":"integer"},"line":{"description":"Line is 0-BASED, per the LSP specification — one less than the line an editor\nshows a human.","type":"integer"}},"type":"object"},"PostList":{"properties":{"data":{"description":"Data is the page, ordered by scheduledAt descending — the furthest-out\npost first and unscheduled drafts (scheduledAt 0) last. An empty array\nwhen the org's calendar holds no matching post.","items":{"$ref":"#/components/schemas/CalendarPost"},"type":"array"}},"type":"object"},"Preset":{"properties":{"arms":{"description":"the blend — model ids from the arena","items":{"type":"string"},"type":"array"},"name":{"description":"served as enso-<name>","type":"string"},"note":{"description":"why this blend (audit)","type":"string"},"owner":{"description":"scoping org (never cross-tenant)","type":"string"},"panel":{"description":"fan-out width (>=1)","type":"integer"},"rank":{"description":"escalation order over arms","items":{"type":"string"},"type":"array"}},"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"},"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":{"description":"Data is every promo in the deployment, oldest first, each with its live\ncounters. The list is fleet-wide rather than per-org. It is normally EMPTY:\nnothing seeds a promo, and the migration purges the one that once shipped\nby accident.","items":{"$ref":"#/components/schemas/PromoStatus"},"type":"array"}},"type":"object"},"PromoStatus":{"properties":{"promo":{"$ref":"#/components/schemas/Promo","description":"Promo is the offer itself. It is fleet-wide, identical for every org — only\nthe two counters beside it move."},"redeemed":{"description":"Redeemed is how many orgs have taken it, Remaining how many are left under\nthe fleet-wide cap.","type":"integer"},"remaining":{"description":"Remaining is MaxRedemptions minus Redeemed, floored at 0. At 0 the next\nredeem is declined, and a quote reports ineligible rather than pricing an\noffer that cannot be taken.","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"},"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 provider.","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"},"PublishInput":{"properties":{"doctype":{"description":"DocType is the content type holding the item: Campaign, SocialPost or Asset.\nAny other name is refused as an unknown content type.","type":"string"},"name":{"description":"Name is the document within that type — the item to distribute. Its caption,\nmedia and channel list come off the stored document, so this names WHICH\nitem and says nothing about what goes out.","type":"string"},"scheduleAt":{"description":"ScheduleAt hands a future go-live to the channel's own scheduler, as an\nISO-8601 time. Empty posts now.","type":"string"}},"type":"object"},"PublishResult":{"properties":{"channels":{"description":"Channels is the channel list read off the content document — integration ids\nor provider names, as the item declares them. Empty when the item names\nnone, which targets every connected, enabled channel. It is what was ASKED\nfor; Results is what happened.","items":{"type":"string"},"type":"array"},"externalIds":{"additionalProperties":{"type":"string"},"description":"ExternalIDs maps channel id → the post id that channel returned, merged with\neverything earlier publishes recorded. Successes only, and it is the\nidempotency ledger: a channel named here is skipped by every later publish\nof this item, so the map only ever grows.","type":"object"},"results":{"description":"Results is the outcome per channel — which went out, which did not and why —\ncovering the whole fan-out including failures, so partial success is never\nflattened into one verdict. A channel the org has not connected appears here\nas failed with \"channel not connected\".","items":{"$ref":"#/components/schemas/ChannelResult"},"type":"array"},"status":{"description":"Status is the ONE headline, drawn from: \"distributed\" (something is on\nrecord and went out now), \"scheduled\" (same, handed to the channel's own\nscheduler for later), \"failed\" (nothing is on record — this fan-out missed\nentirely and no earlier one landed), \"in_progress\" (another publisher holds\nthe item, so this call posted NOTHING and the caller retries), and\n\"not_configured\" (no distribution edge is wired; a transition records it\ninstead of failing). A partial fan-out is \"distributed\"/\"scheduled\", never\n\"failed\" — the per-channel truth is in Results.","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":{"description":"Account is the chart number the questioned entry posted to, where one applies.","type":"string"},"amount":{"description":"Amount is the figure that makes the question concrete, already FORMATTED for\ndisplay with its currency symbol — a string, not cents, and not for arithmetic.","type":"string"},"id":{"description":"ID is the source transaction the question is about, so answering it leads\nstraight back to the entry that raised it.","type":"string"},"kind":{"description":"Kind is what looked wrong: outlier (a charge far above the usual), reversal (a\nposting undone), roundoff (a balancing plug big enough to be worth explaining),\nuncosted (revenue booked with no cost matched to it), or overdrawn (a wallet\nspent past its balance).","type":"string"},"postedAt":{"description":"PostedAt anchors the question in time — when the entry it concerns posted.","type":"string"},"text":{"description":"Text is the question itself, written for a founder to answer directly.","type":"string"}},"type":"object"},"QuestionsResponse":{"properties":{"questions":{"description":"Questions are what the books want explained, SHARPEST FIRST — largest amounts\nahead of smaller ones, and capped, so this is the top of the list rather than\neverything. Empty means the ledger looks clean; the detector is deterministic\nover what was posted and invents nothing.","items":{"$ref":"#/components/schemas/Question"},"type":"array"}},"type":"object"},"Quote":{"properties":{"chargeCents":{"description":"ChargeCents is what month one costs after the discount, in USD cents,\ntotalled over the seats quoted. On team that is a multiple of the seat\ncount, so it is not ListCents minus DiscountCents.","type":"integer"},"code":{"description":"Code is the promo that was priced, as stored.","type":"string"},"discountCents":{"description":"DiscountCents is what the promo takes off month one, in USD cents. The\npromo rate reaches at most TeamSeatCap seats; seats past the cap bill at\nfull list and add nothing here. It is arithmetic only — quoting credits\nnothing, counts nothing and reserves nothing.","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 in USD cents: PER SEAT on team,\nthe whole month on pro and max, 0 for a plan with no list price.","type":"integer"},"plan":{"description":"Plan is the tier priced, lower-cased and trimmed: pro, max or team.\nUnlike a redemption's plan this one comes from the REQUEST — quoting has no\nside effects, so it will happily price a plan the caller does not hold.","type":"string"},"reason":{"description":"Reason is why Eligible is false, drawn from: \"promo redemption is closed\"\n(the subsystem is off, which is how it ships), \"promo redemption cap\nreached\", \"promo is not active\", \"plan is free or unknown; nothing to\ndiscount\", \"promo does not cover plan <plan>\". Absent when Eligible is true.","type":"string"},"remaining":{"description":"Remaining is how many redemptions are left under the fleet-wide cap.","type":"integer"},"seats":{"description":"Seats is the seat count priced; a request of 0 or less was read as 1. It\nonly bites on team, the one per-seat plan — pro and max are single-seat and\nignore it.","type":"integer"}},"type":"object"},"RaiseIn":{"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/InvoiceLine"},"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"},"Ran":{"properties":{"exitCode":{"description":"ExitCode is the PROGRAM's own status — 0 succeeded, anything else is what it\nreturned, and a Command runs under `sh -c` so its shell's conventions apply.\nA command that never reached an exit does not arrive here at all: a timeout\nor a stop cancels the channel, and that is an error on the call rather than a\ncode of ours invented to fill this field.","type":"integer"},"stderr":{"description":"Stderr is standard error, kept apart from Stdout so a caller reading a\nprogram's OUTPUT is not reading its diagnostics as data. Same 1 MiB cap, same\nredaction. A program that failed usually says why here and nowhere else.","type":"string"},"stdout":{"description":"Stdout is what the program wrote to standard output, collected whole rather\nthan streamed — to watch it arrive instead, name a RunIn.Session and read that\nsession's feed. Capped at 1 MiB, past which it ends in \"[truncated at 1MiB]\".\nEvery string named in RunIn.Blind is replaced by \"[redacted]\" before it gets\nhere, and before it reaches the session.","type":"string"}},"type":"object"},"Range":{"properties":{"end":{"$ref":"#/components/schemas/Position","description":"End is the position just past the span, excluded — the range is half-open, so\nan empty range has Start equal to End."},"start":{"$ref":"#/components/schemas/Position","description":"Start is the first position in the span, included."}},"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"},"Receipt":{"properties":{"amount":{"description":"Amount is what actually moved, as an exact 18-decimal-place USD string. It\nis NOT the atomic-unit figure the client signed: the challenge quotes the\nasset's own units (USDC's 6 dp) and truncates to fit them, while the ledger\nmoves this exact value.","type":"string"},"from":{"description":"From is the payer's EVM address: the account that signed the EIP-3009\nauthorization, recovered from the signature rather than taken on trust.","type":"string"},"id":{"description":"ID is the settle-once key: \"x402_\" + keccak(from|nonce) in hex. It is\nDERIVED, not minted, so a client that re-submits the same authorization\naddresses the same settlement and is served again for free rather than\ncharged twice. It is also the id GET /v1/x402/settlements/:id takes.","type":"string"},"network":{"description":"Network is the CAIP-2 identifier the payment was settled under, e.g.\n\"eip155:36963\". Its eip155 reference is the chain id in the EIP-712 domain\nthe payer signed, so it is not a label — changing it invalidates the\nsignature.","type":"string"},"nonce":{"description":"Nonce is the client-chosen nonce from the authorization, hex — up to 32\nbytes, left-padded to the contract's bytes32. It is the replay anchor: the\ntoken contract refuses a second on-chain transfer for one (from, nonce), and\nthis rail refuses a second settlement for the same pair, so a ledger\nsettlement inherits the identical guarantee.","type":"string"},"payee":{"description":"Payee is the recipient's EVM address — the `payTo` the challenge advertised\nand the authorization named. A payment to any other address never settles.","type":"string"},"payeeOrg":{"description":"PayeeOrg is the tenant that owns the recipient wallet, resolved at\nsettlement. It is who got PAID, as Payer is who paid.","type":"string"},"payer":{"description":"Payer is the payer ORG — the tenant whose ledger was debited — and not an\naddress. It is the org the request was authenticated as, so it answers who\nis billed, which the payer address alone cannot.","type":"string"},"resource":{"description":"Resource is what was paid for, in the same spelling the price table and the\nchallenge used: the request path for a priced route, \"tool:<id>\" for a\npriced tool.","type":"string"},"settledAt":{"description":"SettledAt is when this settlement was CLAIMED, in unix seconds — the moment\nthe authorization was accepted, which is also the moment the time window it\ncarried stopped applying. A settlement finished later by reconciliation\nkeeps this instant.","type":"integer"},"settledVia":{"description":"SettledVia is which rail moved the money: \"ledger\", the live default, or\n\"chain\" when the authorization is broadcast. Those two values and no others.","type":"string"},"txHash":{"description":"TxHash is the chain transaction hash, present only for a \"chain\"\nsettlement. Empty on a ledger settlement — that is the normal case today,\nand it means the money moved without a chain, not that it failed. The\nwire's PAYMENT-RESPONSE `transaction` falls back to ID when this is empty.","type":"string"}},"type":"object"},"Recharge":{"properties":{"charged":{"description":"Charged is how many of them were actually charged. It is at most Orgs, and\nthe difference is orgs whose balance was already above their threshold.","type":"integer"},"orgs":{"description":"Orgs is how many orgs the sweep considered — every org with auto-recharge\narmed, whether or not it needed charging.","type":"integer"},"results":{"description":"Results is one row per org considered, so a sweep that charged nobody is\nstill explainable. Never null.","items":{"$ref":"#/components/schemas/Recharged"},"type":"array"}},"type":"object"},"Recharged":{"properties":{"amountCents":{"type":"integer"},"balanceCents":{"type":"integer"},"charged":{"type":"boolean"},"error":{"type":"string"},"orgName":{"type":"string"},"transactionId":{"type":"string"},"userId":{"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"},"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":{"description":"DiscountCents is the discount claimed for month one, in USD cents, at the\nsingle-seat floor. It is the same figure recorded on the Redemption, and it\nis evidence an admin may later grant against — not a balance.","type":"integer"},"redemption":{"$ref":"#/components/schemas/Redemption","description":"Redemption is the row that was recorded — the org's claim on this promo,\nwith the server-derived plan and seat count. On a replay it is the ORIGINAL\nrow, so its redeemedAt is when the org first took the promo, not now."}},"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 is the tier redeemed against: pro, max or team. It is DERIVED from the\norg's live ACTIVE/TRIALING subscription, never read from the request, so it\nis what the org actually holds rather than what it claimed.","type":"string"},"redeemedAt":{"description":"RedeemedAt is unix seconds.","type":"integer"},"seats":{"description":"Seats is the seat count the claim was priced at, and it is ALWAYS 1. No\nserver-side authority on this surface answers \"how many seats\", and the\ncaller's own number is exactly the input that once inflated these claims,\nso a redemption records the single-seat floor and an admin resolves the\nreal count against subscription data at grant time.","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 gap (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"},"RegisterResult":{"properties":{"quote":{"$ref":"#/components/schemas/Offer","description":"the price it was bought at"},"record":{"$ref":"#/components/schemas/Holding","description":"the ownership row this purchase issued"}},"type":"object"},"Registrant":{"properties":{"address1":{"description":"street address","type":"string"},"address2":{"description":"second address line","type":"string"},"city":{"description":"city or locality","type":"string"},"companyName":{"description":"the organisation the contact acts for","type":"string"},"country":{"description":"ISO-3166 alpha-2, e.g. \"US\"","type":"string"},"email":{"description":"where WHOIS correspondence is sent","type":"string"},"fax":{"description":"fax number, in the same form as phone","type":"string"},"firstName":{"description":"the contact's given name","type":"string"},"lastName":{"description":"the contact's family name","type":"string"},"phone":{"description":"+NN.NNNNNNN","type":"string"},"state":{"description":"state, province or region","type":"string"},"zip":{"description":"postal code","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"},"RenewResult":{"properties":{"paidCents":{"description":"what this renewal cost, in cents","type":"integer"},"record":{"$ref":"#/components/schemas/Holding","description":"the ownership row with its new expiry"}},"type":"object"},"Report":{"properties":{"answer":{"description":"Answer is the grounded prose, with inline markdown citations. Every link in\nit points at a page in Sources: the citation check runs on the text before\nit leaves the engine, so a cited URL is one THIS call fetched.","type":"string"},"follow_ups":{"description":"FollowUps are the questions worth asking next. Best-effort — an empty list\nis a normal outcome, not a fault.","items":{"type":"string"},"type":"array"},"mode":{"description":"Mode is the profile that ran: search, news, research or deep.","type":"string"},"model":{"description":"Model is the model that synthesized the answer.","type":"string"},"sources":{"description":"Sources are the pages the answer was written from, deduplicated and ranked.\nAlways an array, never null.","items":{"$ref":"#/components/schemas/Source"},"type":"array"}},"type":"object"},"Request":{"properties":{"doctypes":{"description":"DocTypes restricts the semantic leg to a subset of indexed knowledge types.","items":{"type":"string"},"type":"array"},"index":{"description":"Index names the lexical index to query. Defaults to \"kb\".","type":"string"},"limit":{"description":"Limit bounds the FUSED result set (default 10, max 50).","type":"integer"},"mode":{"description":"Mode selects the legs: auto (default) | text | semantic | hybrid.","type":"string"},"offset":{"description":"Offset pages the fused result set.","type":"integer"},"project":{"description":"Project narrows to one project scope within the org.","type":"string"},"query":{"description":"Query is the natural-language or keyword query. Required.","type":"string"}},"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"},"Response":{"properties":{"backends":{"description":"Backends is the per-leg report. Always populated.","items":{"$ref":"#/components/schemas/BackendStatus"},"type":"array"},"hits":{"description":"Hits is the fused, ranked result set.","items":{"$ref":"#/components/schemas/Hit"},"type":"array"},"mode":{"description":"Mode is the mode actually used after `auto` resolution.","type":"string"},"status":{"description":"Status is the query's overall honesty signal:\n ok every consulted leg answered.\n partial at least one leg failed; Hits holds the survivors' results.\n unavailable every consulted leg failed; Hits is empty AND that is stated.","type":"string"},"took_ms":{"description":"TookMS is the whole query's wall time in milliseconds — every leg it\nconsulted, plus fusion and paging. Each leg's own share is in\nBackends[].TookMS; the legs run in sequence, so this is at least their sum.","type":"integer"}},"type":"object"},"Responsible":{"properties":{"country":{"description":"Country is where they reside, ISO 3166-1 alpha-2.","type":"string"},"email":{"description":"Email reaches them for signature.","type":"string"},"name":{"description":"Name is their full legal name as the IRS will hold it.","type":"string"},"usTaxId":{"description":"USTaxID reports that they hold an SSN or ITIN. It is a BOOLEAN on purpose:\nthe number itself is never needed here and a field that could hold it is a\nfield that will eventually be logged.","type":"boolean"}},"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"},"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"},"Rollup":{"properties":{"balance":{"$ref":"#/components/schemas/RollupBalance"},"consumedCents":{"type":"integer"},"currency":{"type":"string"},"included":{"$ref":"#/components/schemas/RollupAllotment"},"overageCents":{"type":"integer"},"period":{"type":"string"},"plan":{"type":"string"},"user":{"type":"string"},"windows":{"items":{"$ref":"#/components/schemas/Window"},"type":"array"}},"type":"object"},"RollupAllotment":{"properties":{"consumedCents":{"type":"integer"},"grantedCents":{"type":"integer"},"monthlyCents":{"type":"integer"},"remainingCents":{"type":"integer"}},"type":"object"},"RollupBalance":{"properties":{"availableCents":{"type":"integer"},"balanceCents":{"type":"integer"},"holdsCents":{"type":"integer"}},"type":"object"},"Root":{"type":"object","properties":{"_links":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Link"}},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/Capability"}}}},"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"},"RuleView":{"properties":{"description":{"description":"what kind of secret this rule recognises","type":"string"},"id":{"description":"the rule identifier a finding cites","type":"string"},"name":{"description":"the rule's human name","type":"string"},"severity":{"description":"how serious a match is: critical, high, medium or low","type":"string"}},"type":"object"},"RunIn":{"properties":{"argv":{"description":"Argv is the program and its arguments, already split — the form no shell can\nmisread. Give this or Command, not both.","items":{"type":"string"},"type":"array"},"blind":{"description":"Blind is the set of secrets this command must never publish.\n\nIt exists because output is redacted where it is PRODUCED or not at all. A\ncaller that scrubbed the returned result would still have streamed the\nunredacted bytes into the session as they were written — to a durable event\nstore, an SSE feed and a chat thread — because the narration leaves the\nsandbox by a different path from the result. Nothing downstream can take a\nsecret back out of a message that has already been delivered.\n\nThe sandbox holds these only for the life of the one command, applies them\nto every stream leaving it, and never logs or stores them.","items":{"type":"string"},"type":"array"},"command":{"description":"Command is a shell line, run by `sh -c`. Use it when a pipeline or a\nredirection is the point, and Argv when it is not.","type":"string"},"dir":{"description":"Dir runs the command somewhere other than the sandbox's working directory,\nwhich Leased.Workdir names.","type":"string"},"id":{"description":"ID is the sandbox to run in, from an earlier lease.","type":"string"},"session":{"description":"Session is the live agent session this command narrates into: its output is\nappended there AS IT IS PRODUCED, so every surface watching that session\nwatches the command work instead of a blank pause. A long run is otherwise a\nsilence with a verdict at the end.\n\nIt names a SESSION and never a tenant. The org is the one the caller already\nproved, so a session belonging to somebody else is simply absent from the org\nthis call acts for and the append is refused there. Empty means nothing is\nwatching, and then nothing is sent.","type":"string"},"stdin":{"description":"Stdin is fed to the program on standard input. This is how bytes reach a file\nwithout being quoted into a shell line: `cat > path` with the contents here\nwrites them exactly.","type":"string"},"timeoutSec":{"description":"TimeoutSec bounds this ONE command, so a wedged program holds the caller for\nits own timeout rather than for the whole lease.","type":"integer"}},"type":"object"},"RunPoint":{"properties":{"at":{"description":"At is when the run was recorded.","format":"date-time","type":"string"},"delta":{"description":"Delta is the change in score from the previous run for this model, absent\non the first. It is the number the whole surface exists to make visible.","type":"number"},"n":{"description":"N is how many items the run covered. Two runs are only comparable at the\nsame n, which is why it travels with every point rather than being assumed.","type":"integer"},"run":{"description":"Run is the measurement id these attempts were recorded under.","type":"string"},"score":{"description":"Score is accuracy over the items this run covered, as a percentage.","type":"number"}},"type":"object"},"SMS":{"properties":{"from":{"description":"From is the sending number in E.164, and must be one this org holds.","type":"string"},"id":{"description":"ID is the carrier's handle for the message.","type":"string"},"org":{"description":"Org is the tenant the message was sent for or received by.","type":"string"},"status":{"description":"Status is where the message is: \"queued\", \"sent\", \"delivered\" or \"failed\".\n\"sent\" means the carrier took it; \"delivered\" means the handset got it, and\nnot every carrier or destination reports that.","type":"string"},"text":{"description":"Text is the message body. Empty is legal when the message carried only media.","type":"string"},"to":{"description":"To is the receiving number in E.164.","type":"string"}},"type":"object"},"Sandbox":{"properties":{"class":{"description":"Class is what the sandbox is FOR, and it decides the image, the working\ndirectory and the isolation: \"exec\" for a code-interpreter call (workdir\n/mnt/data, no project, bounded per org), \"dev\" for a workspace bound to a\nproject (workdir /work, single-attach), \"desktop\" for one with a screen.","type":"string"},"connectedAt":{"description":"ConnectedAt is when somebody was last known to have this sandbox's project\nOPEN, Unix seconds. It is a fact with an EXPIRY rather than a flag: a\nwatcher restamps it every beat of its stream, and it goes stale on its own\nwhen the stream dies, so nothing has to be turned off by a process that may\nnot be there any more. The reaper reads it to choose WHICH idle allowance\napplies — see lifecycle.go.\n\nZero means nobody has said so, which puts the sandbox on the short clock.","type":"integer"},"createdAt":{"description":"CreatedAt is when the lease was first taken, Unix seconds.","type":"integer"},"error":{"description":"Error is why the sandbox could not come up, in plain words. Present only\nwith status \"error\", and it is the field to read rather than inferring a\ncause from the absence of a pod.","type":"string"},"expiresAt":{"description":"ExpiresAt is when the lease ends, Unix seconds. Past it the reaper may take\nthe sandbox at any time; it is a deadline, not a guarantee of survival until\nthen, since an idle sandbox goes sooner.","type":"integer"},"id":{"description":"ID is the sandbox's server-minted handle and what every operation addresses\nit by. The caller does not choose it.","type":"string"},"image":{"description":"Image is the container image this sandbox is actually running — the one the\nclass chose, or an override the policy admitted. It is what ran, not what\nwas asked for.","type":"string"},"kind":{"description":"Kind is the resource family this row belongs to. Always \"sandbox\" here; it\nexists because the store this shares is keyed across kinds.","type":"string"},"lastUsedAt":{"description":"LastUsedAt is when the sandbox last did work, Unix seconds. The reaper reads\nit: a sandbox idle past the idle window is reclaimed even inside its TTL,\nbecause an idle lease is capacity nobody is using.","type":"integer"},"org":{"description":"Org is the org that holds the lease — the validated caller's, never a value\na request supplied. It is also the store's key, so a sandbox is not merely\nfiltered out of another org's answers; it is unreachable from them.","type":"string"},"project":{"description":"Project is the project this sandbox is bound to. A dev or desktop sandbox\nhas one and is SINGLE-ATTACH under it, so asking twice resumes rather than\nleasing a second; an exec sandbox has none.","type":"string"},"runtime":{"description":"Runtime is the isolation boundary this sandbox GOT, which is not always the\none it asked for: a caller states a preference and runtimeFor answers with\nwhat the sandbox can actually have. Reported so a person comparing two\nruntimes is comparing the runtimes they got rather than the ones they typed\n— the difference between those two is the whole reason to record it.\n\nEmpty means the node's default runtime, which is a real answer and not a\nmissing one.\n\nThis is not a copy that can go stale. runtimeClassName is IMMUTABLE on a\npod, a sandbox's pod is created once and never recreated (restartPolicy\nNever, no pool), and its name is never reused — so for as long as the pod\nthis row names exists, it is running this runtime. The alternative, asking\nthe apiserver on every read, buys nothing and costs a round trip per row.","type":"string"},"status":{"description":"Status is where the sandbox is in its life: \"pending\" while the pod is\ncoming up, \"running\" once it can take work, \"error\" when it cannot. Only a\nrunning sandbox takes an exec or mints an interactive ticket.","type":"string"},"volume":{"description":"Volume is the persistent volume attached to the sandbox, when it has one. A\ndev sandbox keeps its work across leases through it; an exec sandbox has\nnone and loses everything outside /mnt/data when the lease ends.","type":"string"}},"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"},"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"},"SecretMeta":{"properties":{"env":{"description":"Env is the environment the secret belongs to. It is part of the storage\nkey, so the same name in two environments is two secrets.","type":"string"},"name":{"description":"Name is the secret's name within its path and environment.","type":"string"},"path":{"description":"Path is the subpath the secret is stored under, beneath the org root.","type":"string"},"scheme":{"description":"Scheme names how the value is sealed at rest, so a caller can tell a\nmigrated record from a current one without opening it.","type":"string"}},"type":"object"},"Section":{"properties":{"detail":{"description":"Detail is what this phase of the journey is for, in prose.","type":"string"},"enabled":{"description":"Enabled is the admin lever. Absent reads as ON, so only an explicit false\nturns a phase off — and it takes every step filed under it out of the\njourney, not just the heading.","type":"boolean"},"id":{"description":"ID is the slug a step's `section` names to file itself under this phase.","type":"string"},"order":{"description":"Order places the phase in the journey, ascending. Ties fall back to\nauthoring order, and an omitted order sorts as 0 — ahead of everything.","type":"integer"},"title":{"description":"Title is the phase heading a person reads above its steps.","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 <= 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 is unix seconds when the sequence was registered, server-assigned\nand never rewritten.","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":{"description":"UpdatedAt is unix seconds of the last status flip, server-assigned, and the\nkey the sequence list is ordered by (newest first). Adding a step or\nenrolling a contact does NOT touch it — only draft/active/archived does —\nso it tracks activation rather than activity.","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","description":"Sequence is the definition itself — the same record create and the list\nreturn. Its status is the one that decides whether enroll is accepted."},"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"},"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"},"Source":{"properties":{"engine":{"description":"Engine is the search backend the hit came from: bing, ddg, mojeek or brave.\nOmitted when the backend did not name itself. Results are merged across\nbackends, so two sources in one answer can carry different engines.","type":"string"},"favicon":{"description":"Favicon is a 64px icon URL derived from the host for the client to render\nbeside the citation. It is Google's s2 service, not something we host or\nfetched — an empty host yields the empty string.","type":"string"},"snippet":{"description":"Snippet is the engine's summary of the page, clipped to 600 runes. THIS IS\nWHAT THE CLIENT SHOWS. What the model reads is the fetched page, which is\nfar larger and deliberately never on the wire.","type":"string"},"title":{"description":"Title is the page title the engine reported, stripped of the bracketed\nfurniture engines staple on (\"[PDF]\", \"(Official Site)\"). It falls back to\nthe www-stripped host when the engine gave none, so it is never empty and\nis safe to use as link text.","type":"string"},"url":{"description":"URL is the page, absolute, exactly as the engine gave it. It is also the\ndedupe key — one source per URL, and at most hostCap per host — and what a\nmarkdown citation in the answer is checked against, so a link in the prose\nalways matches a URL here.","type":"string"}},"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"},"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":{"description":"EndLine is the last line of the span, inclusive. It equals Line for a\none-line span rather than being zero or absent.","type":"integer"},"file":{"description":"File is the path inside the repo, relative to its root and never absolute.","type":"string"},"kind":{"description":"Kind is what the indexer decided this chunk IS — \"func\", \"method\", \"type\",\n\"struct\", \"interface\", \"var\", \"const\", or \"block\" for a run of code that\ndeclares nothing. Absent when the chunker could not classify it.","type":"string"},"line":{"description":"Line is where the span starts, 1-based, as an editor counts.","type":"integer"},"repo":{"description":"Repo is the indexed repository the span was found in, as it was indexed\n(\"owner/name\"). A search may be scoped to one repo or run across all of them,\nso this is how a caller tells the results apart.","type":"string"},"role":{"description":"context: match | definition | caller","type":"string"},"score":{"description":"Score ranks this span against the OTHERS IN THE SAME RESPONSE and means\nnothing across responses or between tiers: the hybrid tier's number is a\nreciprocal-rank fusion sum (Σ 1/(60+rank), so tenths at best), the symbol\ntier's is a descending position count, and the text and semantic tiers pass\nthrough bm25 and cosine. Compare within a list; never threshold on it.","type":"number"},"snippet":{"description":"Snippet is the code itself: a bounded excerpt on /search, the whole chunk on\n/context — which is why the same type serves both and why a /context span is\nthe one an agent pastes into its window.","type":"string"},"symbol":{"description":"Symbol is the declared name, when the span declares one. Absent on a block.","type":"string"},"tier":{"description":"Tier is which retrieval produced the span: \"hybrid\" (the default — all three\nfused), \"text\" (trigram/FTS), \"regex\", \"semantic\" (vector), or \"symbol\". It is\nwhat explains a Score, so the two travel together.","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":{"description":"GPUs is every accelerator the machine advertises, one entry each, capped at\n32 on write. Empty means the probe found none — and that is the answer a\nNeed is checked against, so a machine with no entry here clears no\naccelerator floor. The list is not vendor-filtered: what satisfies a job is\ncounts and VRAM, never a brand (see Need).","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 (<slug>.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"},"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 >= 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":{"description":"Data is every step of the sequence, idx ascending — the order they send\nin. It is not paged: a sequence's steps are a handful, and a partial list\nwould misstate the drip. An empty array for a sequence with no messages\nyet, which enrolls fine and completes immediately.","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"},"StopIn":{"properties":{"id":{"description":"ID is the sandbox to interrupt, from an earlier lease. Every command running\nin it stops; the lease itself survives, so the checkout and the half-written\nfiles are still there to read. Use EndIn to give the computer back.","type":"string"}},"type":"object"},"Stopped":{"properties":{"stopped":{"description":"Stopped counts the commands that were still running and were interrupted.\nZero says the sandbox was idle, not that the stop failed — see above.","type":"integer"}},"type":"object"},"StorefrontResult":{"properties":{"imageUrl":{"description":"ImageURL is the absolute URL the listing's headerImage now points at. The\nimage is REFERENCED, not copied — it stays in the org's studio output\nbucket, so removing it there empties the storefront tile.","type":"string"},"slug":{"description":"Slug is the product handle the image was attached to. It IS the asset's\n`design` field — that equality is the whole join between the studio and the\ncatalog, which is why an asset with no design produces no storefront result\nat all.","type":"string"},"status":{"description":"Status is one of \"published\" (the product image was set), \"not_configured\"\n(no commerce edge, no store provisioned for the org, or a token that is not\nadmin on the store — a fail-closed no-op) or \"failed\" (commerce answered and\nerrored). None of the three fails the transition that produced it.","type":"string"},"store":{"description":"Store is the commerce store id the image landed in, resolved for the org\nmid-call. Present only on \"published\": a result that never got that far\ncarries none.","type":"string"}},"type":"object"},"Strategy":{"properties":{"action":{"description":"Action is the tactic itself: the thing to go and do, stated imperatively.","type":"string"},"blog":{"$ref":"#/components/schemas/Blog","description":"Blog is the tactic's long-form explainer; absent for tactics that have none."},"category":{"description":"Category is the growth discipline the tactic belongs to — the axis\n`?category=` narrows the corpus on, and one of the facets a caller browses by.","type":"string"},"enabled":{"description":"Enabled is the admin lever. Absent reads as ON; an explicit false drops the\ntactic from every org-facing corpus read while leaving it in the document.","type":"boolean"},"era":{"description":"Era separates an AI-era tactic (`modern`) from a classical one (`heritage`).","type":"string"},"id":{"description":"ID is the tactic's stable slug, unique across the corpus.","type":"string"},"principle":{"description":"Principle is the spine slug this tactic files under (a Principle.Slug).","type":"string"},"source":{"description":"Source is where the tactic came from — the attribution a reader is owed.","type":"string"},"tags":{"description":"Tags are PRECONDITIONS, not labels — every one must be satisfied by the org's\nobserved profile before the tactic surfaces, so an untagged tactic is\nuniversally applicable. Two vocabularies: `stage:<research|formed|launched|\nactivated|scaling>` reads the org's growth stage, `has:<capability>` reads an\nobserved signal.","items":{"type":"string"},"type":"array"},"workload":{"description":"Workload is how much effort running the tactic costs, so a corpus can be\nnarrowed to what an org has the hands for right now.","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":{"description":"CreatedAt is when the subject was first recorded, Unix SECONDS.","type":"integer"},"email":{"description":"Email is the party's address, when the org supplied one. It is PII: sealed at\nrest, returned only to the owning org, and never copied into a check record.","type":"string"},"id":{"description":"ID is the opaque handle every other record uses to point at this party. It is\nthe only reference that leaves this type, which is what keeps the PII in one\nplace: a check, an accreditation and an audit row all carry the id and none of\nthem carry the name.","type":"string"},"kind":{"description":"Kind is what is being verified: \"individual\" (a natural person, so KYC) or\n\"business\" (a legal entity, so KYB). It decides which provider flow runs.","type":"string"},"name":{"description":"Name is the party's name, under the same PII rule as Email. For a business it\nis the legal entity name rather than a trading name, since that is what a\nprovider verifies against.","type":"string"},"org":{"description":"Org is the tenant that is doing the verifying — the party who must answer for\nthis record, not the party being verified. A subject is returned only to it.","type":"string"},"ref":{"description":"Ref is the org's OWN identifier for this party, carried so a caller can match a\nsubject back to their system without keeping a second mapping. Opaque here:\nnothing in this plane parses or enforces it.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the subject's own fields last changed, Unix seconds. A check\nmoving to a new status does not touch it — that history lives on the check.","type":"integer"}},"type":"object"},"Subscription":{"properties":{"cancelAtPeriodEnd":{"type":"boolean"},"canceledAt":{"type":"string"},"createdAt":{"type":"string"},"currentPeriodEnd":{"type":"string"},"currentPeriodStart":{"type":"string"},"defaultPaymentMethod":{"type":"string"},"endedAt":{"type":"string"},"id":{"type":"string"},"mrrCents":{"description":"MRRCents is what this subscription contributes per month — commerce's own\nfigure, interval-normalized and multiplied by its seats, so no reader\nre-derives it from price and interval.","type":"integer"},"plan":{"$ref":"#/components/schemas/SubscriptionPlan"},"planId":{"type":"string"},"providerType":{"type":"string"},"quantity":{"type":"integer"},"status":{"type":"string"},"trialEnd":{"type":"string"},"trialStart":{"type":"string"},"updatedAt":{"type":"string"},"userId":{"type":"string"}},"type":"object"},"SubscriptionPlan":{"properties":{"currency":{"type":"string"},"id":{"type":"string"},"interval":{"type":"string"},"name":{"type":"string"},"price":{"type":"integer"}},"type":"object"},"SubscriptionRef":{"properties":{"atPeriodEnd":{"description":"AtPeriodEnd cancels at the end of the paid period rather than at once. It\ndefaults TRUE on the endpoint, because a customer who cancels has already\npaid for the period they are in.","type":"boolean"},"id":{"type":"string"}},"type":"object"},"Subscriptions":{"properties":{"count":{"description":"Count is the row count beside the rows, which is the shape this address\nhas always answered with.","type":"integer"},"subscriptions":{"items":{"$ref":"#/components/schemas/Subscription"},"type":"array"}},"type":"object"},"Summary":{"properties":{"active":{"description":"Active is how many of them sit in the \"active\" state exactly. A scheduled\nor paused campaign counts in Campaigns and not here, so Active is never a\nshare of anything but the whole.","type":"integer"},"budget":{"description":"Budget is every campaign's budget summed, in USD cents.","type":"integer"},"campaigns":{"description":"Campaigns is how many campaigns the org has, counted in every lifecycle\nstate including draft and completed.","type":"integer"},"spend":{"description":"Spend is every campaign's spend summed, in USD cents. It adds up figures\ncallers wrote on the campaigns themselves — this app meters no delivery\nagainst a budget — so it is a reported total, not an observed one.","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":{"description":"Data is the page: every (channel, address) this org's send gate refuses,\nnewest opt-out first. Absence from it is not permission to mail someone —\nit only means no opt-out was recorded on that channel.","items":{"$ref":"#/components/schemas/Suppression"},"type":"array"}},"type":"object"},"Symbol":{"properties":{"detail":{"description":"Detail is the server's short elaboration, typically the signature. Absent when\nit offered none.","type":"string"},"kind":{"description":"Kind is the LSP SymbolKind number (5 class, 6 method, 12 function, 23 struct,\n…), passed through rather than translated to a word — these callers already\nspeak LSP, and inventing a second vocabulary is how the two drift.","type":"integer"},"name":{"description":"Name is the declared identifier.","type":"string"},"range":{"$ref":"#/components/schemas/Range","description":"Range is the declaration's span in the file."}},"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"},"Tariff":{"properties":{"currency":{"description":"Currency is the ISO code every amount on this quote is denominated in.","type":"string"},"dueNowCents":{"description":"DueNowCents is what is charged to begin: every non-recurring line.","type":"integer"},"jurisdiction":{"description":"Jurisdiction is the state of formation the filing fee belongs to.","type":"string"},"lines":{"description":"Lines are the charges, in the order a reader should see them.","items":{"$ref":"#/components/schemas/Charge"},"type":"array"},"recurring":{"description":"Recurring is how often RecurringCents repeats — \"yearly\" for an agent of\nrecord. Empty when nothing on this quote recurs.","type":"string"},"recurringCents":{"description":"RecurringCents is what repeats, and Recurring says how often.","type":"integer"},"structure":{"description":"Structure is the entity this prices: c-corp, llc or dao-llc.","type":"string"}},"type":"object"},"Taxon":{"properties":{"brands":{"description":"Brands are the brands whose console shows this taxon. Absent means every\nbrand its category admits.","items":{"type":"string"},"type":"array"},"category":{"description":"Category is the id of the category this taxon is filed under.","type":"string"},"description":{"description":"Description is the one line shown beneath the name in the catalogue and nav.","type":"string"},"href":{"description":"Href is the absolute URL an external product launches, for the taxa that\ngenuinely live at their own domain. Empty for an in-console product.","type":"string"},"icon":{"description":"Icon names the icon the surface renders, e.g. \"Database\". It is a NAME, not\nan image: which icon set draws it is the rendering surface's business.","type":"string"},"id":{"description":"ID is the stable slug this taxon is addressed by, e.g. \"vector\".","type":"string"},"name":{"description":"Name is the display name, e.g. \"Vector\".","type":"string"},"order":{"description":"Order is where the taxon sits within its category, ascending.","type":"integer"},"owner":{"description":"Owner is the org this product belongs to: the platform's own org for one\nevery tenant sees, or your org for one you added. Where two rows share an id,\nyours is the one served.","type":"string"},"published":{"description":"Published is whether the taxon is shown. An unpublished taxon is served only\nto an editor, so a product can be staged before anyone sees it.","type":"boolean"},"route":{"description":"Route is the in-console path this taxon opens, e.g. \"/vector\". Set for a\nproduct the console renders itself; empty for an external one.","type":"string"},"tags":{"description":"Tags are free-form labels for search and grouping across categories.","items":{"type":"string"},"type":"array"}},"type":"object"},"Taxonomy":{"properties":{"categories":{"description":"Categories are the groupings, in display order, each with its own taxa.","items":{"$ref":"#/components/schemas/Category"},"type":"array"}},"type":"object"},"Tier":{"properties":{"balance":{"$ref":"#/components/schemas/TierBalance"},"tier":{"$ref":"#/components/schemas/TierLimits"},"user":{"type":"string"},"windows":{"items":{"$ref":"#/components/schemas/Window"},"type":"array"}},"type":"object"},"TierBalance":{"properties":{"creditsRemaining":{"type":"integer"},"currency":{"type":"string"},"dailyRemaining":{"type":"integer"},"effectiveAvailable":{"type":"integer"},"prepaidAvailable":{"type":"integer"}},"type":"object"},"TierLimits":{"properties":{"allowedModels":{"items":{"type":"string"},"type":"array"},"dailyCreditsCents":{"type":"integer"},"displayName":{"type":"string"},"maxAgents":{"type":"integer"},"name":{"type":"string"},"unlimitedAgents":{"description":"UnlimitedAgents reports that MaxAgents 0 means \"no ceiling\" rather than\n\"no agents\" — the reading a bare zero cannot carry.","type":"boolean"}},"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 client; 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"},"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:<addr>\" 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"},"Transaction":{"properties":{"amount":{"type":"integer"},"createdAt":{"type":"string"},"currency":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"metadata":{},"notes":{"type":"string"},"tags":{"type":"string"},"type":{"type":"string"}},"type":"object"},"Transactions":{"properties":{"count":{"type":"integer"},"transactions":{"items":{"$ref":"#/components/schemas/Transaction"},"type":"array"},"user":{"type":"string"}},"type":"object"},"TransitionResult":{"properties":{"distribution":{"$ref":"#/components/schemas/PublishResult","description":"Distribution is the channel fan-out this move triggered. Present ONLY on the\nmove to published, the single edge that distributes — so its absence means\nno fan-out was attempted, never that one failed quietly. A fan-out that DID\nfail is present carrying its own honest status, because distribution never\nrolls the status change back."},"doctype":{"description":"DocType is the content type that moved — Campaign, SocialPost or Asset —\nechoed from the path.","type":"string"},"from":{"description":"From is the state the item held when it was read. A document carrying no\nstatus yet reads as \"draft\".","type":"string"},"name":{"description":"Name is the document that moved, echoed from the path.","type":"string"},"storefront":{"$ref":"#/components/schemas/StorefrontResult","description":"Storefront is the catalog side effect, present only when a published Asset\nwas product imagery — it carries a design and a kind of ecom, product or\nlifestyle. Absent for everything else, so absence reads as \"not catalog\nimagery\" rather than \"the catalog failed\"."},"to":{"description":"To is the state it holds now. From == To on an idempotent re-transition,\nwhich is legal and is where a caller that lost a publish race lands.","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 > 0: at least some payout is backable","type":"boolean"}},"type":"object"},"TreeEntry":{"properties":{"lang":{"description":"Lang is the language the indexer parsed the file as (\"go\", \"python\", …), or\nempty when it recognised none — in which case Symbols is 0 because nothing was\nextracted, not because the file declares nothing.","type":"string"},"path":{"description":"Path is the file, relative to the repo root. The list is ordered by it, so a\nreader can see module layout without sorting.","type":"string"},"symbols":{"description":"Symbols is how many top-level declarations the file defines. A file with none\nis still listed: the file set is the authority here and the counts decorate it.","type":"integer"}},"type":"object"},"Trial":{"properties":{"createdAt":{"description":"when it started assigning","type":"string"},"createdBy":{"description":"the credential that registered it","type":"string"},"decidedAt":{"description":"when the promotion took effect","type":"string"},"decidedBy":{"description":"the credential that promoted the winner","type":"string"},"exposureEvent":{"description":"the event that enrols a subject — the analysis denominator","type":"string"},"flagKey":{"description":"the assignment flag this experiment drives","type":"string"},"id":{"description":"the experiment's slug, unique within the project","type":"string"},"metricEvent":{"description":"the event that counts as a conversion — the numerator","type":"string"},"name":{"description":"free text for a reader","type":"string"},"project":{"description":"the sub-scope within the org, stamped from the principal","type":"string"},"status":{"description":"running while it assigns and measures, decided once a winner is promoted","type":"string"},"subjectKind":{"description":"the unit assigned and measured: user, org, session or audience","type":"string"},"variants":{"description":"the arms, weighted, one of them the control","items":{"$ref":"#/components/schemas/Arm"},"type":"array"},"winner":{"description":"the arm promoted to the whole rollout","type":"string"}},"type":"object"},"TrialBalance":{"properties":{"balanced":{"description":"Balanced is the proof this report exists to give: whether total debits equal\ntotal credits. It is computed from the rows above, never assumed, and false\nmeans the ledger itself is broken rather than that the report is wrong.","type":"boolean"},"from":{"description":"From is the posting time the window opens at, as it was asked for. Absent\nmeans the report runs from the beginning of the ledger.","type":"string"},"rows":{"description":"Rows are the accounts that MOVED in one of the windows. An account that never\nmoved is omitted rather than listed at zero, so this is shorter than the chart.","items":{"$ref":"#/components/schemas/TrialBalanceRow"},"type":"array"},"to":{"description":"To is the posting time the window closes at, inclusive. Absent means \"up to\nnow\" — every posting the ledger holds.","type":"string"},"totalCredit":{"description":"TotalCredit is the sum of every row's closing credit column, in cents.","type":"integer"},"totalDebit":{"description":"TotalDebit is the sum of every row's CLOSING debit column, in cents.","type":"integer"}},"type":"object"},"TrialBalanceRow":{"properties":{"account":{"description":"Account is the chart-of-accounts NUMBER this line reports on (\"1000\", \"4000\")\n— the stable posting key, not a display label.","type":"string"},"closingCredit":{"description":"ClosingCredit is that closing balance in cents when it is a credit balance.","type":"integer"},"closingDebit":{"description":"ClosingDebit is the balance at the end of the window, in cents, when it is a\ndebit balance. This is the column the report's totals are summed from.","type":"integer"},"credit":{"description":"Credit is the same window movement in cents when it was net credit.","type":"integer"},"debit":{"description":"Debit is the account's MOVEMENT within the window — closing minus opening, not\nthe closing balance — in cents, when that movement was net debit. Zero when the\naccount moved net credit.","type":"integer"},"name":{"description":"Name is that account's human name from the fixed chart.","type":"string"},"openingCredit":{"description":"OpeningCredit is the same opening balance in cents when it fell on the credit\nside. Zero when the balance was a debit one.","type":"integer"},"openingDebit":{"description":"OpeningDebit is the account's balance before the window began, in whole cents,\nwhen that balance was on the debit side. Zero when the balance was a credit\none — the pair is exclusive, never two halves of one number.","type":"integer"},"type":{"description":"Type is the account's fundamental class — asset, liability, income, expense or\nequity — which is also its normal balance side. It is carried for presentation\nand does NOT decide which column an amount lands in: placement follows the\nsign of the real net, so a contra balance shows up as one.","type":"string"}},"type":"object"},"Txn":{"properties":{"amountCents":{"description":"AmountCents is the voucher's total, in whole cents — its total debit, which\nequals its total credit because every voucher balances. It is the size of the\nentry and carries no direction; the category says which way it went.","type":"integer"},"category":{"description":"Category is the chart-of-accounts NUMBER of the income or expense account this\nvoucher touched — where it lands on the P&L, not a free-text label.","type":"string"},"categoryName":{"description":"CategoryName is that account's human name, so a caller need not carry the\nchart to render the row.","type":"string"},"date":{"description":"Date is when the voucher POSTED — the accounting date the reports window on,\nwhich for an imported bank row is the bank's date and not the day it landed\nhere.","type":"string"},"description":{"description":"Description is the line a person reads: the memo carried in from the source.","type":"string"},"source":{"description":"Source is where the entry came from: bank_txn for an imported statement line,\nscan for a receipt or bill read by the scanner, commerce_txn for a sale\nbooked by the store.","type":"string"},"vendor":{"description":"Vendor is the counterparty, resolved from whatever the source knew — a bank\nrow's merchant, a scanned bill's supplier. Absent when the source named none.","type":"string"},"voucherId":{"description":"VoucherID identifies the underlying double-entry voucher, so a caller can open\nthe full set of legs behind this single register line.","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":{"description":"Address is the recipient now opted out, normalized (lower-cased, trimmed)\nto the form the send gate matches on — so it can differ in case from the\naddress the link carried.","type":"string"},"channel":{"description":"Channel is the ONE surface opted out of: email, sms, social, meta, google\nor tiktok. The other channels are untouched, and so is this address in\nevery other org.","type":"string"},"unsubscribed":{"description":"Unsubscribed is always true here: the opt-out is idempotent, so a second\nclick on the same link confirms the same thing rather than reporting\nnothing changed. A refused token never reaches this shape — it is a 403.","type":"boolean"}},"type":"object"},"Upkeep":{"properties":{"atLeast":{"description":"AtLeast reports that some obligation is a minimum, so YearlyCents is a floor\nrather than a final figure.","type":"boolean"},"currency":{"description":"Currency is the ISO code every amount here is denominated in.","type":"string"},"jurisdiction":{"description":"Jurisdiction is the state whose obligations these are.","type":"string"},"obligations":{"description":"Obligations are the recurring charges, in the order a reader should see them.","items":{"$ref":"#/components/schemas/Obligation"},"type":"array"},"structure":{"description":"Structure is the entity this prices.","type":"string"},"yearlyCents":{"description":"YearlyCents is what the entity owes every year, all obligations summed.","type":"integer"}},"type":"object"},"Upstream":{"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"},"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"},"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":{"description":"Flags are the findings behind the severity, in detection order:\nfloating-declared, floating-running, stale, un-rolled, then the\nrelease-artifact ones. Always present — `[]` for a row that runs what it\ndeclares, never null.","items":{"$ref":"#/components/schemas/DriftFlag"},"type":"array"},"severity":{"description":"Severity is the roll-up over Flags — red if any flag is red, else yellow if\nany is yellow, else ok. It is the column a board sorts and filters on, and\n\"ok\" is exactly what no flags means.","type":"string"}},"type":"object"},"VersionMeta":{"properties":{"brand":{"description":"Brand is the white-label key this revision was authored under; empty is the\nshared base playbook. Revisions of two brands never share a number line.","type":"string"},"updatedAt":{"description":"UpdatedAt is when this revision was written, as Unix seconds — the \"who\nchanged the playbook, and when\" half of the audit trail.","type":"integer"},"version":{"description":"Version is the store's own revision counter for that brand, starting at 1 for\nthe seeded playbook and incrementing on every edit. Nothing is overwritten, so\nthe highest number is the live one and every lower number is still readable.\nIt is not the playbook's authored `version` string.","type":"integer"}},"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":{"description":"Address is the on-chain address. For kms/mpc/treasury it is the EOA a\nsignature from this wallet recovers to; for safe it is the CREATE2 address\nof the Safe CONTRACT, which holds no key — its approvals recover to the MPC\nowner instead. Rotating a kms wallet mints a new key and therefore a NEW\naddress, and funds and approvals at the old one do not follow; mpc,\ntreasury and safe addresses are invariant under rotation.","type":"string"},"agent":{"type":"string"},"chain":{"description":"Chain is the EVM chain the wallet is bound to, CAIP-2 \"eip155:<n>\" or a\nbare decimal chain id. Empty is chain-agnostic: the ring signs an unbound\ndigest, and a Safe falls back to the Hanzo L1 (36963) because a Safe and\nits EIP-712 domain must be chain-bound.","type":"string"},"createdAt":{"description":"CreatedAt is when the wallet was provisioned, Unix seconds. Listings order\nby it, newest first.","type":"integer"},"custody":{"description":"Custody is the backend holding the signing material, fixed at creation:\n\"kms\" (a secp256k1 key sealed under KMS and opened in-process), \"mpc\" or\n\"treasury\" (an m-of-n threshold key on the deployed ring, which differ by\ngovernance and not by signing mechanics), or \"safe\" (a Safe contract owned\nby an MPC key). A kind the deployment has not wired refuses with 503 rather\nthan fabricating a signature.","type":"string"},"financeAccount":{"description":"FinanceAccount is the finance ledger account bound to this wallet — the\nlookup that turns a ledger account back into an on-chain signer. Absent is\nthe normal state and means unbound; the column is NULL until something\nbinds it.","type":"string"},"id":{"description":"ID is the wallet id, minted by the server as \"wal_\" + 24 hex. It is the\nlast segment of the key ref, and it is the LEDGER SUBJECT an x402 payment\ninto this wallet credits — so it names money as well as key material.","type":"string"},"name":{"description":"Name is the display label given at creation. It addresses nothing: the key\nref is derived from the scope and the id, so renaming moves no material.","type":"string"},"org":{"type":"string"},"project":{"type":"string"},"tier":{"description":"Tier is the wallet tier the ring keys its TierPolicy on: hot, warm, cold,\ngas, bridge, contract_admin, validator, quarantine or disaster_recovery.\nIt defaults to hot and is refused at the boundary if it is none of the nine.","type":"string"}},"type":"object"},"WalletAccount":{"properties":{"createdAt":{"description":"CreatedAt is when the account was opened, Unix seconds. Listings order by\nit, newest first.","type":"integer"},"id":{"description":"ID is the account id, minted by the server as \"acct_\" + 24 hex. Wallets\nname it as their accountId, and it becomes a segment of each of their key\nrefs — so it addresses key material and cannot be reassigned.","type":"string"},"name":{"description":"Name is the label given at creation, trimmed and required. It groups\nwallets: it is not a key, holds no balance, and is not unique in the org.","type":"string"},"org":{"description":"Org is the tenant that owns the account, stamped from the validated\nprincipal rather than taken from the request. Every read is physically\nscoped to it, so another tenant's accounts are not reachable at all.","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"},"Window":{"properties":{"limit":{"type":"integer"},"remaining":{"type":"integer"},"resets":{"type":"string"},"span":{"type":"string"},"used":{"type":"integer"}},"type":"object"},"Wire":{"properties":{"action":{"description":"Action is the verb that was performed. It is the event's name, not the HTTP\nmethod — a request-sourced record carries both, and the pair is what makes a\nrow readable (\"grant.create\" at POST /v1/admin/grants).","type":"string"},"authMethod":{"description":"Auth is the credential the actor presented: \"jwt\", \"api-key\", or \"none\".","type":"string"},"email":{"description":"Email is the actor's validated address, absent when the credential carried\nnone. It comes from the verified token, never from a client header.","type":"string"},"hash":{"description":"Hash is this record's SHA-256 over its own canonical JSON with both hash\nfields cleared, folded with prevHash. Recomputing it from the row's other\nfields is what proves the row has not been edited.","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":{"description":"IsAdmin is the VALIDATED platform-SuperAdmin bit at decision time (membership\nof the reserved admin org), never the client's own claim to be one.","type":"boolean"},"method":{"description":"Method is the HTTP verb, on a record a request produced. Absent on an event\nemitted from inside the binary with no request behind it.","type":"string"},"org":{"description":"Org is the tenant the action was taken IN — the effective org, which for\neveryone but an impersonating SuperAdmin is also the actor's own. Empty on an\nunauthenticated request.","type":"string"},"path":{"description":"Path is the request's route. Any segment shaped like a credential is replaced\nbefore the record is written, so a key that rides in a path is not preserved\nhere by the very control meant to watch it.","type":"string"},"prevHash":{"description":"PrevHash is the hash of record seq-1, which is what links the rows into a\nchain: a deleted or reordered record breaks the recomputation at that point.\nThe first record of a chain carries 64 zeros rather than an empty string, so\n\"start of chain\" and \"field missing\" cannot look alike.","type":"string"},"reason":{"description":"Reason is a short explanation for a deny or an error (\"SuperAdmin required\",\n\"insufficient_balance\"). It is never a secret and never a raw upstream error\nbody; absent on a success.","type":"string"},"requestId":{"description":"RequestID ties this row to the request-line log and any downstream trace — the\nX-Request-Id the pipeline minted for that request.","type":"string"},"resource":{"description":"Resource is the KIND of thing acted upon (\"org\", \"role\", \"secret\",\n\"provider-config\", \"credit\"). Where a mutation has no finer semantics than its\nroute, this is the route family and resourceId is empty — the action and the\npath already pin the object.","type":"string"},"resourceId":{"description":"ResourceID is the specific instance, absent when the kind alone identifies it.","type":"string"},"result":{"description":"Result is how the action ended: \"success\", \"deny\" or \"error\". A deny is a\ndecision this binary made and is as much evidence as a success.","type":"string"},"seq":{"description":"Seq is the record's position in the chain, 0-based and gapless. The Recorder\nassigns it under its own lock, so it is a true total order: seq n+1 was\nwritten after seq n, and a missing number is a missing record.","type":"integer"},"sourceIp":{"description":"SourceIP is the client address the edge resolved for the request, after the\nproxy chain — the address a responder would act on, not the socket peer.","type":"string"},"status":{"description":"Status is the HTTP status the caller received. It is the outcome as the client\nsaw it, so a 200 carrying a domain refusal still reads 200 here.","type":"integer"},"sub":{"description":"Sub is the acting user (the IAM subject). Empty for a machine principal or an\nanonymous request, which is how a service action is told from a person's.","type":"string"},"time":{"description":"Time is when the action happened, RFC3339Nano in UTC. The stored column has\nthe same precision and sorts the same way, so a client can range and order on\nthis string verbatim.","type":"string"},"userAgent":{"description":"UserAgent is the client the request announced itself as. Client-supplied, so\nit is evidence about what claimed to act, not proof of it.","type":"string"}},"type":"object"},"WireInstructions":{"properties":{"accountName":{"type":"string"},"accountNumber":{"type":"string"},"bankAddress":{"type":"string"},"bankName":{"type":"string"},"iban":{"type":"string"},"memo":{"type":"string"},"reference":{"type":"string"},"routingNumber":{"type":"string"},"swiftCode":{"type":"string"}},"type":"object"},"WorkerScriptPut":{"properties":{"bindings":{},"compatibilityDate":{"type":"string"},"compatibilityFlags":{"items":{"type":"string"},"type":"array"},"mainModule":{"type":"string"},"script":{"type":"string"}},"type":"object"},"WriteIn":{"properties":{"data":{"contentEncoding":"base64","description":"Data is the file's bytes, and replaces whatever was there.","type":"string"},"id":{"description":"ID is the sandbox to write into, from an earlier lease.","type":"string"},"path":{"description":"Path is confined the same way PathIn.Path is. Missing parent directories are\ncreated.","type":"string"}},"type":"object"},"Wrote":{"properties":{"bytes":{"description":"Bytes is how many bytes the file now holds. A write REPLACES the file, so this\nis its whole length and not an amount appended, and 0 is a legitimate answer:\na WriteIn with no Data truncates the file to nothing.","type":"integer"},"path":{"description":"Path is where the bytes actually landed: the caller's path resolved against\nthe sandbox's working directory (Leased.Workdir), which is what a later read\nor a shell line inside the sandbox has to name.","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"},"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"},"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":{"description":"ID identifies the event, and its shape says which kind it is: a run event\ncarries the run's own id, while an agent event is the agent id suffixed\n\":created\" or \":updated\". Unique within a feed, and not an address — there is\nnothing to fetch it by.","type":"string"},"kind":{"description":"invoked|failed|created|updated (from real events)","type":"string"},"message":{"description":"Message is the line to render, already bounded: \"Invoked <model>\" for a run\nthat worked, the run's own error truncated to 200 characters for one that did\nnot (or \"Run failed\" when it said nothing), and a fixed phrase for the two\nagent events. Nothing here is invented — every event is a row that exists.","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"},"admission":{"properties":{"benchmarks":{"description":"Benchmarks are the catalog ids admitted.","items":{"type":"string"},"type":"array"},"endpoint":{"description":"Endpoint is the caller's own endpoint the run targets.","type":"string"},"model":{"description":"Model is the catalog model the run targets.","type":"string"},"note":{"description":"Note explains what admission does and does not promise.","type":"string"},"status":{"description":"Status is \"queued\": the run is admitted, not finished.","type":"string"}},"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"},"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"},"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":{"description":"AgentName is the cloud Agent (/v1/agents) this machine runs — the agent a\nmessage to the bot is actually run against. It is the one field that decides\nwhat the bot DOES.","type":"string"},"botVersion":{"description":"BotVersion pins the @hanzo/bot runtime version the machine runs. Empty means\nthe machine took the default in force when it was bound.","type":"string"},"createdTime":{"description":"CreatedTime is when the binding was first made.","type":"string"},"machineId":{"description":"MachineId is the bound machine as vm addresses it, owner-qualified\n(\"<org>/<machine>\"). The unqualified half is what this surface's :id routes\ntake.","type":"string"},"message":{"description":"Message is vm's human-readable detail on Status (\"machine provisioning;\n@hanzo/bot runtime not yet confirmed\") — the reason behind the state, not a\nsecond state.","type":"string"},"name":{"description":"Name is the binding's own key, which is the machine's id: a machine hosts at\nmost one agent, so the binding is named for it. This is the key a bots list\njoins bindings onto machines by.","type":"string"},"org":{"description":"Org is the Hanzo tenant the binding belongs to.","type":"string"},"owner":{"description":"Owner is the tenant vm filed the binding under, resolved from the ?owner it\nwas called with — which is the caller's validated org and never a body field.","type":"string"},"provider":{"description":"Provider is the cloud the bound machine runs on, carried here so a bindings\nlist says where each bot lives without a second read per machine.","type":"string"},"publicIp":{"description":"PublicIp is the bound machine's public address as vm recorded it on the\nbinding. Empty while the machine has none yet.","type":"string"},"status":{"description":"Status is the binding's lifecycle in VM's OWN words — \"Pending\" while the\nmachine provisions and the runtime is unconfirmed, \"running\" once vm has\nconfirmed it. The vocabulary is vm's and passes through unmapped, which is\nwhy its capitalization does not match the machine states beside it, and it is\nvm's reconciled reading rather than anything asserted here.","type":"string"},"updatedTime":{"description":"UpdatedTime is when vm last reconciled it — the age of Status.","type":"string"}},"type":"object"},"agentDetail":{"properties":{"computeRef":{"type":"string"},"createdAt":{"type":"string"},"description":{"type":"string"},"executionMode":{"type":"string"},"id":{"type":"string"},"instructions":{"description":"Instructions is the agent's system prompt, verbatim, up to 32 KiB. It is the\none field the list read withholds, because it is the agent's whole behaviour\nand a page of them would be a page of prompts.","type":"string"},"model":{"type":"string"},"name":{"type":"string"},"recentRuns":{"description":"RecentRuns is the agent's 20 most recent executions, newest first. It is a\nwindow on the history, not the history: the count beside it is `runs`.","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":{"description":"Actor is the \"org/sub\" identity the run was executed and billed AS. Empty\nmeans there was no PERSON — a schedule or a service token — which is a\ndifferent fact from \"we do not know\", and the difference is what an audit\nasks about.","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":{"description":"CompletionTokens is the same measurement for what the model produced, on the\nsame final completion. It is a count of TOKENS, not of turns and not of money.","type":"integer"},"createdAt":{"description":"CreatedAt is when the run finished, RFC 3339 in UTC to the second — the\nduration above already says how long it had been going.","type":"string"},"durationMs":{"description":"DurationMs is wall-clock milliseconds around the completion, including a\nfailover's retries. It is time SPENT, not time billed.","type":"integer"},"error":{"description":"Error is why an \"ok\"-less run failed, as the failing call reported it. Empty\non every successful run.","type":"string"},"id":{"description":"ID is the run's handle, minted as \"run_\" + 32 hex characters. It is the key\nthe metering ledger records this run's per-round token spend under, so it is\nhow a bill and a run are joined.","type":"string"},"input":{"description":"Input is the text the run was given, verbatim.","type":"string"},"model":{"description":"Model is the model that actually SERVED this run, which is not always the one\nthe agent is defined on — a failover records what answered. Normalized to our\nname on the way out; the stored row is left exactly as it happened, because a\nrun is a record and rewriting it would be worse than the name it carries.","type":"string"},"output":{"description":"Output is what the model produced. Empty on an error run, and empty is also a\nlegitimate answer from a run that succeeded with nothing to say — Status is\nwhat separates those.","type":"string"},"promptTokens":{"description":"PromptTokens is what the gateway reported for the run's FINAL completion, and\nonly that one — a tool loop's earlier rounds are the metering ledger's account,\njoined by this run's id. Reading it as the run's total spend undercounts a\nloop.","type":"integer"},"status":{"description":"Status is the run's outcome, and there are exactly two: \"ok\" when the model\nanswered, \"error\" when it did not. It is written when the run ends, so no row\nhere is in flight.","type":"string"},"toolCalls":{"description":"ToolCalls is how many tool dispatches the run made — a count of ACTIONS, which\nis a different measurement from the token counts above and from the turns a\nbuild reports. Zero is a run that answered straight from the model.","type":"integer"},"traceId":{"description":"TraceID is the trace this run IS, so the record and its spans are one thing to\nmove between: it opens the waterfall for THIS run rather than a search that\nlands near it. Empty when the process had no tracer, never a fabricated id.","type":"string"}},"type":"object"},"agentView":{"properties":{"computeRef":{"description":"ComputeRef is the visor machine this bot is bound to, opaque here: this\npackage stores and echoes it, and the binding's lifecycle belongs elsewhere.\nEmpty means unbound, which is what every one-shot agent is.","type":"string"},"createdAt":{"description":"CreatedAt is when the agent was defined, RFC 3339 in UTC to the second.","type":"string"},"description":{"description":"Description is the one line another agent reads when deciding whether to call\nthis one: the tool catalogue publishes it as the description of `agent_<name>`,\nfalling back to \"agent <name>\" when it is empty. It is not part of the prompt —\nInstructions is — so writing the behaviour here reaches the caller and not the\nmodel.","type":"string"},"executionMode":{"description":"ExecutionMode is one-shot or long-running, and it decides who may start this\nagent. one-shot runs only when something POSTs to it; long-running is\nadditionally invoked by the scheduler on Schedule, once a minute against the\ncron. An org's long-running agents are capped, so a switch INTO it can be\nrefused with 409.","type":"string"},"id":{"description":"ID is the agent's stable handle, minted here as \"agent_\" + 32 hex characters\nof crypto/rand. A caller cannot choose it, and it never changes — unlike Name,\nwhich is the other way to address the same agent.","type":"string"},"model":{"description":"Model is the Zen model this agent runs on, and it is always OUR name for it:\nwrites normalize through cloud.ZenModel and the read normalizes again, so an\nupstream family name never leaves here even from a row written before that\nrule existed. A create that named none took the deployment's configured\ndefault, so this is where a caller learns which model it actually got.","type":"string"},"name":{"description":"Name is the agent's org-unique handle, matching\n^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. It addresses the agent everywhere ID does,\nit is what a run row records, and it is the suffix of the `agent_<name>` tool\nother agents call this one by. Set once at create; no update route moves it,\nbecause moving it would orphan that history.","type":"string"},"runs":{"description":"Runs is how many executions the org has recorded against this agent, counted\nat read time. The list and update reads count the WHOLE history; the detail\nread reports the size of the RecentRuns page it carries, which stops at 20 —\nso a detail row saying 20 means \"at least 20\", not \"exactly 20\".","type":"integer"},"schedule":{"description":"Schedule is the 5-field cron the scheduler fires a long-running agent on,\nevaluated once a minute. Required for long-running and DROPPED for one-shot —\na one-shot agent's schedule is not stored, so absence here is the mode's\nanswer rather than a value nobody set.","type":"string"},"serviceAccountId":{"description":"ServiceAccountID is the IAM agent service account (<org>-<agent>) a scheduled\nrun is billed AS. It is what makes an autonomous run attributable to a\nprincipal rather than only to the org; empty means the org itself wears the\nspend.","type":"string"},"status":{"description":"Status is the agent's readiness, and today it is \"ready\" on every row: an\nagent is a definition rather than a provisioned thing, so nothing transitions\nit. Server-set at create; no route accepts it.","type":"string"},"tools":{"description":"Tools are the tool names this agent may call, and the list IS the authority:\nan agent that declares none gets none. The single entry \"*\" means whatever the\nfleet's MCP server serves at the moment of the run, resolved per run rather\nthan frozen here, which is how the default assistant reaches subsystems that\nshipped after it was defined. Empty array, never null.","items":{"type":"string"},"type":"array"},"updatedAt":{"description":"UpdatedAt is the last time any field above was written, same format. It moves\non an update to the DEFINITION and never on a run, so a busy agent nobody has\nedited keeps an old one.","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 MCP server rather than behind a sibling this process only knows the\nname of.","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 MCP server carries: its own typed-op\nregistry, projected. It is the only number a subsystem can state honestly —\nwhat the FLEET's server 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"},"allowlistPutIn":{"properties":{"accessGroups":{"additionalProperties":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},"description":"AccessGroups REPLACES the org's named access groups, as\ngroup name -> channel -> 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 -> channel ->\nmember entries, held once for the whole org. A DM or Group entry written\n`accessGroup:<name>` 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:<name>` 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"},"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"},"analyzeQuery":{"properties":{"alpha":{"description":"Alpha overrides the 0.05 two-tailed significance threshold when it lies\nstrictly between 0 and 1; anything else leaves the default in place.","type":"number"},"days":{"description":"Days is how far back to read when no start is given: 1 to 365, 30 by\ndefault. A value outside that range leaves the default in place.","type":"integer"},"end":{"description":"End is the window's exclusive end in RFC3339, defaulting to now.","type":"string"},"id":{"description":"ID is the experiment the URL names.","type":"string"},"start":{"description":"Start is the window's inclusive start in RFC3339. Given, it wins over days.","type":"string"}},"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"},"limit":{"description":"Limit is what this key may reach, as `kind:name` entries — `model:zen5`,\n`project:acme`, `product:commerce`. Absent means the key reaches whatever\nits holder does, which is what every key minted before limits existed does\nand must keep doing.","items":{"type":"string"},"type":"array"},"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":{"description":"BuildType is how a git app builds: `pack`, the zero-config default that\ndetects the project, or `dockerfile`. An image app carries `image`, which\nmeans it never builds.","type":"string"},"createdAt":{"description":"CreatedAt is when the app was created, unix seconds.","type":"integer"},"currentDeploymentId":{"description":"CurrentDeploymentID is the deployment that is live — the pointer a deploy\nadvances monotonically by version, so it never regresses to an older one.\nEmpty until the first deploy reaches the cluster.","type":"string"},"description":{"description":"Description is free text about what the app is. Nothing derives from it.","type":"string"},"dockerfile":{"description":"Dockerfile is the path inside the repo to build from, for buildType\n`dockerfile`. The build path keys off its presence, and it is validated at\ncreate against the same allowlist the privileged build enforces.","type":"string"},"domains":{"description":"Domains are the ingress hosts rendered into the app's CR, its own\n`<slug>.<org>.<sites host>` first. That one is seeded at create and cannot be\nremoved; a custom host joins only after add-domain and DNS verification.","items":{"type":"string"},"type":"array"},"env":{"description":"Env is the app's environment variables, with every SECRET value masked to \"\"\n— the plaintext is in KMS and this surface never echoes it. That masking is\nwhy an empty secret value means \"keep what is sealed\" when posted back.","items":{"$ref":"#/components/schemas/EnvVarJSON"},"type":"array"},"environment":{"description":"Environment is the deploy target this app names, `production` when none was\ngiven. It is a LABEL: /v1/platform/environments derives the environment list\nfrom the apps that name one, so an environment exists as long as an app\npoints at it and no route creates or deletes one.","type":"string"},"health":{"description":"Health rolls ready-vs-desired replicas up to a colour: green (all ready),\nyellow (some ready, or deliberately scaled to zero), red (none), or \"\" when\nthe cluster reports no replica counts at all — unknown, never a guessed green.","type":"string"},"id":{"description":"ID is the server-minted application id (`app_…`). Routes address an app by\nproject and slug; this is the key its deployments and builds carry.","type":"string"},"image":{"$ref":"#/components/schemas/imageView","description":"Image is the image a source `image` app runs. For a git app only the tag is\nfilled, stamped by the deploy that went live; the built ref is on the\ndeployment."},"name":{"description":"Name is the display name. It is not an address — the slug is.","type":"string"},"namespace":{"description":"Namespace is where the app's cluster objects live, `tenant-<org>`. It is\nderived from the validated org and is never accepted from a request.","type":"string"},"org":{"description":"Org is the tenant that owns the app. It comes from the validated identity,\nnever from the request, and it is the boundary every route is scoped to.","type":"string"},"phase":{"description":"Phase is the operator's own `status.phase` for the app's Service CR, read\nfrom the cluster on this request. Empty when there is no CR yet or the\ncluster could not be read.","type":"string"},"port":{"description":"Port is the container port traffic is sent to. 8080 when the create asked\nfor none, or for one outside 1–65535.","type":"integer"},"projectId":{"description":"ProjectID is the IAM project the app lives under, and it is that project's\nNAME — the (org,name) key IAM identifies it by, which is also what the\n`:project` path segment carries. There is no platform-minted project id.","type":"string"},"replicas":{"description":"Replicas is how many copies the CR declares. It is CLAMPED to the\ndeployment's ceiling rather than refused, so it can be below what was asked.","type":"integer"},"repo":{"$ref":"#/components/schemas/gitSource","description":"Repo is the git origin a source `git` app builds from, and the repo+branch a\nlanded push has to match to build it."},"secretSync":{"description":"SecretSync is how far the app's secret env has got into the cluster:\n\"\"|pending|syncing|ready|failed (secrets.go). It is best-effort and never\nfails a deploy, so `pending` is ordinary right after one.","type":"string"},"secretSyncDetail":{"description":"SecretSyncDetail is the honest reason when the sync is not ready — a missing\nCRD, an RBAC grant, a per-tenant credential. Empty when it is.","type":"string"},"slug":{"description":"Slug is the app's identity in the cluster: the operator CR's name, the first\nlabel of its default host, and the `:app` path segment. Unique per project.","type":"string"},"source":{"description":"Source is what the app deploys FROM: `git`, which builds Repo, or `image`,\nwhich runs Image as it is. It decides whether a deploy builds at all.","type":"string"},"status":{"description":"Status is the lifecycle THIS store records: draft (created, nothing in the\ncluster yet), building, deploying, live, stopped or error. What the cluster\nitself says is Phase and Health.","type":"string"},"storageGb":{"description":"StorageGB is the persistent volume size in GiB. Absent means stateless —\nno volume at all — and it is clamped like Replicas.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when it last changed, unix seconds. Every lifecycle transition\nmoves it, so it tracks deploys as well as edits.","type":"integer"}},"type":"object"},"appearance":{"properties":{"accent":{"description":"Accent is the one hue — a CSS colour token (a hex, or a bounded functional\ncolour like rgb()/oklch()). Anything else is dropped rather than stored.","type":"string"},"density":{"description":"Density is the spacing step: \"compact\", \"default\" or \"comfortable\".","type":"string"},"type":{"description":"Type is the text-size multiplier, clamped to the ramp window [0.85, 1.4].\nAbsent (0) leaves the published default.","type":"number"}},"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"},"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"},"argoApp":{"properties":{"apiVersion":{"description":"APIVersion is the constant \"argoproj.io/v1alpha1\" — the shape, not the source.\nThese are projections of operator App CRs and Hanzo CD Applications; no\nargoproj.io object is stored anywhere behind this plane.","type":"string"},"kind":{"description":"Kind is the constant \"Application\".","type":"string"},"metadata":{"$ref":"#/components/schemas/argoMeta","description":"Metadata is the projected object's identity."},"spec":{"$ref":"#/components/schemas/argoSpec","description":"Spec is the desired state: where it comes from, where it lands, what project\nit belongs to."},"status":{"$ref":"#/components/schemas/argoStatus","description":"Status is what was observed: the sync verdict, the health, and the owned\nobjects when this is a detail read."}},"type":"object"},"argoAppList":{"properties":{"apiVersion":{"description":"APIVersion is the constant \"argoproj.io/v1alpha1\".","type":"string"},"items":{"description":"Items is one entry per operator App CR the caller may see — its own org's, or\nevery platform namespace's for a SuperAdmin — followed, for a SuperAdmin\nonly, by every Hanzo CD Application in the cluster. Empty (never null) rather\nthan absent when the caller owns nothing.","items":{"$ref":"#/components/schemas/argoApp"},"type":"array"},"kind":{"description":"Kind is the constant \"ApplicationList\".","type":"string"},"metadata":{"$ref":"#/components/schemas/argoListMeta","description":"Metadata is the list envelope the SPA expects; it carries no resume point."}},"type":"object"},"argoCluster":{"properties":{"connectionState":{"$ref":"#/components/schemas/argoConnectionState","description":"ConnectionState is whether the destination is reachable."},"info":{"$ref":"#/components/schemas/argoClusterInfo","description":"Info is the connection state again plus the count of applications targeting\nthis destination."},"name":{"description":"Name is what the Destination column shows: \"in-cluster\" for this cluster,\notherwise whatever spec.destination.name declares, falling back to the server\nURL when it declares none.","type":"string"},"server":{"description":"Server is the destination's API URL, and the key the list is deduplicated by.\nhttps://kubernetes.default.svc is this cluster.","type":"string"}},"type":"object"},"argoClusterInfo":{"properties":{"applicationsCount":{"description":"ApplicationsCount is how many of THE CALLER'S applications reconcile into\nthis cluster, so a tenant sees its own count and a SuperAdmin the fleet's. It\nis zero for the in-cluster destination when the caller owns nothing, since\nthat destination is listed whether or not anything targets it.","type":"integer"},"connectionState":{"$ref":"#/components/schemas/argoConnectionState","description":"ConnectionState repeats the cluster's own connection state, which is where\nArgoCD's UI reads it from on this object."},"serverVersion":{"description":"ServerVersion is the kubernetes version of the destination. Always absent:\nnothing here queries the API server for it.","type":"string"}},"type":"object"},"argoClusterList":{"properties":{"items":{"description":"Items is one entry per distinct destination server, in first-seen order with\nthe in-cluster destination first. Never empty: an empty fleet still has the\none cluster it would deploy into.","items":{"$ref":"#/components/schemas/argoCluster"},"type":"array"},"metadata":{"$ref":"#/components/schemas/argoListMeta","description":"Metadata is the list envelope the SPA expects; it carries no resume point."}},"type":"object"},"argoConnectionState":{"properties":{"attemptedAt":{"description":"AttemptedAt is when the connection was last probed. Always absent: nothing is\nprobed, and a fabricated timestamp would claim a check that never ran.","type":"string"},"message":{"description":"Message is why a connection failed. Always absent, since none does.","type":"string"},"status":{"description":"Status is ArgoCD's ConnectionStatus — Successful, Failed or Unknown. Always\nSuccessful here: the destination is the cluster this process is already\nrunning in, so it is reachable by construction and there is no credential to\nprobe.","type":"string"}},"type":"object"},"argoDestination":{"properties":{"name":{"description":"ArgoCD allows a destination by cluster name; omitted for the in-cluster projection.","type":"string"},"namespace":{"description":"Namespace is where in that cluster the workload lands. \"*\" on a project's\ndestination fence means any namespace.","type":"string"},"server":{"description":"Server is the cluster API URL the application reconciles into. Everything\nthis plane projects lands in the cluster it runs in, so it is\nhttps://kubernetes.default.svc — except on a project's destination fence,\nwhere \"*\" means any cluster.","type":"string"}},"type":"object"},"argoGroupKind":{"properties":{"group":{"description":"Group is the API group a project admits, \"*\" for any. Empty names the core\ngroup.","type":"string"},"kind":{"description":"Kind is the kind it admits, \"*\" for any.","type":"string"}},"type":"object"},"argoHealth":{"properties":{"message":{"description":"Message is why the status is what it is — \"Running: no replicas ready\",\n\"iam: CrashLoopBackOff\". A Healthy object carries one too (\"Running: all\nreplicas ready\"), so this is not a failure signal. Always absent on a CD row,\nwhich reports no health message.","type":"string"},"status":{"description":"Status is the ArgoCD health vocabulary, Capitalized: Healthy, Progressing,\nDegraded, Suspended, Missing or Unknown. For an App CR it is derived per\nobject from what the operator reconciled (a workload with every replica ready\nis Healthy, one scaled to zero is Suspended, a crash-looping pod is\nDegraded); for a CD row it is the verdict CD wrote.","type":"string"}},"type":"object"},"argoInfoItem":{"properties":{"name":{"description":"Name is the chip's label. The only one this projection produces is\n\"Image Tag\".","type":"string"},"value":{"description":"Value is the chip's value — for \"Image Tag\", the tag the node runs.","type":"string"}},"type":"object"},"argoListMeta":{"properties":{"resourceVersion":{"description":"ResourceVersion is the k8s list version a watch would resume from. Always\nempty: every list on this plane is COMPUTED per request rather than read from\none etcd revision, so there is no point to resume from. The live view is the\nSSE stream, not a resumed watch.","type":"string"}},"type":"object"},"argoMeta":{"properties":{"creationTimestamp":{"description":"CreationTimestamp is when the source object was created, RFC 3339 to the\nsecond. Empty for a synthesized project.","type":"string"},"labels":{"additionalProperties":{"type":"string"},"description":"Labels are the labels this projection puts on the row, not the source\nobject's full label set. An application projected from an App CR carries\nhanzo.ai/instance (its name), hanzo.ai/env (main, test or dev, from the\nnamespace it was read from) and hanzo.ai/org when the CR declares a tenant. A\nHanzo CD Application carries the CR's own labels verbatim. A project\nreflected from IAM carries hanzo.ai/org alone.","type":"object"},"name":{"description":"Name is the projected object's name: the App CR's metadata.name for an\napplication, the CD Application's name for a CD row, and the IAM project name\nfor a project.","type":"string"},"namespace":{"description":"Namespace is the namespace the source object was read from — the tenant or\nplatform namespace for an App CR, CD's controller namespace for a CD row.\nEmpty for a project synthesized here, which lives in no namespace.","type":"string"},"uid":{"description":"UID is the k8s metadata.uid of the source object, which is what the SPA keys\na row on across refreshes. Empty for a synthesized project — there is no\nobject to take one from.","type":"string"}},"type":"object"},"argoNode":{"properties":{"createdAt":{"description":"CreatedAt is the object's creationTimestamp, RFC 3339 UTC to the second.\nAbsent when the object carries none.","type":"string"},"group":{"type":"string"},"health":{"$ref":"#/components/schemas/argoHealth","description":"Health is the node's own derived health. Always present on a node of this\ntree; a kind with no health signal of its own reports Healthy, since a\nConfigMap existing IS its healthy state."},"images":{"description":"Images are the container images running on this node. Always absent — the tag\ntravels as the \"Image Tag\" chip in Info instead, which is where the SPA reads\nit on a node.","items":{"type":"string"},"type":"array"},"info":{"description":"Info are the chips shown on the node. At most one: the image tag — the\nRUNNING tag on a Deployment, ReplicaSet or Pod, and the DECLARED tag on the\nApp CR at the root. Absent on a node that carries no image at all.","items":{"$ref":"#/components/schemas/argoInfoItem"},"type":"array"},"kind":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"parentRefs":{"description":"ParentRefs are the node's edges UPWARD, which is how the SPA draws the DAG\nfrom this flat list. Exactly one entry where present: a depth-1 object points\nat the App CR, a ReplicaSet at its Deployment, a Pod at its ReplicaSet (or at\nthe Deployment whose selector matches it, when the ReplicaSet is gone).\nAbsent on the root.","items":{"$ref":"#/components/schemas/argoResourceRef"},"type":"array"},"resourceVersion":{"description":"ResourceVersion is the k8s version a watch would resume from. Always empty:\nthe tree is rebuilt from live reads on every request, including on every\nframe of the SSE stream, so there is no revision to resume from.","type":"string"},"uid":{"type":"string"},"version":{"type":"string"}},"type":"object"},"argoProject":{"properties":{"apiVersion":{"description":"APIVersion is the constant \"argoproj.io/v1alpha1\". A project here is an IAM\nresource wearing that shape; no argoproj.io object is stored behind it.","type":"string"},"kind":{"description":"Kind is the constant \"AppProject\".","type":"string"},"metadata":{"$ref":"#/components/schemas/argoMeta","description":"Metadata is the project's identity: its name is the key an application's\nspec.project matches, and is the same string an App CR carries in its\napp.kubernetes.io/part-of label."},"spec":{"$ref":"#/components/schemas/argoProjectSpec","description":"Spec is the fence the SPA displays — repos, destinations, admitted kinds."},"status":{"$ref":"#/components/schemas/argoProjectStat","description":"Status is always the empty object. A project has no reconciled state here;\nthe field exists because the SPA reads it."}},"type":"object"},"argoProjectList":{"properties":{"items":{"description":"Items is the projects visible to the caller — its own organization's, or\nevery organization's for a SuperAdmin. A project named \"default\" is always\npresent and is prepended when IAM does not carry one, because that is what an\napplication with no project label groups under.","items":{"$ref":"#/components/schemas/argoProject"},"type":"array"},"metadata":{"$ref":"#/components/schemas/argoListMeta","description":"Metadata is the list envelope the SPA expects; it carries no resume point."}},"type":"object"},"argoProjectSpec":{"properties":{"clusterResourceWhitelist":{"description":"ClusterResourceWhitelist are the cluster-scoped kinds it may create —\n[{group:\"*\", kind:\"*\"}] on a synthesized project.","items":{"$ref":"#/components/schemas/argoGroupKind"},"type":"array"},"description":{"description":"Description is the project's human label: the IAM project's display name, or\nits description when it has no display name. Absent when IAM carries neither.","type":"string"},"destinations":{"description":"Destinations are the cluster/namespace pairs it may write to — a single\n{server:\"*\", namespace:\"*\"} on a synthesized project, for the same reason.","items":{"$ref":"#/components/schemas/argoDestination"},"type":"array"},"sourceRepos":{"description":"SourceRepos are the git repos applications in this project may pull from.\n[\"*\"] for every project this plane synthesizes or reflects from IAM: the\nboundary that actually holds on this platform is the IAM org, resolved before\na row is ever projected, so the projected fence is deliberately permissive\nand is NOT an authorization statement.","items":{"type":"string"},"type":"array"}},"type":"object"},"argoProjectStat":{"properties":{},"type":"object"},"argoResourceRef":{"properties":{"group":{"description":"Group is the object's API group: empty for the core group (Pod, Service,\nConfigMap), otherwise apps, networking.k8s.io, autoscaling or policy — and\nhanzo.ai for the App CR at the root.","type":"string"},"kind":{"description":"Kind is the object kind. The root is the App CR; below it come Deployment,\nReplicaSet, Pod, Service, Ingress, HorizontalPodAutoscaler,\nPodDisruptionBudget and ConfigMap. Never Secret — the walk does not visit\nthem, so no materialized environment can reach the tree.","type":"string"},"name":{"description":"Name is the object's metadata.name.","type":"string"},"namespace":{"description":"Namespace is the namespace the walk ran in, the same for every node of one\ntree.","type":"string"},"uid":{"description":"UID is the object's metadata.uid. Absent on a PARENT reference, which\naddresses its target by kind and name rather than by identity.","type":"string"},"version":{"description":"Version is the object's API version as the live object reports it: v1 for\nevery kind the walk reaches except the HorizontalPodAutoscaler, which is\nautoscaling/v2.","type":"string"}},"type":"object"},"argoResourceStatus":{"properties":{"group":{"description":"Group is the object's API group: empty for the core group (Pod, Service,\nConfigMap), otherwise apps, networking.k8s.io, autoscaling or policy — and\nhanzo.ai for the App CR itself.","type":"string"},"health":{"$ref":"#/components/schemas/argoHealth","description":"Health is this object's own health, derived from its live state by the same\nrule the resource tree uses."},"kind":{"description":"Kind is the object kind — App, Deployment, ReplicaSet, Pod, Service, Ingress,\nHorizontalPodAutoscaler, PodDisruptionBudget, ConfigMap. Never Secret: the\nwalk that produces these does not visit them.","type":"string"},"name":{"description":"Name is the object's metadata.name.","type":"string"},"namespace":{"description":"Namespace is the namespace the object was found in — the same one for every\nentry of an application, since the walk is confined to it.","type":"string"},"status":{"description":"Status is the APPLICATION's sync verdict repeated on every row, not a\nper-object one. The operator owns these children, so no child has a desired\nstate of its own to compare against.","type":"string"},"version":{"description":"Version is the object's API version as the live object reports it: v1 for\nevery kind here except the HorizontalPodAutoscaler, which is autoscaling/v2.","type":"string"}},"type":"object"},"argoRevisionMetadata":{"properties":{"author":{"description":"Author is the commit author. Always absent: an App CR pins an IMAGE, so this\nprocess has no commit to read one from and will not invent one.","type":"string"},"date":{"description":"Date is when the App CR was created, RFC 3339 UTC — the only real timestamp\nthere is here. It is NOT the date of the revision asked for.","type":"string"},"message":{"description":"Message is the revision asked for, echoed back — not a commit message. The\nempty revision and \"HEAD\" resolve to the image tag the CR declares\n(spec.image.tag), and anything longer than 256 characters is truncated to it.","type":"string"},"signatureInfo":{"description":"SignatureInfo is the GPG verification result for the revision. Always absent:\nnothing here verifies a signature, and an empty field says so rather than\nimplying an unsigned commit.","type":"string"},"tags":{"description":"Tags are the git tags pointing at the revision. Always absent, for the same\nreason as Author.","items":{"type":"string"},"type":"array"}},"type":"object"},"argoSource":{"properties":{"path":{"description":"Path is the directory within RepoURL. Display-only alongside a display-only\nRepoURL; CD's own value for a CD row.","type":"string"},"repoURL":{"description":"RepoURL is the git repository the desired state comes from. For an\napplication projected from an App CR it is the fleet manifest repo and is\nDISPLAY ONLY — an App CR pins an image, and nothing is rendered from this\nrepo to produce it. For a CD row it is the repo CD actually polls.","type":"string"},"targetRevision":{"description":"TargetRevision is the git ref tracked there — a branch such as \"main\".\nDisplay-only for a projected App CR; the ref CD tracks for a CD row.","type":"string"}},"type":"object"},"argoSpec":{"properties":{"destination":{"$ref":"#/components/schemas/argoDestination","description":"Destination is which cluster and namespace it lands in. Zero-valued on a CD\nrow: this projection reports CD's source, not its destination."},"project":{"description":"Project is the AppProject this application is grouped and filtered under. For\nan App CR it is the app.kubernetes.io/part-of label — the IAM project name —\nfalling back to \"default\" when the CR carries no such label.","type":"string"},"source":{"$ref":"#/components/schemas/argoSource","description":"Source is where the desired state is declared."}},"type":"object"},"argoStatus":{"properties":{"health":{"$ref":"#/components/schemas/argoHealth","description":"Health is the application's reconciled health."},"reconciledAt":{"description":"ReconciledAt is when the desired state was last compared against the cluster,\nRFC 3339. Empty for an App CR — the projection derives its verdict at read\ntime and nothing records a comparison — and CD's own status.reconciledAt for\na CD row.","type":"string"},"resources":{"description":"Resources are the objects the application owns. EMPTY on the list — filling\nit would walk the cluster once per row — and populated only by the read of\nONE application, which is what makes that the detail view.","items":{"$ref":"#/components/schemas/argoResourceStatus"},"type":"array"},"summary":{"$ref":"#/components/schemas/argoSummary","description":"Summary is the small aggregate the list column renders: the images."},"sync":{"$ref":"#/components/schemas/argoSyncStatus","description":"Sync is the declared-versus-running verdict and what it was reached against."}},"type":"object"},"argoSummary":{"properties":{"images":{"description":"Images are the container images the application runs. One entry for an App\nCR, built from its spec.image as \"repository:tag\" — the bare repository when\nit declares no tag, and absent when it declares neither. Absent on a CD row,\nwhich tracks commits rather than images.","items":{"type":"string"},"type":"array"}},"type":"object"},"argoSyncStatus":{"properties":{"revision":{"description":"Revision is what Status was reached against. For an App CR that is the\ndeclared IMAGE TAG, not a commit — the CR is image-pinned. For a CD row it is\nthe commit CD last applied.","type":"string"},"status":{"description":"Status is the ArgoCD sync vocabulary, Capitalized: Synced, OutOfSync or\nUnknown. For an App CR it compares the tag the CR DECLARES against the tag\nthe cluster's Deployment is RUNNING — equal is Synced, both known and\ndifferent is OutOfSync, either unknown is Unknown. For a CD row it is CD's\nown git-versus-cluster verdict.","type":"string"}},"type":"object"},"argoSyncWindows":{"properties":{"activeWindows":{"description":"ActiveWindows are the sync windows in force right now. Always null: this\nplatform declares none, so nothing is ever in force.","items":{"type":"object"},"type":"array"},"assignedWindows":{"description":"AssignedWindows are the windows configured for this application at all,\nwhether or not currently in force. Always null, for the same reason.","items":{"type":"object"},"type":"array"},"canSync":{"description":"CanSync is whether a sync would be permitted at this moment. Always true —\nwith no windows there is nothing to deny it. A caller must not read this as\n\"a sync will succeed\"; it only means no window is blocking one.","type":"boolean"}},"type":"object"},"argoTree":{"properties":{"hosts":{"description":"Hosts is ArgoCD's per-node machine inventory. Always empty: this plane\nprojects applications and serves no cluster-node view.","items":{"type":"object"},"type":"array"},"nodes":{"description":"Nodes is the FLAT node list, root first: the App CR, then the objects the\noperator owns, then their ReplicaSets and Pods. The hierarchy is in\nParentRefs, not in the ordering.","items":{"$ref":"#/components/schemas/argoNode"},"type":"array"},"orphanedNodes":{"description":"OrphanedNodes are objects in the namespace belonging to no application.\nAlways empty: this walk reaches an object only THROUGH ownership from the App\nCR, so it can never hold one that is orphaned.","items":{"$ref":"#/components/schemas/argoNode"},"type":"array"}},"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"},"assignment":{"properties":{"experiment":{"description":"Trial is the experiment that was evaluated.","type":"string"},"on":{"description":"On is false when the flag returned nothing for this subject, which means the\nsubject is not enrolled — not an error.","type":"boolean"},"payload":{"description":"Payload is the opaque JSON the winning arm carries, which the caller\ninterprets: a feature config, an ad-creative id, a subject line, a model id."},"subject":{"description":"Subject is the unit that was bucketed.","type":"string"},"variant":{"description":"Arm is the arm the subject falls in, empty when the flag enrolled it in\nnone.","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"},"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"},"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"},"balances":{"properties":{"address":{"description":"Address is the account they belong to.","type":"string"},"chain":{"description":"Chain is the chain the balances were read from.","type":"string"},"native":{"description":"Native is the chain's own currency, as a 0x-quantity — the RPC's own\nencoding, not a float, because a wei value does not survive float64.","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"},"baseView":{"properties":{"bytes":{"description":"Bytes is the store's size on disk, present only once it exists. It is what\nthis Base occupies, not a quota.","type":"integer"},"exists":{"description":"Exists reports whether this Base's store has been provisioned. False is an\norg nobody has stored anything for yet, which is a state to name rather\nthan an error: the store is created the first time anything writes.","type":"boolean"},"org":{"description":"Org is the org this Base belongs to. It is the address every other Base\ncall is scoped by, and a Base has no name of its own.","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"},"benchmarkCatalog":{"properties":{"data":{"description":"Data is one row per benchmark, in the catalog's own order.","items":{"$ref":"#/components/schemas/Benchmark"},"type":"array"},"total":{"description":"Total is how many rows Data holds.","type":"integer"}},"type":"object"},"binarySpec":{"properties":{"image":{"description":"Image is the toolchain image the recipe runs in, a Go bookworm image by\ndefault. It is the one field the GitHub lane ignores: there the runner IS\nthe toolchain, and a cluster has to be told what a runner already is.","type":"string"},"ldflags":{"description":"Ldflags are the Go linker flags, `-s -w` when the recipe names none, on one\nline. Go lane only.","type":"string"},"main":{"description":"Main is the Go package to build, repo-relative (`.` or `./cmd/x`), and it\nselects the GO LANE. Defaults to `.` when neither lane is named; declaring\nit together with `run` is refused.","type":"string"},"name":{"description":"Name is the artifact's base name: the prefix of every file published for\nthis entry, and the name a host later asks for. It must match\n`^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$`, which is what makes it safe as both a\nfilename and a URL path segment.","type":"string"},"out":{"description":"Out is the glob of files `run` produced, relative to the repo root; matching\nnothing FAILS the build rather than publishing an empty entry. It expands\nunquoted, so it is bounded to path and glob characters. The Go lane names\nits own files and ignores this.","type":"string"},"platforms":{"description":"Platforms are the `<os>/<arch>` pairs the Go lane cross-compiles, [linux/amd64]\nby default. Each one publishes as `<name>-<os>-<arch>`, which is the shape a\nhost resolves a binary BY — so the list is what a caller can ask for later.","items":{"type":"string"},"type":"array"},"run":{"description":"Run is any other toolchain's build command, run by `sh -c` in this entry's\nimage, and it selects the OTHER LANE. Arbitrary shell is the point — it is\nthe same trust as a Dockerfile RUN — which is why it executes with no\nobject-store credential and no service-account token. It requires `out`.","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":{"description":"Principles is how many spine archetypes the playbook carries (64 in the\nshipped corpus).","type":"integer"},"sections":{"description":"Sections is how many phases the journey has.","type":"integer"},"steps":{"description":"Steps is how many checklist items the playbook holds, DISABLED ONES INCLUDED\n— this counts the authored document, not the journey an org runs, so it is\nnormally larger than the `total` on a progress view.","type":"integer"},"strategies":{"description":"Strategies is how many tactics the corpus holds, again counting disabled ones.","type":"integer"},"templates":{"description":"Templates is how many reusable prompts the playbook carries.","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/<id>/.","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":{"description":"DocType is which content type the row came from: Campaign, SocialPost or\nAsset. The board spans all three at once, so this is what tells them apart.","type":"string"},"name":{"description":"Name is the document within that type. (doctype, name) is the pair every\n/v1/content write addresses an item by.","type":"string"},"project":{"description":"Project is the brand/site sub-scope within the org. Absent for an item held\nat org level rather than under one brand.","type":"string"},"status":{"description":"Status is the lifecycle state: draft, in_review, approved, queued, published\nor archived. It decides what a reader may see — the public site pulls\nexactly \"published\" and nothing else — so it is a visibility fact, not a\nworkflow label.","type":"string"},"title":{"description":"Title is the item's headline, read from its type's own title field. Empty\nfor a document that has none.","type":"string"},"updatedAt":{"description":"UpdatedAt is unix seconds of the document's last write, and the key the\nboard sorts on, newest first.","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 is when the resolved window opens, 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":{"description":"To is where it closes, EXCLUSIVE, RFC 3339 UTC — the instant the read was\nserved, so the window walks forward with the clock and two reads a minute\napart do not cover the same period.","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":{"description":"Agent is the cloud Agent this machine runs, lifted out of the binding so a\nlist of bots reads without following one. Empty means the machine is a bot\nmachine with nothing bound — it costs money and answers nothing.","type":"string"},"binding":{"$ref":"#/components/schemas/agentBinding","description":"Binding is the record joining this machine to that agent, carrying vm's own\nreconciled status and its reason. Absent means no runtime is bound, which is\nalso what a stopped bot looks like: stopping unbinds and leaves the machine\nrunning."},"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"},"browserTagOut":{"properties":{"id":{"type":"string"},"platform":{"type":"string"},"type":{"type":"string"}},"type":"object"},"bucketCreateIn":{"properties":{"name":{"description":"Name is the bucket name to create.","type":"string"}},"type":"object"},"bucketIn":{"properties":{"name":{"description":"Name is the bucket's friendly name, matching\n^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. It is validated AS GIVEN and never\nlower-cased for you: a client that creates \"Photos\" and then lists \"photos\"\nwould be reading a bucket it did not make, so mixed case is a clean 400.","type":"string"}},"type":"object"},"bucketItem":{"properties":{"createdAt":{"description":"unix seconds","type":"integer"},"name":{"description":"friendly name","type":"string"}},"type":"object"},"bucketList":{"properties":{"buckets":{"description":"Buckets are the caller org's buckets, friendly names, oldest first as the\nstore returns them.","items":{"$ref":"#/components/schemas/bucketItem"},"type":"array"},"total":{"description":"Total is how many buckets this org has. It equals len(buckets): the listing\nis not paged, because an org's bucket count is small by construction.","type":"integer"}},"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":{"description":"Bytes is the size of the bundled CommonJS the runtime will execute.","type":"integer"},"generated":{"description":"Generated is whether a model wrote the source from a spec, rather than the\ncaller posting the source itself.","type":"boolean"},"plugin":{"$ref":"#/components/schemas/AuthoredPlugin","description":"Plugin is the plugin as stored, with its derived id and build time."}},"type":"object"},"buildRequest":{"properties":{"name":{"description":"Name is the plugin's name: one lowercase path segment (a-z0-9, _ or -),\nand the id the runtime loads it by.","type":"string"},"provider":{"description":"Provider is the connectors provider whose credential the plugin reads at\nrun time. Empty for a plugin that needs none.","type":"string"},"source":{"description":"Source is TypeScript to build as-is. Exactly one of Source or Spec.","type":"string"},"spec":{"description":"Spec is API documentation — an OpenAPI document, or prose describing the\nendpoints — that the generator turns into Source. The generated source is\nreturned in the response, so a caller can read what will run before it runs.","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":{"description":"Agent is the label the surface that did the work calls itself by.","type":"string"},"endedAt":{"description":"EndedAt is when it finished, same format. Empty means it is still going.","type":"string"},"org":{"description":"Org and Project are the build's public ADDRESS — the pair the full story is\nread at, and the pair a visitor sees in the URL bar. Not a tenant key: this\nindex is anonymous and lists only what authors published.","type":"string"},"project":{"description":"Project is the product's slug, the second half of that address.","type":"string"},"repo":{"description":"Repo is the repository the work was done in, as the session reported it.","type":"string"},"session":{"description":"Session is the agent session behind the build, and the value its commits name\nin their `Hanzo-Session:` trailer.","type":"string"},"startedAt":{"description":"StartedAt is when the session opened, RFC 3339 in UTC.","type":"string"},"status":{"description":"Status is the session's own: running, paused, done or error — so a card can\nshow a build still being written.","type":"string"},"title":{"description":"Title is the human line for the card. Sent even when empty, like every field\nhere, because that is what this route has always sent.","type":"string"},"turns":{"description":"Turns is HOW MANY turns the transcript holds — a COUNT, unlike the `turn` on\neach turn of the full story, which is that turn's position. The full read\nreturns at most 1000 of them; this number is not capped.","type":"integer"}},"type":"object"},"buildTurn":{"properties":{"actor":{"description":"Actor is who took the turn. A deploy turn's actor is the literal \"deploy\",\nbecause nobody took it.","type":"string"},"at":{"description":"At is when the turn was recorded, RFC 3339 in UTC to the second.","type":"string"},"body":{"description":"Body is the readable text of the turn, taken from the stored event's `text`.\nEmpty when the event carried a payload of some other shape — this route reads\ntranscripts and does not invent prose for turns that are not one.","type":"string"},"commit":{"description":"Commit is the full sha this turn produced, empty when the turn changed\nnothing. It is ECHOED from the transcript, and the authority is the commit\nitself: it carries the `Hanzo-Session:`/`Hanzo-Turn:` trailer, or a note under\nrefs/notes/hanzo-provenance saying the same, so the claim is checkable at\nsource with the command in `verify`.","type":"string"},"kind":{"description":"Kind is what the turn was, from the log's closed six: message, tool-call,\nspawn, log, status, control. A deploy arrives as a `status` turn.","type":"string"},"subject":{"description":"Subject is that commit's subject line, from the same transcript, so a reader\nsees what the commit says without fetching the repository.","type":"string"},"turn":{"description":"Seq is this turn's POSITION in the session's log — monotonic from 1, per\nsession — and it is what a commit's `Hanzo-Turn:` trailer names. It is not a\ncount of anything: the count is `turns` on the summary beside it.","type":"integer"}},"type":"object"},"buildView":{"properties":{"agent":{"description":"Agent is the label the surface that did the work calls itself by.","type":"string"},"endedAt":{"description":"EndedAt is when it finished, same format. Empty means it has not — the build\nis still going.","type":"string"},"model":{"description":"Model is the model that did the work, taken from the FIRST turn whose body\nnames one — a transcript states it, this route does not resolve it. Empty when\nno turn said.","type":"string"},"org":{"description":"Org is the org that published this build, echoed from the URL. It is part of\nthe build's public ADDRESS and not a tenant key — this route is anonymous, and\nthe only rows it can reach are ones an author explicitly published.","type":"string"},"project":{"description":"Project is the product's slug, the other half of that address.","type":"string"},"repo":{"description":"Repo is the repository the work was done in, as the session reported it.","type":"string"},"session":{"description":"Session is the id of the agent session this story IS — the same value a\nproduced commit carries in its `Hanzo-Session:` trailer, which is what ties\nthe repository's history to this page.","type":"string"},"startedAt":{"description":"StartedAt is when the session opened, RFC 3339 in UTC.","type":"string"},"status":{"description":"Status is the session's own: running, paused, done or error. A build can be\nread while it is still being written, so this is not always terminal — and an\n`error` build is still a readable story, not a missing page.","type":"string"},"title":{"description":"Title is the human line the session was opened or renamed with. Empty when\nnobody gave it one.","type":"string"},"turns":{"description":"Turns is the whole transcript, oldest first, capped at 1000: a published build\nis a story to read down, not an archive to page.","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"},"buyInput":{"properties":{"e164":{"description":"E164 is the number to buy, in E.164 (a leading + and digits), exactly as the\nsearch returned it. This is the number itself, not the id from a search result.","type":"string"}},"type":"object"},"byoGPU":{"properties":{"arch":{"description":"Arch is the card's native compile target (\"gfx1151\"), which is what a kernel\nhas to be built for. AMD reports one; NVIDIA cards leave it empty.","type":"string"},"memoryTotal":{"description":"MemoryTotal is the card's VRAM in the units the host reported it in\n(\"122880 MiB\") — a display string, not a byte count. On a unified part it is\nthe shared CPU/GPU pool, so it is not memory reserved for the GPU.","type":"string"},"name":{"description":"Name is the card's model exactly as its own tooling named it (\"NVIDIA\nGB10\"), never normalized — an operator matches what they see here against\nwhat nvidia-smi tells them on the box.","type":"string"},"unified":{"description":"Unified reports that CPU and GPU share one memory pool (an APU or SoC), so\nMemoryTotal is not private to the GPU and the host competes for it.","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/visor/fleet\nboard renders a linked node's arch + cores + RAM like any other unit.","type":"string"},"capabilities":{"description":"Capabilities is what this worker offers the org: \"studio.render\" when the\nnode can render, \"engine.serve\" when it serves a model endpoint. A node\nadvertises one only once it can honour it, so an absent list means a node\nthat has dialed in but is not ready to serve any of them yet.","items":{"type":"string"},"type":"array"},"cpuModel":{"description":"CPUModel is the processor as the host names it (\"Apple M3 Max\"), for display.","type":"string"},"cpus":{"description":"CPUs is the host's logical core count.","type":"integer"},"cuda":{"description":"Cuda is the host's CUDA toolkit version. NVIDIA hosts report it.","type":"string"},"driver":{"description":"Driver is the host's NVIDIA kernel driver version — distinct from Cuda, and\nthe one that bounds which CUDA versions can run on this box.","type":"string"},"engine":{"$ref":"#/components/schemas/engineAdvertisement","description":"Engine is the hanzo-engine model server this node runs, when it runs one\n(`hanzo link --serve-engine`). Absent means the node takes jobs but serves\nno model endpoint."},"firstSeen":{"description":"FirstSeen is when this node first dialed in, RFC 3339 — the start of its\npresence record, which `hanzo unlink` ends.","type":"string"},"gpus":{"description":"GPUs are the accelerators the host found on itself. Empty is a real answer:\na CPU-only machine can dial in and take non-GPU work.","items":{"$ref":"#/components/schemas/byoGPU"},"type":"array"},"hip":{"description":"Hip is the host's HIP runtime version, the AMD counterpart to Cuda.","type":"string"},"hostname":{"description":"Hostname is what the host calls itself. It equals ID for any hostname already\nin the [a-z0-9-] alphabet, and differs when sanitizing had to change it.","type":"string"},"id":{"description":"ID is the node's id in the fleet — the sanitized hostname it registered\nunder, which is also the `unit` its samples and its gpu-jobs lane key on.\nThis is the id to use everywhere else on the compute surface.","type":"string"},"jobQueue":{"description":"JobQueue is the tasks NAMESPACE this worker claims render jobs out of —\n\"gpu-jobs\" unless `hanzo link` was pointed at another. Within it, a job aimed\nat this node alone rides the task-queue value \"gpu:<id>\".","type":"string"},"lastHeartbeat":{"description":"LastHeartbeat is the most recent beat this node sent, RFC 3339. It is what\nStatus is computed from, so a reader can check the judgement.","type":"string"},"location":{"description":"Location is always \"on-prem\" — a machine that dialed in has no cloud region,\nand inventing one would put it somewhere it is not.","type":"string"},"memory":{"description":"Memory is the host's total RAM in BYTES.","type":"integer"},"os":{"description":"Os is the host's operating system: linux, darwin or windows.","type":"string"},"provider":{"description":"Provider is always \"byo\": this machine is the operator's, not one Hanzo\nprovisioned. It exists so a fold into the machines/GPUs pages says which\nrows are rented and which are the customer's own.","type":"string"},"rocm":{"description":"Rocm is the host's ROCm version. AMD hosts report it; empty otherwise.","type":"string"},"status":{"description":"Status is \"online\" when the last heartbeat landed within 90s, else\n\"offline\" — so it is a fact about heartbeat freshness, not about the box\nbeing powered on. A worker that has never beaten reads offline.","type":"string"},"version":{"description":"Version is the `hanzo` CLI version running on the node. It is what to check\nwhen a worker is missing a field a newer registration reports.","type":"string"}},"type":"object"},"callInput":{"properties":{"agent":{"description":"Agent hands the answered call to a Hanzo assistant by name instead of\nconnecting it to a person. Empty places an ordinary call.","type":"string"},"from":{"description":"From is the number to call FROM, in E.164. It must be one this org holds.","type":"string"},"record":{"description":"Record is a per-call flag rather than a product. Where a recording lands and\nhow long it is kept is the org's retention policy, not this call's.","type":"boolean"},"to":{"description":"To is the number to call, in E.164.","type":"string"},"webhook":{"description":"Webhook is a URL the carrier posts this call's events to as it progresses.\nEmpty means the call's outcome is only visible by reading it back.","type":"string"}},"type":"object"},"callList":{"properties":{"data":{"description":"Data is this org's own calls, newest first — what this platform placed or\nreceived on its behalf, which is our record rather than the carrier's.","items":{"$ref":"#/components/schemas/Call"},"type":"array"}},"type":"object"},"campaignInput":{"properties":{"account":{"description":"Account is the provider ad-account this campaign runs on (Meta act_<id>). 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(experiment.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"},"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: <id>`. 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 endpoint —\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 send 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"},"captableConvertibleIn":{"properties":{"additionalTerms":{"description":"AdditionalTerms is free text for anything the fields above do not carry. Optional."},"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved it, YYYY-MM-DD."},"capital":{"description":"Capital is the principal lent, in the company's currency."},"conversionCap":{"description":"ConversionCap is the valuation the note converts at, at most. Optional."},"discountRate":{"description":"DiscountRate is the discount to the next round's price, as a percentage. Optional."},"interestRate":{"description":"InterestRate is the annual interest the principal accrues, as a percentage. Optional."},"issueDate":{"description":"IssueDate is when the note was issued, YYYY-MM-DD."},"publicId":{"description":"PublicID is the note's identifier. It must be UNIQUE within the company; reusing one is refused 409."},"stakeholderId":{"description":"StakeholderID is the lender. It must name a stakeholder of THIS company."},"status":{"description":"Status is DRAFT, ACTIVE, PENDING, EXPIRED or CANCELLED."},"type":{"description":"Type is CCD, OCD or NOTE."}},"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"},"captableCreated":{"properties":{"id":{"description":"ID is the created row's id.","type":"string"},"message":{"description":"Message is the human sentence the cap table wrote, e.g. \"Share issued\".","type":"string"},"success":{"description":"Success is true when the row was written.","type":"boolean"}},"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"},"captableEquityPlanIn":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is when the board adopted the plan, YYYY-MM-DD."},"comments":{"description":"Comments is a free-text note kept with the plan. Optional."},"defaultCancellatonBehavior":{"description":"DefaultCancellatonBehavior is what happens to cancelled grants: RETIRE, RETURN_TO_POOL, HOLD_AS_CAPITAL_STOCK or DEFINED_PER_PLAN_SECURITY.\n\nThe key is spelled `defaultCancellatonBehavior` — one `l` — because that is\nthe wire, and correcting it here would silently drop the field."},"initialSharesReserved":{"description":"InitialSharesReserved is how many shares the plan may grant. A whole number."},"name":{"description":"Name is the plan's name, e.g. \"2026 Stock Option Plan\"."},"planEffectiveDate":{"description":"PlanEffectiveDate is when the plan takes effect, YYYY-MM-DD. Optional."},"shareClassId":{"description":"ShareClassID is the class the plan grants from. It must name a class of THIS company."}},"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"},"captableInvested":{"properties":{"id":{"description":"ID is the investment record's id.","type":"string"},"message":{"description":"Message is the human sentence the cap table wrote.","type":"string"},"newShareId":{"description":"NewShareID names the certificate the investment issued, when the round\ncarries a price per share. Null when the round prices later, which is a\nrecorded investment and not a failure.","type":"string"},"success":{"description":"Success is true when the investment was recorded.","type":"boolean"}},"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"},"captableInvestmentIn":{"properties":{"amount":{"description":"Amount is the money invested, in the round's own currency. Required and must\nbe positive."},"comments":{"description":"Comments is a free-text note kept with the investment. Optional."},"date":{"description":"Date is when the investment was made, YYYY-MM-DD. Optional: omitted, it is\nrecorded as today."},"id":{"description":"ID is the round to invest in. The URL is the addressing authority — a path\nsegment binds after the body and after the query — so the address decides\nwhich round is written whatever a body claims.","type":"string"},"stakeholderId":{"description":"StakeholderID is the investor. Required, and it must reference a stakeholder\nof the same company."}},"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"},"captableOptionIn":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved the grant, YYYY-MM-DD."},"cliffYears":{"description":"CliffYears is how long before ANY of the grant vests. A whole number; 0 for no cliff."},"equityPlanId":{"description":"EquityPlanID is the plan the options come from. It must name a plan of THIS company."},"exercisePrice":{"description":"ExercisePrice is the strike price per share, in the company's currency."},"expirationDate":{"description":"ExpirationDate is when unexercised options lapse, YYYY-MM-DD."},"grantId":{"description":"GrantID is the grant's identifier. It must be UNIQUE within the company; reusing one is refused 409."},"issueDate":{"description":"IssueDate is when the grant was made, YYYY-MM-DD."},"notes":{"description":"Notes is a free-text note kept with the grant. Optional."},"quantity":{"description":"Quantity is how many options to grant. A whole number above 0."},"rule144Date":{"description":"Rule144Date is when the Rule 144 holding period starts, YYYY-MM-DD."},"stakeholderId":{"description":"StakeholderID is who receives the grant. It must name a stakeholder of THIS company."},"status":{"description":"Status is DRAFT, ACTIVE, EXERCISED, EXPIRED or CANCELLED."},"type":{"description":"Type is ISO, NSO or RSU."},"vestingStartDate":{"description":"VestingStartDate is when vesting begins, YYYY-MM-DD."},"vestingYears":{"description":"VestingYears is the total vesting period. A whole number."}},"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"},"captableRoundIn":{"properties":{"name":{"description":"Name is the round's name, e.g. \"Series A\"."},"preMoneyValuation":{"description":"PreMoneyValuation is the company's valuation before the new money. Optional."},"pricePerShare":{"description":"PricePerShare is the price investors pay per share. An investment into a round with a price ISSUES the shares it buys; one into a round priced later records the money alone."},"roundType":{"description":"RoundType is PRICED, SAFE or CONVERTIBLE."},"shareClassId":{"description":"ShareClassID is the class investors receive. It must name a class of THIS company."},"targetAmount":{"description":"TargetAmount is how much the round aims to raise, in the company's currency."}},"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"},"captableSafeIn":{"properties":{"additionalTerms":{"description":"AdditionalTerms is free text for anything the fields above do not carry. Optional."},"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved it, YYYY-MM-DD."},"capital":{"description":"Capital is the money invested, in the company's currency."},"discountRate":{"description":"DiscountRate is the discount to the next round's price, as a percentage. Optional."},"issueDate":{"description":"IssueDate is when the SAFE was signed, YYYY-MM-DD."},"publicId":{"description":"PublicID is the SAFE's identifier. It must be UNIQUE within the company; reusing one is refused 409."},"stakeholderId":{"description":"StakeholderID is the investor. It must name a stakeholder of THIS company."},"status":{"description":"Status is DRAFT, ACTIVE, PENDING, EXPIRED or CANCELLED."},"type":{"description":"Type is PRE_MONEY or POST_MONEY — which side of the new money the cap is measured on."},"valuationCap":{"description":"ValuationCap is the valuation the SAFE converts at, at most. Optional; omit for an uncapped SAFE."}},"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"},"captableShareClassAmend":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved the class, YYYY-MM-DD."},"classType":{"description":"ClassType is COMMON or PREFERRED."},"conversionRights":{"description":"ConversionRights is CONVERTS_TO_FUTURE_ROUND or CONVERTS_TO_SHARE_CLASS_ID. The second requires convertsToShareClassId."},"convertsToShareClassId":{"description":"ConvertsToShareClassID names the class this one converts into. Required when conversionRights is CONVERTS_TO_SHARE_CLASS_ID, ignored otherwise."},"id":{"description":"ID addresses the resource. The URL is the addressing authority — a path\nsegment binds after the body and after the query — so the address decides\nwhich row is written whatever a body claims.","type":"string"},"initialSharesAuthorized":{"description":"InitialSharesAuthorized is how many shares this class may issue. A whole number."},"liquidationPreferenceMultiple":{"description":"LiquidationPreferenceMultiple is the multiple of invested capital paid out before junior classes — 1 for a 1x preference."},"name":{"description":"Name is the share class's name, e.g. \"Series A Preferred\"."},"parValue":{"description":"ParValue is the nominal per-share value on the certificate, in the company's currency."},"participationCapMultiple":{"description":"ParticipationCapMultiple caps participation after the preference is paid, as a multiple of invested capital. 0 for no cap."},"pricePerShare":{"description":"PricePerShare is the issue price for this class, in the company's currency."},"seniority":{"description":"Seniority orders liquidation preference: LOWER is more senior, so 1 is paid before 2."},"stockholderApprovalDate":{"description":"StockholderApprovalDate is when the stockholders approved it, YYYY-MM-DD."},"votesPerShare":{"description":"VotesPerShare is how many votes one share of this class carries. A whole number; 0 for non-voting."}},"type":"object"},"captableShareClassIn":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved the class, YYYY-MM-DD."},"classType":{"description":"ClassType is COMMON or PREFERRED."},"conversionRights":{"description":"ConversionRights is CONVERTS_TO_FUTURE_ROUND or CONVERTS_TO_SHARE_CLASS_ID. The second requires convertsToShareClassId."},"convertsToShareClassId":{"description":"ConvertsToShareClassID names the class this one converts into. Required when conversionRights is CONVERTS_TO_SHARE_CLASS_ID, ignored otherwise."},"initialSharesAuthorized":{"description":"InitialSharesAuthorized is how many shares this class may issue. A whole number."},"liquidationPreferenceMultiple":{"description":"LiquidationPreferenceMultiple is the multiple of invested capital paid out before junior classes — 1 for a 1x preference."},"name":{"description":"Name is the share class's name, e.g. \"Series A Preferred\"."},"parValue":{"description":"ParValue is the nominal per-share value on the certificate, in the company's currency."},"participationCapMultiple":{"description":"ParticipationCapMultiple caps participation after the preference is paid, as a multiple of invested capital. 0 for no cap."},"pricePerShare":{"description":"PricePerShare is the issue price for this class, in the company's currency."},"seniority":{"description":"Seniority orders liquidation preference: LOWER is more senior, so 1 is paid before 2."},"stockholderApprovalDate":{"description":"StockholderApprovalDate is when the stockholders approved it, YYYY-MM-DD."},"votesPerShare":{"description":"VotesPerShare is how many votes one share of this class carries. A whole number; 0 for non-voting."}},"type":"object"},"captableShareIn":{"properties":{"boardApprovalDate":{"description":"BoardApprovalDate is when the board approved the issuance, YYYY-MM-DD."},"capitalContribution":{"description":"CapitalContribution is cash paid for the shares. Optional."},"certificateId":{"description":"CertificateID is the certificate number. It must be UNIQUE within the company; reusing one is refused 409."},"cliffYears":{"description":"CliffYears is how long before ANY of the grant vests. A whole number; 0 for no cliff."},"companyLegends":{"description":"CompanyLegends are the restrictive legends printed on the certificate.\n\nA LIST, and it must be sent as one: the bundle substitutes an EMPTY list for\nanything that is not an array, so a single string would be accepted and\nsilently discarded — the certificate issued with no legends and nothing\nreporting it.","items":{},"type":"array"},"debtCancelled":{"description":"DebtCancelled is debt forgiven as consideration. Optional."},"ipContribution":{"description":"IPContribution is intellectual property assigned as consideration, valued in the company's currency. Optional."},"issueDate":{"description":"IssueDate is when the shares were issued, YYYY-MM-DD."},"otherContributions":{"description":"OtherContributions is any other consideration, valued in the company's currency. Optional."},"pricePerShare":{"description":"PricePerShare is what was paid per share. Optional."},"quantity":{"description":"Quantity is how many shares to issue. A whole number above 0."},"rule144Date":{"description":"Rule144Date is when the Rule 144 holding period starts, YYYY-MM-DD. Optional."},"shareClassId":{"description":"ShareClassID is the class being issued. It must name a class of THIS company."},"stakeholderId":{"description":"StakeholderID is who receives the shares. It must name a stakeholder of THIS company."},"status":{"description":"Status is ACTIVE, DRAFT, SIGNED or PENDING."},"vestingStartDate":{"description":"VestingStartDate is when vesting begins, YYYY-MM-DD. Optional; defaults to the issue date."},"vestingYears":{"description":"VestingYears is the total vesting period. A whole number; 0 for fully vested at issue."}},"type":"object"},"captableShareTransfer":{"properties":{"certificateId":{"description":"CertificateID names the NEW certificate a partial transfer issues, and is\nrequired for one — a partial transfer without it is refused 400. It is unused\nby a full transfer, which reassigns the existing certificate.\n\nIt must be unique within the company; reusing one is refused 409. Declaring\nit is not optional in the way the tag suggests: omitting this field from the\nGo type would leave `quantity` accepted and every PARTIAL transfer answering\n\"certificateId is required\" with no way for a caller to supply it — the\nsilent-drop failure this whole conversion was blocked on, one field wide."},"quantity":{"description":"Quantity is how many shares to move. OPTIONAL, and its absence is not zero:\nomitted or null transfers the WHOLE certificate. A value outside 1..held is\nrefused, so a partial transfer can never over-issue.\n\nA full transfer REASSIGNS the certificate and mints no new one; a partial\ntransfer splits it and answers with the new share's id. That is the fact a\ncaller needs from the answer, not from this comment."},"shareId":{"description":"ShareID is the certificate being transferred. Required, and it must name a\nshare in the caller org's own company — a share id from another tenant\nresolves to not-found rather than to another company's certificate."},"toStakeholderId":{"description":"ToStakeholderID is who receives them. Required, and it must reference a\nstakeholder of the SAME company; one that does not is refused 400 rather\nthan creating a dangling holder."}},"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"},"captableTransferred":{"properties":{"message":{"description":"Message is the human sentence the cap table wrote, e.g. \"Share transferred\".","type":"string"},"newShareId":{"description":"NewShareID names the certificate a PARTIAL transfer created. It is null on a\nfull transfer, which reassigns the existing certificate instead of splitting\nit — so null here means \"no new certificate\", never \"the transfer failed\".","type":"string"},"success":{"description":"Success is true when the transfer was applied.","type":"boolean"},"transferred":{"description":"Transferred is how many shares moved.","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":{"description":"Configured is whether THIS DEPLOYMENT holds the OAuth client credentials for\nthe provider. False means Connect would dead-end, so the console can offer it\ndisabled instead of broken. It is deployment-wide and says nothing about\nwhether the caller's org has connected the source — that is the connector\nlist's `status`.","type":"boolean"},"description":{"description":"Description is one line of shop copy: what connecting this source pulls in.\nNative connectors carry written prose; a piece-backed one reads\n\"activepieces connector (<piece>)\".","type":"string"},"displayName":{"description":"DisplayName is the label to show a person. First-party connectors carry a\nwritten name (\"GitHub\", \"Google Drive\"); a piece-backed one falls back to the\nprovider capitalized, because the rich activepieces metadata lives behind a\ncross-service call this read will not make.","type":"string"},"kind":{"description":"\"native\" | \"piece\"","type":"string"},"provider":{"description":"Provider is the source's id and the address every connector op takes it by\n(/v1/knowledge/connectors/:provider). One of github, slack, google, notion.","type":"string"}},"type":"object"},"catalogList":{"properties":{"data":{"description":"Data is every starter prompt, each importable as-is with POST /v1/prompt.","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"},"categoryIn":{"properties":{"brands":{"description":"Brands are the brands whose console shows it. Omit for every brand.","items":{"type":"string"},"type":"array"},"id":{"description":"ID is the category slug to write, from the path.","type":"string"},"label":{"description":"Label is the display name. Required.","type":"string"},"order":{"description":"Order is where the category sits among its siblings, ascending.","type":"integer"},"summary":{"description":"Summary is the one line describing what the category groups.","type":"string"}},"type":"object"},"cdResp":{"properties":{"applications":{"items":{"$ref":"#/components/schemas/CDApp"},"type":"array"}},"type":"object"},"centre":{"properties":{"controls":{"description":"Controls is the control inventory, each entry naming what it asserts, the\nmechanism, where it is enforced, how it is verified and the clauses it maps\nto.","items":{},"type":"array"},"coverage":{"description":"Coverage is the per-framework counts, computed from Controls against each\nframework's whole published clause list.","items":{"$ref":"#/components/schemas/coverRow"},"type":"array"},"documents":{"description":"Documents are the artifacts, each saying whether this reader may read it.","items":{"$ref":"#/components/schemas/docRow"},"type":"array"},"faq":{"description":"Faq is the knowledge base — the questions a reviewer asks, answered.","items":{},"type":"array"},"frameworks":{"description":"Frameworks are the clause universes the coverage is computed against.","items":{"$ref":"#/components/schemas/frameworkRow"},"type":"array"},"generated":{"description":"Generated is when this answer was computed, unix milliseconds.","type":"integer"},"inventory":{"$ref":"#/components/schemas/trustTally","description":"Inventory is how the controls themselves stand, independent of framework."},"org":{"description":"Org is whose centre this is.","type":"string"},"policies":{"description":"Policies are the published policies.","items":{},"type":"array"},"profile":{"description":"Profile is the organization's own description of itself."},"risk":{"description":"Risk is the risk profile — label and value pairs describing what this\norganization handles and how."},"subprocessors":{"description":"Subprocessors are the third parties this organization sends data to.","items":{},"type":"array"},"updates":{"description":"Updates is the changelog, newest as the organization ordered it.","items":{},"type":"array"},"version":{"description":"Version is the embedded inventory's version.","type":"string"}},"type":"object"},"chainList":{"properties":{"chains":{"description":"Chains is every chain this deployment is configured to reach, sorted by\nid. Empty when none are configured — never a fabricated entry.","items":{"$ref":"#/components/schemas/Chain"},"type":"array"}},"type":"object"},"chainStatus":{"properties":{"chainId":{"type":"integer"},"height":{"description":"Height is the latest block, omitted when the chain did not answer rather\nthan reported as zero — a zero height is a real value on a fresh chain.","type":"integer"},"id":{"type":"string"},"live":{"description":"Live is whether the upstream answered eth_blockNumber.","type":"boolean"},"name":{"type":"string"}},"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"},"claimsOut":{"properties":{"data":{"description":"Data is one row per (benchmark, model, SOURCE) — every independent claim,\nnot one per model. Effective values only: the row that wins after layering\nfor each source, never the superseded readings behind it.","items":{"$ref":"#/components/schemas/ClaimRow"},"type":"array"},"total":{"description":"Total is how many rows Data holds.","type":"integer"}},"type":"object"},"clauseCoverage":{"properties":{"automated":{"description":"Automated is how many clauses have an automated control behind them that\nsomething can fail on behalf of.","type":"integer"},"clauses":{"description":"Clauses is every clause the standard publishes, with what stands behind it.","items":{"$ref":"#/components/schemas/clauseRow"},"type":"array"},"edition":{"description":"Edition is which edition this clause list is taken from.","type":"string"},"framework":{"description":"Framework is the framework id — \"soc2\", \"iso27001\", \"nist80053\".","type":"string"},"generated":{"description":"Generated is when this was computed, unix milliseconds.","type":"integer"},"name":{"description":"Name is the published standard's name.","type":"string"},"none":{"description":"None is how many have nothing behind them.","type":"integer"},"note":{"description":"Note is what this clause list is scoped to, when a count alone would\nmisrepresent it.","type":"string"},"partial":{"description":"Partial is how many are answered in part.","type":"integer"},"publisher":{"description":"Publisher is who publishes it — AICPA, ISO/IEC, NIST.","type":"string"},"statement":{"description":"Statement is the counts as one sentence, carrying the unit.","type":"string"},"total":{"description":"Total is the framework's WHOLE published clause list — the denominator.","type":"integer"},"unit":{"description":"Unit is what ONE clause is — \"criterion\", \"control\", \"family\".","type":"string"},"units":{"description":"Units is the plural of Unit, for rendering a sentence.","type":"string"},"version":{"description":"Version is the embedded inventory's version.","type":"string"}},"type":"object"},"clauseRow":{"properties":{"controls":{"description":"Controls are the ids behind it, strongest first. Empty when nothing covers\nit — and an absent control is never listed here, however it maps.","items":{"type":"string"},"type":"array"},"group":{"description":"Group is the clause's section within the standard, when it has one.","type":"string"},"id":{"description":"ID is the clause id as the standard publishes it — \"CC6.1\", \"A.5.15\", \"AC\".","type":"string"},"level":{"description":"Level is what the strongest control pointed at this clause is worth:\n\"automated\", \"partial\" or \"none\".","type":"string"},"title":{"description":"Title is the standard's own words for that clause.","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"},"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":{"description":"Nodes is every worker node in the cluster, each in the same shape the\nmachines surface uses — a node IS a machine, addressable by its own id. This\nis the individual hardware behind the pool counts above.","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"},"clusterView":{"properties":{"amdGpu":{"description":"AmdGPU is the same count for `amd.com/gpu`: AMD accelerators across the BYO\ncluster's nodes, as of the attach.","type":"integer"},"createdAt":{"description":"CreatedAt is when the cluster started existing: the earliest creation time\namong its pools for a managed cluster, and for a BYO one the RFC 3339 moment\nit was attached. Empty when the source states none.","type":"string"},"doClusterId":{"description":"DoClusterID carries the SAME id as DoksClusterID. Both names exist because\nthe console's Cluster type reads either one; neither is a second identifier.","type":"string"},"doksClusterId":{"description":"DoksClusterID is the provider's own id for the cluster, and the value the\n/v1/visor/k8s/clusters/:id routes take. Empty for a BYO cluster: an attached\nkubeconfig was never provisioned, so there is no provider id to state.","type":"string"},"kind":{"description":"Kind says which of the two kinds of cluster this row is, and there are only\ntwo: \"managed\" — Visor provisioned it and Hanzo's account pays the provider —\nor \"byo\", an existing cluster the org attached by kubeconfig.","type":"string"},"name":{"description":"Name is the cluster's name: the provider's for a managed cluster, and for a\nBYO one the lower-cased fleet name it was attached under — which is also how\nthe detach route addresses it.","type":"string"},"nodeCount":{"description":"NodeCount is how many worker nodes the cluster has — the sum over its pools\nfor a managed cluster, and for a BYO one the node count read off the cluster\nwhen it was attached.","type":"integer"},"nodePools":{"description":"NodePools is the authoritative node inventory — every pool, each with its own\nsize and count. It is empty in two cases that are not \"no pools\": a row from\nthe /v1/visor/k8s/clusters LIST, which is deliberately lightweight and whose\n:id detail carries them, and a BYO cluster, whose pools were never read.","items":{"$ref":"#/components/schemas/nodePoolView"},"type":"array"},"nodeSize":{"description":"NodeSize is a display convenience: the size slug of the FIRST pool. A cluster\nmixing sizes has more than one, and NodePools is where they all are.","type":"string"},"nvidiaGpu":{"description":"NvidiaGPU is how many NVIDIA accelerators the cluster's nodes advertise, the\nsum of `nvidia.com/gpu` allocatable across them. BYO only, and counted ONCE\nwhen the cluster was attached — it is an inventory, not live capacity.","type":"integer"},"region":{"description":"Region is the provider region slug for a managed cluster. A BYO cluster has\nno region we can read, so it carries the free-form `provider` label the\nattach named it with (\"gke\", \"on-prem\") instead.","type":"string"},"status":{"description":"Status is the cluster's state: the provider's own word for a managed cluster\n(\"running\", \"provisioning\"), \"unknown\" when the provider stated none, and\nalways \"attached\" for a BYO cluster — that one says the kubeconfig is on\nfile, not that the cluster is reachable this second.","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=<code>. 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\"<workspaceUuid>|<objectClass>|<objectId>|<objectAttr>\" — 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"},"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"},"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":{"description":"Command is what was asked, from a closed four: pause, resume, stop, message.\nIt is an INTENT — the poller decides what to do about it, and the session's\nstatus changes only when the poller reports back that it did.","type":"string"},"message":{"description":"Message is the text that came with the command: what to say into the run for\n`message`, and the cancellation reason for `stop`. Up to 16 KiB. Empty on a\nbare pause or resume.","type":"string"},"payload":{"description":"Payload is the structured half a caller sent instead of (or beside) Message,\nembedded as JSON. Its shape is the caller's business; this surface carries it\nthrough untouched."},"seq":{"description":"Seq is this command's position in the session's log — the same monotonic\nnumber every other turn is ordered by, so a command sits in the transcript\nwhere it was issued. Send the highest one you applied back as `after` and it\nis never redelivered.","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"},"controlIn":{"properties":{"id":{"description":"ID is the session to steer, from the path.","type":"string"},"message":{"description":"Message is free text for the running agent, up to 16 KiB. On a stop it is\nrecorded as the cancellation reason.","type":"string"},"payload":{"description":"Payload is a structured argument for the command: any valid JSON up to\n64 KiB, scanned for credentials before it is stored (a hit refuses the\ncommand with 422 and the findings). A task-backed session receives it as\nthe forwarded signal's argument."}},"type":"object"},"controlList":{"properties":{"absent":{"description":"Absent is how many the organization does not have. Each still names the\nclause it would satisfy, and none of them moves a coverage number.","type":"integer"},"automated":{"description":"Automated is how many run with nobody in the loop.","type":"integer"},"controls":{"description":"Controls is every control, opaque because the organization owns its shape.","items":{},"type":"array"},"partial":{"description":"Partial is how many run but do not cover their whole claim.","type":"integer"},"statement":{"description":"Statement is the counts as one sentence, safe to quote.","type":"string"},"total":{"description":"Total is how many controls this organization publishes.","type":"integer"},"unverified":{"description":"Unverified is how many rest on somebody having read the source rather than\non a test or an audit row.","type":"integer"},"version":{"description":"Version is the embedded inventory's version.","type":"string"}},"type":"object"},"controlResult":{"properties":{"command":{"description":"Command is the verb that was recorded: pause, resume, stop or message.","type":"string"},"event":{"$ref":"#/components/schemas/eventView","description":"Event is the durable control event the command became. The intent is\nrecorded whether or not it reached an engine, which is what makes a\nstream-consuming surface able to act on it."},"forwarded":{"description":"Forwarded is whether the command also reached the durable-execution engine.\nFALSE IS NOT A FAILURE: a session with no workflow link, or a deployment\nwith no tasks backend, is record-only by design. A forward that was\nattempted and failed is a 502, never a false here.","type":"boolean"}},"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"},"costLine":{"properties":{"key":{"description":"the function the line is about","type":"string"},"points":{"description":"one point per bucket, oldest first","items":{"$ref":"#/components/schemas/pointView"},"type":"array"}},"type":"object"},"coverRow":{"properties":{"automated":{"description":"Automated is how many clauses have an automated control behind them that\nsomething can fail on behalf of.","type":"integer"},"edition":{"description":"Edition is which edition the clause list is taken from.","type":"string"},"framework":{"description":"Framework is the framework id — \"soc2\", \"iso27001\", \"nist80053\".","type":"string"},"name":{"description":"Name is the published standard's name.","type":"string"},"none":{"description":"None is how many have nothing behind them. It stays visible rather than\ndropping out of the fraction.","type":"integer"},"note":{"description":"Note is what the clause list itself is scoped to, when the framework's\ncatalog says something a count alone would misrepresent.","type":"string"},"partial":{"description":"Partial is how many are answered in part.","type":"integer"},"publisher":{"description":"Publisher is who publishes it — AICPA, ISO/IEC, NIST.","type":"string"},"statement":{"description":"Statement is the counts as one sentence, carrying the unit.","type":"string"},"total":{"description":"Total is the framework's WHOLE published clause list — the denominator.\nCounting only the clauses some control happened to name would report 100%\nevery time.","type":"integer"},"unit":{"description":"Unit is what ONE clause is — \"criterion\", \"control\", \"family\". A count\nwithout its unit is not a fact, so it travels with every number here.","type":"string"},"units":{"description":"Units is the plural of Unit, for rendering a sentence.","type":"string"}},"type":"object"},"crawlDocument":{"properties":{"markdown":{"description":"Markdown is the page's content, extracted and rendered to markdown. This is\nthe field to read.","type":"string"},"metadata":{"description":"Metadata is whatever the document said about itself — description, og:*,\nlanguage — plus the response status, the final URL and the content type. It\nis an OPEN key space, so it is carried as raw JSON: `map[string]any`\npublishes `additionalProperties:{\"type\":\"object\"}`, which the integer\n`status` inside it refutes, while raw JSON publishes `{}` — \"any JSON\",\nwhich is true. The bytes are identical either way (encoding/json writes a\nmap's keys sorted, and this IS that marshalling)."},"title":{"description":"Title is the document's title, when it carried one.","type":"string"},"url":{"description":"URL is the address actually read, after redirects.","type":"string"}},"type":"object"},"crawlRequest":{"properties":{"url":{"description":"URL is the page to read, absolute and http or https — no other scheme is\ndialled. It is resolved from inside the cluster, so an address that turns out\nto be loopback, link-local, private or multicast is refused at the dialer,\nredirects included. Empty is not an error status: the answer comes back with\nsuccess false and the reason in error.","type":"string"}},"type":"object"},"crawlResult":{"properties":{"data":{"$ref":"#/components/schemas/crawlDocument","description":"Data is the page, present exactly when Success."},"error":{"description":"Error says what stopped the fetch: the host was refused, unreachable, or\nserved something that is not a document.","type":"string"},"success":{"description":"Success is whether the page was fetched and read. FALSE with an Error is a\ncomplete answer, not a fault — check this before reading Data.","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":{"description":"ComputeRef optionally binds this bot to a visor machine. Opaque here, bounded\nat 256 characters, and not resolved — this package stores the reference and\nthe binding's lifecycle belongs elsewhere.","type":"string"},"description":{"description":"Description is the one line published as the description of the `agent_<name>`\ntool, which is how another agent decides whether to call this one. Optional,\nand worth writing for exactly that reason.","type":"string"},"executionMode":{"description":"ExecutionMode is one-shot or long-running. Empty takes one-shot, which runs\nonly when something POSTs to it. long-running additionally requires Schedule,\nand counts against a per-org cap that answers 409 when it is full.","type":"string"},"instructions":{"description":"Instructions is the system prompt, up to 32 KiB, stored verbatim. This is what\nthe model reads; Description is what other CALLERS read.","type":"string"},"model":{"description":"Model names the model to run on. Omit it to take the deployment's configured\ndefault; name one and it is checked against the gateway's served catalogue\nhere, so a model this deployment cannot serve is refused now rather than at\nthe first run. Stored under our own name for it, whatever spelling arrives.","type":"string"},"name":{"description":"Name is the agent's org-unique handle and the only required field. It must\nmatch ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$, and a name already taken in this org\nis a 409 rather than an overwrite. It is permanent: no update route moves it.","type":"string"},"schedule":{"description":"Schedule is the 5-field cron a long-running agent fires on, parsed here so a\nbad expression is a 400 and not an agent that silently never runs. Required\nwith long-running; DISCARDED for one-shot rather than stored unused.","type":"string"},"serviceAccountId":{"description":"ServiceAccountID optionally names the IAM agent service account (<org>-<agent>)\na scheduled run should be billed AS, so an autonomous run is attributable to a\nprincipal rather than only to the org. Same 256-character bound, also\nunresolved here.","type":"string"},"tools":{"description":"Tools are the tool names this agent may call. Omitted or empty grants NONE —\nthat default is the agent's authority and is not widened anywhere. The single\nentry \"*\" means whatever the fleet's MCP server serves at the time of each run.","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"},"createBody":{"properties":{"exposureEvent":{"description":"ExposureEvent is the event that marks a subject as enrolled — the analysis\ndenominator — defaulting to the SDK's $feature_flag_called marker.","type":"string"},"flagKey":{"description":"FlagKey names the assignment flag this experiment writes, defaulting to\nexp_<id>. It must be a slug.","type":"string"},"id":{"description":"ID is the experiment's slug, claimed once per project. It must match\n[A-Za-z0-9][A-Za-z0-9._-]{0,127}.","type":"string"},"metricEvent":{"description":"MetricEvent is the event that counts as a conversion — the analysis\nnumerator.","type":"string"},"name":{"description":"Name is free text for a reader; the id is what addresses the experiment.","type":"string"},"subjectKind":{"description":"SubjectKind is the unit assigned and measured: user (the default), org,\nsession or audience.","type":"string"},"variants":{"description":"Arms are the arms, at least two. Weights that are all zero become an\neven split; otherwise they must sum to 100, and at most one arm may be\nflagged control.","items":{"$ref":"#/components/schemas/Arm"},"type":"array"}},"required":["id","metricEvent","variants"],"type":"object"},"createClusterReq":{"properties":{"name":{"description":"Name is the cluster's name. Required.","type":"string"},"nodePool":{"description":"NodePool is the ONE pool the cluster is born with — a cluster with no nodes\nruns nothing, so it is not optional. More pools are added afterwards through\nPOST /v1/visor/clusters/:clusterId/pools.","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.>\").\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/webhook/{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"},"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"},"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:<n>\" 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":"Connection 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"},"cryptoAsset":{"properties":{"amountCents":{"description":"AmountCents is what the payer intends to send, for the record. Optional —\nthe credit is what actually arrives, never what was announced.","type":"integer"},"chain":{"description":"Chain is the network to receive on. Empty takes the rail's default.","type":"string"},"token":{"description":"Token is the asset on that chain. Empty takes the chain's native one.","type":"string"}},"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 (\"[email protected]\"), 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"},"dataroomLiveness":{"properties":{"service":{"description":"Service names the subsystem answering, so a probe response is attributable\nwhen several are collected together.","type":"string"},"status":{"description":"Status is `ok`. This probe has no degraded answer by design: it reports\nprocess liveness and nothing that could be false while the process serves.","type":"string"}},"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"},"datasetList":{"properties":{"data":{"description":"Data is the caller org's datasets, newest first, bounded by limit.","items":{"$ref":"#/components/schemas/datasetView"},"type":"array"}},"type":"object"},"datasetReq":{"properties":{"description":{"description":"Description is free text about what this set measures; over 64 KiB is refused.","type":"string"},"metadata":{"additionalProperties":{"type":"object"},"description":"Metadata is a free-form object stored with the set and echoed back verbatim.","type":"object"},"name":{"description":"Name is the dataset's org-unique handle and the segment that will address\nit, so it must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$.","type":"string"}},"required":["name"],"type":"object"},"datasetView":{"properties":{"createdAt":{"description":"CreatedAt is when the name was first written, kept across later edits.","type":"string"},"description":{"description":"Description is free text the org wrote about what this set measures.","type":"string"},"items":{"description":"Items is how many examples the set holds. It is filled only by the single\nread — a listing does not count, so it is absent there rather than zero.","type":"integer"},"metadata":{"additionalProperties":{"type":"object"},"description":"Metadata is the free-form object stored with the set, echoed back verbatim.","type":"object"},"name":{"description":"Name is the dataset's org-unique handle and the segment that addresses it.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the description or metadata last changed.","type":"string"}},"type":"object"},"decideBody":{"properties":{"winner":{"description":"Winner is the variant to promote. It must name one of this experiment's own\narms.","type":"string"}},"required":["winner"],"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"},"declareEnv":{"properties":{"name":{"type":"string"},"public":{"description":"Public marks a value that may be WRITTEN INTO GIT. Absent, it is false,\nand the value is sealed into KMS and referenced.\n\n★ THE DEFAULT IS SECRET, AND THE POLARITY IS THE WHOLE DESIGN. This lane's\noutput is a commit in a repository replicated to every clone, so a\nmisclassification is not a bug to fix later — it is a credential published\nforever. A heuristic classifier fails in both directions; what decides is\nwhich direction it fails IN. Seal-by-default makes the failure mode \"an\noperator cannot read back a config value\", which is a support ticket.\nClassify-by-shape made it \"a password is in git history\", which is an\nincident with no rollback.\n\nIt is also the only rule that needs no list. PGPASSWORD, *_PW, a\nsymbol-rich password, a KUBECONFIG, a base32 MFA seed — every one of them\nslipped a shape classifier, and each miss was a different reason. There is\nno reason left when the default is to seal.","type":"boolean"},"value":{"type":"string"}},"type":"object"},"declared":{"properties":{"application":{"type":"string"},"automated":{"type":"boolean"},"cd":{"$ref":"#/components/schemas/CDApp"},"digest":{"type":"string"},"env":{"items":{"$ref":"#/components/schemas/declareEnv"},"type":"array"},"hosts":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"org":{"type":"string"},"path":{"type":"string"},"project":{"type":"string"},"replicas":{"type":"integer"},"repository":{"type":"string"},"tag":{"type":"string"}},"type":"object"},"declaredResp":{"properties":{"apps":{"items":{"$ref":"#/components/schemas/declared"},"type":"array"},"cdUnavailable":{"$ref":"#/components/schemas/unreadable"},"org":{"description":"Org is the directory read — the caller's own, or another when a SuperAdmin\nasked to act as it.","type":"string"}},"type":"object"},"definition":{"properties":{"code":{"description":"Code is the source to run, capped so one function cannot amplify the store.","type":"string"},"envNames":{"description":"EnvNames are the secret NAMES to mount. Values live in the secret store and\nare never carried here.","items":{"type":"string"},"type":"array"},"environment":{"description":"Environment is a second spelling of runtime, accepted so a console that says\n\"environment\" needs no translation.","type":"string"},"handler":{"description":"Handler is the entry point within the code.","type":"string"},"image":{"description":"Image names a prebuilt image to run instead of source.","type":"string"},"memoryLimit":{"description":"MemoryLimit is the memory the function runs with, defaulting to 256Mi. It is\nalso the multiplier on the GB-seconds compute charge.","type":"string"},"name":{"description":"Name is the function's org-unique handle and the segment that addresses it,\nmatching ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. The names that would shadow a\ncollection route are reserved.","type":"string"},"namespace":{"description":"Namespace groups functions for display. It is cosmetic — the org is the\nisolation key — and is normalised to a DNS-safe label.","type":"string"},"runtime":{"description":"Runtime is the language the code runs under: node, python or deno.","type":"string"},"target":{"description":"Target is where the function runs: sandbox (the default) or fleet, the org's\nown GPU fleet. fleet supports runtime=python only.","type":"string"},"timeoutSec":{"description":"TimeoutSec is the per-invocation deadline, defaulting to 30 and clamped at\n900 — a larger value is capped rather than reset to the default.","type":"integer"}},"required":["name"],"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"},"deleted":{"properties":{"deleted":{"description":"Deleted is the id that no longer exists.","type":"string"}},"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"},"deployHealth":{"properties":{"crd":{"description":"CRD reports whether the App custom resource is served and listable. Absent\nwhen the apiserver was unreachable, because then it is unknown rather than\nfalse.","type":"boolean"},"k8s":{"description":"K8s reports whether the Kubernetes API is reachable. Absent when the probe\ndid not get far enough to find out.","type":"boolean"},"service":{"description":"Service names the subsystem answering, so a probe response is attributable\nwhen several are collected together.","type":"string"},"status":{"description":"Status is `ok` when this deployment can serve the delivery plane, and\n`degraded` otherwise. It agrees with the HTTP status by construction — see\nStatusCode.","type":"string"}},"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":{"description":"ApplicationID is the app this deployed — the app's `id`, not its slug.","type":"string"},"buildId":{"description":"BuildID is the build record behind a git deploy, whose logs and status live\nat /v1/platform/builds. Empty for an image deploy.","type":"string"},"commit":{"description":"Commit is the git ref this built — the commit a deploy or a push named,\nelse the app's branch. Empty for an image deploy, which builds nothing.","type":"string"},"createdAt":{"description":"CreatedAt is when the attempt was recorded, unix seconds.","type":"integer"},"id":{"description":"ID is the deployment's id (`dep_…`), minted when the attempt is recorded.\nThe app's currentDeploymentId points at one of these.","type":"string"},"image":{"description":"Image is the full `repo:tag` this deployment put in the CR. For a git deploy\nit is the ref the in-cluster build pushes to, known before the build runs.","type":"string"},"message":{"description":"Message is why this attempt is not live: the failure, or the note that a\nnewer deployment went live before this build finished. Empty while it is fine.","type":"string"},"org":{"description":"Org is the tenant the deployment belongs to, from the validated identity.","type":"string"},"source":{"description":"Source is which lane produced it: `git` (built from the repo) or `image`\n(an already-built ref deployed as-is, including promote and rollback).","type":"string"},"status":{"description":"Status is where the attempt got to: `building` while its image is being\nbuilt, `deploying` once its CR reached the cluster — which is the terminal\nsuccess state, the app's own status is what turns `live` — `error` with the\nreason in Message, or `superseded` when a newer version went live first.","type":"string"},"updatedAt":{"description":"UpdatedAt is its last transition, unix seconds — so for a terminal\ndeployment it is when it reached that state.","type":"integer"},"version":{"description":"Version counts this app's deployments, from 1 and monotonically. It is what\nORDERS them: a deploy only goes live if no higher version already is, so a\nbuild that finishes late is superseded instead of overwriting a newer one.","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":"Connection 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"},"disconnectOut":{"properties":{"disconnected":{"description":"Disconnected is always true — the org's secrets and connection row are gone.","type":"boolean"}},"type":"object"},"docRow":{"properties":{"attested":{"description":"Attested reports whether somebody OUTSIDE this organization put their name\nto it. Those are the artifacts a reviewer asks for, and they are released\nthrough a grant rather than published — there is no field that can say\notherwise.","type":"boolean"},"href":{"description":"Href is where to read it, present only when this reader may.","type":"string"},"id":{"description":"ID is the document's id within this organization's centre.","type":"string"},"kind":{"description":"Kind is the artifact type — soc2, iso, pentest, letter, caiq, sig, vsa,\nquestionnaire, policy or other.","type":"string"},"label":{"description":"Label is the artifact type in words, for rendering.","type":"string"},"note":{"description":"Note is anything the organization says about this artifact.","type":"string"},"released":{"description":"Released reports whether THIS reader may read it. False means the artifact\nexists and is available on request.","type":"boolean"},"tier":{"description":"Tier is \"public\" or \"gated\". It defaults to gated, so a new artifact is\nclosed until somebody opens it deliberately.","type":"string"},"title":{"description":"Title is what the document is called.","type":"string"},"updated":{"description":"Updated is when the record last changed, unix milliseconds.","type":"integer"}},"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"},"dropped":{"properties":{"deleted":{"description":"Deleted is always true; a record that was not there is a 404 instead.","type":"boolean"},"id":{"description":"ID is the record removed.","type":"string"},"kind":{"description":"Kind is the section.","type":"string"}},"type":"object"},"edgeState":{"properties":{"configured":{"description":"Configured is whether the edge holds credentials to act at all. False means\nevery purge is a no-op.","type":"boolean"},"error":{"description":"Error is the blocker, so an operator reads it instead of guessing at it.","type":"string"},"freshness":{"description":"Freshness says, in one phrase, how long after a publish a reader sees it.\nIt is the sentence an operator actually wants; the booleans above are how\na machine reads the same fact.","type":"string"},"policy":{"additionalProperties":{"type":"string"},"description":"Policy is the Cache-Control this edge serves each class of object with. It\nis DERIVED from the one canonical function, never a second copy: half the\nconfusion when a publish looks stale is not knowing what the TTLs are, and\nreading them out of the source is not something an operator should have to\ndo to answer \"how long until this is live\".","type":"object"},"provider":{"description":"Provider is the CDN behind this edge, or \"none\". It is the first thing an\noperator wants and the only vendor name this API returns.","type":"string"},"reach":{"description":"Reach is the apexes a publish is invalidated on. A site is served on more\nthan one — the site plane's own and the first-party apex — and a purge that\ncovers one of them looks identical from here to a purge that covers both.","items":{"type":"string"},"type":"array"},"status":{"description":"Status is \"ok\" when a publish reaches readers immediately, else \"degraded\".","type":"string"}},"type":"object"},"einIn":{"properties":{"expedited":{"description":"Expedited asks for prioritised handling. Only meaningful when the\nresponsible party cannot file online.","type":"boolean"},"naics":{"description":"NAICS is the six-digit code for what the business does.","type":"string"},"responsible":{"$ref":"#/components/schemas/Responsible","description":"Responsible is the person the IRS holds answerable for the entity."}},"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":{"description":"Connector names a connected account from the org's connector registry, when the\nendpoint reaches its provider through one. Absent means the locator stands on\nits own; the pair below is always sufficient either way.","type":"string"},"locator":{"description":"Locator addresses the thing INSIDE that provider, in the provider's own terms —\nan https clone URL for a hosted forge, a bare repository name for hanzo-git. It\nnever carries a credential.","type":"string"},"provider":{"description":"Provider is the concrete integration: \"github\", \"gitlab\" or \"hanzo-git\".","type":"string"}},"type":"object"},"engineAdvertisement":{"properties":{"apis":{"description":"APIs are the wire formats the engine serves on that one port: \"openai\",\n\"anthropic\", or both.","items":{"type":"string"},"type":"array"},"models":{"description":"Models are the model ids the node's own GET /v1/models answered with — what\nthis GPU can actually be asked for.","items":{"type":"string"},"type":"array"},"status":{"description":"Status is \"ready\" when the node's engine answered, \"unreachable\" when it did\nnot. Advertised is not the same as serving, and this is the difference.","type":"string"},"url":{"description":"URL is the base address the node advertised its engine on — where a model\ncall to this GPU is sent. The node chose it, so reaching it is a question\nabout the node's network, not about this surface.","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"},"esignCompletion":{"properties":{"documentStatus":{"description":"DocumentStatus is COMPLETED when this was the last signature and the document\nsealed here, PENDING while others have still to sign.","type":"string"},"recipientId":{"description":"RecipientID is the recipient who finished.","type":"string"},"sealed":{"description":"Sealed is whether the document sealed on this call — the field values\nrendered onto the PDF and a real x509 PKCS#7 signature applied.","type":"boolean"}},"type":"object"},"esignDocument":{"properties":{"completedAt":{"description":"CompletedAt is when the document sealed, in unix milliseconds; null until it\ndoes.","type":"integer"},"createdAt":{"description":"CreatedAt is when the document was uploaded, in unix milliseconds.","type":"integer"},"externalId":{"description":"ExternalID is the caller's own identifier for this document, echoed back as\nit was given; null when none was.","type":"string"},"fields":{"description":"Fields is every field on the document, ordered by page and then by when it\nwas placed.","items":{"$ref":"#/components/schemas/esignField"},"type":"array"},"id":{"description":"ID is the document id.","type":"string"},"message":{"description":"Message is the covering message stored with the document; null when none was\ngiven. Nothing in this surface sends it.","type":"string"},"recipients":{"description":"Recipients is everyone on the document, ordered by signing order and then by\nwhen they were added — which is also the order a SEQUENTIAL document enforces.","items":{"$ref":"#/components/schemas/esignRecipient"},"type":"array"},"signingOrder":{"description":"SigningOrder is PARALLEL or SEQUENTIAL, fixed when the document was created.","type":"string"},"source":{"description":"Source is how the document came to exist. It is DOCUMENT for everything this\nsurface creates.","type":"string"},"status":{"description":"Status is DRAFT while recipients and fields may still be added, PENDING once\nit has gone out, then COMPLETED when every signer has finished or REJECTED if\nany one of them declined.","type":"string"},"subject":{"description":"Subject is the covering subject line stored with the document; null when none\nwas given.","type":"string"},"title":{"description":"Title is the document's name, and the stem of the download filename.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the document last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"esignDocuments":{"properties":{"documents":{"description":"Documents is the caller org's documents, newest first, capped at 200. There\nis no paging, so read it as the recent window rather than a complete export.","items":{"$ref":"#/components/schemas/esignDocument"},"type":"array"}},"type":"object"},"esignEvent":{"properties":{"createdAt":{"description":"CreatedAt is when it happened, in unix milliseconds.","type":"integer"},"data":{"description":"Data is the event's own detail, whose shape depends on the type — the field\nand recipient a signature was inserted for, the reason a document was\nrejected. Null when the event carried none."},"email":{"description":"Email is the email of whoever caused it, null for an event with no actor —\nthe sender's own calls are recorded without one.","type":"string"},"id":{"description":"ID is the entry id.","type":"string"},"name":{"description":"Name is the name of whoever caused it, null when it was not recorded.","type":"string"},"type":{"description":"Type is what happened: DOCUMENT_CREATED, RECIPIENT_CREATED, FIELD_CREATED,\nDOCUMENT_SENT, DOCUMENT_OPENED, DOCUMENT_FIELD_INSERTED,\nDOCUMENT_RECIPIENT_COMPLETED, DOCUMENT_RECIPIENT_REJECTED or\nDOCUMENT_COMPLETED.","type":"string"}},"type":"object"},"esignField":{"properties":{"customText":{"description":"CustomText is the value a non-signature field was filled with, empty until it\nis. A signature's value is not here: it is stored separately and rendered\nonto the page at sealing.","type":"string"},"fieldMeta":{"description":"FieldMeta is the caller's own metadata for this field, stored verbatim at\nplacement and never interpreted. Null when none was supplied."},"height":{"description":"Height is the field's height, -1 when the renderer is to choose one.","type":"number"},"id":{"description":"ID is the field id.","type":"string"},"inserted":{"description":"Inserted is whether this field has been filled in.","type":"boolean"},"page":{"description":"Page is the 1-based page the field sits on.","type":"number"},"positionX":{"description":"PositionX is the field's horizontal position on that page.","type":"number"},"positionY":{"description":"PositionY is the field's vertical position on that page.","type":"number"},"recipientId":{"description":"RecipientID is who must fill this field. It is absent on a signer's own view\nof a document, where every field returned is already theirs.","type":"string"},"type":{"description":"Type is what the field collects — SIGNATURE, DATE, NAME, EMAIL, TEXT and the\nrest.","type":"string"},"width":{"description":"Width is the field's width, -1 when the renderer is to choose one.","type":"number"}},"type":"object"},"esignFieldIn":{"properties":{"fieldMeta":{"description":"FieldMeta is your own metadata for this field — a label, a placeholder, a\nrequired flag — stored verbatim and handed back on every read of the\ndocument. Any JSON, and esign never interprets it."},"height":{"description":"Height is the field's height, defaulting to -1, which means the renderer\nchooses one when the document is sealed."},"page":{"description":"Page is the 1-based page the field sits on, defaulting to 1."},"positionX":{"description":"PositionX is the field's horizontal position on that page, defaulting to 0."},"positionY":{"description":"PositionY is the field's vertical position on that page, defaulting to 0."},"recipientId":{"description":"RecipientID is who must fill this field. Required, and it must belong to this\ndocument — a recipient from elsewhere is refused rather than silently\naccepted."},"type":{"description":"Type is what the field collects: SIGNATURE, FREE_SIGNATURE, INITIALS, NAME,\nEMAIL, DATE, TEXT, NUMBER, RADIO, CHECKBOX or DROPDOWN. Required, and\nanything else is refused."},"width":{"description":"Width is the field's width, defaulting to -1, which means the renderer\nchooses one when the document is sealed."}},"type":"object"},"esignHealth":{"properties":{"service":{"description":"Service names the subsystem that answered, so a probe reading several looks\nthe same on each.","type":"string"},"status":{"description":"Status is ok whenever the subsystem is mounted. It is never anything else:\nthis route is registered before the document host is built, so it is\nreachability and not a promise that documents can be stored.","type":"string"}},"type":"object"},"esignInsertion":{"properties":{"fieldId":{"description":"FieldID is the field that was filled.","type":"string"},"inserted":{"description":"Inserted is true — the field now holds a value. Filling every field still\nleaves the document pending until the completion call.","type":"boolean"}},"type":"object"},"esignInvite":{"properties":{"email":{"description":"Email is the address the invitation is for, lower-cased.","type":"string"},"id":{"description":"ID is the new recipient's id, which is what a field is placed against.","type":"string"},"name":{"description":"Name is the recipient's display name, empty when none was given.","type":"string"},"role":{"description":"Role is the role they were recorded with — SIGNER unless another was asked\nfor.","type":"string"},"token":{"description":"Token is the crypto-random signing capability for this recipient. It is the\nentire credential their signing endpoint accepts, so treat it as a secret\nand hand it only to them: the signing link is built from it.","type":"string"}},"type":"object"},"esignLink":{"properties":{"email":{"description":"Email is the address this link is meant for.","type":"string"},"recipientId":{"description":"RecipientID is the recipient the link identifies.","type":"string"},"role":{"description":"Role is their role — only a SIGNER or an APPROVER gets a link, because only\nthey are asked to act.","type":"string"},"signingPath":{"description":"SigningPath is the tail of the address to send them, relative to wherever the\nsigning page is served.","type":"string"},"token":{"description":"Token is the crypto-random signing capability. It is the entire credential,\nso treat it as a secret and give each one only to the recipient it names.","type":"string"}},"type":"object"},"esignLinks":{"properties":{"id":{"description":"ID is the document that went out.","type":"string"},"recipients":{"description":"Recipients is one link per signing recipient. Nothing is emailed by this\ncall; delivering the links is the caller's.","items":{"$ref":"#/components/schemas/esignLink"},"type":"array"},"status":{"description":"Status is PENDING — the state a sent document is in until every signer has\nfinished.","type":"string"}},"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"},"esignPDF":{"properties":{"filename":{"description":"Filename is the name to save it under, built from the title and marked\n_signed once it is sealed.","type":"string"},"id":{"description":"ID is the document.","type":"string"},"pdfBase64":{"description":"PdfBase64 is the PDF itself, base64-encoded. There is one field either way,\nso Sealed is what tells you which artifact you are holding.","type":"string"},"sealed":{"description":"Sealed is whether this is the SEALED artifact — the field values rendered\nonto the page and a real x509 PKCS#7 signature applied — rather than the\noriginal upload.","type":"boolean"},"status":{"description":"Status is the document's state at the moment it was read.","type":"string"}},"type":"object"},"esignPlacement":{"properties":{"id":{"description":"ID is the new field's id.","type":"string"},"page":{"description":"Page is the page it was placed on.","type":"number"},"recipientId":{"description":"RecipientID is who must fill it.","type":"string"},"type":{"description":"Type is what it collects.","type":"string"}},"type":"object"},"esignRecipient":{"properties":{"email":{"description":"Email is where this recipient's signing link is meant to go, lower-cased.","type":"string"},"id":{"description":"ID is the recipient id, which is what a field is placed against.","type":"string"},"name":{"description":"Name is the recipient's display name, empty when none was given.","type":"string"},"readStatus":{"description":"ReadStatus is NOT_OPENED until they first open their link, then OPENED.","type":"string"},"rejectionReason":{"description":"RejectionReason is why they declined, null unless they did.","type":"string"},"role":{"description":"Role is SIGNER, CC, VIEWER, APPROVER or ASSISTANT. A document waits only for\nits SIGNERs and APPROVERs before it can seal.","type":"string"},"sendStatus":{"description":"SendStatus is NOT_SENT until the document goes out, then SENT. A CC recipient\nis SENT from the moment they are added.","type":"string"},"signedAt":{"description":"SignedAt is when they finished or declined, in unix milliseconds; null while\nneither has happened.","type":"integer"},"signingOrder":{"description":"SigningOrder is their position in a SEQUENTIAL document, null when they were\nadded without one. A PARALLEL document ignores it.","type":"number"},"signingStatus":{"description":"SigningStatus is NOT_SIGNED, SIGNED or REJECTED. A CC recipient is SIGNED\nfrom the moment they are added, because they are never asked.","type":"string"}},"type":"object"},"esignRecipientIn":{"properties":{"email":{"description":"Email is where the signing link is meant to go, lower-cased on the way in.\nRequired."},"name":{"description":"Name is the recipient's display name, also the fallback a NAME field is\nfilled with when they leave it blank. Optional."},"role":{"description":"Role is SIGNER (the default), CC, VIEWER, APPROVER or ASSISTANT; anything\nelse reads as SIGNER. A CC recipient is recorded as already complete, because\nthey are never asked to sign. SIGNER and APPROVER are the roles a document\nwaits for before it can seal."},"signingOrder":{"description":"SigningOrder is this recipient's position when the document is SEQUENTIAL —\nlower signs first, and ties fall back to the order they were added. Ignored\nby a PARALLEL document. Optional."}},"type":"object"},"esignRejectIn":{"properties":{"reason":{"description":"Reason is why the signer is declining. Optional, stored, and written onto the\naudit trail with the rejection — it is what the sender sees."}},"type":"object"},"esignRejection":{"properties":{"recipientId":{"description":"RecipientID is the recipient who declined.","type":"string"},"status":{"description":"Status is REJECTED — one declining signer ends the document for everyone, and\nthere is no route back.","type":"string"}},"type":"object"},"esignSession":{"properties":{"document":{"$ref":"#/components/schemas/esignState","description":"Document is what is being signed."},"fields":{"description":"Fields is only the fields this recipient must fill — never another party's,\nso the layout a signer sees cannot reveal what anyone else was asked for.","items":{"$ref":"#/components/schemas/esignField"},"type":"array"},"pdfBase64":{"description":"PdfBase64 is the PDF to display, base64-encoded. Null when the document's\nstored bytes are missing.","type":"string"},"recipient":{"$ref":"#/components/schemas/esignSigner","description":"Recipient is who the token says you are."}},"type":"object"},"esignSigner":{"properties":{"email":{"description":"Email is the address the link was issued to.","type":"string"},"id":{"description":"ID is the recipient id.","type":"string"},"name":{"description":"Name is the display name recorded for them, empty when none was given.","type":"string"},"role":{"description":"Role is the role they were added with.","type":"string"},"signingStatus":{"description":"SigningStatus is NOT_SIGNED until they finish or decline.","type":"string"}},"type":"object"},"esignState":{"properties":{"id":{"description":"ID is the document id.","type":"string"},"status":{"description":"Status is PENDING while it is out for signature.","type":"string"},"title":{"description":"Title is the document's name.","type":"string"}},"type":"object"},"esignTrail":{"properties":{"documentId":{"description":"DocumentID is the document the trail belongs to.","type":"string"},"entries":{"description":"Entries is every recorded event in order, oldest first. It is append-only:\nnothing in this surface edits or removes an entry.","items":{"$ref":"#/components/schemas/esignEvent"},"type":"array"}},"type":"object"},"esignUploadIn":{"properties":{"externalId":{"description":"ExternalID is your own identifier for this document, stored and echoed back\nso a document here can be matched to a record in your system. Optional."},"message":{"description":"Message is the covering message carried with the document. Optional, and like\nthe subject it is stored rather than sent."},"pdfBase64":{"description":"PdfBase64 is the document itself, base64-encoded; a `data:` URL prefix is\naccepted and stripped. Required. The bytes go to object storage rather than\ninto the tenant database, and this ORIGINAL is kept under its own key so it\nsurvives sealing untouched."},"signingOrder":{"description":"SigningOrder chooses PARALLEL — the default, where everyone may sign at once\n— or SEQUENTIAL, where each signer waits for the ones ahead of them. Anything\nelse reads as PARALLEL, and the choice is fixed for the document's life."},"subject":{"description":"Subject is the covering subject line carried with the document. Optional, and\nnothing in this surface emails it — delivering the signing links is the\ncaller's."},"title":{"description":"Title is the document's name, shown to every recipient and used to build the\ndownload filename. Required — a request without one is refused and nothing is\nstored."}},"type":"object"},"esignValueIn":{"properties":{"isBase64":{"description":"IsBase64 marks Value as drawn signature image bytes rather than a typed\nsignature. Only a signature field reads it, and a `data:` URL prefix is\nstripped."},"value":{"description":"Value is what goes in the field. A signature takes the drawn image bytes or a\ntyped name; a DATE, NAME or EMAIL field falls back to today, the recipient's\nname or their email when this is omitted; every other type requires it."}},"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"},"evaluatorList":{"properties":{"data":{"description":"Data is the caller org's judges, bounded by limit.","items":{"$ref":"#/components/schemas/evaluatorView"},"type":"array"}},"type":"object"},"evaluatorReq":{"properties":{"criteria":{"description":"Criteria is the written standard the judge applies; over 64 KiB is refused.","type":"string"},"model":{"description":"Model is the model that will do the grading.","type":"string"},"name":{"description":"Name is the judge's org-unique handle, matching\n^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. Re-posting a name edits that judge\nrather than adding a second one.","type":"string"},"scoreName":{"description":"ScoreName is the name the resulting scores are filed under. It defaults to\nthe judge's own name and must match the same pattern.","type":"string"}},"required":["name"],"type":"object"},"evaluatorView":{"properties":{"createdAt":{"description":"CreatedAt is when the judge was first defined.","type":"string"},"criteria":{"description":"Criteria is the written standard the judge applies.","type":"string"},"model":{"description":"Model is the model that does the grading.","type":"string"},"name":{"description":"Name is the judge's org-unique handle.","type":"string"},"scoreName":{"description":"ScoreName is the name the resulting scores are filed under.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","type":"string"}},"type":"object"},"eventIn":{"properties":{"actor":{"description":"Actor is who produced the turn. Empty takes the validated caller, which is\nwhat an agent writing its own transcript wants; naming one is for a surface\nrecording on somebody else's behalf.","type":"string"},"id":{"description":"ID is the session to append to, from the path.","type":"string"},"kind":{"description":"Kind is what this turn IS: message, tool-call, spawn, log, status or\ncontrol. Anything else is refused — the vocabulary is closed so a reader\ncan branch on it.","type":"string"},"payload":{"description":"Payload is the turn's own body, any valid JSON up to 64 KiB. It is SCANNED\nfor credentials before it is stored, and a hit refuses the whole append with\n422 and the findings — this plane never redacts a secret into a transcript\nit then keeps."}},"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":{"description":"Actor is who produced the turn. A write that names nobody takes the calling\nprincipal, so this is rarely empty in practice.","type":"string"},"createdAt":{"description":"CreatedAt is when the turn was recorded, RFC 3339 in UTC to the second. Seconds\nare coarse enough that two turns can share one, which is why Seq and not this\nis the order.","type":"string"},"id":{"description":"ID is the event's own handle, minted as \"evt_\" + 32 hex characters. It\nidentifies the turn; Seq is what ORDERS it.","type":"string"},"kind":{"description":"Kind is what the turn IS, from a closed six: message (a model turn),\ntool-call, spawn (a subagent started), log, status, control (a steering\ncommand the running surface consumes). Anything else is refused at the write.","type":"string"},"payload":{"description":"Payload is the turn's body, embedded as JSON rather than as a string —\nwhatever the writer sent, up to 64 KiB, checked for well-formedness and\nscanned for credentials before it was stored. Its SHAPE is the writer's\nbusiness and varies by Kind; this surface does not interpret it."},"seq":{"description":"Seq is the turn's position in this session's log: monotonic from 1, assigned\nby the store inside the insert, and unique PER SESSION rather than globally.\nIt is the cursor a reader resumes from after a reconnect — ask for everything\nafter your last-seen seq.","type":"integer"},"sessionId":{"description":"SessionID is the session this turn belongs to. Carried on every event so a\nstream frame stands alone — a subscriber watching a whole tree gets turns from\nseveral sessions down one connection.","type":"string"}},"type":"object"},"execRequest":{"properties":{"argv":{"description":"Argv is the command as an argument vector, which is the honest form: it\ncannot be word-split by accident. Send this OR Command, not both.","items":{"type":"string"},"type":"array"},"command":{"description":"Command is a shell line, for a caller that holds one. It is a convenience\nover Argv and is the only input that ever reaches a shell.","type":"string"},"dir":{"description":"Dir is the working directory to run in. Empty runs in the class's own\nworkdir — /mnt/data for exec, /work for dev.","type":"string"},"id":{"description":"ID is the sandbox to run in, from the path.","type":"string"},"stdin":{"description":"Stdin is fed to the command on its standard input.","type":"string"},"timeoutSec":{"description":"TimeoutSec bounds the run in seconds. Zero takes the default.","type":"integer"}},"type":"object"},"experimentList":{"properties":{"data":{"description":"Data is the org's experiments, ordered by project then id.","items":{"$ref":"#/components/schemas/Trial"},"type":"array"},"total":{"description":"Total is how many rows Data holds.","type":"integer"}},"type":"object"},"faqList":{"properties":{"faq":{"description":"Faq is the questions and their answers.","items":{},"type":"array"}},"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"},"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"},"findingList":{"properties":{"data":{"description":"Data is the caller org's findings, newest first.","items":{"$ref":"#/components/schemas/findingView"},"type":"array"}},"type":"object"},"findingView":{"properties":{"createdAt":{"description":"CreatedAt is when the finding was recorded, in Unix milliseconds.","type":"integer"},"fingerprint":{"description":"Fingerprint is the SHA-256 of the raw secret. It is what makes the same\nsecret recognisable across scans and after rotation without the secret ever\nbeing written down.","type":"string"},"id":{"description":"ID addresses this finding.","type":"string"},"line":{"description":"Line is where in that file.","type":"integer"},"path":{"description":"Path is the file the secret was found in.","type":"string"},"preview":{"description":"Preview is the secret MASKED — first and last characters kept, the middle\nstarred — so a reviewer can recognise it without it being disclosed.","type":"string"},"ruleId":{"description":"RuleID is the detection rule that fired.","type":"string"},"ruleName":{"description":"RuleName is that rule's human name.","type":"string"},"scanId":{"description":"ScanID is the scan that produced it.","type":"string"},"severity":{"description":"Severity ranks the finding: critical, high, medium or low.","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":{"description":"At is when this reading was MEASURED, RFC 3339 in UTC — not when the board\nwas built. A console decides staleness by comparing it to now; the board\ndeliberately does not decide that for it.","type":"string"},"gpuUtil":{"description":"GPUUtil is aggregate accelerator utilization as a FRACTION of 1 — 0.42 is\n42% busy, never 42. Across all of the unit's cards, not one of them.","type":"number"},"load1":{"description":"Load1 is the host's 1-minute load average — runnable processes, not a\npercentage, so it is read against the unit's core count and can exceed 1.","type":"number"},"memFree":{"description":"MemFree is host memory still available, in BYTES. It is what the source\nreported, not fleetSpec.Memory minus MemUsed.","type":"integer"},"memUsed":{"description":"MemUsed is host memory in use, in BYTES.","type":"integer"}},"type":"object"},"fleetSpec":{"properties":{"arch":{"description":"Arch is the CPU architecture, amd64 or arm64, and it is what decides whether\na binary built for the fleet will run here. Only the sources that report one\ncarry it (a linked run-target, a BYO worker).","type":"string"},"cpus":{"description":"CPUs is logical cores on the unit.","type":"integer"},"gpuModel":{"description":"GPUModel names the FIRST accelerator (\"NVIDIA GB10\") as the representative of\nthe set; GPUs carries how many. Empty for a cluster, whose cards are counted\nrather than modelled, and for a unit with none.","type":"string"},"gpus":{"description":"GPUs is how many accelerators the unit has. For a cluster it is the vendor\ntotals summed across every node, so it counts cards, not machines.","type":"integer"},"memory":{"description":"Memory is total system RAM in BYTES — not GB, and not what is free right now\n(fleetMetrics carries that). Absent when the source reports no RAM figure.","type":"integer"},"os":{"description":"OS is the operating system the unit runs: linux, darwin or windows. Empty\nwhen the source does not report one — a cluster row does not.","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":{"description":"Host is the unit's hostname. Empty for a unit that is not one host: a cluster\nrow has no hostname to report.","type":"string"},"kind":{"description":"Kind is what the unit IS: laptop, cloud, gpu, cluster, machine or worker.","type":"string"},"label":{"description":"Label is the name to show a human — a target's label, a worker's hostname, a\nmachine's display name. Empty when the source has none to give.","type":"string"},"metrics":{"$ref":"#/components/schemas/fleetMetrics","description":"Metrics is the unit's latest utilization: its own live snapshot when it keeps\none (a run-target's heartbeat wins), else the newest sample from the series\nfor the SAME source. Absent means nothing is known about this unit's load —\nwhich is deliberately not the same as a reading of zero."},"queued":{"description":"Queued is how many renders are waiting on THIS GPU's own lane in the org's\ngpu-jobs queue. BYO units only — an agent run-target dispatches, it does not\nqueue — and omitted when nothing is waiting.","type":"integer"},"running":{"description":"Running is what the unit is executing right now: agent sessions in flight for\na run-target, claimed renders for a BYO GPU.","type":"integer"},"sessions":{"description":"Sessions is how many agent sessions are open on this unit. Always present,\nand 0 for a source that cannot host agent sessions at all — a fact about that\nplane, not a gap in the reading.","type":"integer"},"source":{"description":"Source is the plane this row came from: \"agent\" (a linked run-target), \"byo\"\n(a worker or cluster the org dialed in) or \"visor\" (a machine Hanzo\nprovisioned). It is half the row's identity, and it says which face owns the\nunit — /v1/agents/targets, /v1/visor/fleet/workers, /v1/visor/machines.","type":"string"},"spec":{"$ref":"#/components/schemas/fleetSpec","description":"Spec is the unit's static capability. Absent when the source reported none —\nunknown capability, never a zeroed one."},"status":{"description":"Status is liveness in the SOURCE's own vocabulary, because each plane decides\nit differently: a run-target's is derived from its heartbeat, a BYO worker's\nis online/offline on the 90s window, a BYO cluster's is \"attached\", and a\nVisor machine's is the provider's word for its lifecycle state.","type":"string"},"unit":{"description":"Unit is the SOURCE's own id for this unit — a run-target id, a BYO worker id,\na Visor machine name — so a row links straight back to the face that owns it.\nIt is unique within a source, not across them: two planes may mint the same\nid, which is why (source, unit) together is the identity.","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"},"fnList":{"properties":{"functions":{"description":"Functions is one row per published function.","items":{"$ref":"#/components/schemas/functionView"},"type":"array"}},"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"},"frameworkList":{"properties":{"frameworks":{"description":"Frameworks is each framework and how many clauses it publishes.","items":{"$ref":"#/components/schemas/frameworkRow"},"type":"array"}},"type":"object"},"frameworkRow":{"properties":{"edition":{"description":"Edition is which edition this clause list is taken from.","type":"string"},"framework":{"description":"Framework is the framework id.","type":"string"},"name":{"description":"Name is the published standard's name.","type":"string"},"publisher":{"description":"Publisher is who publishes it.","type":"string"},"total":{"description":"Total is how many clauses the standard publishes.","type":"integer"},"unit":{"description":"Unit is what one clause is; Units is its plural.","type":"string"},"units":{"description":"Units is the plural of Unit.","type":"string"}},"type":"object"},"functionDetail":{"properties":{"avgDurationMs":{"type":"number"},"createdAt":{"type":"string"},"endpoint":{"type":"string"},"envCount":{"type":"integer"},"environment":{"type":"string"},"errors7d":{"type":"integer"},"image":{"type":"string"},"invocations7d":{"type":"integer"},"lastDeployedAt":{"type":"string"},"memoryLimit":{"type":"string"},"name":{"type":"string"},"namespace":{"type":"string"},"recentInvocations":{"description":"RecentInvocations is its twenty most recent runs, newest first.","items":{"$ref":"#/components/schemas/invocationView"},"type":"array"},"secrets":{"description":"Secrets are the NAMES it mounts. Values are never read or returned.","items":{"type":"string"},"type":"array"},"status":{"type":"string"},"successRate":{"type":"number"},"target":{"type":"string"},"timeoutSec":{"type":"integer"},"triggers":{"description":"Triggers is how this function is reached.","items":{"$ref":"#/components/schemas/triggerView"},"type":"array"}},"type":"object"},"functionView":{"properties":{"avgDurationMs":{"description":"mean wall-clock of those runs","type":"number"},"createdAt":{"description":"when it was first published","type":"string"},"endpoint":{"description":"the path that invokes it","type":"string"},"envCount":{"description":"how many secret NAMES it mounts; values are never carried","type":"integer"},"environment":{"description":"the language it runs under","type":"string"},"errors7d":{"description":"how many of those runs failed","type":"integer"},"image":{"description":"the prebuilt image it runs, when it runs one instead of source","type":"string"},"invocations7d":{"description":"runs in the last 7 days; ABSENT, never 0, when it has not run","type":"integer"},"lastDeployedAt":{"description":"when its code last changed","type":"string"},"memoryLimit":{"description":"the memory it runs with, and the multiplier on its compute charge","type":"string"},"name":{"description":"the function's org-unique handle","type":"string"},"namespace":{"description":"the display group it belongs to; the org is the isolation key","type":"string"},"status":{"description":"whether it is ready to serve","type":"string"},"successRate":{"description":"share of those runs that succeeded, 0..1","type":"number"},"target":{"description":"where it runs: empty for the sandbox, \"fleet\" for the org's GPU fleet","type":"string"},"timeoutSec":{"description":"its per-invocation deadline","type":"integer"}},"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":{"description":"Branch is the branch a push-to-deploy build tracks, `main` when the create\nnamed none — a push to any other branch, and every tag push, builds nothing\nhere. A deploy may name a commit instead, for that deploy alone.","type":"string"},"provider":{"description":"Provider is derived from the URL — github, gitlab, bitbucket, or `git` for\nanything else. It is a label for display; no behaviour keys on it.","type":"string"},"url":{"description":"URL is the clone URL a git app builds from, stored as sent once the build\npath's allowlist accepted it (validateRepoURL). It is also what a landed\npush is MATCHED against, so a push to any other repo never builds this app.","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"},"githubClaimIn":{"properties":{"accounts":{"description":"Accounts names GitHub logins the App is installed on (\"hanzoai\"). Matched\ncase-insensitively, since GitHub logins are. Ignored when all is true.","items":{"type":"string"},"type":"array"},"all":{"description":"All binds every account the App holds, instead of naming them.","type":"boolean"}},"type":"object"},"githubClaimOut":{"properties":{"already":{"description":"Already were bound before the call and are unchanged by it.","items":{"type":"string"},"type":"array"},"claimed":{"description":"Claimed are the accounts this call bound. Never null; [] when none.","items":{"type":"string"},"type":"array"}},"type":"object"},"githubForkOut":{"properties":{"clone_url":{"description":"CloneURL is the fork's https git remote. GitHub populates a new fork in the\nbackground, so a clone issued the moment this answers can still find it empty.","type":"string"},"default_branch":{"description":"DefaultBranch is the branch the fork checks out, inherited from upstream.","type":"string"},"existing":{"description":"Existing reports that the fork was already there. GitHub answers 202 either\nway, so without this a caller cannot tell \"made you one\" from \"you had one\".","type":"boolean"},"full_name":{"description":"FullName is the fork's \"owner/repo\". The owner is the account it landed in\n— the request's org, or the installation's own account when none was named.","type":"string"},"html_url":{"description":"HTMLURL is the fork's page on github.com.","type":"string"}},"type":"object"},"githubForkReq":{"properties":{"org":{"description":"Org is the GitHub account to fork INTO; empty forks to the installation's\nown account, which is the common case.","type":"string"},"repo":{"description":"Repo is a repository the org's installation was GRANTED, by name.","type":"string"}},"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"},"githubInstallationView":{"properties":{"connected":{"description":"Connected reports whether THIS org has already bound this account.","type":"boolean"},"grant":{"description":"Grant is \"all\" or \"selected\" — how many of the account's repositories the\ninstall covers. A reader deciding what to import needs the reach, not just\nthe name.","type":"string"},"htmlUrl":{"description":"HTMLURL is the account's page on GitHub.","type":"string"},"login":{"description":"Login is the GitHub account name — the org or user the App is installed on.","type":"string"},"type":{"description":"Type is \"Organization\" or \"User\".","type":"string"}},"type":"object"},"githubInstallationsOut":{"properties":{"installUrl":{"description":"InstallURL is where to grant a new account, so a UI with an empty list has\nsomewhere to send the reader instead of a dead end.","type":"string"},"installations":{"description":"Installations is every account the caller may see: the ones its own org has\nbound, or — for a super admin — every account the App is installed on.\nNever null; [] when none.","items":{"$ref":"#/components/schemas/githubInstallationView"},"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"},"unread":{"description":"Unread names the connected accounts this answer could NOT read, so a short\nlist is distinguishable from a complete one. Absent when the answer is whole.\n\nThe fan-out is per installation, and one account failing used to be dropped\nin silence: the response stayed 200 and simply carried fewer repositories,\nerroring only when EVERY account failed. Measured, twice in a row, minutes\napart: 1475 repositories, then 1157 — a whole installation missing with\nnothing in the answer to say so. Anything driven off the list then\nunder-covers and reports success, which is the failure this field ends.","items":{"type":"string"},"type":"array"}},"type":"object"},"githubSearchHit":{"properties":{"clone_url":{"description":"CloneURL is the repository's https git remote.","type":"string"},"default_branch":{"description":"DefaultBranch is the branch a clone checks out.","type":"string"},"description":{"description":"Description is the blurb the repository's owner wrote. Empty when it has none.","type":"string"},"full_name":{"description":"FullName is the repository's \"owner/repo\" on GitHub. Finding it here does\nNOT make it forkable: githubFork takes a repo the org's installation was\ngranted, and a hit from the public index usually is not one.","type":"string"},"html_url":{"description":"HTMLURL is the repository's page on github.com.","type":"string"},"language":{"description":"Language is the primary language GitHub detected from the file mix (\"Go\",\n\"TypeScript\"). Empty when GitHub attributes none.","type":"string"},"private":{"description":"Private is GitHub's visibility flag, passed through. This op reads the\npublic index — the org's token only charges the rate limit to the\ninstallation — so it is false for everything a search can reach.","type":"boolean"},"stars":{"description":"Stars is GitHub's stargazers_count as the SEARCH INDEX held it when the\nquery ran — a snapshot, not a live count off the repository.","type":"integer"}},"type":"object"},"githubSearchOut":{"properties":{"count":{"description":"Count is how many hits Repos carries. It is that array's length, NOT\nGitHub's total_count, so it never exceeds limit and says nothing about how\nmany more repositories matched.","type":"integer"},"repos":{"description":"Repos are the matching repositories in GitHub's own relevance order, capped\nat limit. Always an array, never null.","items":{"$ref":"#/components/schemas/githubSearchHit"},"type":"array"}},"type":"object"},"githubSearchReq":{"properties":{"limit":{"description":"Limit caps the answer; 0 takes the default and anything above the ceiling\nis clamped rather than refused.","type":"integer"},"q":{"description":"Q is GitHub's own search syntax, passed through: \"tetris language:go\",\n\"org:hanzoai stars:>10\". Passing it through rather than inventing a\nvocabulary means one thing to learn, and it is theirs.","type":"string"}},"type":"object"},"gitlabProjectView":{"properties":{"cloneUrl":{"description":"CloneURL is the https remote to clone.","type":"string"},"defaultBranch":{"description":"DefaultBranch is the branch a clone lands on (\"main\" when GitLab names none,\nwhich is what an empty project reports).","type":"string"},"description":{"description":"Description is the project's own, empty when it has none.","type":"string"},"fullName":{"description":"FullName is the namespace path (\"acme/widgets\", \"acme/team/widgets\" for a\nsubgroup) — the string GitLab calls path_with_namespace.","type":"string"},"htmlUrl":{"description":"HTMLURL is the project's page.","type":"string"},"name":{"description":"Name is the project's path segment (\"widgets\"), not its display name.","type":"string"},"private":{"description":"Private is true for anything not publicly visible (private or internal).","type":"boolean"},"pushedAt":{"description":"PushedAt is RFC3339 last activity, so a client can sort or say \"2h ago\".","type":"string"}},"type":"object"},"gitlabProjectsOut":{"properties":{"account":{"description":"Account is the connected GitLab username, so a client can label the list\nwithout a second call. Empty when the connection recorded none.","type":"string"},"projects":{"description":"Projects is every project the token reaches, newest activity first. Never\nnull; [] when the account has none.","items":{"$ref":"#/components/schemas/gitlabProjectView"},"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":{"description":"Attempt is which try this is, counting from 1. Above 1 means the job was\nretried after a failed or abandoned run.","type":"integer"},"closeTime":{"description":"CloseTime is when the job reached a terminal state, RFC 3339. Empty means it\nis still live — queued, running or stalled.","type":"string"},"failureCause":{"description":"FailureCause is the engine's reason the job failed. Empty unless it did.","type":"string"},"gpu":{"description":"GPU is the node this job is aimed AT — the lane \"gpu:<node>\" it was submitted\non. Empty means the shared any-GPU lane: it was not aimed anywhere and the\nfirst free worker takes it.","type":"string"},"id":{"description":"ID is the job's id, and the id the cancel route takes. The dispatcher sets it\nequal to the render's prompt id, so it is the same value the studio knows the\njob by.","type":"string"},"label":{"description":"Label is the cheap human name for the render — the output filename prefix\nlifted out of the submitted graph. Empty when the graph carried none. The\ngraph itself is never in this list; the tasks describe endpoint serves it.","type":"string"},"lastHeartbeat":{"description":"LastHeartbeat is the claiming worker's most recent beat on this job, RFC 3339\n— the evidence a long render is still alive rather than wedged.","type":"string"},"leaseExpiry":{"description":"LeaseExpiry is when the worker's claim lapses, RFC 3339. Past it with the job\nstill STARTED, the claimant is presumed dead and Status reads \"stalled\".","type":"string"},"runId":{"description":"RunID identifies this execution of the job. It equals ID for a job the\ndispatcher submitted, which is why a cancel that omits it still works.","type":"string"},"startTime":{"description":"StartTime is when a worker began executing the job, RFC 3339. Empty while it\nis still queued.","type":"string"},"status":{"description":"Status is the job's lifecycle state: queued, running, completed, failed or\ncanceled — plus \"stalled\", which is this surface's own reading of a job that\nis STARTED whose worker died: its lease has elapsed and no reaper has taken\nit back yet. Without it such a job reads \"running\" forever. An engine state\nthis surface does not recognize passes through lower-cased rather than being\ncoerced into one of these.","type":"string"},"type":{"description":"Type is the work being done (\"studio.render\") — what the claiming worker has\nto be able to execute.","type":"string"},"worker":{"description":"Worker is the node that actually CLAIMED the job, which is not always the one\nit was aimed at: a shared-lane job has no GPU but does have a Worker once\npicked up. Empty while the job is still waiting.","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":{"description":"ID is the card's address: its host machine's id, \"#\", and the card's ordinal\nwithin that machine (\"gpu-1#0\"). Stable for as long as the machine is, and\nthe only id a single accelerator has — providers do not name cards.","type":"string"},"location":{"description":"Location is where the card physically sits, which for every source today is\nthe same value Region carries — the console renders it in its own column.","type":"string"},"machine":{"description":"Machine is the id of the machine holding this card, addressable as-is on\n/v1/visor/machines/:id.","type":"string"},"memory":{"description":"Memory is the card's VRAM as its own tooling reported it (\"122880 MiB\") — a\ndisplay string in the reporter's units, not a byte count. BYO cards carry it\n(nvidia-smi); Visor's machine object states no VRAM, so a rented card leaves\nit empty and the console renders \"—\" rather than a fabricated 0.","type":"string"},"model":{"description":"Model is the accelerator: the model token read out of the size slug for a\nVisor GPU droplet (\"H100\", \"MI300X\"), or the name nvidia-smi reported for a\nBYO card (\"NVIDIA GB10\").","type":"string"},"name":{"description":"Name is the HOST MACHINE's display name, not the card's — every card in a\ngpu-h100x8 node repeats it. Model is what says which accelerator this is.","type":"string"},"provider":{"description":"Provider distinguishes a BYO accelerator (\"byo\") from a Visor-provisioned one\n(the host machine's real provider). It is what tells a card the org owns from\na card the org rents.","type":"string"},"region":{"description":"Region is the host machine's provider region slug; \"on-prem\" for a BYO card.","type":"string"},"status":{"description":"Status is the HOST MACHINE's lifecycle state, because nothing upstream\nreports a card's own health. A card reads running because its machine does.","type":"string"}},"type":"object"},"graphAssertIn":{"properties":{"assertions":{"description":"Assertions is the batch. Each member is judged on its own: one refusal\ndoes not discard the rest, because a caller redelivering five facts must\nnot lose four of them to one malformed fifth.","items":{"$ref":"#/components/schemas/graphFact"},"type":"array"}},"type":"object"},"graphAssertOut":{"properties":{"duplicate":{"description":"Duplicate is how many members this plane already held. A redelivery\ncollides on its content address and is counted here, not refused: it is\nthe success a retrying caller depends on.","type":"integer"},"reasons":{"description":"Reasons names why each refused member was refused, in the order sent.","items":{"type":"string"},"type":"array"},"recorded":{"description":"Recorded is how many members became new rows.","type":"integer"},"refused":{"description":"Refused is how many members were turned away on arrival, before the store\nwas touched — a missing entity, a timestamp that is not RFC 3339, a\nconfidence outside [0,1]. The rest of the batch was still recorded.","type":"integer"}},"type":"object"},"graphEdge":{"properties":{"from":{"description":"From is the id of the node the edge starts at: the child page for a parent\nedge, the page holding the wikilink for a link edge, the kb-source for a\nprovenance edge. Always one of Nodes.","type":"string"},"kind":{"description":"parent | link | provenance","type":"string"},"to":{"description":"To is the id of the node the edge points at: the parent page, the linked\npage, the kb-connector. Always one of Nodes — a wikilink matching no page\npoints at a synthetic \"unresolved:<lowercased title>\" node rather than\ndangling.","type":"string"}},"type":"object"},"graphFact":{"properties":{"at":{"description":"At is when the thing was so, RFC 3339. Required, and refused when it sits\nmore than five minutes ahead of the server clock — an assertion dated\nfurther out would never mature and would skew every read until it did.","type":"string"},"confidence":{"description":"Confidence in [0,1]. A tie-breaker within the order, never a substitute\nfor it. Absent is 0, the weakest an assertion can be.","type":"number"},"entity":{"description":"Entity is the thing being described, in the organization's own namespace.\nIt is not created: an entity exists because something was asserted about it.\nRequired, 512 bytes at most.","type":"string"},"evidence":{"description":"Evidence points at the record this claim came from, 512 bytes at most. An\nassertion without one is admitted and carries no defence.","type":"string"},"names":{"description":"Names says the value is an entity. A walk reads only the edges, so this is\na declaration and never a guess about the value's shape.","type":"boolean"},"relation":{"description":"Relation is what is being asserted — `depends`, `owner`, `same`, `title`.\nIt is open: this plane holds no vocabulary of its own. Required, 128 bytes\nat most.","type":"string"},"seen":{"description":"Seen is when this assertion became knowable, RFC 3339. Defaults to At and\nmay not precede it. It is provenance and it decides nothing: the instant\nevery read uses is derived as the later of Seen and the server's own clock.","type":"string"},"source":{"description":"Source names who asserted. Required, because an assertion nobody is named\nfor cannot be weighed against one that is. Open text: this plane ranks no\nsource above another.","type":"string"},"value":{"description":"Value is what the relation points at. When Names is true it is another\nentity's key and the assertion is an EDGE; otherwise it is a scalar and\nthe assertion is a property. 2048 bytes at most, or 512 when it names an\nentity.","type":"string"}},"type":"object"},"graphNeighborsIn":{"properties":{"as_of":{"description":"AsOf walks the graph as it stood at an instant, RFC 3339. Absent walks it\nas it stands now.","type":"string"},"depth":{"description":"Depth is how many hops. Absent is one.","type":"integer"},"direction":{"description":"Direction is out, in or both. Out follows an edge from its entity to its\nvalue — what the node points at; in follows it the other way — what points\nat the node; both is the union of the two, not a third rule. Absent is out.","type":"string"},"relation":{"description":"Relation narrows the walk to one edge relation. Absent follows all. Only\nedges are ever followed: an assertion whose value is a scalar is a property\nand is never a hop.","type":"string"},"seeds":{"description":"Seeds is where the walk starts. At least one.","items":{"type":"string"},"type":"array"}},"type":"object"},"graphNeighborsOut":{"properties":{"bound":{"description":"Bound is the ceiling this walk was held to, the same for every caller, so\nTruncated can be read against a number rather than guessed at.","type":"integer"},"depth":{"description":"Depth is the deepest hop count actually reached. It is at most the depth\nasked for, and smaller when the walk ran out of edges first.","type":"integer"},"entities":{"description":"Entities is everything reached, the seeds included, ordered by the fewest\nhops that reach each one and then by key.","items":{"type":"string"},"type":"array"},"truncated":{"description":"Truncated says the bound stopped the walk. The bound is part of the answer\nrather than a silent short read.","type":"boolean"}},"type":"object"},"graphNode":{"properties":{"id":{"description":"\"<doctype>:<name>\" — globally unique, click-to-open key","type":"string"},"name":{"description":"the document name (empty for synthetic nodes)","type":"string"},"project":{"description":"Project is the project scope the underlying document was saved under. Absent\nfor a document saved with none, and for the synthetic nodes — unresolved link\ntargets and connectors belong to no project. When ?project= narrows the\ngraph, every page, memory and source node carries that value.","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"},"graphQLError":{"properties":{"message":{"type":"string"},"path":{"items":{"type":"string"},"type":"array"}},"type":"object"},"graphQLIn":{"properties":{"operationName":{"type":"string"},"query":{"type":"string"},"variables":{}},"type":"object"},"graphQLOut":{"properties":{"data":{},"errors":{"items":{"$ref":"#/components/schemas/graphQLError"},"type":"array"}},"type":"object"},"graphReadOut":{"properties":{"assertions":{"description":"Assertions are the matching rows in the order they were written, oldest\nfirst. Every version is here: this read resolves nothing and withholds\nnothing, so a superseded claim and the one that superseded it both appear.","items":{"$ref":"#/components/schemas/wireFact"},"type":"array"}},"type":"object"},"graphResolveIn":{"properties":{"as_of":{"description":"AsOf is the instant to answer at, RFC 3339. Absent means now.","type":"string"},"entity":{"description":"Entity is the thing to answer about. Required.","type":"string"},"relation":{"description":"Relation is the one relation to settle. Required: this answers a single\n(entity, relation) pair, never a whole entity at once.","type":"string"}},"type":"object"},"graphResolveOut":{"properties":{"as_of":{"description":"AsOf is the instant this answer was taken at, RFC 3339: the one asked for,\nor the server's clock when none was.","type":"string"},"conflicts":{"description":"Conflicts is every OTHER assertion knowable at AsOf, strongest first. They\nare not all disagreements: one that repeats the winner's value ranks below\nit and is listed here too.","items":{"$ref":"#/components/schemas/wireFact"},"type":"array"},"contested":{"description":"Contested is true when at least one conflict claims a value different from\nthe winner's. Any number of conflicts that all agree leaves it false.","type":"boolean"},"entity":{"description":"Entity is the entity the question named, echoed so a stored answer still\nsays what it is about.","type":"string"},"known":{"description":"Known is false when this plane held nothing knowable at AsOf. That is an\nanswer, not an error.","type":"boolean"},"relation":{"description":"Relation is the relation the question named, echoed for the same reason.","type":"string"},"truncated":{"description":"Truncated says this pair holds more assertions than one read returns, so\nthe winner was decided from the most recent ceiling-full of them. It is\nreported because a provenance plane that trims silently is a plane that\nanswers confidently and wrongly; narrow the question with as_of to see\nwhat it dropped.","type":"boolean"},"winner":{"$ref":"#/components/schemas/wireFact","description":"Winner is the assertion in force — the strongest of those knowable at AsOf\nunder the order `rule` names. Absent exactly when Known is false."}},"type":"object"},"graphVocabularyOut":{"properties":{"bound":{"description":"Bound is the ceiling on one walk.","type":"integer"},"relations":{"description":"Relations is what this organization has actually asserted, which is the\nonly vocabulary there is: this plane declares none of its own.","items":{"type":"string"},"type":"array"},"rule":{"description":"Rule names the terms of the precedence order, in the order they apply. A\nreader who is told a winner without the rule cannot check it.","items":{"type":"string"},"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"},"health":{"properties":{"ok":{"description":"OK is true whenever this route answers at all: reaching the handler IS the\nproof that the routes are registered and dispatching.","type":"boolean"},"subsystem":{"description":"Subsystem names what answered, so a health response read out of context still\nsays which surface it came from.","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 endpoint 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"},"historyOut":{"properties":{"benchmark":{"description":"Benchmark is the catalog id these histories are about.","type":"string"},"data":{"description":"Data is one entry per model, ordered by model name.","items":{"$ref":"#/components/schemas/ModelHistory"},"type":"array"},"total":{"description":"Total is how many models Data holds.","type":"integer"}},"type":"object"},"hit":{"properties":{"doctype":{"description":"DocType is which kind of knowledge matched: kb-page (a wiki page), kb-memory\n(a unit of agent memory) or kb-source (a document a connector ingested).\nThose three are the whole indexed set, and searchIn.DocTypes filters on them.","type":"string"},"name":{"description":"Name is the document's name in the framework store — the id to read or open\nit with. Unique per (org, doctype), so it identifies the document with\nDocType and not alone.","type":"string"},"project":{"description":"Project is the project scope the document was saved under. Absent for a\ndocument saved with none, which is also why a project-scoped query cannot\nreach it.","type":"string"},"provider":{"description":"Provider is the connector that ingested the document — github, slack, google\nor notion. Absent for a page or memory written in the product, which came\nfrom no connector.","type":"string"},"score":{"description":"Score is the cosine similarity between the query's embedding and the\ndocument's, from -1 to 1, higher being closer — the collection is created\nwith Cosine distance. Hits arrive ordered by it, descending. There is no\nabsolute cutoff: what counts as a good score moves with the query and the\nembedding model, so compare scores within one response and not across\nqueries.","type":"number"},"title":{"description":"Title is the document's title as it was indexed. Empty for a document saved\nwithout one; it is a label to show, never the id (that is Name).","type":"string"},"url":{"description":"URL is the document's link back into the app it was ingested from. Absent\nwhen the indexed payload carries none, which is the normal case for pages and\nmemories.","type":"string"}},"type":"object"},"holdings":{"properties":{"domains":{"description":"Domains is the caller org's domains, newest registration first.","items":{"$ref":"#/components/schemas/Holding"},"type":"array"}},"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.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"},"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"},"avatar":{"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"},"emoji":{"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.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":{"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.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.\nAccessSecret IS NOT PERSISTED for a key minted at or after the digest\nchange: it carries the secret out to its holder once, in the mint response,\nand the row keeps only AccessSecretDigest. It stays on the struct because\nthat one-time reveal is the whole point of minting, and it stays in the\nschema because rows written before the change still hold a plaintext secret\nthat the resolver drains on first use.","type":"string"},"accessSecret":{"type":"string"},"accessSecretDigest":{"description":"AccessSecretDigest is how a presented secret finds its key: the resolver\ndigests what the caller sent and looks THAT up. It is what lets the row hold\nno plaintext and still be found in one indexed read — a salted hash cannot be\nlooked up by value, which is the reason the plaintext was here.","type":"string"},"act":{"description":"Act is the durable, opt-in grant that lets this key act FOR a user in its\nown org — the credential behind as(): presenting it authorizes minting a\nshort-lived, user-bound token for a member of the key's tenant. Default\nfalse, so a server key mints nothing on anyone's behalf until the grant is\nset deliberately — the capability is never inherited by every key. It is\nconfined at mint time to the key's OWN Owner, and a reserved-org or\nSuperAdmin target is refused, so the grant reaches only ordinary members of\nthe one tenant that holds the key.","type":"boolean"},"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 endpoint 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":{"cursor":{"type":"string"},"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.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"},"avatar":{"description":"How the organization appears across Hanzo — the square mark beside its\nname — as an image or as one emoji, never both. It is the pair a person\ncarries (User.Avatar) under the same names, resolved the same way, so a\nscreen draws a subject without asking which kind of subject it has. Both\nhalves live on the row: a mark that appears everywhere cannot be kept on\none device. Written through schema.MarkOf; Logo and LogoDark above are a\ndifferent thing, the wordmark a login screen draws.","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"},"emoji":{"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.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.SetAvatarInput":{"properties":{"avatar":{"type":"string"},"emoji":{"type":"string"},"name":{"type":"string"},"owner":{"type":"string"}},"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"},"avatar":{"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"},"emoji":{"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"},"mfaRememberDigest":{"description":"MfaRememberDigest is the digest of the token held by the ONE browser the\ndeadline above applies to. Without it the deadline is account-wide and \"don't\nask again on this browser\" switches the second factor off everywhere. It is a\ndigest, never the token, so a database dump yields nothing presentable — and it\ncarries a REAL json tag because orm persists via json.Marshal, so `json:\"-\"`\nwould never be stored (the trap PasswordHash documents above); Mask() strips it\nfrom every response instead.","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"},"attestationFormat":{"description":"AttestationFormat is the statement format the authenticator attested in\n(\"packed\", \"apple\", \"none\", …), which is a DIFFERENT value from the\nattestation type above. The library reads it back when resolving the FIDO\nAppID extension, so a row that dropped it would round-trip a credential the\nverifier no longer recognises as the one it stored.","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.accountBody":{"properties":{"avatar":{"type":"string"},"bio":{"type":"string"},"displayName":{"type":"string"},"homepage":{"type":"string"}},"type":"object"},"iam.assumeBody":{"properties":{"org":{"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.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.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.passwordBody":{"properties":{"code":{"description":"Code is the one-time code delivered to the account's own address.","type":"string"},"oldPassword":{"description":"OldPassword is the credential being replaced, the proof a signed-in caller\ngives instead of a code.","type":"string"},"organization":{"description":"The account being recovered, for a caller who cannot be signed in. Read on\nthe CODE arm only — a signed-in caller is resolved from its own session or\ntoken, never from these.","type":"string"},"password":{"description":"Password is the new credential. It must satisfy the platform floor and the\norganization's own complexity options.","type":"string"},"username":{"description":"email, username OR phone","type":"string"}},"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.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.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.projects.ListOutput":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/iam.Project"},"type":"array"},"total":{"type":"integer"}},"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"},"enableCodeSignin":{"description":"EnableCodeSignin offers sign-in by an emailed or texted one-time code\nbeside the password. A POINTER for the same reason as IsShared: a plain\nbool reads as false on every reconcile that says nothing and would switch\nthe method off for every app whose caller never mentioned it.","type":"boolean"},"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->token 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.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.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.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.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"},"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":{"description":"Repository is the image path without a tag (ghcr.io/acme/api). Required for\nsource `image`, which runs it as-is. A git app's built image is NOT this: the\nbuild pushes to a path derived from the org and slug, and the deployment\nrecords that full ref.","type":"string"},"tag":{"description":"Tag is the tag to run: what the create declared, then RE-STAMPED on every\ntransition to live with the tag that actually went live. So after a deploy\nit names what is running, not what was asked for.","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"},"indexCount":{"properties":{"isIndexing":{"description":"IsIndexing is always false: writes are applied before their response, so\nthere is never a background pass a caller could be waiting on.","type":"boolean"},"numberOfDocuments":{"description":"NumberOfDocuments is how many documents this org holds in that index.","type":"integer"}},"type":"object"},"indexDocuments":{"properties":{"limit":{"description":"Limit is how many documents this page could hold.","type":"integer"},"offset":{"description":"Offset is where this page starts.","type":"integer"},"results":{"description":"Results are the documents themselves, exactly as they were stored.","items":{},"type":"array"},"total":{"description":"Total is how many documents the index holds altogether.","type":"integer"}},"type":"object"},"indexEnqueued":{"properties":{"enqueuedAt":{"description":"EnqueuedAt is when the task was recorded, RFC 3339 — which is also when it\ncompleted.","type":"string"},"indexUid":{"description":"IndexUID names the index the write landed in.","type":"string"},"status":{"description":"Status is always `enqueued`, for dialect compatibility. The work is\nalready done.","type":"string"},"taskUid":{"description":"TaskUID identifies the task for a client that polls it. Polling resolves\nimmediately.","type":"integer"},"type":{"description":"Type is the dialect's name for the kind of write: indexCreation,\nindexDeletion, settingsUpdate, documentAdditionOrUpdate, documentDeletion.","type":"string"}},"type":"object"},"indexFilter":{"properties":{"filterableAttributes":{"description":"FilterableAttributes replaces the whole filterable set. Omitted leaves it\nunchanged; an empty array clears it.","items":{"type":"string"},"type":"array"}},"type":"object"},"indexHealth":{"properties":{"status":{"description":"Status is `available` when the store is readable and `unavailable` when it\nis not — the second answer rides a 503, so a pod with a broken volume is\ntaken out of rotation rather than serving empty searches.","type":"string"}},"type":"object"},"indexHits":{"properties":{"estimatedTotalHits":{"description":"EstimatedTotalHits is the dialect's name for the match count. Every hit is\nmaterialised here, so for this page it is exact rather than estimated.","type":"integer"},"hits":{"description":"Hits are the matching documents, most relevant first, exactly as stored.","items":{},"type":"array"},"limit":{"description":"Limit is how many hits this page could hold.","type":"integer"},"offset":{"description":"Offset is where this page starts.","type":"integer"},"processingTimeMs":{"description":"ProcessingTimeMs is how long the query took, in milliseconds.","type":"integer"},"query":{"description":"Query echoes the search terms, which is what a client renders above the\nresults.","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"},"indexList":{"properties":{"limit":{"description":"Limit is how many rows this page could hold.","type":"integer"},"offset":{"description":"Offset is where this page starts.","type":"integer"},"results":{"description":"Results are the index definitions themselves.","items":{"$ref":"#/components/schemas/indexView"},"type":"array"},"total":{"description":"Total is how many indexes the org holds altogether.","type":"integer"}},"type":"object"},"indexNew":{"properties":{"primaryKey":{"description":"PrimaryKey is the document field that identifies a row. Optional — the\nfirst write establishes one when it is omitted.","type":"string"},"uid":{"description":"UID is the index's name within the org. Required.","type":"string"}},"type":"object"},"indexQuery":{"properties":{"filter":{"description":"Filter narrows the hits, in the dialect's filter syntax: a string, or an\narray of strings combined with AND. Only attributes named in the index's\nfilterableAttributes can be constrained. Left unconstrained here because\nthe dialect accepts both shapes and naming one would publish an API that\ncannot send the other."},"limit":{"description":"Limit is how many hits to return. Absent means 20; the ceiling is 1000.","type":"integer"},"offset":{"description":"Offset is where to start. Absent means 0.","type":"integer"},"q":{"description":"Q is the search text. Typos are forgiven. An empty Q matches everything,\nwhich is how a client lists an index by relevance rather than by insertion\norder.","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"},"indexSettings":{"properties":{"filterableAttributes":{"description":"FilterableAttributes are the document fields a search `filter` may\nconstrain. A field not listed here cannot be filtered on.","items":{"type":"string"},"type":"array"}},"type":"object"},"indexStats":{"properties":{"databaseSize":{"description":"DatabaseSize is the org's total document count across its indexes. It is a\ncount, not bytes: the store is shared by every tenant, so a byte figure\nwould either be the whole file (another tenant's size) or a fiction.","type":"integer"},"indexes":{"additionalProperties":{"$ref":"#/components/schemas/indexCount"},"description":"Indexes maps each index uid to its own count.","type":"object"}},"type":"object"},"indexTask":{"properties":{"enqueuedAt":{"description":"EnqueuedAt, StartedAt and FinishedAt are the same instant: the write was\napplied before its task id was minted.","type":"string"},"finishedAt":{"description":"FinishedAt is when the write completed.","type":"string"},"startedAt":{"description":"StartedAt is when the write began.","type":"string"},"status":{"description":"Status is always `succeeded`.","type":"string"},"type":{"description":"Type names the kind of write, for a client that inspects it.","type":"string"},"uid":{"description":"UID echoes the task id that was asked about.","type":"integer"}},"type":"object"},"indexVersion":{"properties":{"commitDate":{"description":"CommitDate is empty here: this surface is a dialect implementation, not a\nbuild of Meilisearch, so there is no upstream commit to date.","type":"string"},"commitSha":{"description":"CommitSha names the implementation (`hanzo-cloud`) rather than a build\nhash, so a client logging it records which server answered instead of\nimplying a Meilisearch release.","type":"string"},"pkgVersion":{"description":"PkgVersion is this dialect implementation's own version.","type":"string"}},"type":"object"},"indexView":{"properties":{"createdAt":{"description":"CreatedAt is when this org first created the index, RFC 3339.","type":"string"},"primaryKey":{"description":"PrimaryKey is the document field that identifies a row; an upsert is keyed\non it. Empty until a document establishes one.","type":"string"},"uid":{"description":"UID is the index's name within the org. Two orgs may both hold `messages`.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the index or its settings last changed, RFC 3339.","type":"string"}},"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"},"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/Upstream"},"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/event/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"},"invocationList":{"properties":{"invocations":{"description":"Invocations is one row per past run, newest first.","items":{"$ref":"#/components/schemas/invocationView"},"type":"array"}},"type":"object"},"invocationView":{"properties":{"durationMs":{"description":"how long it took","type":"integer"},"id":{"description":"the invocation's handle","type":"string"},"method":{"description":"the HTTP method that triggered it","type":"string"},"status":{"description":"how the run ended: ok, error or timeout","type":"string"},"statusCode":{"description":"Code is the status the function's OWN code answered with, which is not the\nstatus of the reply — a program can answer 500 through a healthy sandbox.","type":"integer"},"time":{"description":"when it ran, RFC3339","type":"string"}},"type":"object"},"invokeReq":{"properties":{"input":{"description":"Input is what the function is given on stdin. It is opaque to this surface.","type":"string"}},"type":"object"},"issueEdit":{"properties":{"description":{"description":"Description rewrites the body.","type":"string"},"key":{"description":"Key is the board — the repository name, from the path.","type":"string"},"num":{"description":"Num is the issue number on that repository, from the path.","type":"integer"},"priority":{"description":"Priority re-prioritises it.","type":"string"},"status":{"description":"Status moves the card to another column.","type":"string"},"title":{"description":"Title renames the work item.","type":"string"}},"type":"object"},"issueHit":{"properties":{"assignee":{"description":"Assignee is who holds the work. EMPTY MEANS UNHELD, which is what makes the\nissue claimable: claiming one already held by someone else is refused with\n409 rather than quietly taken.","type":"string"},"kind":{"description":"Kind is what the row IS: issue, pr or epic.","type":"string"},"number":{"description":"Number is the issue's number on that board, from 1 and monotonic there.\nUnique per board, never across the org — so it addresses an issue only\ntogether with Project.","type":"integer"},"priority":{"description":"Priority is urgent, high, medium, low or none. Never empty — an unset\npriority is the value \"none\".","type":"string"},"project":{"description":"Project is the board key the issue is on. It and Number are the issue's\naddress in every other route on this surface, which is why a hit carries it.","type":"string"},"repo":{"description":"Repo is the git repository the issue is bound to, empty when it is not\nrepo-bound.","type":"string"},"source":{"description":"Source is which surface opened it: team, git, crm, helpdesk, cms or agent.\n\"git\" is how the mirrored forge and GitHub rows are spelled.","type":"string"},"status":{"description":"Status is the board column: backlog, todo, in_progress, done or canceled.\nClaiming moves backlog and todo to in_progress and leaves the other three\nwhere they are.","type":"string"},"title":{"description":"Title is the issue's one-line summary — what the q filter matched, along with\nthe description.","type":"string"},"url":{"description":"URL is the row's external anchor — its extRef — which is a link only when the\nfeeder sent one. A mirrored GitHub issue carries \"github:owner/repo#123\" and\nan agent's PR row carries the pushed branch. Empty for a row opened here.","type":"string"}},"type":"object"},"issueHits":{"properties":{"count":{"description":"Count is how many rows Issues carries — the size of THIS answer after the\ncap, not how many issues matched. A count equal to the limit means there are\nprobably more; there is no total and no cursor.","type":"integer"},"issues":{"description":"Issues are the matching rows grouped by status and oldest-first within a\ngroup, capped by the search's limit (50 by default, 200 at most). The cap is\napplied to that order, so a broad search returns the head of it rather than a\nsample.","items":{"$ref":"#/components/schemas/issueHit"},"type":"array"}},"type":"object"},"issueView":{"properties":{"assignee":{"description":"Assignee is who holds the work — an IAM username, or the login of the FIRST\nassignee when a forge issue has several. Absent when nobody holds it, which\nis exactly the state a claim needs.","type":"string"},"createdAt":{"description":"CreatedAt is when the item was opened, in unix seconds. 0 when the source\ngave no parseable timestamp.","type":"integer"},"description":{"description":"Description is the body, markdown as its author wrote it. Absent when empty.","type":"string"},"dueAt":{"description":"DueAt is when the work is due, in unix seconds; absent means no due date. A\nforge row takes it from its MILESTONE's due date, since a forge issue has no\ndeadline of its own. Never before StartAt, and never past 2200-01-01.","type":"integer"},"extRef":{"description":"ExtRef anchors the item to something outside the todo — a mirrored issue\n(\"github:owner/repo#123\"), a pushed PR branch, or a record on another plane.\nIt is the idempotency key the mirror upsert matches on. Absent when the item\nhas no external origin.","type":"string"},"id":{"description":"ID is the work item's opaque handle, and it is NOT how you address it —\nProjectKey plus Number is. Its shape says which source answered: a forge\nissue's is the forge's own numeric id in decimal, an index row's a minted\n\"issue_\" id.","type":"string"},"identifier":{"description":"Identifier is the human handle, \"<key>#<number>\" — the board and the number\non it, joined. ONE spelling whichever source answered, because a list where\nforge rows read cli#1 and index rows read OPS-3 is two products in one list.","type":"string"},"kind":{"description":"Kind is what the item IS: issue, pr or epic. Set once at create and never\nchanged, so a row does not migrate between surfaces. Deliberately not \"task\"\n— that word is the async plane (contract.go).","type":"string"},"labels":{"description":"Labels are the item's remaining tags, with the status and priority labels\nlifted OUT — a column that stayed here would render twice, once as the card's\ncolumn and once as a chip on the card. Always present; empty is [].","items":{"type":"string"},"type":"array"},"number":{"description":"Number is the item's number ON ITS BOARD, from 1 and monotonic there — the\nforge's own issue number for a forge row, allocated inside the create\ntransaction for an index row so it cannot race. Unique per board, never\nacross the org.","type":"integer"},"priority":{"description":"Priority is urgent, high, medium, low or none. Also a label on a forge row.\nNever empty: \"none\" when nothing names one, so callers compare a value rather\nthan test for absence.","type":"string"},"projectKey":{"description":"ProjectKey is the board this item is on: the repository name for a forge\nissue, the index board's key otherwise. With Number it is the item's address\nin every other route.","type":"string"},"repo":{"description":"Repo is the git repository the item is bound to, so a repository's Issues and\nPRs tabs are filters over this one table. Absent when the item is not\nrepo-bound.","type":"string"},"source":{"description":"Source is which surface OPENED it: team, git, crm, helpdesk, cms or agent.\nAlso set once. It is the ORIGIN, not the subject — source=helpdesk is an\nengineering issue opened from a support escalation, not a support ticket.","type":"string"},"startAt":{"description":"StartAt is when the work starts, in unix seconds; absent means unscheduled.\nA forge row takes it from when the issue was opened, but only once the issue\nhas a due date — an interval needs both ends.","type":"integer"},"status":{"description":"Status is the board column: backlog, todo, in_progress, done or canceled, and\nnothing else. On a forge row it is read off a LABEL, so relabelling in the\nforge web UI moves the card here and vice versa — and a CLOSED forge issue\nreads done whatever its labels say. Never empty: \"backlog\" when nothing names\na column.","type":"string"},"title":{"description":"Title is the item's one-line summary.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed, in unix seconds.","type":"integer"}},"type":"object"},"itemList":{"properties":{"data":{"description":"Data is the examples of the one dataset named in the path, archived ones\nincluded, so the caller sees the whole set rather than what a run would use.","items":{"$ref":"#/components/schemas/itemView"},"type":"array"}},"type":"object"},"itemReq":{"properties":{"expectedOutput":{"description":"Expected is the answer a correct model produces, which the judge grades\nagainst, stored as raw JSON exactly as sent, up to 64 KiB."},"id":{"description":"ID makes the write idempotent — re-posting the same id replaces that example\nin place. Omit it and one is generated. An id that already exists in a\nDIFFERENT dataset is 409 rather than a move.","type":"string"},"input":{"description":"Input is what the model under test will be given, stored as raw JSON exactly\nas sent, up to 64 KiB."},"metadata":{"additionalProperties":{"type":"object"},"description":"Metadata is a free-form object stored with the example.","type":"object"},"status":{"description":"Status is ACTIVE (the default) or ARCHIVED. Only ACTIVE examples are fed to\na run, which is how an example is retired without being deleted.","type":"string"}},"type":"object"},"itemResult":{"properties":{"error":{"description":"Error is why this example produced no score — the model, the judge, or the\nrun's deadline. A result carrying one is not counted in Scored.","type":"string"},"itemId":{"description":"ItemID is the example that was scored.","type":"string"},"output":{"description":"Output is what the model under test answered, truncated at 2000 characters.","type":"string"},"score":{"description":"Score is the judge's grade.","type":"number"},"traceId":{"description":"TraceID is the model call this result came from.","type":"string"}},"type":"object"},"itemView":{"properties":{"createdAt":{"description":"CreatedAt is when the example was first written.","type":"string"},"datasetName":{"description":"Dataset is the set this example belongs to.","type":"string"},"expectedOutput":{"description":"Expected is the answer a correct model produces, which the judge grades against.","type":"object"},"id":{"description":"ID is the example's handle, unique within the caller's org.","type":"string"},"input":{"description":"Input is what the model under test is given, as it was written.","type":"object"},"metadata":{"additionalProperties":{"type":"object"},"description":"Metadata is the free-form object stored with the example.","type":"object"},"status":{"description":"Status is ACTIVE or ARCHIVED. Only ACTIVE examples are fed to a run, which\nis how one is retired without being deleted.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","type":"string"}},"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"},"judgeSpec":{"properties":{"criteria":{"description":"Criteria is the standard the judge applies, defaulting to a correctness\ncriterion.","type":"string"},"model":{"description":"Model is the model that grades. It defaults to the model under test, so a\nrun with no judge named has the model grade itself.","type":"string"},"name":{"description":"Name is the score name the judge's grades are filed under, \"llm-judge\" by\ndefault. A name that is not a legal handle falls back to that default\nrather than being stored as sent.","type":"string"}},"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":{"limit":{"description":"Limit narrows what the minted key may reach, as `kind:name` entries:\n`model:zen5`, `project:acme`, `product:commerce`, or `model:*` for a whole\nkind. It only ever NARROWS — a key can never reach further than the person\nwho minted it — so an unrecognised kind costs availability, never privilege.\n\nOmitted mints an unrestricted key, because that is what every key in the\nestate is today and a default that restricted would revoke all of them.\n\nExample: {\"type\": \"secret\", \"limit\": [\"model:zen5\", \"project:acme\"]}","items":{"type":"string"},"type":"array"},"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"},"kmsConfig":{"properties":{"apiBase":{"description":"APIBase is this subsystem's own prefix, `/v1/kms`.","type":"string"},"brand":{"description":"Brand is the deployment's brand, so the console renders as the right\nproduct.","type":"string"},"issuer":{"description":"Issuer is the OIDC issuer the console authenticates against.","type":"string"},"loginPath":{"description":"LoginPath is the credential exchange's address.","type":"string"}},"type":"object"},"kmsHealth":{"properties":{"error":{"description":"Error is the honest reason readiness is false: no in-process KMS client,\nor no master key. Absent when ready.","type":"string"},"ready":{"description":"Ready is whether a secret operation would actually succeed right now.\nThese are exactly the two states in which the secret operations refuse.","type":"boolean"},"service":{"description":"Service names the subsystem answering, `kms`.","type":"string"},"signing":{"description":"Signing reports whether signing keys are configured. Absent when there is\nno in-process client to ask.","type":"boolean"},"status":{"description":"Status is `ok` or `degraded`, the one-word form of Ready.","type":"string"}},"type":"object"},"kmsLogin":{"properties":{"clientId":{"description":"ClientID is the machine identity's id, as IAM issued it.","type":"string"},"clientSecret":{"description":"ClientSecret is that identity's secret. It is never logged, never echoed,\nand never carried in an error.","type":"string"}},"type":"object"},"kmsPut":{"properties":{"env":{"description":"Env is the environment to write under. REQUIRED, with no default: it is\npart of the storage key, so a silently defaulted write lands in a bucket\nthe readers that resolve project, environment and path never look in, and\nthe stale value keeps being served.","type":"string"},"name":{"description":"Name is the secret's name. Required.","type":"string"},"path":{"description":"Path is an optional subpath beneath the org root, e.g. \"/ci\".","type":"string"},"value":{"description":"Value is the secret itself. It is sealed under a fresh per-secret data key\nbefore storage, so plaintext never reaches disk, and it is never echoed\nback, logged, or carried in an error.","type":"string"}},"type":"object"},"kmsSecrets":{"properties":{"names":{"description":"Names is the same listing reduced to bare names, which is the shape the\nKMS operator reads. Both are emitted so either consumer keeps working.","items":{"type":"string"},"type":"array"},"secrets":{"description":"Secrets are the descriptors: name, path, environment and sealing scheme.\nNo value and no ciphertext appears here.","items":{"$ref":"#/components/schemas/SecretMeta"},"type":"array"},"total":{"description":"Total is how many descriptors this listing carries.","type":"integer"}},"type":"object"},"kmsStored":{"properties":{"env":{"description":"Env is the environment the secret was written under.","type":"string"},"name":{"description":"Name is the secret's name.","type":"string"},"stored":{"description":"Stored is true; a write confirms by not failing.","type":"boolean"}},"type":"object"},"kmsToken":{"properties":{"accessToken":{"description":"AccessToken is IAM's own JWT, verbatim. Its `owner` claim scopes it to\nexactly one org, which is what every secret operation then reads.","type":"string"},"expiresIn":{"description":"ExpiresIn is the token's lifetime in seconds, as IAM reported it.","type":"integer"},"tokenType":{"description":"TokenType is `Bearer`.","type":"string"}},"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":{"description":"Actor is who produced the turn, defaulted to the calling principal when the\nwriter named nobody.","type":"string"},"at":{"description":"At is when the turn was recorded, RFC 3339 in UTC to the second.","type":"string"},"kind":{"description":"Kind is what the turn was, from the log's closed six: message, tool-call,\nspawn, log, status, control.","type":"string"},"preview":{"description":"Preview is the first 240 bytes of the event's payload, cut without regard for\nthe JSON inside it — it is a string to SHOW, never a value to parse. Read the\ndetail or the stream for the whole payload.","type":"string"},"seq":{"description":"Seq is that event's position in the session's log — monotonic from 1, per\nsession. A reader holding it can ask the detail or stream reads for\neverything after it, so this doubles as the list's resume cursor.","type":"integer"}},"type":"object"},"leaderboard":{"properties":{"benchmark":{"description":"Benchmark is the catalog id these rows are about.","type":"string"},"rows":{"description":"Rows is one per model, ordered by measured accuracy descending.","items":{"$ref":"#/components/schemas/LeaderRow"},"type":"array"}},"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"},"leaseIn":{"properties":{"class":{"description":"Class is what the sandbox is FOR: \"exec\" for a code-interpreter call,\n\"dev\" for a workspace bound to a project, \"desktop\" for one with a screen.\nIt decides the image, the working directory and the isolation.","type":"string"},"image":{"description":"Image overrides the image the class would pick. Honoured only for a caller\nthe policy admits, and the sandbox that comes back names the image it GOT.","type":"string"},"project":{"description":"Project binds the sandbox to one of the org's projects. Required for a dev\nor desktop class, which are single-attach per project; an exec sandbox\ncarries none.","type":"string"},"runtime":{"description":"Runtime asks for an isolation: runc, gvisor, kata-clh or kata-fc. It is a\nREQUEST, not a guarantee — the sandbox that comes back carries the runtime\nit was actually given, which is the field to read.","type":"string"},"ttlSec":{"description":"TTLSec is how long the lease runs before the reaper may take it, in\nseconds. Zero takes the class's own default.","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"},"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"},"licensing.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"},"licensing.FingerprintRequest":{"properties":{"signals":{"$ref":"#/components/schemas/licensing.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"},"licensing.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"},"licensing.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"},"licensing.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:<id>\" feature so a single bad release can be revoked on its own.","type":"string"},"signals":{"$ref":"#/components/schemas/licensing.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"},"licensing.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"},"licensing.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"},"licensing.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/licensing.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"},"licensing.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 \"<os>/<arch>\", 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"},"licensing.ReleaseAsset":{"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/licensing.Release"}},"type":"object"},"licensing.ReleaseList":{"properties":{"releases":{"description":"Releases is the published releases, always an array and never null.","items":{"$ref":"#/components/schemas/licensing.Release"},"type":"array"}},"type":"object"},"licensing.RevocationEntry":{"properties":{"at":{"type":"integer"},"by":{"type":"string"},"reason":{"type":"string"},"scope":{"type":"string"},"value":{"type":"string"}},"type":"object"},"licensing.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"},"licensing.RevokeResponse":{"properties":{"entry":{"$ref":"#/components/schemas/licensing.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"},"licensing.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"},"licensing.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"},"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"},"listing":{"properties":{"lastModified":{"type":"string"},"name":{"type":"string"}},"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"},"logLines":{"properties":{"logs":{"description":"Logs is that run's error text when it failed, else its output. It is empty\nwhen the function has never run.","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":{"description":"CreatedTime is when the machine came into being: the provider's own creation\ntimestamp for a Visor machine, passed through in whatever form it states it,\nand for a BYO machine the RFC 3339 moment it first dialed in.","type":"string"},"gpu":{"description":"GPU names the accelerators this machine holds (\"H100\", or \"2× NVIDIA GB10\"\nfor a BYO machine reporting a matched pair). Empty means the machine is not\na GPU machine — the size slug does not parse as one, or nvidia-smi found\nnothing.","type":"string"},"id":{"description":"ID addresses this machine on the /v1/visor/machines/:id routes: the\norg-scoped NAME Visor keys a machine by, falling back to the provider id for\na machine that has no name. A BYO machine's is the id it dialed in under.","type":"string"},"image":{"description":"Image is the OS image the machine booted from, as the provider names it.","type":"string"},"mem":{"description":"Mem is system RAM rendered for a human (\"8 GB\"), not a number to compute\nwith. Empty when the provider's figure is ambiguous, or when the only figure\navailable is a GPU slug's gb — that is VRAM, and reporting it as system RAM\nwould be a fabrication. A BYO machine's RAM is on /v1/visor/fleet/workers.","type":"string"},"name":{"description":"Name is the label to show a human — Visor's displayName, or the machine name\nwhen it carries none. A BYO machine's is its hostname. It is not an address:\nID is what the routes take.","type":"string"},"os":{"description":"Os is the operating system on the machine — Visor's record for a provisioned\none, the host's own report (linux, darwin, windows) for a BYO one.","type":"string"},"privateIp":{"description":"PrivateIp is the address on the provider's own network, reachable from the\norg's other machines in the same region. Empty on the same terms as PublicIp.","type":"string"},"provider":{"description":"Provider is the cloud that runs the machine (\"digitalocean\"), or \"byo\" for\none the operator dialed in with `hanzo link`.","type":"string"},"publicIp":{"description":"PublicIp is the internet-facing address the provider assigned. Empty while a\nmachine is still provisioning, and empty for a BYO machine — it dials out\nfrom behind NAT, so no address is ever learned for it.","type":"string"},"region":{"description":"Region is the provider region slug (\"sfo3\"), or the zone when the provider\nreports only that. \"on-prem\" for a BYO machine, which has no cloud region.","type":"string"},"status":{"description":"Status is the lifecycle state in the PROVIDER's own words (\"active\",\n\"running\", \"off\"), passed through rather than mapped onto a vocabulary of\nours. A BYO machine's is \"online\" or \"offline\", decided by whether its last\nheartbeat is within 90s.","type":"string"},"type":{"description":"Type is the provider SIZE SLUG the machine runs at (\"s-2vcpu-4gb\",\n\"gpu-h100x8-640gb\") — the value a launch asks for, and what Vcpu/Mem/GPU are\nread out of when the provider states them no other way. \"byo-gpu\" for a\ndialed-in machine, which was never bought from a size catalog.","type":"string"},"vcpu":{"description":"Vcpu is logical cores — the provider's own cpuSize when that is a clean\ninteger, else the count read out of the size slug (4 from \"s-4vcpu-8gb\").\nABSENT, never 0, when neither says. A BYO machine leaves it absent here; its\nreal core count is on GET /v1/visor/fleet/workers.","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":{"description":"Category is that same listing's grouping. Free text chosen by the publisher,\nabsent when there is no public listing or the publisher left it blank.","type":"string"},"description":{"type":"string"},"dispatchable":{"type":"boolean"},"inputSchema":{},"installed":{"description":"Installed is whether the tool is activated for THIS caller's (org, project):\nthe same bit as Activated, under the shop's name for it, which install and\nuninstall are the writes for. It is per caller, so one listing reads\ninstalled for one org and not for another.","type":"boolean"},"name":{"type":"string"},"price":{"$ref":"#/components/schemas/Price"},"source":{"type":"string"},"title":{"description":"Title is the shop-window name, painted over the registry Name from the\nCHEAPEST public listing for this tool — several orgs may list the same one,\nand the row shown is the one a buyer would pay. Absent when no org has listed\nthe tool publicly: that row is a plain capability, not an offer.","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"},"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"},"messageInput":{"properties":{"from":{"description":"From is the number to send FROM, in E.164. It must be one this org holds and it\nmust be sms-capable.","type":"string"},"media":{"description":"Media are URLs to attach. A message with any is an MMS to the carrier — the\ndistinction is the carrier's to make, not something the caller declares.","items":{"type":"string"},"type":"array"},"text":{"description":"Text is the message body. It may be empty when Media carries the message.","type":"string"},"to":{"description":"To is the number to send to, in E.164.","type":"string"}},"type":"object"},"messageList":{"properties":{"data":{"description":"Data is this org's own messages, newest first — from our store rather than the\ncarrier's, so it is the set an audit or a bill has to agree with.","items":{"$ref":"#/components/schemas/SMS"},"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","description":"Resource is the Resource Usage panel's rollup, and every field of it is\ncurrently null — see resourceUsage. It is present rather than omitted so a\npanel renders \"—\" instead of guessing."},"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"},"limit":{"description":"Limit is what the minted key may reach, echoed back so the caller can see\nthe narrowing took. Absent means unrestricted.","items":{"type":"string"},"type":"array"},"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 a\ncredential is sent only if we hold one NAMED FOR that host — so a\ntenant-supplied URL to anywhere else fetches anonymously.","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"},"mlCreate":{"properties":{"labels":{"additionalProperties":{"type":"string"},"description":"Labels are extra labels to set on the object, merged UNDER the tenancy\nlabels this plane derives from the validated principal — so a label naming\nanother org's scope cannot displace the real one.","type":"object"},"name":{"description":"Name is the resource's name: a DNS-1123 label\n(^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$), lowercased and trimmed. It is the\nname the resource answers to for the life of the caller's org.","type":"string"},"spec":{"description":"Spec is the resource's own spec, passed to Kubernetes unchanged. Required —\nan empty spec is 400 rather than an empty resource."}},"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"},"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/network/{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"},"newIssue":{"properties":{"description":{"description":"Description becomes the issue body.","type":"string"},"key":{"description":"Key is the board — the repository name, from the path.","type":"string"},"priority":{"description":"Priority is one of none, urgent, high, medium or low.","type":"string"},"status":{"description":"Status is the board column to open into: backlog, todo, in_progress, done\nor canceled. Empty opens into backlog.","type":"string"},"title":{"description":"Title is required.","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"},"noInput":{"properties":{},"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":{"description":"AutoScale reports whether the provider's cluster autoscaler owns this pool's\nsize, moving Count between MinNodes and MaxNodes as workloads demand. False\nmeans Count changes only when someone scales the pool.","type":"boolean"},"count":{"description":"Count is how many nodes the pool has right now. Always present, so 0 means a\npool that is genuinely empty rather than a figure the provider withheld.","type":"integer"},"maxNodes":{"description":"MaxNodes is the ceiling the autoscaler will not grow the pool past, and so\nthe bound on what this pool can cost. Read it only with AutoScale set.","type":"integer"},"minNodes":{"description":"MinNodes is the floor the autoscaler will not shrink the pool below. Read it\nonly with AutoScale set — the provider ignores it otherwise.","type":"integer"},"name":{"description":"Name is the pool's name as the provider knows it.","type":"string"},"poolId":{"description":"PoolID is the provider's id for the pool — the value the scale and delete\nroutes address it by. It falls back to the pool's name when the provider\nanswered without one, so it is always something the routes accept.","type":"string"},"size":{"description":"Size is the provider size slug every node in the pool runs at\n(\"s-4vcpu-8gb\", \"gpu-h100x8-640gb\"). One pool is one size — a mixed cluster\nis several pools.","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/node/{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"},"none":{"properties":{},"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"},"numberList":{"properties":{"data":{"description":"Data is the numbers, and which numbers depends on the route: a search answers\nwhat the carrier has available, a list answers what this org already holds.","items":{"$ref":"#/components/schemas/Number"},"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/<id>, /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 >= 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":{"description":"ID is the component's stable handle, which on this platform IS the service\nname — there is no separate component registry to allocate ids from.","type":"string"},"name":{"description":"Name is the service as the fleet prober knows it (the `service` label on\nhanzo_service_up), so a reader can match a component to what is being probed.","type":"string"}},"type":"object"},"o11y.StatusIncident":{"properties":{"affected_components":{"description":"AffectedComponents is what this incident covers. It is COUNTED rather than\nclassified: some services down is a partial outage and every probed service\ndown is a full one, because deciding that one service is critical and another\nis not would need a judgement nobody has measured.","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":{"description":"ID is derived from the service, so the same outage keeps one id across reads\nrather than being reported as a new incident every 15 seconds.","type":"string"},"last_update_at":{"description":"LastUpdateAt is when the failing measurement this incident reports was\nread, RFC3339 UTC.","type":"string"},"last_update_message":{"description":"LastUpdateMessage says what was observed, not what is being done about it —\nthere is no operator writing updates here, only the probe that failed.","type":"string"},"name":{"description":"Name is the one-line headline, built from the service that stopped answering.","type":"string"},"status":{"description":"Status is always \"investigating\" — the member of the client's closed set that\nmeans detected, cause not yet established, which is exactly what an automated\nprober knows. Nothing here ever claims \"identified\": that would assert a\ndiagnosis no measurement made.","type":"string"},"url":{"description":"URL points at the HUMAN status page, not back at this JSON. Every link in this\ndocument goes to the same place.","type":"string"}},"type":"object"},"o11y.StatusMaintenance":{"properties":{"affected_components":{"description":"AffectedComponents is what the window touches.","items":{"$ref":"#/components/schemas/o11y.StatusComponent"},"type":"array"},"ends_at":{"description":"EndsAt is when it is expected to finish, RFC3339 UTC.","type":"string"},"id":{"description":"ID is the window's handle.","type":"string"},"last_update_at":{"description":"LastUpdateAt is when the window was last revised, RFC3339 UTC.","type":"string"},"last_update_message":{"description":"LastUpdateMessage is the text of that revision.","type":"string"},"name":{"description":"Name is its one-line headline.","type":"string"},"starts_at":{"description":"StartsAt is when work begins, RFC3339 UTC.","type":"string"},"status":{"description":"Status is where the window is in its life, in the client's own vocabulary.","type":"string"},"url":{"description":"URL points at the human status page, as every link in this document does.","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":{"description":"InProgressMaintenances is always empty: this platform has no maintenance\nscheduling plane, so \"nothing is running\" is a true statement rather than a\nplaceholder.","items":{"$ref":"#/components/schemas/o11y.StatusMaintenance"},"type":"array"},"ongoing_incidents":{"description":"OngoingIncidents is one entry per service that failed its health probe, sorted\nby name. Empty means every probed service answered — which is a measurement,\nnot an absence of reports.","items":{"$ref":"#/components/schemas/o11y.StatusIncident"},"type":"array"},"page_title":{"description":"PageTitle is the brand's own status-page title, resolved per request from the\nHost — a lux caller must never be shown Hanzo's.","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":{"description":"ScheduledMaintenances is always empty, for the same reason.","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":{"description":"Range is the window and bucket width actually used, after clamping — not what\nwas asked for, which is why a caller reads it back rather than assuming.","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":{"description":"Instance is the replica as the telemetry store labels it — the address the\nseries was recorded against, which is what distinguishes two replicas of one\nservice.","type":"string"},"up":{"description":"Up is that replica's last reported state. Every target emits on every cycle,\nso a replica missing from the list is one the prober is not reporting at all,\nwhich is a different fact from down.","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":{"description":"Product is the service these numbers are about, echoed back.","type":"string"},"range":{"description":"Range is the window actually used, after clamping — not what was asked for.\nA caller that wants to know what it got reads this rather than its own query.","properties":{"sinceSec":{"type":"integer"},"stepSec":{"type":"integer"}},"type":"object"},"series":{"description":"Series is the RED trend: request rate, error rate and two latency quantiles,\nbucketed at range.stepSec and oldest bucket first. Latencies are in\nMILLISECONDS.","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":{"description":"Summary totals the window in one object, so a tile does not have to sum the\nseries to render. errorRate is a RATIO of the window's requests, not a\npercentage, and p95Ms is over the window rather than the worst bucket.","properties":{"errorRate":{"type":"number"},"errors":{"type":"integer"},"p95Ms":{"type":"number"},"requests":{"type":"integer"}},"type":"object"},"usage":{"description":"Usage is the LLM side of the same window: how many calls, how many tokens and\nwhat they cost. It counts model traffic, not HTTP requests, so it does not\nreconcile with series.requests.","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":{"description":"CheckedAt is when this answer was measured, RFC3339 UTC.","type":"string"},"deployments":{"description":"Deployments is the per-replica inventory behind the verdict. Empty means the\ntelemetry store reported none, not that the service runs on none.","items":{"$ref":"#/components/schemas/o11y.deployment"},"type":"array"},"latencyMs":{"description":"LatencyMs is the health probe's round trip in MILLISECONDS, time-boxed at two\nseconds. It is 0 when no probe answered, which is not a fast service.","type":"integer"},"product":{"description":"Product is the service this answer is about, echoed back.","type":"string"},"source":{"description":"Source is where the verdict came from: \"probe\" (we asked and it answered),\n\"datastore\" (the probe did not answer and the replica inventory decided it),\n\"unreachable\" (neither), or \"unknown-service\" for a well-formed product name\nnothing backs — which is answered without probing, since dialling an\narbitrary host on a caller's say-so is the request forgery this refuses.","type":"string"},"up":{"description":"Up is true when the health probe succeeded OR any replica reports up, so a\nservice reachable by either route reads up. Read Source to know which.","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"},"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"},"objectItem":{"properties":{"etag":{"type":"string"},"isDir":{"description":"true for a folder (common prefix)","type":"boolean"},"key":{"description":"key RELATIVE to the requested prefix","type":"string"},"lastModified":{"description":"unix seconds (0 for a folder)","type":"integer"},"size":{"description":"bytes (0 for a folder)","type":"integer"}},"type":"object"},"objectList":{"properties":{"bucket":{"description":"Bucket is the bucket that was listed, friendly name.","type":"string"},"objects":{"description":"Objects are the entries at this level, keys RELATIVE to Prefix.","items":{"$ref":"#/components/schemas/objectItem"},"type":"array"},"prefix":{"description":"Prefix is the sub-folder the listing was scoped to, cleaned. Empty for the\nbucket root.","type":"string"},"total":{"description":"Total is how many entries came back. The listing is BOUNDED, so a bucket\nwith more keys than the cap answers the cap and this says so — it is not a\ncount of what the bucket holds.","type":"integer"}},"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"},"openReq":{"properties":{"base":{"description":"Base is the branch the work is proposed INTO, by short name. Defaults to\nthe repo's default branch, which is where a proposal goes when nobody says\notherwise.","type":"string"},"body":{"description":"Body is the longer description. Optional.","type":"string"},"head":{"description":"Head is the branch holding the work, by short name (agent/fix-503).\nRequired, and must already exist.","type":"string"},"name":{"description":"Name is the repo the proposal belongs to, from the :name path segment.","type":"string"},"title":{"description":"Title is the one-line summary of what is being proposed. Required.","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"},"order":{"properties":{"contacts":{"$ref":"#/components/schemas/Contacts","description":"Contacts is the WHOIS contact set. Omit it and the registrar uses the\nreseller account's default contacts."},"domain":{"description":"Domain is the name to buy. It is required.","type":"string"},"years":{"description":"Years is the term to buy, defaulting to 1.","type":"integer"}},"required":["domain"],"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"},"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"},"overviewView":{"properties":{"custom":{"description":"Custom is true when the org replaced the shared playbook with one of its own\n— the difference between \"everyone's checklist\" and \"the one you authored\".","type":"boolean"},"funnel":{"$ref":"#/components/schemas/Funnel","description":"Funnel is the org's analytics lens, present only where the read asked for it\n— absent means it was not requested, never that the org has no traffic."},"progress":{"$ref":"#/components/schemas/progressView","description":"Progress is how far through the journey the org is."},"steps":{"description":"Steps are every enabled step with the org's own state folded in, in\nauthoring order.","items":{"$ref":"#/components/schemas/stepView"},"type":"array"},"title":{"description":"Title is the playbook's name as it heads the checklist.","type":"string"},"version":{"description":"Version identifies the playbook this journey came from, so a caller can tell\nthat the checklist itself changed under them.","type":"string"}},"type":"object"},"pairing":{"properties":{"a":{"description":"A is the first model id.","type":"string"},"a_correct":{"description":"ACorrect is how many of those common items A got right.","type":"integer"},"b":{"description":"B is the second model id.","type":"string"},"b_correct":{"description":"BCorrect is how many of those common items B got right.","type":"integer"},"benchmark":{"description":"Benchmark is the catalog id the two arms were compared on.","type":"string"},"mcnemar_p":{"description":"McnemarP is the two-sided exact binomial p on the discordant pairs. It is 1\nwhen nothing is discordant, which is \"no evidence of a difference\", not an\nerror.","type":"number"},"n_common":{"description":"NCommon is how many items BOTH arms completed. It is the denominator, and\nthe reason this comparison is valid where a raw accuracy difference is not.","type":"integer"},"net_a_minus_b":{"description":"NetAMinusB is the two rescue counts subtracted — A's advantage in items.","type":"integer"},"rescue_a_over_b":{"description":"RescueAOverB is how many items A got right and B got wrong.","type":"integer"},"rescue_b_over_a":{"description":"RescueBOverA is how many items B got right and A got wrong.","type":"integer"}},"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":{"description":"Published opens the session's story to the public build route; false withdraws\nit, and withdrawing is always allowed. PUBLISHING is refused unless the\nsession names a Project — the one set in this same request, or the one already\nstored — because that route is keyed on (org, project). It widens READ access\nto what is already there and grants nothing else.","type":"boolean"},"status":{"description":"Status moves the session to running, paused, done or error. A session that has\nalready finished refuses any change with 409 — done and error are monotonic —\nand moving INTO one stamps the end time. This is the surface REPORTING what\nhappened; a control command never writes it.","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":{"description":"Title rewrites the human line, up to 512 characters — usually because the work\nturned out to be something other than what it was opened as.","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"},"kind":{"description":"Kind names a different kind of sync, and is refused, for the same reason.","type":"string"},"source":{"$ref":"#/components/schemas/endpointReq","description":"Source, Target and Kind are DECLARED HERE IN ORDER TO BE REFUSED.\n\nThey are immutable by design — re-pointing a sync is a delete and a create, so\na link can never silently start syncing somewhere else — but an UNDECLARED\nfield is dropped by the binder before the handler sees it, so a request asking\nto repoint answered 200, changed nothing, and said nothing. The operator then\nbelieves a moved repository has been repointed and it has not.\n\nLive: a sync still naming github.com/hanzoai/cloud after the repository moved\nto hanzo-inc/cloud failed every reconcile with \"Repository not found\", and the\nPATCH that appeared to fix it did nothing at all. Declaring the fields is what\nlets the documented immutability actually answer.\nSource names a new upstream, and is refused. Delete this sync and create the\none you want."},"target":{"$ref":"#/components/schemas/endpointReq","description":"Target names a new native repository, and is refused, for the same reason."},"trigger":{"description":"Trigger is webhook, poll or manual. Omitted, the stored trigger stands.","type":"string"}},"type":"object"},"patchTargetIn":{"properties":{"capacity":{"description":"Capacity rewrites the human summary, up to 256 characters. \"\" clears it.","type":"string"},"host":{"description":"Host re-points the hostname sessions are matched by. Moving it moves the load:\nthe session counts follow the new name from the next read.","type":"string"},"id":{"description":"ID is the target to update, from the path.","type":"string"},"kind":{"description":"Kind re-files it under laptop | cloud | gpu | cluster | machine.","type":"string"},"label":{"description":"Label renames the machine, up to 128 characters. Empty STRING is refused — a\ntarget with no name is a row nobody can pick out of a fleet.","type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics","description":"Metrics replaces the live sample, and sending one IS A HEARTBEAT: the server\nstamps the time and appends the point to the fleet series. Sending an all-zero\nsample CLEARS the heartbeat — the machine goes back to having no liveness fact\nat all, and its stored status is taken at face value again."},"spec":{"$ref":"#/components/schemas/Spec","description":"Spec replaces the static capability whole, sanitized and clamped the same way\na register's is."},"status":{"description":"Status sets operator INTENT: online | offline | draining. Draining is how a\nmachine is taken out of dispatch without ending what is already on it. What\ncomes back may still read offline, because the heartbeat outranks the intent.","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"},"pointView":{"properties":{"t":{"description":"the bucket's start, RFC3339 (UTC)","type":"string"},"v":{"description":"how many invocations fell in it — a real count, never interpolated","type":"integer"}},"type":"object"},"policyList":{"properties":{"policies":{"description":"Policies is the list.","items":{},"type":"array"}},"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":{"description":"MaxNodes is the ceiling the autoscaler may not grow the pool past, and so the\nbound on what this pool can spend. Ignored unless AutoScale is set.","type":"integer"},"minNodes":{"description":"MinNodes is the floor the autoscaler may not shrink the pool below. Ignored\nunless AutoScale 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 is the cluster holding 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":{"description":"PoolID is the pool to resize, from the URL path — the `poolId` a cluster read\nreports for it. Required.","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"},"presetAccepted":{"properties":{"note":{"description":"Note explains what acceptance does and does not promise.","type":"string"},"preset":{"$ref":"#/components/schemas/Preset","description":"Preset is the blend with its defaults filled in."},"served_as":{"description":"ServedAs is the model id the serving layer would resolve this blend under.","type":"string"},"status":{"description":"Status is \"accepted\": the blend is well-formed, not that it is now served.","type":"string"}},"type":"object"},"presetList":{"properties":{"data":{"description":"Data is the blends available to compose from.","items":{"$ref":"#/components/schemas/Preset"},"type":"array"}},"type":"object"},"presignResponse":{"properties":{"expiresIn":{"description":"seconds until the URL expires","type":"integer"},"key":{"type":"string"},"method":{"description":"\"PUT\" (upload) or \"GET\" (download)","type":"string"},"url":{"description":"presigned URL the browser follows directly","type":"string"}},"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, `<app>-<branch>`.","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/event/errors.)","type":"string"},"url":{"description":"URL is the full page address the event fired on. Omitted when absent.","type":"string"}},"type":"object"},"profileMetrics":{"properties":{"funnel":{"$ref":"#/components/schemas/Funnel","description":"Funnel is what the org's analytics observed over the trailing window. Read\nits `available` first: an org with no analytics reports zeros here, and zero\ntraffic and no measurement are different facts."},"launchProgress":{"$ref":"#/components/schemas/progressView","description":"LaunchProgress is the org's own position in the launch checklist, folded in\nso a profile carries both what the org has BUILT and what it has DONE."},"records":{"description":"Records is how many business records the org holds — the volume that tells a\nreal book of customers from an empty account. It feeds the `customers`\nsignal, which crosses at a threshold rather than at one row.","type":"integer"},"revenueCents":{"description":"RevenueCents is the org's money OF RECORD — what its books say, in whole\ncents, never a float and never a display string. This is the number the\nscaling stage is decided on; funnel.revenue is the beacon's separate,\nunreconciled view of the same business. Zero when the org has none, and also\nzero when the books could not be read, which is why the `revenue` signal\nbeside it is the thing to trust.","type":"integer"}},"type":"object"},"profileResponse":{"properties":{"keyMetrics":{"$ref":"#/components/schemas/profileMetrics","description":"KeyMetrics are the org's OWN numbers behind those signals — never another\norg's, and never a platform aggregate."},"signals":{"additionalProperties":{"type":"boolean"},"description":"Signals is what was observed of the org right now, one boolean per probe.\nA probe that could not be run reports FALSE, not absent — the shape is stable\nso a caller never has to tell a missing key from a negative answer, and the\ncost is that \"not observed\" and \"not there\" look alike here. Keys are the\nprobe names, including the `module:<name>` and `connected:<provider>`\nfamilies.","type":"object"},"stage":{"description":"Stage is how far the business itself has got — formed, launched, activated or\nscaling — decided purely from the signals below, and by what the org has\nACHIEVED rather than what it has configured. It reads the STRONGEST evidence\npresent, so money of record makes an org scaling even if an earlier rung's\nsignal was never observed. It is unrelated to checklist progress.","type":"string"}},"type":"object"},"progressView":{"properties":{"done":{"description":"Done counts steps that are FINISHED — done and skipped alike, since a step\nthe org deliberately passed over is not still owed. It therefore rises when\nsomebody skips, which is the intended reading of a checklist.","type":"integer"},"next":{"description":"Next is the id of the step to do next: the first available, unfinished step\nin authoring order. Empty when the journey is complete, and also empty when\nevery remaining step is blocked by a dependency.","type":"string"},"percent":{"description":"Percent is done/total as a whole number 0-100, rounded, so a caller renders\na bar without recomputing it. Total zero reads as 0.","type":"integer"},"total":{"description":"Total is how many steps this org's journey holds — the ENABLED steps of the\nplaybook, so it shrinks when an operator disables one and does not match the\nauthored step count.","type":"integer"}},"type":"object"},"projectView":{"properties":{"applications":{"description":"Applications is how many platform apps this org has under the project,\ncounted per request. It is the one fact IAM cannot answer about a project.","type":"integer"},"createdAt":{"description":"CreatedAt is IAM's creation time as unix seconds. 0 when IAM's timestamp is\nabsent or unparseable — never a fabricated time.","type":"integer"},"description":{"description":"Description is IAM's free text about the project. Nothing derives from it.","type":"string"},"name":{"description":"Name is IAM's display name, falling back to the slug when the project has\nnone, so this is never empty.","type":"string"},"org":{"description":"Org is the project's IAM owner, and the tenant every app under it deploys\ninto. It comes from the validated identity, never from the request.","type":"string"},"slug":{"description":"Slug is the project's IAM name — half of the (org,name) identity, the\n`:project` path segment, and the scope key an app is filed under. It is the\nproject's address; Name is not.","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 is the organisation that owns the site.","type":"string"},"slug":{"description":"Slug is the site the hosts were bound to.","type":"string"}},"type":"object"},"projectsBuildSite":{"properties":{"brief":{"description":"Brief is what the site should be, in plain language. It is the whole input the\nmodel gets and it is size-bounded.","type":"string"},"model":{"description":"Model names which model writes the site. Absent takes the deployment's\ndefault — this route spends inference on the caller's org either way.","type":"string"},"name":{"description":"Name is the site's display name. Taken from what the model writes when omitted.","type":"string"},"slug":{"description":"Slug is the handle and public host label to publish under. Derived from the\nname, or from the brief, when omitted.","type":"string"}},"type":"object"},"projectsComplete":{"properties":{"bytes":{"description":"Bytes is their total size in bytes.","type":"integer"},"commit":{"description":"Commit is the revision that was built, recorded on the deployment.","type":"string"},"files":{"description":"Files is how many objects CI published.","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":{"description":"LiveURL is a HINT at the address the site should serve at. The public host is\nclaimed by cloud first, so this can refine the URL a deployment reports but\ncan never assert a subdomain another tenant holds.","type":"string"},"message":{"description":"Message is what happened, in words — on an error completion, why it failed.","type":"string"},"slug":{"description":"Slug is the project the deployment belongs to, from the path.","type":"string"},"status":{"description":"Status is how the build ended: `live` if it succeeded, `error` if it did not.\nNothing else is accepted.","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":{"description":"Description is the one-line summary, copied onto anything forked from this\nproject.","type":"string"},"framework":{"description":"Framework is a BUILD HINT from a closed set, defaulting to static. It tells CI\nhow to build a linked repo and never gates a deploy.","type":"string"},"license":{"description":"License is the terms that upstream work carries.","type":"string"},"name":{"description":"Name is the project's display name and the only REQUIRED field. When slug is\nomitted it is also what the slug is derived from.","type":"string"},"repo":{"description":"Repo links a git source, so pushes to it rebuild this project. Omit it for a\nproject deployed by uploading an artifact.","properties":{"branch":{"type":"string"},"url":{"type":"string"}},"type":"object"},"slug":{"description":"Slug is the handle everything else addresses this project by: the public host\n`<slug>.hanzo.app`, the object-store key segment, and the path parameter of\nevery later call. Derived from the name when omitted. It is a hostname label,\nso it is constrained and reserved labels such as `api` or `admin` are refused.","type":"string"},"upstream":{"description":"Upstream credits the third-party work this project was published from. It is\naccepted from any caller: giving away credit can only cost the publisher, so\nit needs no gate.","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":{"description":"Files is the whole site, inline — every file it consists of. It REPLACES what\nis there rather than merging, so an omitted file is a deleted one.","items":{"$ref":"#/components/schemas/projectsFile"},"type":"array"},"name":{"description":"Name is the site's display name.","type":"string"},"slug":{"description":"Slug is the handle and public host label to publish under.","type":"string"}},"type":"object"},"projectsDeployStart":{"properties":{"commit":{"description":"Commit is the git sha this build was produced from, recorded on the\ndeployment so a released site can be traced back to its source. Optional.\n\nIt is the ONLY field here, and deliberately: the predecessor also accepted\n`source` and `branch`. `source` was the Content-Type discriminator this\nsplit removed. `branch` was accepted and DISCARDED — there is no branch\ncolumn on a deployment, and the lifecycle event derives the branch from the\nproject's own linked one — so declaring it would publish a settable field\nthat does nothing into the document, every generated SDK and the MCP input\nschema. A field that is read by nothing is not described as if it were.\n\n`url:\"-\"` because zip binds the query string OVER a decoded body, so\nwithout it a `?commit=` the caller never sent would outrank the one it did.","type":"string"},"slug":{"description":"Slug is the site to deploy, from the path.","type":"string"}},"type":"object"},"projectsDeployment":{"properties":{"bucket":{"description":"Bucket is the object-store bucket its files were written to.","type":"string"},"bytes":{"description":"Bytes is their total size in bytes.","type":"integer"},"commit":{"description":"Commit is the revision that was built, for a deployment that came from a\nrepository. Absent for an uploaded artifact, which has no revision.","type":"string"},"createdAt":{"description":"CreatedAt is when the deployment was queued, as Unix seconds.","type":"integer"},"files":{"description":"Files is how many objects the deployment published.","type":"integer"},"id":{"description":"ID identifies this one deployment attempt, and is what CI quotes back to\ncomplete it.","type":"string"},"liveUrl":{"description":"LiveURL is where this deployment serves, once it is live.","type":"string"},"message":{"description":"Message is what happened, in words — the build's own note, or on a failure\nwhy it failed.","type":"string"},"prefix":{"description":"Prefix is the key prefix within that bucket holding EXACTLY this deployment's\nobjects — the unit an upload grant is scoped to, so a grant for one deployment\ncannot write over another.","type":"string"},"projectId":{"description":"ProjectID is the project this deployment belongs to.","type":"string"},"source":{"description":"Source is what caused the deployment — a git push, an uploaded artifact, a\ngenerated site.","type":"string"},"status":{"description":"Status is where the attempt got to — queued, live, or failed. A deployment\nthat is live is not necessarily the one SERVING: the project's own\ncurrentDeploymentId says which is.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed state, as Unix seconds — so the gap between\nthe two is how long the build took.","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":{"description":"Version counts deployments of this project from 1, so the history reads as an\nordered sequence rather than by timestamp. It is per project, not global.","type":"integer"}},"type":"object"},"projectsDomain":{"properties":{"createdAt":{"description":"CreatedAt is when the host was claimed, as Unix seconds — not when it went live.","type":"integer"},"detail":{"description":"Detail is what is holding the claim up, in words a person can act on.","type":"string"},"host":{"description":"Host is the custom hostname claimed for this site.","type":"string"},"records":{"description":"Records are EXACTLY the DNS records to publish to prove ownership and route\nthe host. Present only while pending, because a live host has already proved\nit; absent is therefore \"nothing left to do\", not \"we cannot say what to do\".","items":{"$ref":"#/components/schemas/Record"},"type":"array"},"status":{"description":"Status is `live` when the edge answers for this host now, `pending` while the\nclaim is waiting on DNS proof of ownership. A pending host is claimed but\nserves nothing.","type":"string"},"url":{"description":"URL is where the host will serve once it is live — present on a pending claim\ntoo, so a console can show the destination before it works.","type":"string"},"verified":{"description":"Verified is the same fact as a boolean, for a caller that only needs the yes\nor no. It cannot disagree with status.","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 is the organisation that owns the site.","type":"string"},"slug":{"description":"Slug is the site the panel belongs to.","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":{"description":"Content is the file's whole text, inline. There is no upload step and no\nreference to fetch: a site is sent as its bytes, and each file and the site as\na whole are size-bounded.","type":"string"},"path":{"description":"Path is where the file lands in the site, RELATIVE to its root — so\n\"index.html\" is the page served at /. Leading slashes and any attempt to\nescape the root are refused.","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"},"projectsProject":{"properties":{"analytics":{"description":"Analytics is whether the web-analytics beacon is injected into this site's\npages. It is ON by default — a project has to opt out — and it is what the\nstatic builder reads to decide whether to inject at all.","type":"boolean"},"bucket":{"description":"Bucket is the object-store bucket the site's files are served out of.","type":"string"},"cacheControl":{"description":"CacheControl is the Cache-Control policy the edge serves this site's HTML\nunder — how long a reader may hold a stale page before asking again. Assets\nare content-addressed and are not governed by it.","type":"string"},"createdAt":{"description":"CreatedAt is when the project was created, as Unix seconds.","type":"integer"},"currentDeploymentId":{"description":"CurrentDeploymentID names the deployment currently serving, so a caller can\nask what is live without scanning the history.","type":"string"},"description":{"description":"Description is the one-line summary, which is copied onto forks of this\nproject and shown on a gallery card.","type":"string"},"forkedFrom":{"description":"ForkedFrom is the parent this project was forked from (\"<org>/<slug>\" of a\npublished project, or a catalog template slug) — the attribution edge a\ngallery credits.","type":"string"},"framework":{"description":"Framework is a BUILD HINT from a closed set, defaulting to static. It tells CI\nhow to build a linked repo and never gates a deploy, so a wrong value costs a\nbuild rather than access.","type":"string"},"hidden":{"description":"Hidden is PLATFORM MODERATION, and it is a different axis from visibility: it\npulls a public project out of the catalogue without editing the publisher's\nown choice, so un-hiding restores exactly what they asked for. A project is\nlisted only when it is public AND not hidden. Always present, never omitted,\nfor the same reason as visibility.","type":"boolean"},"hiddenReason":{"description":"HiddenReason is why moderation hid it. Absent when it is not hidden.","type":"string"},"id":{"description":"ID is the project's internal identifier. It is stable across a rename, but it\nis not what the API addresses this project by — `slug` is.","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":{"description":"LastPurgeAt is when the edge cache was last cleared, as Unix seconds, so a\nconsole can say how fresh what readers see actually is. Absent means never.","type":"integer"},"license":{"description":"License is the terms that upstream work carries. Absent has the same reading:\nundeclared, not unencumbered.","type":"string"},"liveUrl":{"description":"LiveURL is where the site answers today. Absent until something has been\ndeployed.","type":"string"},"name":{"description":"Name is the project's display name, free text a person chose.","type":"string"},"org":{"description":"Org is the organisation that owns the project, and therefore who pays for it\nand who may change it. It is also the AUTHORSHIP line a gallery credits;\nthere is no separate author field.","type":"string"},"repo":{"$ref":"#/components/schemas/projectsRepo","description":"Repo is the git source this project builds from, empty when it is deployed by\nuploading an artifact instead."},"slug":{"description":"Slug is the identifier that MATTERS: the handle every later call addresses,\nthe S3 key segment the site's objects live under, and the label of the public\nhost `<slug>.hanzo.app`. Because it is a hostname it is constrained and\nreserved labels such as `api` are refused.","type":"string"},"space":{"description":"Space is the project's Base data space, which is where a deployed site's form,\nforum and data submissions land. Absent means the site stores nothing.","type":"string"},"starred":{"description":"Starred is THIS CALLER's star, not a property of the project — two people\nin the same org see different values for the same row, which is the whole\npoint of it. Always present so a client can tell \"not starred\" from \"this\nAPI is too old to say\", the same reason visibility and hidden are.","type":"boolean"},"status":{"description":"Status is where the project stands — whether a build has ever succeeded and\nwhether anything is serving right now.","type":"string"},"tags":{"additionalProperties":{"type":"string"},"description":"Tags is the site's browser tag config: platform slug → non-secret pixel id (GA\nmeasurement, Meta pixel, …) — what track.js injects and the server CAPI reads,\nper site. Omitted when none are set. The API SECRET is never here (KMS).","type":"object"},"updatedAt":{"description":"UpdatedAt is when the project's own record last changed, as Unix seconds. A\ndeploy is not an edit of the project, so this does not move on every publish.","type":"integer"},"upstream":{"description":"Upstream credits the third-party work this project was published from — a\nfree-text line, because the honest answer is a name and a title that no enum\ncould hold. Absent means NOBODY HAS SAID, not that 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":{"description":"Active is whether this is the release the site is SERVING right now. Exactly\none release of a site is active; the others are kept so they can be activated\nagain, until retention reclaims them.","type":"boolean"},"bytes":{"description":"Bytes is their total size in bytes.","type":"integer"},"createdAt":{"description":"CreatedAt is when the release was cut, as Unix seconds — not when it was last\nactivated.","type":"integer"},"objects":{"description":"Objects is how many files the release holds.","type":"integer"},"releaseId":{"description":"ReleaseID is derived from a DIGEST of the release's own manifest, so identical\ncontent is the same release and a release can never be confused with another\none. Activating an older id IS the rollback.","type":"string"},"slug":{"description":"Slug is the site this release belongs to.","type":"string"},"source":{"description":"Source is what the release was cut from — the build output or upload it was\npromoted out of.","type":"string"},"url":{"description":"URL is where the site serves. Present only on the ACTIVE release, since an\ninactive one is not answering anywhere.","type":"string"}},"type":"object"},"projectsRepo":{"properties":{"branch":{"description":"Branch is the ref a push has to touch for this project to rebuild. Pushes to\nany other branch are ignored.","type":"string"},"provider":{"description":"Provider is the forge the URL was recognised as — it decides which webhook\nand which credential reach the repository, and is DERIVED from the URL rather\nthan chosen by the caller.","type":"string"},"url":{"description":"URL is the clone address of the repository this project builds from.","type":"string"}},"type":"object"},"projectsSite":{"properties":{"name":{"description":"Name is the site's display name.","type":"string"},"slug":{"description":"Slug is the site's handle — also the label of the host it serves at.","type":"string"},"status":{"description":"Status is the project's state behind the site — whether it is serving, still\nbuilding, or failed its last build. A site that is listed is not necessarily\none that answers.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the project last changed, as Unix seconds.","type":"integer"},"url":{"description":"URL is the pretty address readers use, not the object-store path behind it.","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://<slug>.<apex> — empty when the\nsubdomain belongs to another tenant and this site has none.","type":"string"}},"type":"object"},"projectsStar":{"properties":{"starred":{"description":"Starred is whether THIS caller has starred the project after the toggle —\ntheir own bookmark, not a property the project carries, so two people see\ntwo answers for one project.","type":"boolean"}},"type":"object"},"projectsUpdate":{"properties":{"cacheControl":{"description":"CacheControl replaces the Cache-Control policy the edge serves this site's\nHTML under. Absent leaves it.","type":"string"},"description":{"description":"Description replaces the one-line summary. Absent leaves it.","type":"string"},"framework":{"description":"Framework replaces the build hint. It affects the NEXT build only — nothing\nalready deployed is rebuilt.","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":{"description":"HiddenReason records WHY moderation hid it, so the action can be explained\nand reviewed later. Admin-gated like hidden itself.","type":"string"},"license":{"description":"License is the terms that upstream work carries, with the same clear-versus-\nleave rule.","type":"string"},"name":{"description":"Name replaces the display name. Absent leaves it; the slug never moves with it.","type":"string"},"repo":{"description":"Repo relinks the git source. Absent leaves the existing link.","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"},"tags":{"additionalProperties":{"type":"string"},"description":"Tags sets the site's browser tag config: platform slug → non-secret pixel id\n(e.g. {\"ga4\":\"G-…\",\"meta\":\"…\"}). track.js injects these first-party and the\nserver CAPI reads them, per site. Absent LEAVES them; a present object REPLACES\nthe set (send {} to clear). The ids are public — they ship in the page — so this\nis not the SECRET path (a CAPI token is sealed via POST /v1/destination).","type":"object"},"upstream":{"description":"Upstream credits the third-party work this project was published from, and is\nsettable after the fact because the live demos are the ones that most need\ncrediting. An explicit empty string CLEARS the credit; 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":{"description":"ExpiresAt is when the grant stops being accepted, as Unix seconds. It is\nshort-lived by design and is handed out ONCE, on the response that queues the\ndeployment — a later read of that deployment does not carry it, so a grant\ncannot be fetched again after the build it was minted for.","type":"integer"},"fields":{"additionalProperties":{"type":"string"},"description":"Fields are form values every POST must carry VERBATIM, alongside `key` and\n`file`. The signature covers them, so altering any one of them — including\nwidening the key to reach outside the prefix — invalidates the grant rather\nthan extending it.","type":"object"},"maxBytes":{"description":"MaxBytes bounds ONE object, not the upload as a whole.","type":"integer"},"prefix":{"description":"Prefix is the only place this grant can write: the deployment's own key\nprefix. It authorizes WRITES ONLY, which is why completing a deployment\nreconciles the prefix against a manifest instead of letting CI delete.","type":"string"},"url":{"description":"URL is the address to POST each object to. It is signed for the PUBLIC\nendpoint, because the signature covers the host and CI posts from outside the\ncluster.","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/prompt/<name>.","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"},"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"},"provisionRequest":{"properties":{"instance":{"description":"Instance binds a DEDICATED add-on to the app instance whose\n<instance>-addons Secret receives the <KIND>_URL (e.g. \"commerce\").\nOptional: empty means \"not instance-bound\" — the DSN is returned once and\nwired by the caller.","type":"string"},"name":{"description":"Name is the org-unique slug for the new resource, matching\n^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$. Every physical name derives from it.","type":"string"}},"type":"object"},"provisionResult":{"properties":{"connectionString":{"description":"ConnectionString is the ready-to-use DSN, credential included. RETURNED\nHERE ONCE: no read beside this one carries it, so a caller that does not\nkeep it must provision again.","type":"string"},"database":{"description":"Database is the logical database, collection, index or bucket this resource\nresolves to on its backend. It is derived from Name under an org-namespacing\nhash, so it is not Name and two orgs cannot land on one.","type":"string"},"host":{"description":"Host is the address that routes to this resource — a dedicated instance's\nown in-cluster Service, or the public gateway for a shared one. Never the\ninternal admin address of a shared backend.","type":"string"},"id":{"description":"ID is the resource's server-minted handle, \"rs_\"-prefixed. The caller does\nnot choose it, and it is what every read and the delete address.","type":"string"},"kind":{"description":"Kind is the product provisioned: sql, vector, datastore, kv, search, s3 or\ndocdb. It is the route that was called, not a body field.","type":"string"},"name":{"description":"Name is the org-unique slug the caller asked for, lower-cased. Every\nphysical name on the backend derives from it.","type":"string"},"password":{"description":"Password is the minted credential, in plaintext, for the kinds that have\none. RETURNED HERE ONCE — where KMS is configured it is sealed there and\nonly a reference is persisted; where it is not, it is stored nowhere at all.\nIt is never held in plaintext on either side.","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. A shared-backend create is \"ready\" here;\na dedicated one answers 201 still launching, and reaches ready only when a\nlater read reconciles it against the operator's live CR — never fabricated.","type":"string"},"username":{"description":"Username is the credential's user, for the kinds that mint one per resource.\nAbsent for a kind whose backend authenticates with a shared, out-of-band key.","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"},"publishedClaim":{"properties":{"benchmark":{"description":"Benchmark is the canonical test id the claim is about, from /catalog.","type":"string"},"model":{"description":"Model is the system the score is claimed for.","type":"string"},"protocol":{"description":"Protocol records HOW it was scored — provider-reported, agentic,\nthird-party-leaderboard — because a provider card and a third party running\nits own harness are different kinds of number and must not be blended.","type":"string"},"provider":{"description":"Provider is who the claim belongs to — the lab or leaderboard whose number\nthis is. It joins a claim to the attempts measured for that same model.","type":"string"},"score":{"description":"Score is the reported aggregate, as a percentage.","type":"number"},"source":{"description":"Source is the citation the row was read from. A claim without one is a\nnumber nobody can check, so every write requires it.","type":"string"}},"type":"object"},"pullList":{"properties":{"data":{"description":"Data holds the repo's pull requests, newest number first.","items":{"$ref":"#/components/schemas/pullView"},"type":"array"}},"type":"object"},"pullView":{"properties":{"author":{"description":"Author is the user who opened it; empty for a caller with no user.","type":"string"},"base":{"description":"Base is the branch the work is proposed into.","type":"string"},"body":{"description":"Body is the longer description; empty when none was given.","type":"string"},"createdAt":{"description":"CreatedAt is RFC 3339 UTC.","type":"string"},"head":{"description":"Head is the branch holding the work.","type":"string"},"mergedRev":{"description":"MergedRev is what base points at now that the merge landed. Empty while\nthe proposal is open.","type":"string"},"number":{"description":"Number is the proposal's per-repo handle, dense from 1.","type":"integer"},"repo":{"description":"Repo is the repository the proposal belongs to.","type":"string"},"state":{"description":"State is \"open\" or \"merged\".","type":"string"},"title":{"description":"Title is the one-line summary.","type":"string"},"updatedAt":{"description":"UpdatedAt is RFC 3339 UTC.","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"},"push":{"properties":{"after":{"type":"string"},"before":{"type":"string"},"pusher":{"properties":{"login":{"type":"string"},"username":{"type":"string"}},"type":"object"},"ref":{"type":"string"},"repository":{"properties":{"name":{"type":"string"},"owner":{"properties":{"login":{"type":"string"},"username":{"type":"string"}},"type":"object"}},"type":"object"}},"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"},"putClaimsIn":{"properties":{"by":{"description":"By is who is recording them — a person, or the importer's name.","type":"string"},"data":{"description":"Data is the claims to record. One row is a correction; many is an import.\nThere is no separate bulk endpoint because there is no separate operation:\nimporting a leaderboard and fixing one number are the same write.","items":{"$ref":"#/components/schemas/publishedClaim"},"type":"array"}},"type":"object"},"putClaimsOut":{"properties":{"recorded":{"description":"Recorded is how many rows were written.","type":"integer"},"rejected":{"description":"Rejected names the rows that were not, and why.","items":{"type":"string"},"type":"array"}},"type":"object"},"quoteList":{"properties":{"results":{"description":"Results is one quote per name, priced RETAIL — this deployment's markup is\nalready applied and the wholesale cost is never on the wire.","items":{"$ref":"#/components/schemas/Offer"},"type":"array"}},"type":"object"},"reachability":{"properties":{"configured":{"description":"Configured is whether the wholesale credentials are present at all.","type":"boolean"},"env":{"description":"Env is the registrar environment. It is the fact that decides whether money\nmoves: only \"prod\" reaches the live, billable registrar — anything else,\nincluding unset, is the sandbox.","type":"string"},"error":{"description":"Error is the blocker, so an operator reads it instead of guessing at it.","type":"string"},"reachable":{"description":"Reachable is whether the registrar accepted those credentials on a live call\nmade while the caller waited.","type":"boolean"},"registrar":{"description":"Registrar names the wholesale registrar behind it.","type":"string"},"service":{"description":"Service names the subsystem answering.","type":"string"},"status":{"description":"Status is \"ok\" when a live call succeeded, else \"degraded\".","type":"string"}},"type":"object"},"readOut":{"properties":{"messages":{"description":"Messages is what was read, stream-ordered.","items":{"$ref":"#/components/schemas/Delivery"},"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"},"recordIn":{"properties":{"room":{"description":"Room is the LiveKit room, named the way the office client names one\n(`<workspace>_<name>_<id>`). Its leading segment is what binds the room to a\ntenant, and it is the segment the caller's membership is checked against.","type":"string"}},"required":["room"],"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"},"recording":{"properties":{"bucket":{"description":"Bucket is the object store bucket the recording is written to. It is stated\nbeside the key rather than folded into one URI so a client reads two facts\ninstead of splitting a string.","type":"string"},"error":{"description":"Error is the media server's reason when a recording failed, and empty\notherwise.","type":"string"},"id":{"description":"ID is the media server's egress id — the handle a later read names.","type":"string"},"object":{"description":"Object is the key inside that bucket. Empty only while the media server has\nnot named a file yet.\n\nIt says WHERE the recording is, not how to fetch it. Reading one back is a\nseparate decision this surface deliberately does not make: a link to a\nprivate conversation needs its own answer about who may follow it and for\nhow long, and inventing a short one here would be worse than not having it.","type":"string"},"room":{"description":"Room is the room this recording is of.","type":"string"},"started":{"description":"Started is when the recording began, as the media server reports it: its\nown `started_at`, verbatim and unconverted. LiveKit's egress service sets\nthat field from UnixNano, and a conversion this side cannot check against\nthe running server would be a number that looks right and is wrong by a\nfactor of a billion. 0 means it has not started.","type":"integer"},"status":{"description":"Status is the media server's own state name: EGRESS_STARTING, EGRESS_ACTIVE,\nEGRESS_ENDING, EGRESS_COMPLETE, EGRESS_FAILED, EGRESS_ABORTED or\nEGRESS_LIMIT_REACHED. It is passed through rather than folded into a\nvocabulary of ours, so the answer cannot mean something the media server did\nnot say.","type":"string"}},"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"},"refreshOut":{"properties":{"connector":{"$ref":"#/components/schemas/connView","description":"Connection 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":{"description":"Account is which subscription or API account under that provider served the\nrun, up to 256 characters. It is what lets a revoke of that login stop exactly\nthe sessions it was paying for.","type":"string"},"actor":{"description":"Actor is the \"org/sub\" identity to record the session under, up to 256\ncharacters. Omit it and the calling principal is used, which is almost always\nwhat you want: it is what a login revoke matches on to stop this session.","type":"string"},"agent":{"description":"Agent is the label the surface opening this session calls itself by\n(\"hanzo-dev\"). REQUIRED, up to 128 characters, and free text — nothing\nresolves it against a defined agent.","type":"string"},"cwd":{"description":"Cwd is the directory the session starts in, up to 1024 characters. It can be\nmoved later, because a linked shell walks around.","type":"string"},"host":{"description":"Execution context — where this session runs (all optional).","type":"string"},"parentSessionId":{"description":"ParentSessionID makes this a subagent of that session: it inherits the\nparent's root, so one flow stays one tree. The parent must exist IN THE SAME\nORG — a foreign or unknown id is a 400, never a tree across tenants. Empty\nopens a root session.","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":{"description":"Published opens this session's story to the public build route. It is refused\nwithout a Project, because that route is keyed on (org, project) — a build\nwith no product is not a story anyone can open. False keeps it org-only.","type":"boolean"},"repo":{"description":"Repo is the code being worked on, up to 512 characters. A label the surface\nstates; nothing resolves it against the forge.","type":"string"},"status":{"description":"Status opens the session in one of running, paused, done or error. Empty means\nrunning. A TERMINAL status here (done, error) records a session that has\nalready finished — its end time is stamped now — and nothing can move it\nafterwards.","type":"string"},"target":{"description":"Target names a run-target the org has registered. Unlike Host and Repo it IS\nresolved: a target that does not exist in this org is a 400, so a session can\nnever claim to run on another tenant's machine. Empty names no machine.","type":"string"},"taskRunId":{"description":"TaskRunID is that workflow's particular run, same bound. Recorded, not\nresolved: this surface does not check the workflow exists.","type":"string"},"taskWorkflowId":{"description":"TaskWorkflowID links this session to the hanzoai/tasks workflow that executes\nit, up to 256 characters. Set it and control commands are forwarded to that\nengine; leave it and the running surface polls for them instead.","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":{"description":"Title is the human line a card shows, up to 512 characters. Optional, and\nchangeable later.","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 (`<org>` or `@<org>/…`).","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<n> 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"},"renewReq":{"properties":{"domain":{"description":"Domain is the name to extend. It is required, and the caller's org must hold it.","type":"string"},"years":{"description":"Years is how much longer to hold it, defaulting to 1.","type":"integer"}},"required":["domain"],"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":{"description":"Changed says whether the run produced any commit. It is INDEPENDENT of OK: a\nrun can succeed and change nothing (there was nothing to do), and a run can\nfail after committing some of its work. Two questions, two booleans.","type":"boolean"},"commitSha":{"description":"CommitSha is the tip the run pushed, clamped to 128 characters. Empty when it\npushed nothing, which is the same case Changed reports false for.","type":"string"},"diffstat":{"description":"Diffstat is the run's own summary of what it changed, as text, clamped to\n64 KiB. Free-form: it is shown, never parsed.","type":"string"},"error":{"description":"Error is why the run failed, clamped to 64 KiB. It is CLAMPED rather than\nrefused — a truncated reason is worth more than a rejected report, because a\nrejected report leaves the durable workflow waiting forever.","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":{"description":"CostCents would be the window's spend in cents. Always null here — the money\na run costs is the metering ledger's, joined by the run id, and repeating it\nfrom this side would be a second number that could disagree with the bill.","type":"number"},"cpuVcpuHours":{"description":"CPUVcpuHours would be vCPU-hours over the window. Always null: this store\nholds agent definitions and run I/O, and nothing here meters a CPU. Null is\nthe honest answer and 0 would be a claim.","type":"number"},"memGbHours":{"description":"MemGbHours would be gigabyte-hours of memory. Always null, same reason.","type":"number"},"storageIoBytes":{"description":"StorageIoBytes would be bytes moved to and from storage. Always null, same\nreason.","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"},"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, RFC 3339 UTC.","type":"string"},"by":{"description":"By is who moved it there: the validated user, or the org itself when the\ncaller is a machine with no user behind it.","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 identifies the dataset across all of its versions.","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":{"description":"Version is which version this is, from 1 and monotone within the dataset.\nA number is never reused — not even after a disposal, where the next declare\ncontinues the count — so \"signups v3\" means one thing forever, which is what\nmakes a model's citation of it checkable.","type":"integer"}},"type":"object"},"riskDatasetDisposal":{"properties":{"dataset":{"description":"Dataset is the dataset that was disposed of. The NAME survives: declaring it\nagain continues the version count rather than starting over at 1.","type":"string"},"rows":{"description":"Rows is how many rows they held between them, as the REGISTER recorded them\nwhen each was materialised — not a count of what the drop deleted, which is\ngone by the time this answers.","type":"integer"},"versions":{"description":"Versions is how many versions went.","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 is the subject kind: person, session or account.","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":{"description":"Subject is the identity within that kind — whose row this is. Every row of\none subject is in ONE split, decided by that subject's earliest instant, so\na subject is never on both sides of a cut.","type":"string"}},"type":"object"},"riskDatasetRows":{"properties":{"dataset":{"description":"Dataset is the dataset the page was read from.","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":{"description":"Limit is the page size actually served: the one asked for, clamped to the\nplane's own bound of 5000. Fewer rows than Limit means the version ended.","type":"integer"},"offset":{"description":"Offset is where this page starts in the version's own row order, which is by\nrow id and therefore stable forever.","type":"integer"},"rows":{"description":"Rows is the page. Never null.","items":{"$ref":"#/components/schemas/riskDatasetRow"},"type":"array"},"version":{"description":"Version is which published version it was read from — the one asked for, or\nthe newest published one when the request named none.","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 is where the event window opens, RFC 3339, INCLUSIVE. The window may\nnot be 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":{"description":"To is where the window ends, EXCLUSIVE, so two datasets meeting at one\ninstant share no row. A materialisation reads less than this — the end is\npulled back by Horizon, and the lineage reports the window it actually read.","type":"string"}},"type":"object"},"riskDatasetVersions":{"properties":{"items":{"description":"Items is every version of it, newest first — including the disposed ones,\nwhose record outlives their rows. Never null.","items":{"$ref":"#/components/schemas/riskDataset"},"type":"array"},"name":{"description":"Name is the dataset these versions belong to, as the register holds it.","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 endpoint.","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 >= 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 < 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 endpoint.","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 endpoint.","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":{"description":"Dataset is the dataset traced.","type":"string"},"digest":{"description":"Digest is the version's fingerprint, repeated here so a lineage answer is\nself-contained.","type":"string"},"from":{"description":"From is where the window actually read opens, RFC 3339. Same as 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":{"description":"Refusal says which way it failed — the window expired, or the source now\nholds a different count. Absent when Reproducible is true.","type":"string"},"reproducible":{"description":"Reproducible is true when the source still holds what this version was built\nfrom — measured by asking it again, not recalled. False is ordinary: the\nsource is fed by a rollup that runs behind the events, so \"it holds more\nnow\" is the common case and it means re-running the spec would not produce\nthis version.","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 is how many rows the source held for that window at materialisation\ntime. Holds is the same question asked now, and the difference between them\nis the whole of the reproducibility claim.","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":{"description":"Subjects is how many distinct subjects those rows belonged to. It is the\nreal sample size — the row count flatters it whenever a subject is active.","type":"integer"},"to":{"description":"To is where it ends: the spec's own end pulled BACK by the maturity horizon,\nso it is usually earlier than the spec says. This is the window a\nreproduction has to ask for — asking the spec's would not return these rows.","type":"string"},"version":{"description":"Version is the version traced — the one asked for, or the newest published\none when the request named none.","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 `<family>:<digest>`: 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`<family>:<digest>` — 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, `<home org>/<user>`,\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 `<family>:<digest>`:\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 is how many judged rows carry the one disposition.","type":"integer"},"rows":{"description":"Rows is how many rows the version holds across every split. It is the size\nof the version, not of the source window — the horizon, the cuts and the row\ncap all bind before this number.","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":{"description":"Test is how many fall after the second cut — the LATEST slice, and the only\none a score is honest about, since the split is temporal.","type":"integer"},"train":{"description":"Train is how many rows fall before the first cut — the EARLIEST slice of the\nwindow, which is what a model is fitted on.","type":"integer"},"unproductive":{"description":"Unproductive is how many carry the other. With Productive it accounts for\nJudged, so the class imbalance is visible before anyone trains on it; both\nstay 0 while Judged is 0.","type":"integer"},"val":{"description":"Val is how many fall between the two cuts, held out for tuning.","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":{"description":"Base is the branch to start FROM. Empty means the repository's default —\nresolve it on the machine, since the machine is the one holding the clone.","type":"string"},"branch":{"description":"Branch is the ref the run must push its work to, and the ONLY one it is\npermitted to write: the forge's ref policy refuses anything else from this\nrun's credential. It is decided at dispatch and exists before the work does.","type":"string"},"cloneUrl":{"description":"CloneURL is how to fetch the repository. It carries NO credential — the\nmachine authenticates with the git identity it already holds — which is why\nthis whole shape is safe to hand to a claimed runner.","type":"string"},"project":{"description":"Project is the product slug the run is filed under, so the machine can tag\nwhat it produces. Empty when the dispatch named none.","type":"string"},"prompt":{"description":"Prompt is the task, in full, as the person wrote it. There is no second field\nfor context.","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-<slug>\" 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"},"rpcError":{"properties":{"code":{"description":"Code is the JSON-RPC error code the chain reported, passed through as it\ncame. -32603 (internal error) is the one value this deployment mints\nitself, for an upstream that could not be reached at all.","type":"integer"},"message":{"description":"Message is the chain's own explanation, e.g. \"execution reverted\". It is\n\"upstream unavailable\" when the deployment minted the error rather than the\nchain — that is the one message this side writes.","type":"string"}},"type":"object"},"rpcIn":{"properties":{"chain":{"description":"Chain is the registry id, from the URL.","type":"string"},"id":{"description":"ID is echoed back untouched."},"jsonrpc":{"description":"JSONRPC must be \"2.0\" when present.","type":"string"},"method":{"description":"Method is the RPC method, e.g. eth_getBalance.","type":"string"},"params":{"description":"Params is the method's parameters, passed through unread."}},"type":"object"},"rpcOut":{"properties":{"error":{"$ref":"#/components/schemas/rpcError","description":"Error is the JSON-RPC error object, present instead of Result. Its presence\nis the ONLY way a failure shows up here: the HTTP status stays 200, because\nthat is what a standard JSON-RPC client parses."},"id":{"description":"ID is the request's id, echoed back untouched — any JSON value, because\nJSON-RPC lets the caller choose. A request with no id is sent upstream as 1."},"jsonrpc":{"description":"JSONRPC is always \"2.0\". An upstream that omits it has it filled in, so a\nclient never has to cope with a response that is missing the one field\ntelling it which protocol it is reading.","type":"string"},"result":{"description":"Result is the chain's answer, passed through unread: an eth_getBalance\nresult is a 0x-quantity string, a block is an object. Absent when Error is\npresent — JSON-RPC 2.0 requires exactly one of the two, and this is what the\nchain answered rather than something reassembled here."}},"type":"object"},"ruleList":{"properties":{"data":{"description":"Data is every rule a scan can fire, each with the id, name and severity a\nfinding cites.","items":{"$ref":"#/components/schemas/RuleView"},"type":"array"}},"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"},"ruleset":{"properties":{"rules":{"description":"Rules is how many detection rules the engine holds.","type":"integer"},"status":{"description":"Status is \"ok\" whenever the findings store opened.","type":"string"}},"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"},"runRecord":{"properties":{"avgScore":{"description":"AvgScore is the mean over the scored examples.","type":"number"},"createdAt":{"description":"CreatedAt is when the run first landed.","type":"string"},"dataset":{"description":"Dataset is the set that was scored.","type":"string"},"items":{"description":"Items is how many examples were attempted.","type":"integer"},"judgeModel":{"description":"JudgeModel is the model that graded.","type":"string"},"model":{"description":"Model is the model under test.","type":"string"},"runName":{"description":"RunName is the run's label.","type":"string"},"scored":{"description":"Scored is how many produced a real score.","type":"integer"},"updatedAt":{"description":"UpdatedAt is when the record last changed.","type":"string"}},"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"},"runRequest":{"properties":{"dataset":{"description":"Dataset is the set to score, which must belong to the caller's org and hold\nat least one ACTIVE example.","type":"string"},"judge":{"$ref":"#/components/schemas/judgeSpec","description":"Judge is the judge to grade with. Omitted, the model under test grades\nitself against a default correctness criterion under the score name\n\"llm-judge\"."},"limit":{"description":"Limit caps how many examples this run scores. It defaults to 20, and\nanything above 100 falls back to that default.","type":"integer"},"model":{"description":"Model is the model under test.","type":"string"},"runName":{"description":"RunName labels the run and is generated from the clock when omitted. It\nmust match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$.","type":"string"}},"required":["dataset","model"],"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"},"runSummary":{"properties":{"avgScore":{"description":"AvgScore is the mean over the scored examples, 0 when none scored.","type":"number"},"dataset":{"description":"Dataset is the set that was scored.","type":"string"},"items":{"description":"Items is how many examples the run attempted.","type":"integer"},"judgeModel":{"description":"JudgeModel is the model that graded.","type":"string"},"model":{"description":"Model is the model under test.","type":"string"},"results":{"description":"Results is one row per attempted example.","items":{"$ref":"#/components/schemas/itemResult"},"type":"array"},"runName":{"description":"RunName is the run's label, which scores and traces are filed under.","type":"string"},"scored":{"description":"Scored is how many produced a real score. It counts successes only, so a\npartial run is honest about what it achieved.","type":"integer"}},"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"},"args":{"additionalProperties":{"type":"string"},"description":"Args are --build-arg values. They are what lets several images off ONE\nDockerfile mean different things — the sandbox classes are three entries\ndiffering only by STAGE. Validated at the k8s choke point, with VERSION and\nREVISION taking precedence: those are receipts the builder derives from the\ntag and the commit, and a caller that could overwrite them could make an\nimage lie about which commit it is.","type":"object"},"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"},"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"},"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 entry points 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 its progress is read 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` — the build was accepted and has not finished.","type":"string"},"target":{"description":"Target is the multi-stage build target, echoed back.","type":"string"}},"type":"object"},"runs":{"properties":{"data":{"description":"Data is the caller org's runs, bounded by limit.","items":{"$ref":"#/components/schemas/runRecord"},"type":"array"}},"type":"object"},"s3Health":{"properties":{"error":{"description":"Error is why the probe is degraded, in plain words. Absent when it is not.","type":"string"},"presign":{"description":"Presign is whether presigned upload and download URLs can be minted, which\nneeds a PUBLIC endpoint on top of the credentials. False does not make the\nsurface degraded — listing and creating still work.\n\nNOT omitempty. On the ready path the untyped probe wrote this key\nunconditionally, and omitting a false one would turn what a\nhealthy-but-presignless deployment reports from \"presign: false\" into\nsilence — two different facts. The degraded body now carries it too, which is\nthe one delta of typing this: the probe answers ONE shape under both of its\nstatuses, which is what makes a single declared Out honest.","type":"boolean"},"ready":{"description":"Ready is whether this deployment can serve object operations at all: true\nonly when admin credentials are configured.","type":"boolean"},"service":{"description":"Service names the subsystem this probe is for. Always \"s3\".","type":"string"},"status":{"description":"Status is \"ok\" when the store is reachable in principle, \"degraded\" when it\nis not. It is the field to read; the HTTP status carries the same fact for a\ncaller that only looks at the code.","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"},"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":{"description":"GPUModel names the representative accelerator (\"GB10\"); GPUs carries how\nmany. A heterogeneous host names its first card rather than inventing a\nsummary.","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 this reading covers.","type":"integer"},"host":{"description":"Host is the node's hostname, for display.","type":"string"},"memFree":{"description":"MemFree is host memory still available, in BYTES.","type":"integer"},"memUsed":{"description":"MemUsed is host memory in use, 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":{"description":"At is when the reading was MEASURED, RFC 3339 in UTC — the x-axis a chart\nplots against. The series is returned oldest first, so it only increases.","type":"string"},"costCents":{"description":"CostCents is what this unit resold for over the hour the reading falls in, in\nwhole US cents. 0 means UNPRICED, not free: the operator's own machines — a\nlinked run-target, a dialed-in BYO worker — are metered for utilization and\nnever resold, so only a priced source ever fills it.","type":"integer"},"cpus":{"description":"CPUs is logical cores. The static capability rides every row on purpose: a\nchart can size load against cores without joining a registry whose row may\nsince have been rewritten or the unit deregistered.","type":"integer"},"gpuModel":{"description":"GPUModel names the representative accelerator (\"GB10\"); GPUs carries how many.","type":"string"},"gpuUtil":{"description":"GPUUtil is aggregate accelerator utilization as a FRACTION of 1 — 0.42 is\n42% busy. Anything a reporter sends outside 0..1 is clamped into it on write.","type":"number"},"gpus":{"description":"GPUs is how many accelerators the reading covers.","type":"integer"},"host":{"description":"Host is the hostname the unit reported at the time of the reading.","type":"string"},"kind":{"description":"Kind is what the measured unit is: laptop, cloud, gpu, cluster, machine or\nworker.","type":"string"},"load1":{"description":"Load1 is the 1-minute load average — runnable processes, not a percentage.","type":"number"},"load15":{"description":"Load15 is the 15-minute load average, the same units as Load1.","type":"number"},"load5":{"description":"Load5 is the 5-minute load average, the same units as Load1.","type":"number"},"memFree":{"description":"MemFree is host memory available, in BYTES, as reported rather than derived.","type":"integer"},"memUsed":{"description":"MemUsed is host memory in use, in BYTES.","type":"integer"},"memory":{"description":"Memory is total system RAM in BYTES at the time of the reading.","type":"integer"},"source":{"description":"Source is the plane that reported the reading: \"agent\", \"byo\" or \"visor\" —\nthe same vocabulary the board's rows carry, and what ?source= narrows on.","type":"string"},"unit":{"description":"Unit is the source's own id for the measured unit. With Source it is the key\nthe chart groups by, and the key the board joins a unit's latest reading on.","type":"string"}},"type":"object"},"sandboxList":{"properties":{"sandboxes":{"description":"Sandboxes are the caller org's sandboxes matching the filter. Never null.","items":{"$ref":"#/components/schemas/Sandbox"},"type":"array"}},"type":"object"},"scan":{"properties":{"content":{"description":"Content is the source to scan. It is NEVER stored: what persists is the\nfinding, with a masked preview and a fingerprint.","type":"string"},"path":{"description":"Path is where the file lives, recorded on any finding so a result can be\nlocated in the tree it came from.","type":"string"}},"type":"object"},"scanDetail":{"properties":{"findings":{"description":"Findings is every finding on that scan, so the detail view is one round-trip.","items":{"$ref":"#/components/schemas/findingView"},"type":"array"},"scan":{"$ref":"#/components/schemas/scanView","description":"Scan is the summary."}},"type":"object"},"scanList":{"properties":{"data":{"description":"Data is the caller org's scans, newest first.","items":{"$ref":"#/components/schemas/scanView"},"type":"array"}},"type":"object"},"scanView":{"properties":{"createdAt":{"description":"CreatedAt is when the scan ran, in Unix milliseconds.","type":"integer"},"critical":{"description":"Critical is how many findings carry the highest severity.","type":"integer"},"files":{"description":"Files is how many files the scan read.","type":"integer"},"findings":{"description":"Findings is how many secrets fired across them.","type":"integer"},"high":{"description":"High is how many findings rank high.","type":"integer"},"id":{"description":"ID addresses this scan and every finding on it.","type":"string"},"low":{"description":"Low is how many findings rank low.","type":"integer"},"medium":{"description":"Medium is how many findings rank medium.","type":"integer"},"project":{"description":"Project is the sub-scope the scan was filed under.","type":"string"}},"type":"object"},"scoreConfigList":{"properties":{"data":{"description":"Data is the caller org's rubrics, bounded by limit.","items":{"$ref":"#/components/schemas/scoreConfigView"},"type":"array"}},"type":"object"},"scoreConfigReq":{"properties":{"categories":{"description":"Categories is the closed set of labels a CATEGORICAL score may carry. A\nCATEGORICAL rubric with none is refused.","items":{"type":"string"},"type":"array"},"dataType":{"description":"DataType is NUMERIC (the default), CATEGORICAL or BOOLEAN.","type":"string"},"maxValue":{"description":"MaxValue is the inclusive ceiling a NUMERIC score must stay under, finite.","type":"number"},"minValue":{"description":"MinValue is the inclusive floor a NUMERIC score must clear. It must be\nfinite and must not exceed MaxValue.","type":"number"},"name":{"description":"Name is the score name this rubric governs, matching\n^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. The name is the key, so re-posting one\nreplaces its rules.","type":"string"}},"required":["name"],"type":"object"},"scoreConfigView":{"properties":{"categories":{"description":"Categories is the closed set of labels a CATEGORICAL score may carry.","items":{"type":"string"},"type":"array"},"createdAt":{"description":"CreatedAt is when the rubric was first declared.","type":"string"},"dataType":{"description":"DataType is NUMERIC, CATEGORICAL or BOOLEAN, and is authoritative — a\nscore recorded under this name cannot claim a different one.","type":"string"},"maxValue":{"description":"MaxValue is the inclusive ceiling a NUMERIC score must stay under, absent when unbounded.","type":"number"},"minValue":{"description":"MinValue is the inclusive floor a NUMERIC score must clear, absent when unbounded.","type":"number"},"name":{"description":"Name is the score name this rubric governs.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed.","type":"string"}},"type":"object"},"scoreList":{"properties":{"data":{"description":"Data is the caller org's score events matching the filters, bounded by limit.","items":{"$ref":"#/components/schemas/scoreView"},"type":"array"}},"type":"object"},"scoreReq":{"properties":{"comment":{"description":"Comment is the grader's reasoning, truncated at 2000 characters.","type":"string"},"dataType":{"description":"DataType is NUMERIC, CATEGORICAL or BOOLEAN. A declared rubric overrides it\n— a caller cannot claim a type the org's rubric contradicts.","type":"string"},"datasetItemId":{"description":"ItemID attaches the score to one graded example.","type":"string"},"datasetName":{"description":"Dataset attaches the score to one dataset.","type":"string"},"name":{"description":"Name is the score name. A rubric of the same name, if the org has declared\none, decides this score's type and the values it may take.","type":"string"},"runName":{"description":"RunName attaches the score to one run.","type":"string"},"stringValue":{"description":"StringValue is the label of a CATEGORICAL score, which must be one the\nrubric allows.","type":"string"},"traceId":{"description":"TraceID attaches the score to one model call.","type":"string"},"value":{"description":"Value is the numeric score, which must be finite: NaN and Inf are refused.\nA BOOLEAN score takes 0 or 1.","type":"number"}},"required":["name"],"type":"object"},"scoreView":{"properties":{"comment":{"description":"Comment is the grader's reasoning, truncated at 2000 characters.","type":"string"},"dataType":{"description":"DataType is NUMERIC, CATEGORICAL or BOOLEAN.","type":"string"},"id":{"description":"ID is the score event's handle.","type":"string"},"name":{"description":"Name is the score name, which a rubric of the same name governs.","type":"string"},"runName":{"description":"RunName is the run this score was recorded under, when it came from one.","type":"string"},"stringValue":{"description":"StringValue is the label of a CATEGORICAL score.","type":"string"},"timestamp":{"description":"Timestamp is when the score was recorded.","type":"string"},"traceId":{"description":"TraceID is the model call this score grades, when it grades one.","type":"string"},"value":{"description":"Value is the numeric score; for BOOLEAN it is 0 or 1.","type":"number"}},"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"},"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"},"secretList":{"properties":{"secrets":{"description":"Secrets is one row per distinct (namespace, name) a function mounts. Values\nare NEVER read or returned.","items":{"$ref":"#/components/schemas/secretView"},"type":"array"}},"type":"object"},"secretView":{"properties":{"mountedBy":{"description":"MountedBy is a function that mounts it.","type":"string"},"name":{"description":"Name is the environment variable the function mounts.","type":"string"},"namespace":{"description":"Namespace is the group the mounting function belongs to.","type":"string"}},"type":"object"},"sectionWrite":{"properties":{"data":{"description":"Data is the record. What may be in it depends on the section, and every\nsection's required fields are validated: a control is held to the same rule\na committed one is, a subprocessor must say what it is for, an update must\ncarry the date it describes."},"id":{"description":"ID is the record's id. Omit it on a create and one is minted; the\nsingle-valued sections (profile, risk) hold one record whatever is named.","type":"string"},"kind":{"description":"Kind is the section being written. The URL is the authority.","type":"string"},"ord":{"description":"Ord orders this record within its section, ascending, ties broken by id.\nIt is the organization's own ordering — the page renders in it.","type":"integer"}},"type":"object"},"seoAuditIn":{"properties":{"url":{"description":"URL is the page, absolute and http or https.","type":"string"}},"type":"object"},"seoAuditOut":{"properties":{"checks":{"additionalProperties":{"type":"boolean"},"description":"Checks is every named finding, each a yes or no — \"is_https\", \"no_h1_tag\",\n\"high_loading_time\", around fifty of them. It is an OPEN set the upstream adds\nto, so it is published as an object of booleans rather than as fifty declared\nfields that would be wrong the next time they name a fifty-first.","type":"object"},"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string.","type":"string"},"description":{"description":"Description is its meta description.","type":"string"},"score":{"description":"Score is the upstream's on-page score, 0 to 100.","type":"number"},"status":{"description":"Status is the HTTP status the page answered with.","type":"integer"},"title":{"description":"Title is the page's title.","type":"string"},"url":{"description":"URL is the address actually read, after redirects.","type":"string"},"words":{"description":"Words is how many words of readable text the page carries.","type":"integer"}},"type":"object"},"seoBacklinkIn":{"properties":{"target":{"description":"Target is a domain, a subdomain or a single page URL. A domain summarises the\nwhole site; a URL summarises that page.","type":"string"}},"type":"object"},"seoBacklinkOut":{"properties":{"backlinks":{"description":"Backlinks is how many links point at it.","type":"integer"},"broken":{"description":"Broken is how many of those links point at something that no longer answers.","type":"integer"},"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string.","type":"string"},"domains":{"description":"Domains is how many distinct sites those links come from — the number that\nmatters, since a thousand links from one site is one site.","type":"integer"},"firstSeen":{"description":"FirstSeen is when the upstream first saw a link to this target, RFC 3339.","type":"string"},"pages":{"description":"Pages is how many distinct pages link in.","type":"integer"},"rank":{"description":"Rank is the upstream's authority score for the target, 0 to 1000.","type":"integer"},"spam":{"description":"Spam is the share of the profile judged spam, 0 to 100.","type":"integer"},"target":{"description":"Target is the target as the upstream resolved it.","type":"string"}},"type":"object"},"seoCharge":{"properties":{"op":{"description":"Op is the operation id — seoKeyword, seoRank — so a line here and a tool in a\nmodel's list are the same name.","type":"string"},"request":{"description":"Request is the flat charge for making the call, in USD, as an exact decimal\nstring.","type":"string"},"result":{"description":"Result is the charge for each row returned, in USD, as an exact decimal\nstring. Zero for the ops priced per request.","type":"string"}},"type":"object"},"seoCompetitorIn":{"properties":{"keywords":{"description":"Keywords are the phrases. At least one; blanks are dropped.","items":{"type":"string"},"type":"array"},"language":{"description":"Language is the ISO code. Defaults to \"en\".","type":"string"},"limit":{"description":"Limit is how many domains to return, 1 to 1000. Defaults to 100.","type":"integer"},"location":{"description":"Location is the market, as the upstream's numeric code. Defaults to 2840.","type":"integer"}},"type":"object"},"seoCompetitorOut":{"properties":{"competitors":{"description":"Competitors is one row per domain, strongest first.","items":{"$ref":"#/components/schemas/seoDomain"},"type":"array"},"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string.","type":"string"},"total":{"description":"Total is how many domains the upstream holds for these phrases.","type":"integer"}},"type":"object"},"seoDomain":{"properties":{"domain":{"description":"Domain is the competitor.","type":"string"},"keywords":{"description":"Keywords is how many of the phrases it places for.","type":"integer"},"position":{"description":"Position is its average rank across the phrases.","type":"number"},"traffic":{"description":"Traffic is the estimated monthly visits those placements earn.","type":"number"},"visibility":{"description":"Visibility is its share of the possible attention across those phrases.","type":"number"}},"type":"object"},"seoIdeaIn":{"properties":{"keywords":{"description":"Keywords are the seeds. At least one; blanks are dropped.","items":{"type":"string"},"type":"array"},"language":{"description":"Language is the ISO code. Defaults to \"en\".","type":"string"},"limit":{"description":"Limit is how many phrases to return, 1 to 1000. Defaults to 100. It is what\nthis call is priced on, because the upstream charges per row.","type":"integer"},"location":{"description":"Location is the market, as the upstream's numeric code. Defaults to 2840.","type":"integer"}},"type":"object"},"seoIdeaOut":{"properties":{"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string.","type":"string"},"keywords":{"description":"Keywords is the phrases found, each measured.","items":{"$ref":"#/components/schemas/seoMetric"},"type":"array"},"total":{"description":"Total is how many the upstream holds, which is usually more than Limit\nreturned — it is what raising the limit would reach.","type":"integer"}},"type":"object"},"seoKeywordIn":{"properties":{"keywords":{"description":"Keywords are the phrases. At least one; blanks are dropped.","items":{"type":"string"},"type":"array"},"language":{"description":"Language is the ISO code. Defaults to \"en\".","type":"string"},"location":{"description":"Location is the market, as the upstream's numeric code. Defaults to 2840,\nthe United States.","type":"integer"}},"type":"object"},"seoKeywordOut":{"properties":{"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string. It is the\nupstream's own number and it is what was debited.","type":"string"},"keywords":{"description":"Keywords is one measurement per phrase, in the order the upstream answered.","items":{"$ref":"#/components/schemas/seoMetric"},"type":"array"}},"type":"object"},"seoMetric":{"properties":{"competition":{"description":"Competition is how contested the advertising is, from 0 to 1. The upstream\nreports it as an index out of a hundred on one endpoint and as this fraction\non another; it is the fraction here in both cases.","type":"number"},"cpc":{"description":"CPC is the average cost of one advertising click, in USD. It is a reported\nstatistic about somebody else's auction, not an amount this API moves.","type":"number"},"difficulty":{"description":"Difficulty is how hard the first page is to reach organically, 0 to 100.\nPresent on seoIdea, which measures it; absent on seoKeyword, which does not.","type":"integer"},"keyword":{"description":"Keyword is the phrase.","type":"string"},"level":{"description":"Level is the same fact as a word: low, medium or high.","type":"string"},"volume":{"description":"Volume is the average monthly searches.","type":"integer"}},"type":"object"},"seoRankIn":{"properties":{"domain":{"description":"Domain is the site, with or without a subdomain — \"hanzo.ai\", \"docs.hanzo.ai\".","type":"string"},"language":{"description":"Language is the ISO code. Defaults to \"en\".","type":"string"},"limit":{"description":"Limit is how many placements to return, 1 to 1000. Defaults to 100.","type":"integer"},"location":{"description":"Location is the market, as the upstream's numeric code. Defaults to 2840.","type":"integer"}},"type":"object"},"seoRankOut":{"properties":{"cost":{"description":"Cost is what this call cost, in USD, as an exact decimal string.","type":"string"},"rankings":{"description":"Rankings is one row per phrase the domain places for.","items":{"$ref":"#/components/schemas/seoRanking"},"type":"array"},"total":{"description":"Total is how many placements the upstream holds for this domain.","type":"integer"}},"type":"object"},"seoRanking":{"properties":{"keyword":{"description":"Keyword is the phrase searched.","type":"string"},"position":{"description":"Position is the absolute rank on the results page, counting every element —\nso it is what a person scrolling actually passes, not the organic-only rank.","type":"integer"},"title":{"description":"Title is that result's headline.","type":"string"},"traffic":{"description":"Traffic is the estimated monthly visits this placement earns.","type":"number"},"url":{"description":"URL is the page of the target that placed.","type":"string"},"volume":{"description":"Volume is the phrase's average monthly searches.","type":"integer"}},"type":"object"},"seoRateOut":{"properties":{"rates":{"description":"Rates is one row per op on this surface.","items":{"$ref":"#/components/schemas/seoCharge"},"type":"array"}},"type":"object"},"seriesLine":{"properties":{"key":{"description":"agent name","type":"string"},"points":{"description":"Points is one bucket per interval across the whole window, in time order and\nnever sparse: a bucket with no runs is present with v 0, so two lines drawn\nfrom two agents share an x-axis without the client aligning anything. The\nwindow decides the count — 24 hourly for 24H, 7 daily, 30 daily.","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"},"sessionDetail":{"properties":{"account":{"type":"string"},"actor":{"type":"string"},"agent":{"type":"string"},"childSessions":{"description":"Children is the session's DIRECT children, one level down, each with its own\ncounts. The promoted `children` integer beside it is how many there are; this\nis who they are. For the whole subtree, read the tree.","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":{"description":"RecentEvents is the 50 most recent turns, OLDEST of those first — a transcript\nto read down, not a feed. The promoted `events` integer says how many the log\nholds in total; page the rest from a seq.","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":{"description":"Account is which subscription or API account under that provider served it.\nTogether with Provider it is what a login revoke matches on to stop the\nsessions a withdrawn account was paying for.","type":"string"},"actor":{"description":"Actor is WHO this session belongs to, as \"org/sub\" — the same identity a run\nis billed under. A register that names none takes the calling principal. It is\nwhat scopes a login revoke, so a session with the wrong actor is a session the\nright person cannot stop.","type":"string"},"agent":{"description":"Agent is the label the surface running this session calls itself by\n(\"hanzo-dev\"), up to 128 characters. Required at register. It is free text,\nnot a reference: it need not name a defined agent, and nothing resolves it.","type":"string"},"children":{"description":"Children is the DIRECT fan-out — how many sessions name this one as parent —\nand not the size of the subtree. Read the tree for that.","type":"integer"},"createdAt":{"description":"CreatedAt is when the row was written, same format. Every path that opens a\nsession stamps it and StartedAt from one clock reading, so the two are equal\non every session this surface has ever produced.","type":"string"},"cwd":{"description":"Cwd is the directory the session is working in NOW, not the one it started in:\na linked shell moves around, and a card showing where `hanzo link` was run\nanswers \"which work is this\" with something that was true once.","type":"string"},"endedAt":{"description":"EndedAt is when it reached done or error, same format. Empty while it is still\nrunning or paused, which is how absence reads here: not over yet.","type":"string"},"events":{"description":"Events is how many turns the session's log holds, counted at read time. It is\nthe whole log, however few of them RecentEvents carries.","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":{"description":"ID is the session's handle, minted here as \"sess_\" + 32 hex characters. Every\nlater read, patch, event append and control command is addressed with it, and\na caller cannot choose it.","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":{"description":"ParentSessionID is the session that spawned this one, making this a subagent\nof it. Empty means this session is a root — a flow of its own. A parent always\nbelongs to the same org, so a tree never crosses a tenant.","type":"string"},"project":{"description":"The readable build: the product this session built and whether its story\nis public (provenance.go).","type":"string"},"provider":{"description":"Provider is the linked AI account's provider (claude | codex | hanzo | …) that\nserved this run. Empty when the surface did not say.","type":"string"},"published":{"description":"Published is the author's decision to let anyone read this session's story at\nthe public build route. It only ever widens READ access to a session that\nalready exists and grants nothing else; false, an unpublished session is\ninvisible there no matter who asks. It cannot be true without a Project,\nbecause that route is keyed on (org, project).","type":"boolean"},"repo":{"description":"Repo is the code the session is working on, as the surface reported it. It is\ntruth the SURFACE states, so it is a label rather than something resolved here.","type":"string"},"rootSessionId":{"description":"RootSessionID is the top of this session's tree, inherited from the parent and\nshared by every node in one flow. A root session's own id, when it has no\nparent. It is the key one indexed read pulls a whole flow by, and what ?root=\nnarrows a list or a stream to.","type":"string"},"startedAt":{"description":"StartedAt is when the session opened, RFC 3339 in UTC to the second.","type":"string"},"status":{"description":"Status is one of exactly four: running, paused, done, error. running and\npaused are LIVE; done and error are TERMINAL and monotonic — once a session\nreaches one it can never go back, because reopening a finished run would\nfabricate liveness. A control command never moves it: the surface running the\nagent reports the new status, and until it does the command is only recorded.","type":"string"},"target":{"description":"Target is the registered run-target this session is dispatched to — a machine\nthe org claimed, resolved same-org when it was set, so it can never point at\nanother tenant's computer. Empty means the session names no machine.","type":"string"},"taskRunId":{"description":"TaskRunID is that workflow's particular run. A workflow is the definition and\na run is one execution of it, which is why both are carried.","type":"string"},"taskWorkflowId":{"description":"TaskWorkflowID is the hanzoai/tasks durable workflow that actually EXECUTES\nthis session — this registry is the view, control and stream layer over it.\nSet, a control command is FORWARDED to that engine; empty, the running surface\npolls for commands instead, which is every session today.","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":{"description":"Title is the human line a card shows (\"ship the landing page\"), up to 512\ncharacters. Free text, and the one field a surface may rewrite as the work\nturns out to be something else.","type":"string"},"updatedAt":{"description":"UpdatedAt is the session's last-activity clock, same format. It moves on a\nwrite to the row — a status, a title, a re-dispatch — AND on every appended\nturn, because the append bumps it in the same transaction. The list is ordered\non CreatedAt, so this is the field that says whether a session is still saying\nanything.","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"},"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"},"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"},"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"},"socialAccount":{"properties":{"createdAt":{"description":"CreatedAt is when the account was connected, as a unix timestamp in seconds.","type":"integer"},"handle":{"description":"Handle is the account's public name on the network, as the customer knows\nit. Trimmed and bounded at 1024 characters.\n\nExample: \"@acme\"","type":"string"},"id":{"description":"ID is the account's identifier, minted on connect and the id every later\ncall addresses it by.\n\nExample: \"acct_7f3c1a\"","type":"string"},"provider":{"description":"Provider is the network this account is on: x, facebook, instagram,\nlinkedin, tiktok, youtube or threads.\n\nExample: \"x\"","type":"string"},"status":{"description":"Status is the connection lifecycle: connected, disconnected or error. Only\na connected account is a publish target.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the account row last changed, as a unix timestamp in\nseconds. The listing is ordered by it, newest first.","type":"integer"}},"type":"object"},"socialAccountBody":{"properties":{"handle":{"description":"Handle is the account's public name on the network, as the customer knows it\n(`@acme`). Trimmed and bounded at 1024 characters.\n\nExample: \"@acme\"","type":"string"},"provider":{"description":"Provider is the network this account is on: x, facebook, instagram, linkedin,\ntiktok, youtube or threads. Omitted means x. Anything else is refused rather\nthan coerced, because a stored account on a network that does not exist can\nnever be published through.\n\nExample: \"x\"","type":"string"},"status":{"description":"Status is the connection lifecycle: connected, disconnected or error.\nOmitted means connected. Only a connected account is a publish target.","type":"string"}},"type":"object"},"socialAccountWrite":{"properties":{"handle":{"description":"Handle is the account's public name on the network. Omitting it BLANKS the\nstored handle: this is a replacement, not a merge.\n\nExample: \"@acme\"","type":"string"},"provider":{"description":"Provider is the network this account is on: x, facebook, instagram, linkedin,\ntiktok, youtube or threads. Omitted means x.\n\nExample: \"x\"","type":"string"},"status":{"description":"Status is the connection lifecycle: connected, disconnected or error.\nOmitting it RESETS the account to connected.","type":"string"}},"type":"object"},"socialAccounts":{"properties":{"data":{"description":"Data is the accounts, most-recently-updated first, bounded by the limit.","items":{"$ref":"#/components/schemas/socialAccount"},"type":"array"}},"type":"object"},"socialPost":{"properties":{"accountId":{"description":"AccountID / ExternalID / Error are server-managed publish results, set only by\nthe publish path (never by a client update): the account a post was published\nthrough, the provider's returned external post id (for reconciliation), and the\nlast failure reason. Empty until a publish attempt lands.\nAccountID is the connected account the post went out through. Absent until\na publish succeeds.","type":"string"},"channel":{"description":"Channel is the network this post targets: x, facebook, instagram, linkedin,\ntiktok, youtube or threads. It is the channel whose CONNECTED accounts a\npublish fans out to.\n\nExample: \"x\"","type":"string"},"content":{"description":"Content is the post's text, bounded at 8192 characters.\n\nExample: \"Shipping today.\"","type":"string"},"createdAt":{"description":"CreatedAt is when the post was created, as a unix timestamp in seconds.","type":"integer"},"error":{"description":"Error is why the last publish attempt failed, verbatim and bounded. Absent\nwhen no attempt has failed; cleared by a later success.","type":"string"},"externalId":{"description":"ExternalID is the id the network returned for the published post, which is\nwhat reconciles this row against the post on the network. Absent until a\npublish succeeds.","type":"string"},"id":{"description":"ID is the post's identifier, minted on create and the id every later call\naddresses it by.\n\nExample: \"post_91ab20\"","type":"string"},"media":{"description":"Media is the post's attached media as a list of URLs (images today; the\ncomposer's URL field now, an S3 picker later, populate it). Stored as a JSON\narray in the media TEXT column and ALWAYS serialized as an array (never null),\nso a client can rely on `media` being present. Bounded at the write layer\n(normMedia in social.go): each URL clipped to maxField, the list to maxMedia.","items":{"type":"string"},"type":"array"},"scheduleAt":{"description":"ScheduleAt is when the post is due, as a unix timestamp in SECONDS. 0 means\nunscheduled. It is meaningful only while the status is scheduled — a\nscheduled post whose time has arrived is published by the scheduler.","type":"integer"},"status":{"description":"Status is the post's lifecycle state: draft, scheduled, published or failed.\nA fifth, transient publishing state exists while a publish attempt holds the\nclaim; it is never settable from a request and a caller sees it only if it\nreads a post mid-attempt.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the post row last changed, as a unix timestamp in seconds.\nThe listing is ordered by it, newest first.","type":"integer"}},"type":"object"},"socialPostBody":{"properties":{"channel":{"description":"Channel is the network to publish to: x, facebook, instagram, linkedin,\ntiktok, youtube or threads. Omitted means x.\n\nExample: \"x\"","type":"string"},"content":{"description":"Content is the post's text. Required — an empty body is a 400 — and bounded\nat 8192 characters, comfortably above every network's own limit.\n\nExample: \"Shipping today.\"","type":"string"},"media":{"description":"Media is the post's attached media as URLs, at most 10, each bounded at 1024\ncharacters. Blank entries are dropped. Omitting it CLEARS any stored media.","items":{"type":"string"},"type":"array"},"scheduleAt":{"description":"ScheduleAt is when to publish, as a unix timestamp in SECONDS. 0 means\nunscheduled. A negative value is clamped to 0. It only matters for a post\nwhose status is scheduled.","type":"integer"},"status":{"description":"Status is the post's lifecycle state: draft, scheduled, published or failed.\nOmitted means draft. The transient publishing claim is never settable here —\naccepting it from a request would let a caller wedge or replay the guard that\nstops two publishers double-posting the same row.","type":"string"}},"type":"object"},"socialPostWrite":{"properties":{"channel":{"description":"Channel is the network to publish to: x, facebook, instagram, linkedin,\ntiktok, youtube or threads. Omitted means x.\n\nExample: \"x\"","type":"string"},"content":{"description":"Content is the post's text. Required on every update, and bounded at 8192\ncharacters.\n\nExample: \"Shipping today.\"","type":"string"},"media":{"description":"Media is the post's attached media as URLs, at most 10. Omitting it CLEARS\nany stored media: this is a replacement, not a merge.","items":{"type":"string"},"type":"array"},"scheduleAt":{"description":"ScheduleAt is when to publish, as a unix timestamp in SECONDS. 0 means\nunscheduled. Moving it into the past here does NOT publish the post — that is\nthe scheduler's to notice, or the publish operation's.","type":"integer"},"status":{"description":"Status is the post's lifecycle state: draft, scheduled, published or failed.\nOmitting it RESETS the post to draft.","type":"string"}},"type":"object"},"socialPosts":{"properties":{"data":{"description":"Data is the posts, most-recently-updated first, bounded by the limit.","items":{"$ref":"#/components/schemas/socialPost"},"type":"array"}},"type":"object"},"socialProvider":{"properties":{"credentialsConfigured":{"description":"CredentialsConfigured is whether this deployment holds every OAuth\napplication credential the network needs. It is a statement about the\nDEPLOYMENT, not about the caller: a connected account also needs its own\naccess token before a post can go out.","type":"boolean"},"missingCredentials":{"description":"MissingCredentials names the environment variables still unset for this\nnetwork, so the answer is an installation instruction rather than a refusal.\nAbsent when the credentials are complete. Only the NAMES appear here; a\ncredential value is never reported.","items":{"type":"string"},"type":"array"},"provider":{"description":"Provider is the network: x, facebook, instagram, linkedin, tiktok, youtube\nor threads.\n\nExample: \"x\"","type":"string"}},"type":"object"},"socialProviders":{"properties":{"data":{"description":"Data is one row per supported network, in the product's fixed order: x,\nfacebook, instagram, linkedin, tiktok, youtube, threads.","items":{"$ref":"#/components/schemas/socialProvider"},"type":"array"}},"type":"object"},"socialSummary":{"properties":{"accounts":{"description":"Accounts is how many accounts the org has connected, in any status.","type":"integer"},"posts":{"description":"Posts is how many posts the org has, in any state.","type":"integer"},"published":{"description":"Published is how many of them have published.","type":"integer"},"scheduled":{"description":"Scheduled is how many of them are waiting for their scheduled time.","type":"integer"}},"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":{"description":"Initial is the state a fresh document starts in — \"draft\". A stored document\nwith no status at all is read as this too.","type":"string"},"live":{"description":"Live is the ONE state that is publicly readable — \"published\". The site\npulls only documents in it, so reaching Live IS site-publish; every other\nstate is invisible to a reader.","type":"string"},"states":{"description":"States is every lifecycle state in canonical order: draft, in_review,\napproved, queued, published, archived. The console lays its board columns\nout in exactly this order, so the order is part of the answer.","items":{"type":"string"},"type":"array"},"transitions":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Transitions maps each state to the states it may move to. A target absent\nfrom a state's list is REFUSED, at the endpoint and again at the storage\nboundary — this is the whole rule, not a hint for the UI. A state never\nlists itself; a move that changes nothing is always legal.","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"},"statusBreakdown":{"properties":{"error":{"description":"invocations that ran and failed","type":"integer"},"success":{"description":"invocations whose code ran and wrote nothing to stderr","type":"integer"},"timeout":{"description":"invocations that hit their configured deadline","type":"integer"}},"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"},"description":"Args are the tool's default arguments, merged under whatever the caller\npasses at run time.","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":{"description":"Draft, when set, is the prompt the embedded AI answers first; its output is\nfolded into one of Args before the tool runs.","type":"string"},"draftInto":{"description":"DraftInto names the argument the drafted text lands in. Empty means \"brief\".","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":{"description":"Title is the one-line quest as a person reads it in the checklist.","type":"string"},"tool":{"description":"Tool is the MCP tool the Business AI runs for \"do it for me\". A step naming\nnone can only be completed by a person.","type":"string"}},"type":"object"},"strategyView":{"properties":{"action":{"description":"Action is the tactic itself: the thing to go and do.","type":"string"},"category":{"description":"Category is the growth discipline the tactic belongs to — the axis\n`?category=` narrows on.","type":"string"},"id":{"description":"ID is the tactic's stable slug in the corpus.","type":"string"},"tags":{"description":"Tags are the PRECONDITIONS this tactic already satisfied to appear in the\nanswer — `stage:<name>` and `has:<capability>` predicates over the org's\nobserved profile. They are carried back so a caller can show why a tactic\nsurfaced, not so it can filter again.","items":{"type":"string"},"type":"array"},"workload":{"description":"Workload is how much effort running the tactic costs, so a corpus can be cut\nto what the org has the hands for.","type":"string"}},"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 <script>.<subdomain>.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"},"submitReq":{"properties":{"files":{"description":"Files is the batch to scan, at most 500 files and 8 MiB of content in total.","items":{"$ref":"#/components/schemas/scan"},"type":"array"},"project":{"description":"Project names the sub-scope the scan is filed under. It must be a slug; omit\nit and the caller's project header is used instead.","type":"string"}},"required":["files"],"type":"object"},"subprocessorList":{"properties":{"subprocessors":{"description":"Subprocessors is the list, each naming at least what it is and what it is\nfor — a name alone says nothing.","items":{},"type":"array"}},"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"},"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":{"description":"Automatable is true when the step names a tool, so the Business AI can do it\nrather than only describe it.","type":"boolean"},"detail":{"description":"Detail is the step's own prose — what it asks for.","type":"string"},"rationale":{"description":"Rationale is why this step is being suggested NOW, written for the person\nreading it. It explains the ranking, not the step.","type":"string"},"stepId":{"description":"StepID is the checklist step being recommended — the id every step route\ntakes, so a caller can act on the suggestion directly.","type":"string"},"title":{"description":"Title is the step's own one-line quest.","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"},"suite":{"properties":{"attempts":{"description":"Attempts is how many times to try each item; the harness's default applies\nwhen it is omitted.","type":"integer"},"benchmarks":{"description":"Benchmarks are the catalog ids to run. At least one is required, and every id\nmust be in the catalog.","items":{"type":"string"},"type":"array"},"endpoint":{"description":"Endpoint is your own chat-completions URL, for benchmarking a model this arena\ndoes not host. Either this or model is required.","type":"string"},"model":{"description":"Model is the catalog model id to run. Either this or endpoint is required.","type":"string"}},"required":["benchmarks"],"type":"object"},"summary":{"properties":{"calls":{"description":"Calls is how many calls this org has placed or received, over its whole\nhistory — a running total, not a window.","type":"integer"},"messages":{"description":"Messages is the same running total for messages.","type":"integer"},"numbers":{"description":"Numbers is how many numbers this org holds right now.","type":"integer"}},"type":"object"},"summaryResp":{"properties":{"account":{"$ref":"#/components/schemas/sourceState","description":"Account reports the linked-accounts ledger's own availability, so a partial\nanswer never fabricates this half. It is scoped to the CALLER: the accounts\nthey linked, metered from each provider's own login."},"from":{"description":"From is when the window opens, RFC 3339 UTC. ONE resolver fixes it for both\nledgers, so the account rows and the Hanzo rows always cover the same period\n— two resolvers could drift and turn the union into a lie.","type":"string"},"hanzo":{"$ref":"#/components/schemas/sourceState","description":"Hanzo reports the same for the Hanzo-routed ledger, which is scoped to the\nORG rather than the caller — a different question over the same window. The\ntwo are independent: either can be unavailable while the other answers, and\nRows then carries only the half that did."},"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":{"description":"To is where the window closes, EXCLUSIVE, RFC 3339 UTC — the instant the read\nwas served. Shared by both ledgers, for the reason From gives.","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"},"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"},"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"},"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":{"description":"Actor is the identity a reconcile writes AS. It is the loop guard: a change\nthis identity made is one we already have, so it is not synced back.","type":"string"},"createdAt":{"description":"CreatedAt is when the link was first declared, RFC3339 in UTC.","type":"string"},"direction":{"description":"Direction is which way work flows: \"both\", \"pull\" (target ← source), \"push\"\n(source → target), or \"off\" — which keeps the link declared and moves nothing.","type":"string"},"id":{"description":"ID is the link's handle, derived from its source and target — which is what\nmakes re-declaring the same pair an update rather than a duplicate.","type":"string"},"kind":{"description":"Kind is what is being synced. \"git\" today; the field exists so a storage or\ndatabase link is a value here rather than a second route family.","type":"string"},"source":{"$ref":"#/components/schemas/endpointView","description":"Source is the side read FROM on a pull."},"target":{"$ref":"#/components/schemas/endpointView","description":"Target is the side written TO on a push."},"trigger":{"description":"Trigger is what starts a reconcile: \"webhook\" (the provider tells us),\n\"poll\" (we ask on a schedule), or \"manual\" (only an explicit call).","type":"string"},"updatedAt":{"description":"UpdatedAt is bumped by every reconcile, so it reads as the LAST-SYNCED time\nrather than the last edit. Absent until the first one runs.","type":"string"}},"type":"object"},"tagConfig":{"properties":{"tags":{"items":{"$ref":"#/components/schemas/browserTagOut"},"type":"array"}},"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":{"description":"Capacity is a human summary of the machine's size, up to 256 characters. Prose\nonly; a scheduler reads Spec.","type":"string"},"host":{"description":"Host is the hostname sessions on this machine will report. It is what makes a\nre-link IDEMPOTENT: the same (org, host, owner) refreshes the existing row and\nanswers 200, while a request with no host always creates a new target and\nanswers 201. It never adopts a row owned by somebody else.","type":"string"},"kind":{"description":"Kind is laptop | cloud | gpu | cluster | machine. Empty registers a `machine`;\nanything outside the five is a 400.","type":"string"},"label":{"description":"Label is the name to show for this machine, up to 128 characters. REQUIRED —\nit is the only field here a person reads.","type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics","description":"Metrics is a live sample, and sending one IS A HEARTBEAT: it refreshes the row\nand starts the 90-second liveness window, and it is appended to the fleet\nseries as one point. Its own `at` is ignored — the server stamps the time, so\na client can never age or backdate its own machine. Omit it to register a\nmachine without claiming it is alive."},"spec":{"$ref":"#/components/schemas/Spec","description":"Spec is the machine's static capability — os, arch, cores, RAM, accelerators.\nEvery field is bounded on write and at most 32 accelerators are accepted, so\nwhat comes back may be clamped. Omit it for a destination nothing probes."},"status":{"description":"Status is online | offline | draining. Empty registers `online`. It states\nINTENT — a heartbeat is what decides whether an online machine is actually\nreachable, so declaring online does not make it so.","type":"string"}},"type":"object"},"targetView":{"properties":{"capacity":{"description":"Capacity is a human summary of what the machine has (\"8 vCPU / 32G\", \"1× GB10\"),\nup to 256 characters. Prose for a card — Spec is the same thing in a form a\nscheduler can read, and nothing derives one from the other.","type":"string"},"createdAt":{"description":"CreatedAt is when the machine was first registered, RFC 3339 in UTC. A re-link\nrefreshes the row and leaves this alone, so it dates the machine and not the\nconnection.","type":"string"},"host":{"description":"Host is the hostname sessions on this machine report, and it is a JOIN KEY, not\na label: a session naming this host counts against the load below even when it\nnames no target id, and a re-link of the same (org, host, owner) refreshes this\nrow instead of creating a second. Empty means the machine is addressable only\nby ID.","type":"string"},"id":{"description":"ID is the machine's handle, minted as \"tgt_\" + 32 hex characters. It is what a\nsession records to say it ran here, and what every later patch, claim or\ndelete addresses.","type":"string"},"kind":{"description":"Kind is what sort of destination this is, from a closed five: laptop | cloud |\ngpu | cluster | machine. A register that named none is a `machine`.","type":"string"},"label":{"description":"Label is the name a person gave the machine (\"workshop\"), up to 128\ncharacters. Required at register, free text, and the only field here meant for\nreading rather than matching.","type":"string"},"metrics":{"$ref":"#/components/schemas/Metrics","description":"Metrics is what the machine was DOING at its last heartbeat — loadavg, memory,\naccelerator utilization. Absent when it has never beaten. It is a SNAPSHOT:\nthe series over time lives in the fleet samples, not here."},"metricsAt":{"description":"MetricsAt is when that heartbeat was recorded, RFC 3339 in UTC, and the SERVER\nstamps it — a client cannot backdate or forge the staleness clock. Absent means\nnever beaten, which is exactly the case where Status is taken at its word.","type":"string"},"running":{"description":"Running is how many of those are in `running` right now — the number a\ndispatcher weighs against Capacity. paused sessions are in Sessions and not\nhere.","type":"integer"},"sessions":{"description":"Sessions is how many of the org's sessions are mapped to this machine, by\ntarget id OR by matching Host. All of them, whatever their status.","type":"integer"},"spec":{"$ref":"#/components/schemas/Spec","description":"Spec is what the machine IS — os, arch, cores, RAM, accelerators — the static\nhalf, changed only when something reports it again. Absent when nothing has\never been reported, and a scheduler reads absence as \"cannot satisfy a floor\"\nrather than as \"no limits\"."},"status":{"description":"Status is the EFFECTIVE liveness — online | offline | draining — not the\nstored one. offline and draining are operator INTENT and are reported as they\nstand; `online` is checked against the heartbeat, and a machine that has beaten\nbefore but not in the last 90 seconds reports offline whatever its row says. A\ntarget that has NEVER beaten keeps its stored status, because a hand-registered\ndestination has no fact to check.","type":"string"},"updatedAt":{"description":"UpdatedAt is the last write to the row, same format — which for a beating\nmachine is its last heartbeat, since a heartbeat IS a write.","type":"string"}},"type":"object"},"tariffIn":{"properties":{"agentOfRecord":{"description":"AgentOfRecord puts us on file as the entity's agent, yearly.","type":"boolean"},"expeditedEin":{"description":"ExpeditedEIN prioritises the EIN.","type":"boolean"},"jurisdiction":{"description":"Jurisdiction is the state of formation.","type":"string"},"structure":{"description":"Structure is the entity being formed: c-corp, llc or dao-llc.","type":"string"}},"type":"object"},"taxonIn":{"properties":{"brands":{"description":"Brands are the brands whose console shows it. Omit for every brand its\ncategory admits.","items":{"type":"string"},"type":"array"},"category":{"description":"Category is the id of an EXISTING category to file it under. Required.","type":"string"},"description":{"description":"Description is the one line shown beneath the name.","type":"string"},"href":{"description":"Href is the absolute URL an external product launches. Give this or route,\nnever both.","type":"string"},"icon":{"description":"Icon names the icon the surface renders, e.g. \"Database\".","type":"string"},"id":{"description":"ID is the taxon slug to write, from the path.","type":"string"},"name":{"description":"Name is the display name. Required.","type":"string"},"order":{"description":"Order is where it sits within its category, ascending.","type":"integer"},"published":{"description":"Published is whether it is shown. Omitted means published — a taxon someone\ntook the trouble to write is meant to be seen, and hiding one is the\ndeliberate act.","type":"boolean"},"route":{"description":"Route is the in-console path it opens, e.g. \"/vector\". Give this or href,\nnever both.","type":"string"},"tags":{"description":"Tags are free-form labels for search and grouping across categories.","items":{"type":"string"},"type":"array"}},"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":{"description":"Category is the corporate need the template serves: formation, equity, ops or\nsales. Formation and equity are the securities-class categories, which is what\nforces counselReview.","type":"string"},"counselReview":{"description":"CounselReview marks a template whose rendered documents open with the counsel\nnotice. True for every formation and equity template whatever an override\nsends: the engine prepends the notice and no caller can suppress it.","type":"boolean"},"fields":{"description":"Fields declares the merge fields the body consumes — every key a generation\nmust supply, each with its human label. All are REQUIRED: a missing one is\nrefused rather than rendered as a blank into a contract.","items":{"$ref":"#/components/schemas/Field"},"type":"array"},"id":{"description":"ID is the template's stable id and the path segment that fetches its body —\n\"nda\", \"msa\", \"safe\". An override keeps the built-in's id.","type":"string"},"origin":{"description":"Origin is \"builtin\" for a template the platform ships or \"org\" for one this\norg saved. It separates the catalog every tenant sees from this tenant's own.","type":"string"},"title":{"description":"Title is the display name, e.g. \"Mutual Non-Disclosure Agreement\". A generated\ndocument inherits it.","type":"string"},"version":{"description":"Version is which version of this template the caller's org resolves to. A\nbuilt-in is version 1; the org's first override is 2 and each save increments,\nso an override version never collides with the built-in's.","type":"integer"}},"type":"object"},"testResult":{"properties":{"delivered":{"description":"Delivered is whether the subscriber accepted the test POST. It is the whole\nanswer: the send is synchronous and is not retried.","type":"boolean"},"durationMs":{"description":"DurationMs is how long the single attempt took, in MILLISECONDS.","type":"integer"},"error":{"description":"Error says what stopped it. Empty when delivered.","type":"string"},"httpStatus":{"description":"HTTPStatus is what the subscriber answered, or 0 if it never answered.","type":"integer"}},"type":"object"},"ticketGrant":{"properties":{"expiresIn":{"description":"ExpiresIn is how long the ticket is good for, in seconds.","type":"integer"},"ticket":{"description":"Ticket is the grant itself. It is single-purpose and short-lived, and it\ntravels in a query string because a WebSocket handshake carries no\nAuthorization header a browser can set.","type":"string"},"url":{"description":"URL is the PATH to open, ticket included — not an absolute URL. Which host\nthis address wears in public is the edge's answer and not this process's, so\nan absolute URL would be a guess; the client already knows the host it is\ntalking to. It names the PAGE, which is what a caller embeds — the page\nfinds its own socket, and a caller that wants the raw socket adds `/ws`.","type":"string"}},"type":"object"},"todoProject":{"properties":{"createdAt":{"description":"CreatedAt is when the board was created, in unix seconds. 0 on a forge board\nfor the same reason Description is absent.","type":"integer"},"description":{"description":"Description is whatever an index board was created with. Absent on a forge\nboard: this projection takes the repository's name and nothing else about the\nrepository.","type":"string"},"id":{"description":"ID is the board's opaque handle, and it is NOT how you address it — Key is.\nIts shape says which source answered: a forge board's is the repository's\nfull name (\"hanzoai/cloud\"), an index board's a minted \"prj_\" id.","type":"string"},"key":{"description":"Key addresses the board everywhere else — /v1/todo/projects/<key>/issues —\nand prefixes every issue identifier on it. An index board's key is 2-8\nuppercase alphanumerics starting with a letter (\"ENG\", \"OPS2\") and is matched\ncase-insensitively; a forge board's is the repository name as the forge\nspells it.","type":"string"},"name":{"description":"Name is the board's display name. For a forge board it is the repository\nname, so it equals Key; an index board carries its own.","type":"string"},"org":{"description":"Org is the IAM org the board belongs to, taken from the validated principal\nand never from the request. Every board a caller can see is in it.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the board record last changed, in unix seconds — the BOARD,\nnot the work on it, so filing an issue does not move it. 0 on a forge board.","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"},"topupIn":{"properties":{"amountCents":{"description":"AmountCents is how much to charge, in cents of Currency. Required.","type":"integer"},"currency":{"description":"Currency is the ISO-4217 code to charge in. Empty takes the deployment's\nown default.","type":"string"},"paymentMethodId":{"description":"MethodID names a card the subject already saved, for the saved-card endpoint.","type":"string"},"sourceId":{"description":"SourceID is a single-use card token from the payment form, for the token\nendpoint. It is vaulted as part of the charge, so a caller never holds card\nnumbers and this service never sees one.","type":"string"}},"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"},"traceList":{"properties":{"data":{"description":"Data is the caller org's traces matching the filters, bounded by limit.","items":{"$ref":"#/components/schemas/traceView"},"type":"array"}},"type":"object"},"traceView":{"properties":{"apiKeyHash":{"description":"APIKeyHash is the non-reversible credential ref (never a plaintext key), so\na trace correlates to the key that drove it without the store holding a secret.","type":"string"},"datasetItemId":{"description":"ItemID is the example the call answered.","type":"string"},"datasetName":{"description":"Dataset is the set the graded example came from.","type":"string"},"endTime":{"description":"EndTime is when it returned.","type":"string"},"id":{"description":"ID is the trace's handle, the value a score points at.","type":"string"},"input":{"description":"Input is what the model was given.","type":"object"},"latencyMs":{"description":"LatencyMs is EndTime-StartTime in milliseconds, nil when the trace carries\nno timing (so the console renders \"—\", never a fabricated 0).","type":"number"},"model":{"description":"Model is the model that answered.","type":"string"},"name":{"description":"Name is the trace's label, \"eval:<run>\" for a call a run made.","type":"string"},"output":{"description":"Output is what it answered.","type":"string"},"projectId":{"description":"ProjectID is the sub-scope within the org the call was made under.","type":"string"},"runName":{"description":"RunName is the run the call belongs to.","type":"string"},"sessionId":{"description":"SessionID groups the calls of one run.","type":"string"},"startTime":{"description":"StartTime is when the call began.","type":"string"},"timestamp":{"description":"Timestamp is the trace's own clock, equal to StartTime for a timed call.","type":"string"}},"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"},"transferReq":{"properties":{"authCode":{"description":"AuthCode is the transfer authorization the losing registrar issued. It is\nrequired.","type":"string"},"domain":{"description":"Domain is the name to move in. It is required.","type":"string"},"years":{"description":"Years is the term to buy on transfer, defaulting to 1.","type":"integer"}},"required":["domain","authCode"],"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":{"description":"Children is this node's direct children, each a whole node, so the array nests\nto the depth of the flow. A leaf carries null rather than an empty array. The\nsubtree is materialised in full, up to 10000 nodes, out of one indexed read of\nthe root; nothing is walked node by node.","items":{"$ref":"#/components/schemas/treeNode"},"type":"array"},"session":{"$ref":"#/components/schemas/sessionView","description":"Session is this node's own session, carrying its event count and its direct\nfan-out. It is the same shape the list and detail reads answer with, minus the\nlast-event preview, which the tree does not fetch."}},"type":"object"},"triggerList":{"properties":{"triggers":{"description":"Triggers is one row per function, describing how it is reached.","items":{"$ref":"#/components/schemas/triggerView"},"type":"array"}},"type":"object"},"triggerView":{"properties":{"enabled":{"description":"whether it currently fires","type":"boolean"},"functionName":{"description":"the function it calls","type":"string"},"id":{"description":"the trigger's handle","type":"string"},"name":{"description":"a human label for it","type":"string"},"target":{"description":"the path it calls","type":"string"},"type":{"description":"what kind of trigger it is; HTTP is the only one today","type":"string"}},"type":"object"},"trustAsk":{"properties":{"accept":{"description":"Accept must be true when the centre states an NDA. The text accepted is\nrecorded verbatim on the request, so a later edit to the NDA cannot rewrite\nwhat this party agreed to.","type":"boolean"},"email":{"description":"Email is where the grant will be sent, and the ONLY address the resulting\nlink admits. Required.","type":"string"},"item":{"description":"Item names one published item to ask for. Optional; omitting it asks for\neverything released on request.","type":"string"},"party":{"description":"Party is the company the asker is from. Optional, and recorded as stated.","type":"string"},"reason":{"description":"Reason is why they want it. Optional, and recorded as stated — it is what the\nperson deciding reads.","type":"string"},"slug":{"description":"Slug is the centre's public address, taken from the path.","type":"string"}},"type":"object"},"trustAskView":{"properties":{"createdAt":{"description":"CreatedAt is when the ask arrived, in unix milliseconds.","type":"integer"},"decidedAt":{"description":"DecidedAt is when it was answered, in unix milliseconds; 0 while open.","type":"integer"},"decidedBy":{"description":"DecidedBy is who answered it.","type":"string"},"email":{"description":"Email is the address that asked, as stated and UNVERIFIED — it names a party\nand proves nothing, which is why the grant is addressed to it rather than\ntrusting it.","type":"string"},"expiresAt":{"description":"ExpiresAt is when a granted ask closes, in unix milliseconds.","type":"integer"},"id":{"description":"ID is the request's id.","type":"string"},"item":{"description":"Item is the item asked for, empty when the whole released tier was asked for.","type":"string"},"link":{"description":"Link is the share link a granted ask became.","type":"string"},"nda":{"description":"Nda is the text this party accepted, verbatim as it stood when they accepted.","type":"string"},"note":{"description":"Note is what the decider wrote when refusing.","type":"string"},"party":{"description":"Party is the company the asker stated.","type":"string"},"reason":{"description":"Reason is why they said they want it.","type":"string"},"state":{"description":"State is open, granted or refused.","type":"string"}},"type":"object"},"trustAsked":{"properties":{"id":{"description":"ID is the request's id, so the asker can be told about it later.","type":"string"},"state":{"description":"State is always \"open\": recording an ask decides nothing.","type":"string"}},"type":"object"},"trustCoverage":{"properties":{"controls":{"$ref":"#/components/schemas/trustTally","description":"Controls is how the controls stand, independent of any framework."},"frameworks":{"description":"Frameworks is the per-framework counts.","items":{"$ref":"#/components/schemas/coverRow"},"type":"array"},"generated":{"description":"Generated is when this was computed, unix milliseconds.","type":"integer"},"version":{"description":"Version is the embedded inventory's version.","type":"string"}},"type":"object"},"trustDecision":{"properties":{"days":{"description":"Days is how long the grant stays open, from now. Optional; 14 by default and\n365 at most — a longer release is describing a customer relationship rather\nthan a document.","type":"integer"},"id":{"description":"ID is the request to answer, taken from the path.","type":"string"},"note":{"description":"Note is why. Recorded on the request either way, and it is what the record\nshows a year later.","type":"string"}},"type":"object"},"trustDesk":{"properties":{"grants":{"description":"Grants is every grant that has been made, newest first.","items":{"$ref":"#/components/schemas/trustGrantView"},"type":"array"},"items":{"description":"Items is everything the org holds, both tiers, retired included.","items":{"$ref":"#/components/schemas/trustItemView"},"type":"array"},"name":{"description":"Name is the centre's display name.","type":"string"},"nda":{"description":"Nda is the text a party must accept before asking.","type":"string"},"published":{"description":"Published is whether the centre answers at its public address.","type":"boolean"},"requests":{"description":"Requests is every ask, newest first, open ones included.","items":{"$ref":"#/components/schemas/trustAskView"},"type":"array"},"slug":{"description":"Slug is the public address, empty until the centre is published.","type":"string"}},"type":"object"},"trustDocuments":{"properties":{"documents":{"description":"Documents is the list; a gated entry carries no address.","items":{"$ref":"#/components/schemas/docRow"},"type":"array"}},"type":"object"},"trustEdit":{"properties":{"body":{"description":"Body replaces the item's content.","type":"string"},"document":{"description":"Document replaces the file the item points at — this is how a report is\nsuperseded by its next edition. The new document must already exist in the\ncaller org's own store.","type":"string"},"framework":{"description":"Framework replaces the standard it speaks to.","type":"string"},"id":{"description":"ID is the item to change, taken from the path.","type":"string"},"name":{"description":"Name replaces its title.","type":"string"},"retired":{"description":"Retired withdraws the item, or true→false restores it. A retired item leaves\nthe public centre at once and can no longer be granted; grants already made\nover it stand, because they are part of the record.","type":"boolean"},"summary":{"description":"Summary replaces the line about it.","type":"string"},"tier":{"description":"Tier moves it between public and gated. Moving an auditor-signed item to\npublic is refused.","type":"string"}},"type":"object"},"trustGrantView":{"properties":{"email":{"description":"Email is the one address the link admits.","type":"string"},"expiresAt":{"description":"ExpiresAt is when the grant closes, in unix milliseconds.","type":"integer"},"item":{"description":"Item is the item granted, empty when the whole released tier was granted.","type":"string"},"link":{"description":"Link is the share link's id — the token the party opens. Reading it here does\nnot widen it: the link admits only Email whoever holds the id.","type":"string"},"live":{"description":"Live is whether the grant is still open at the time of reading.","type":"boolean"}},"type":"object"},"trustGranted":{"properties":{"delivery":{"description":"Delivery is empty when the asker was mailed, and otherwise says what happened\ninstead — so an approver is never left believing a mail went out that did not.","type":"string"},"expiresAt":{"description":"ExpiresAt is when the grant closes, in unix milliseconds.","type":"integer"},"link":{"description":"Link is the share link's id. The link admits only the address that asked.","type":"string"},"state":{"description":"State is \"granted\".","type":"string"}},"type":"object"},"trustItem":{"properties":{"available":{"description":"Available is \"now\" when the item can be read immediately, or \"on request\"\nwhen it is released only to a party who asks and is answered. It is the one\nfield a page renders the difference from.","type":"string"},"body":{"description":"Body is the item's content, for the kinds that are text rather than a file —\nan article, a subprocessor entry, a note. Empty for anything released on\nrequest: a summary of a document is still the document.","type":"string"},"framework":{"description":"Framework is the standard the item speaks to, when it speaks to one.","type":"string"},"id":{"description":"ID addresses the item — for reading it if it is available now, or for naming\nit in a request if it is not.","type":"string"},"kind":{"description":"Kind is what the item is: report, letter, policy, questionnaire,\nsubprocessor, article or update.","type":"string"},"name":{"description":"Name is the item's title.","type":"string"},"signed":{"description":"Signed is \"self\" when the org states it itself and \"auditor\" when an\nindependent auditor put their name to it. It is the reason an item is\navailable now or on request, so a reader can see the rule rather than infer it.","type":"string"},"summary":{"description":"Summary is a line about the item.","type":"string"},"updatedAt":{"description":"UpdatedAt is when the item last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"trustItemView":{"properties":{"attester":{"description":"Attester is who vouched for it: self or auditor.","type":"string"},"body":{"description":"Body is the item's content for the kinds that are text rather than a file.","type":"string"},"createdAt":{"description":"CreatedAt is when it was published, in unix milliseconds.","type":"integer"},"document":{"description":"Document is the data-room document holding its bytes, empty when it has none.","type":"string"},"framework":{"description":"Framework is the standard it speaks to, when it speaks to one.","type":"string"},"id":{"description":"ID addresses the item.","type":"string"},"kind":{"description":"Kind is what it is.","type":"string"},"name":{"description":"Name is its title.","type":"string"},"retired":{"description":"Retired is whether it has been withdrawn. A retired item is absent from the\npublic centre and cannot be granted; it is kept because a grant already made\nover it is part of the record.","type":"boolean"},"summary":{"description":"Summary is a line about it.","type":"string"},"tier":{"description":"Tier is public or gated. Gated is the default and an auditor-signed item can\nonly ever be gated.","type":"string"},"updatedAt":{"description":"UpdatedAt is when it last changed, in unix milliseconds.","type":"integer"}},"type":"object"},"trustPage":{"properties":{"items":{"description":"Items is everything the centre publishes: what can be read now, and what\nexists and is released on request.","items":{"$ref":"#/components/schemas/trustItem"},"type":"array"},"name":{"description":"Name is the org's display name for its centre.","type":"string"},"nda":{"description":"Nda is the text a party must accept to ask for the gated items, verbatim.\nEmpty when the org asks for none.","type":"string"},"slug":{"description":"Slug is the centre's public address.","type":"string"}},"type":"object"},"trustPublish":{"properties":{"attester":{"description":"Attester is who vouched for it: \"self\" for anything the org states itself, or\n\"auditor\" for anything an independent auditor put their name to. REQUIRED, and\nanything other than \"self\" is read as \"auditor\" — the safe direction, since an\nauditor-signed item can only ever be released on request.","type":"string"},"body":{"description":"Body is the item's content for the kinds that are text rather than a file: an\narticle, a subprocessor entry, a dated note.","type":"string"},"document":{"description":"Document is a data-room document holding the item's bytes, uploaded first\nthrough POST /v1/dataroom/documents. Optional: an item can be content with no\nfile. The document must already exist in the caller org's own store.","type":"string"},"framework":{"description":"Framework is the standard it speaks to. Optional and free text — the value is\nthe org's own, not a list this API keeps.","type":"string"},"kind":{"description":"Kind is what the item is: report, letter, policy, questionnaire, subprocessor,\narticle or update. Required.","type":"string"},"name":{"description":"Name is the item's title. Required.","type":"string"},"summary":{"description":"Summary is a line about it. Optional.","type":"string"},"tier":{"description":"Tier is who may read it: \"public\" or \"gated\". It DEFAULTS TO GATED and\nanything that is not exactly \"public\" is gated, so an item published by a\ncaller that says nothing is private and someone has to release it on purpose.\n\"public\" is refused for an auditor-signed item.","type":"string"}},"type":"object"},"trustRefused":{"properties":{"state":{"description":"State is \"refused\".","type":"string"}},"type":"object"},"trustSettings":{"properties":{"name":{"description":"Name is the centre's display name. Required to publish.","type":"string"},"nda":{"description":"Nda is the text a party must accept before asking for a document. Optional;\nempty asks for no acceptance. The text in force is copied onto each request as\nit is accepted, so editing it never changes what anyone already agreed to.","type":"string"},"publish":{"description":"Publish makes the centre answer at its public address. False withdraws it: the\naddress stops answering while every item, grant and record stays exactly as it\nwas, so withdrawing is reversible and loses nothing.","type":"boolean"},"slug":{"description":"Slug is the public address to answer at — a lowercase label of letters,\ndigits and hyphens. Required to publish, unique across the deployment, and one\norg holds one: publishing under a new address MOVES the centre rather than\nleaving the old one answering.","type":"string"}},"type":"object"},"trustTally":{"properties":{"absent":{"description":"Absent is how many the organization does not have. An absent control still\nnames the clause it would satisfy — that is a roadmap — but it never moves\na coverage number.","type":"integer"},"automated":{"description":"Automated is how many run with nobody in the loop.","type":"integer"},"partial":{"description":"Partial is how many run but do not cover their whole claim. Each says what\nis missing.","type":"integer"},"statement":{"description":"Statement is the counts as one sentence, safe to quote.","type":"string"},"total":{"description":"Total is how many controls this organization publishes.","type":"integer"},"unverified":{"description":"Unverified is how many rest on somebody having READ the source rather than\non a test or an audit row. Only a check that can FAIL counts as verified,\nand coverage counts those one rung weaker than they claim to be.","type":"integer"}},"type":"object"},"unreadable":{"properties":{"reason":{"type":"string"}},"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":{"description":"ComputeRef re-binds (or, with \"\", unbinds) the visor machine. Opaque here.","type":"string"},"description":{"description":"Description replaces the line other agents read in the tool catalogue.","type":"string"},"executionMode":{"description":"ExecutionMode switches between one-shot and long-running. The RESULTING\nmode+schedule are validated together, so switching to long-running without a\nstored or supplied cron is refused rather than accepted into an agent the\nscheduler would skip forever. A switch INTO long-running counts against the\nper-org cap and can be a 409.","type":"string"},"instructions":{"description":"Instructions replaces the system prompt whole, up to 32 KiB. There is no\nappend: a prompt is one text, and sending \"\" clears it.","type":"string"},"model":{"description":"Model re-points the agent at another model, checked against the gateway's\nserved catalogue exactly as create checks it. Empty STRING is refused — say\nnothing to keep the current one. Past runs keep the model that served them.","type":"string"},"ref":{"description":"Ref is the agent to update — its public id or org-unique name, from the path.","type":"string"},"schedule":{"description":"Schedule replaces the cron. It is validated against the mode this update\nleaves behind, and dropped if that mode is one-shot.","type":"string"},"serviceAccountId":{"description":"ServiceAccountID re-points (or, with \"\", clears) the IAM service account a\nscheduled run is billed as. Clearing it puts that spend back on the org.","type":"string"},"tools":{"description":"Tools replaces the whole allow-list, it does not add to it. Sending [] takes\nevery tool away, which is the only way to say that.","items":{"type":"string"},"type":"array"}},"type":"object"},"updateCampaignIn":{"properties":{"account":{"description":"Account is the provider ad-account this campaign runs on (Meta act_<id>). 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"},"updateList":{"properties":{"updates":{"description":"Updates is the entries, each carrying the date it describes.","items":{},"type":"array"}},"type":"object"},"upkeepIn":{"properties":{"agentOfRecord":{"description":"AgentOfRecord includes our agent fee, which most entities owe someone.","type":"boolean"},"jurisdiction":{"description":"Jurisdiction is the state the entity stands in.","type":"string"},"structure":{"description":"Structure is the entity kind: c-corp, llc or dao-llc.","type":"string"}},"type":"object"},"uploadIn":{"properties":{"bucket":{"description":"Bucket is the bucket to upload into, from the path.","type":"string"},"key":{"description":"Key is the object key relative to the bucket root. It is path-cleaned, so a\n\"../\" cannot escape the bucket, and an empty or unclean key is 400.","type":"string"}},"type":"object"},"usage":{"properties":{"costCents":{"description":"null — no per-invocation cost source","type":"integer"},"series":{"description":"one line per function that ran in the window","items":{"$ref":"#/components/schemas/costLine"},"type":"array"},"status":{"$ref":"#/components/schemas/statusBreakdown","description":"how those invocations ended"}},"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 label that was ASKED for. A plan whose retention is shorter\nthan that window is served the retention instead, so read start and end for\nthe window the rows actually cover and retentionDays for the reason — on a\nclamped read the label is longer than what was served.","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"},"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"},"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 && 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"},"validatorClaim":{"properties":{"nonce":{"description":"Nonce is the value GET /v1/validator/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"},"vendorsOut":{"properties":{"vendors":{"description":"Vendors is every vendor the org has recorded, canonical name ascending.","items":{"$ref":"#/components/schemas/VendorRow"},"type":"array"}},"type":"object"},"verdict":{"properties":{"builds":{"type":"integer"},"commit":{"type":"string"},"fired":{"type":"boolean"},"org":{"type":"string"},"reason":{"type":"string"},"ref":{"type":"string"},"repo":{"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"},"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"},"walletList":{"properties":{"wallets":{"description":"Wallets are the matching wallets, newest first.","items":{"$ref":"#/components/schemas/Wallet"},"type":"array"}},"type":"object"},"webEngine":{"properties":{"name":{"description":"Name is the engine, matching the `engine` stamped on each result.","type":"string"},"outcome":{"description":"Outcome is \"answered\", \"blind\" or \"failed\" — see outcome.go. \"blind\" means\nthe page came back and no results could be read out of it.","type":"string"},"results":{"description":"Results is how many hits this engine contributed, before the merge\ndeduplicated them against the others.","type":"integer"}},"type":"object"},"webQuestion":{"properties":{"language":{"description":"Language narrows the search to a locale, BCP-47-ish (\"en\", \"ja\"). Empty\nmeans no narrowing.","type":"string"},"max_sources":{"description":"MaxSources caps how many pages are read. Empty means the mode's own budget.","type":"integer"},"mode":{"description":"Mode is how much work to do: `search` (fast, one pass), `news` (recency\nbiased), `research` (a plan and several rounds) or `deep` (the widest\nsurvey). Empty means research.","type":"string"},"q":{"description":"Q is the question, in plain language. Required.","type":"string"},"sources":{"description":"Sources narrows where the evidence comes from: any of `web`, `news`,\n`academic`, `github`, `reddit`, `x`. Each becomes a site-scoped search, so\n`[\"x\"]` researches X/Twitter posts rather than the open web.","items":{"type":"string"},"type":"array"}},"type":"object"},"webResult":{"properties":{"content":{"description":"Content is the ENGINE's snippet — the few lines shown under the title, not\nthe page's text. Read the page itself with POST /v1/crawl.","type":"string"},"engine":{"description":"Engine names the backend that found this hit, so one engine's view of a\nquery can be told from another's.","type":"string"},"title":{"description":"Title is the page's title.","type":"string"},"url":{"description":"URL is the page's address, as the engine reported it.","type":"string"}},"type":"object"},"webSearchQuery":{"properties":{"language":{"description":"Language narrows the engines to a locale, BCP-47-ish (\"en\", \"ja\", \"de\").\nEmpty means no narrowing.","type":"string"},"q":{"description":"Q is the query. Required — an empty one is refused rather than answered\nwith the whole web.","type":"string"}},"type":"object"},"webSearchResults":{"properties":{"engines":{"description":"Engines is one entry per engine asked, in the order they were asked. It is\nADDITIVE to the SearXNG contract, which the LibreChat client ignores as an\nunknown field exactly as it ignores `engine` on a result.","items":{"$ref":"#/components/schemas/webEngine"},"type":"array"},"number_of_results":{"description":"NumberOfResults is len(results) — what this answer carries, never an\nestimate of what the web holds.","type":"integer"},"query":{"description":"Query is the query that ran, echoed back.","type":"string"},"results":{"description":"Results are the merged hits, deduplicated by normalised URL and capped at\n30. Always an array and never null: no hits is an ANSWER, not a fault.","items":{"$ref":"#/components/schemas/webResult"},"type":"array"}},"type":"object"},"wireFact":{"properties":{"at":{"description":"At is when the thing was so, RFC 3339, as the asserter gave it.","type":"string"},"by":{"description":"By is the identity that filed it — `owner` or `owner/user` — stamped from\nthe validated principal at the write, never from the body.","type":"string"},"confidence":{"description":"Confidence in [0,1] as the asserter gave it; absent is 0. It breaks a tie\nbetween two assertions equally knowable and decides nothing else.","type":"number"},"entity":{"description":"Entity is the thing described, in the organization's own namespace.","type":"string"},"evidence":{"description":"Evidence points at the record the claim came from. Absent when the asserter\ngave none.","type":"string"},"id":{"description":"ID is the assertion's content address, minted by the server from what was\nasserted. Two callers who assert the identical thing land on one ID and one\nrow; changing any asserted field makes a different ID and a second row.","type":"string"},"knowable":{"description":"Knowable is the first instant this plane could have answered with the\nassertion, RFC 3339: the later of Seen and the server's clock at the write.\nDerived and never supplied, which is what stops history filed today from\nbeing backdated into a past read.","type":"string"},"names":{"description":"Names true means the assertion is an edge and Value is an entity. A walk\nreads only these.","type":"boolean"},"relation":{"description":"Relation is what was asserted of it.","type":"string"},"seen":{"description":"Seen is when the asserter says it became knowable, RFC 3339. Provenance\nonly — Knowable is what an as-of read is bounded by.","type":"string"},"source":{"description":"Source names who asserted, as the caller gave it. This plane ranks no\nsource above another, so it never outweighs a later Knowable.","type":"string"},"value":{"description":"Value is what the relation points at: another entity's key when Names is\ntrue, otherwise a scalar.","type":"string"}},"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 entry point onto World, REST first. It is\ndeliberately NOT a list of REST operations: GET /v1/openapi.json is the one\nenumeration of those, and a second copy here would be a second thing to keep\ntrue.","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"},"written":{"properties":{"id":{"description":"ID is the record's id — the one supplied, or the one minted when the\ncaller supplied none.","type":"string"},"kind":{"description":"Kind is the section written.","type":"string"},"updated":{"description":"Updated is when it was written, unix milliseconds.","type":"integer"}},"type":"object"},"zapProcReq":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"project":{"type":"string"}},"type":"object"}},"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"The caller's credential, sent as `Authorization: Bearer <token>`. It is either an access token minted by Hanzo IAM or an API key (pk-/sk-). X-Authorization, and HTTP Basic carrying the token as the password, are accepted spellings of the same header for clients that cannot set Authorization; a browser presents the session cookie instead. Every operation requires it unless it says `security: []`."}}}} |