{"openapi":"3.1.0","info":{"title":"ORC8R API","description":"JSON API for ORC8R. Authenticate with a bearer API key (`Authorization: Bearer <token>`); browser sessions may instead rely on the session cookie (`__Host-orc_session` over HTTPS, the legacy `orc_session` over plain HTTP). Errors are RFC 9457 Problem Details (`application/problem+json`).","version":"0.6.3-rc41"},"paths":{"/api/auth/user":{"get":{"tags":["auth"],"summary":"Get the current user","description":"Returns the user identified by the session cookie or bearer API key. Requires authentication.","operationId":"auth_user","responses":{"200":{"description":"The signed-in user","content":{"application/json":{"schema":{}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/downloads":{"get":{"tags":["downloads"],"summary":"List downloads","description":"Returns every published download repository with its version tags, newest release first and the floating `default` tag last, plus the title and description the publisher annotated the served artifact with when it carries them. Unauthenticated: downloads are public.","operationId":"list_downloads","responses":{"200":{"description":"Download repositories and their tags","content":{"application/json":{"schema":{}}}}}}},"/api/downloads/{name}":{"get":{"tags":["downloads"],"summary":"Get a download","description":"Returns one download repository: the publisher's title and description when annotated, its version tags and, per tag, the files with size, platform, and sha256 digest. The digest is the file's own sha256 — download bytes are stored verbatim. Unauthenticated: downloads are public.","operationId":"get_download","parameters":[{"name":"name","in":"path","description":"Download repository name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The repository, its tags, and their files","content":{"application/json":{"schema":{}}}},"404":{"description":"No such download","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/license/plans":{"get":{"tags":["licensing"],"summary":"The on-prem plan catalog (public)","description":"Public (no authentication): the machine-readable twin of the pricing page's On-prem pane, in the same display order. `price.amount` is in the minor unit of `price.currency` (49900 is $499.00) and `limits.active_nodes` is -1 for unlimited. Cacheable for five minutes; an empty catalog answers an empty list.","operationId":"plans","responses":{"200":{"description":"The plan catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlansResponse"}}}}}}},"/api/nodes/":{"get":{"tags":["nodes"],"summary":"List visible nodes","description":"Returns every node in the pools the caller can see (via org or project membership), including liveness state, as a paginated envelope. Each node also carries `baking` (true for a pool-image builder) and `bake_phase` — `booting`, `installing`, `snapshotting`, `publishing`, or `null` for a node with no bake in flight. A builder not placed on a host yet also carries `waiting` — `{reason, message}`: its pool's park token (or `null` when the pool is not parked) and what it is waiting for, in words — which is `null` for every placed node. An online node of a `wireguard` provider whose agent registered no WireGuard key carries `waiting` with reason `mesh-key`: its apps are withheld until the agent is upgraded. Each node carries `agent_version`, `agent_os` and `agent_arch`: the build its agent last reported on connect, each `null` while it is unknown (an agent that predates the report, or one not connected since the node was created or rebuilt). `agent_upgrade` is `{to, phase, since}` while an agent upgrade is in flight and `agent_upgrade_last` is `{to, outcome, reason, at}` for the last one that closed, each `null` when there is none. A node whose agent is below the supported floor carries the `agent_below_floor` warning.","operationId":"list_nodes","responses":{"200":{"description":"Paginated node list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}":{"get":{"tags":["nodes"],"summary":"Read one node","description":"Returns one node the caller can see, in the same shape the node listings carry — including its current price, the whole price history behind it, and `baking` / `bake_phase` (`booting`, `installing`, `snapshotting`, `publishing`, or `null`), and `waiting` (`{reason, message}` for a builder not placed on a host yet, or reason `mesh-key` for a node held for want of a WireGuard key; otherwise `null`), and `agent_version` / `agent_os` / `agent_arch` (the build its agent last reported on connect, each `null` while unknown), and `agent_upgrade` / `agent_upgrade_last` (the upgrade in flight and the last one closed, each `null` when there is none).","operationId":"show_node","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The node","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/audit":{"get":{"tags":["nodes"],"summary":"Get a node's audit history","description":"Returns the node aggregate's ordered event log as a paginated envelope; each entry carries the event type, sequence, and decoded payload. Requires the `ReadNode` permission in the node's project.","operationId":"node_audit","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated event history","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/exec":{"post":{"tags":["nodes"],"summary":"Execute a bounded command on a node","description":"Runs an executable with exact arguments, without a TTY, as the node agent account. For shell expressions use sh -c on Linux or powershell.exe -Command on Windows. Returns stdout, stderr, exit_code, signal, timed_out and truncated. Input ends with EOF. Runtime is limited to 45 seconds and retained output to 8192 bytes combined; excess output is drained and discarded. A timeout or lost connection cancels the process. Commands are never retried. Requires terminal_attach permission and MCP orc:write scope. Each call starts a new process; working directory and environment changes do not persist between calls.","operationId":"execute_node","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteRequest"}}},"required":true},"responses":{"200":{"description":"Command result (nonzero exit and timeout included)","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid execution request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Terminal permission required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Access session or node tunnel limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"502":{"description":"The command may have started but its result was lost; check the node before running it again (do not retry blindly)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Node offline, agent does not support execution, or the access ledger is busy (retryable; see Retry-After)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/logs":{"get":{"tags":["nodes"],"summary":"Stream a node's logs","description":"Server-Sent Events stream of a node's durable logs. Replays retained rows then live-tails. Requires the `ReadNodeLogs` permission in the node's project. Debug-level records are returned only to callers that also hold `read_node_debug_logs` at zone scope (the system organization's owners and members); for anyone else `debug=true` is ignored and the stream carries info and above. `Last-Event-ID` resumes while rows are retained.","operationId":"node_logs","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}},{"name":"debug","in":"query","description":"`true` to include debug-level records; honoured only with the zone-scope `read_node_debug_logs` permission, otherwise ignored","required":false,"schema":{"type":"string"}},{"name":"audience","in":"query","description":"`users` to read the log as the node's users do (legacy internals left out or hidden, no debug records), even with `read_node_debug_logs`","required":false,"schema":{"type":"string"}},{"name":"mode","in":"query","description":"`head` (replay then stop) or `tail` (default, keep streaming)","required":false,"schema":{"type":"string"}},{"name":"since","in":"query","description":"Window duration, e.g. `30s`, `15m`, `6h`, `3d`: the replay starts that long ago, oldest first, in either mode","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"SSE stream of log records","content":{"text/event-stream":{}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node's logs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/metrics/contributors":{"get":{"tags":["metrics"],"summary":"Read a node's retained top-process contributors","description":"The bounded union of the largest CPU, resident-memory and disk-I/O consumers retained per scan window, newest window first. Absence from a window's list means the process was not retained, not that it did not run or used nothing; each window carries how many processes were scanned, offered and admitted so coverage is visible. Contributors have their own short retention, independent of the metric tiers.","operationId":"node_metrics_contributors","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Window start, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"Window end, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Maximum scan windows (default 12, max 200)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Contributor windows","content":{"application/json":{"schema":{}}}},"400":{"description":"Out-of-bounds range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node's metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This server has no metrics store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/metrics/devices":{"get":{"tags":["metrics"],"summary":"List a node's metric devices","description":"Every (family, device) pair the node has reported — block devices, interfaces and mount points — with the first and last time each was seen. Block devices are parents only: a disk's counters already include its partitions, so the two are never added together. Each row also names the application it belongs to — empty for the host's own series — so one call tells a caller both which devices and which applications this node has metrics for.","operationId":"node_metrics_devices","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Device list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node's metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This server has no metrics store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/metrics/range":{"get":{"tags":["metrics"],"summary":"Read one node's host metrics","description":"Returns one catalog family's series for a node over a time range. The store picks the tier that fits the point budget and says which one it used, how far it is finalized, how fresh the last sample is, and where the gaps are. Counter fields are answered as per-second rates with reset detection; gauge fields as window averages with their observed minimum and maximum. A core virtual node shares a server's host and answers `unavailable` instead of series. The point budget is for the whole answer, not for each device: it is divided by the devices the family resolves to, never below 32 points each, and a response carries at most 16384 points in total. `points` says how many it carries and `series_omitted` how many devices did not fit — ask for one device, or a shorter range, to see them. \n\n`family` also takes a comma-separated list (`family=cpu,mem,disk`), which answers every family named in one round trip: `{ node, from, to, local_only, finalized_through, families: { \"cpu\": <the single-family body>, … }, caps, gaps }`. The node is resolved and authorised once and one reader serves every family; `caps` and `gaps` describe the node rather than a family, so they are carried once above the map instead of inside each body, and `finalized_through` at the top is the oldest of the families'. The point budget applies to each family separately, exactly as it does to a single-family answer, so a list of seven families may carry seven times the points. `device` is applied only to the families that have a device dimension (disk, net, fs), and ignored for the rest. An unknown name refuses the whole request and says which one it was. Requires the `ReadNodeStats` permission in the node's project.","operationId":"node_metrics_range","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}},{"name":"family","in":"query","description":"Catalog family name (cpu, mem, load, psi, disk, net, fs), or a comma-separated list of them for one answer per family","required":true,"schema":{"type":"string"}},{"name":"device","in":"query","description":"One device; absent or `all` for every device as its own series; `sum` for every device added into one series named `all`, with the devices that fed it listed. Applies only to families with a device dimension","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Range start, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"Range end, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"points","in":"query","description":"Point budget for the whole answer, 10..=2000 (default 300); divided across the family's devices","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The range","content":{"application/json":{"schema":{}}}},"400":{"description":"Unknown family or out-of-bounds range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node's metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This server has no metrics store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/restore-points/":{"get":{"tags":["nodes"],"summary":"List the App data restore points a node produced","description":"Returns the retained restore points captured by this node, newest first within each app's lineage, optionally filtered by slot and app. These are the pool listing's rows narrowed to this node; `bytes` and `files` are what the node declared for that point's contents, and `reference` is the artifact `orc pull` takes (its `tag` within the pool's App-data repository). Requires the `ReadPool` permission in the node's project. Taking a point's contents — downloading it, or being given the key that opens a pulled one — is a separate permission and a separate route, and no key is ever part of this answer.","operationId":"list_node_restore_points","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}},{"name":"slot","in":"query","description":"Only points of this pool slot","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"app","in":"query","description":"Only points of this app","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Restore points, newest first per lineage","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this node's pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/terminal/ws":{"get":{"tags":["nodes"],"summary":"Open an interactive terminal (WebSocket)","description":"Upgrades to a WebSocket carrying an interactive PTY on the node's agent. Binary frames carry stdin/stdout; JSON text frames carry control (`resize`, `close`) and lifecycle events. Requires the `TerminalAttach` permission in the node's project.","operationId":"node_terminal_ws","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}},{"name":"cols","in":"query","description":"Initial terminal columns (default 80)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"rows","in":"query","description":"Initial terminal rows (default 24)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}},{"name":"resume","in":"query","description":"Session id to reattach to, from an earlier connection's `session` frame. Opaque, and honoured on any server for the user that opened it while the node still holds the shell; otherwise a fresh session is opened.","required":false,"schema":{"type":"string"}}],"responses":{"101":{"description":"Switching protocols to WebSocket"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not attach a terminal","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/nodes/{node}/terminate":{"post":{"tags":["nodes"],"summary":"Terminate a node","description":"Requests termination of a node. Online nodes stop their apps through the agent before destroy starts; offline nodes end immediately; a node hosting provider resources cascades to its children. An optional body `{\"reason\": string, \"force\": bool}` escalates a termination already in flight past its apps' graceful stop. Requires the `TerminateNode` permission. Returns the node's post-write lifecycle state.","operationId":"terminate_node","parameters":[{"name":"node","in":"path","description":"Node wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Optional termination options","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TerminateNodeBody"}}},"required":true},"responses":{"200":{"description":"Termination accepted; returns the new state","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not terminate this node","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Node already ended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}":{"get":{"tags":["orgs"],"summary":"Get an organization","description":"Returns the organization's identity (id, code, name) and its effective resource limits (`-1` means unlimited), plus `limits_source` — `default` (derived for the deployment), `override` (set by an operator), or `pinned` (imposed by the deployment's entitlement). Any member who can read the org may call this.","operationId":"get_org","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Organization identity and limits","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/address-plan":{"get":{"tags":["networks"],"summary":"Get an organization's address plan","description":"Returns the org's allocatable and reserved prefixes and its allocated `/24` blocks. Requires the `ReadAddressPlan` permission (system operator).","operationId":"get_address_plan","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The org's address plan","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read address plans","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/address-plan/prefixes":{"post":{"tags":["networks"],"summary":"Add an address-plan prefix","description":"Adds an allocatable (default) or reserved IPv4 prefix to the org's address plan. Requires the `ManageAddressPlans` permission. Overlaps within this plan are rejected (422); overlaps with another org's plan are allowed and reported under `warnings`.","operationId":"add_prefix","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrefixRequest"}}},"required":true},"responses":{"200":{"description":"Updated plan with any cross-org warnings","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage address plans","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid prefix, kind, or overlapping prefix","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/address-plan/prefixes/remove":{"post":{"tags":["networks"],"summary":"Remove an address-plan prefix","description":"Removes an allocatable (default) or reserved IPv4 prefix from the org's address plan. Requires the `ManageAddressPlans` permission.","operationId":"remove_prefix","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrefixRequest"}}},"required":true},"responses":{"200":{"description":"Updated plan","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage address plans","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid prefix or kind","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/apps-configure":{"post":{"tags":["appconfigs"],"summary":"Configure an org-scoped app","description":"Saves (default), resets, or refreshes version discovery for one app's org-scoped configuration. Sensitive values are tokenized server-side. Requires the `ManageOrgApps` permission. `reset` returns 204.","operationId":"configure_org_apps","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppsConfigureRequest"}}},"required":true},"responses":{"200":{"description":"Saved config (or refresh result)","content":{"application/json":{"schema":{}}}},"204":{"description":"Config reset"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage org apps","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Version discovery is disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid app, action, param, or locked key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/billing/account":{"get":{"tags":["billing"],"summary":"Get an organization's billing account","description":"Returns the org's billing account: plan, billing interval, cycle anchor, and next billing date. Requires the `read_billing` permission. Unknown orgs or orgs without an account are 404.","operationId":"get_account","parameters":[{"name":"org","in":"path","description":"Organization wire ID or code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The billing account","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller lacks read_billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/billing/invoices/":{"get":{"tags":["billing"],"summary":"List an organization's invoices","description":"Lists the org's invoices, newest first, with offset pagination. Requires the `read_billing` permission.","operationId":"list_invoices","parameters":[{"name":"org","in":"path","description":"Organization wire ID or code","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","description":"Filter by invoice status (e.g. `draft`, `finalized`)","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size (1–100, default 30)","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"after","in":"query","description":"Numeric offset cursor from a prior page","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated invoice list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller lacks read_billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/billing/invoices/{invoice_id}":{"get":{"tags":["billing"],"summary":"Get one invoice","description":"Returns a single invoice for the org. Requires the `read_billing` permission. Malformed invoice IDs are 400; unknown or foreign invoices are 404.","operationId":"get_invoice","parameters":[{"name":"org","in":"path","description":"Organization wire ID or code","required":true,"schema":{"type":"string"}},{"name":"invoice_id","in":"path","description":"Invoice wire ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The invoice","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed invoice_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller lacks read_billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Invoice not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/billing/invoices/{invoice_id}/lines":{"get":{"tags":["billing"],"summary":"Get an invoice's line items","description":"Returns the per-pool line items and computed total for an invoice. Requires the `read_billing` permission.","operationId":"get_invoice_lines","parameters":[{"name":"org","in":"path","description":"Organization wire ID or code","required":true,"schema":{"type":"string"}},{"name":"invoice_id","in":"path","description":"Invoice wire ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Line items and total","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed invoice_id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller lacks read_billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Invoice not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/billing/upcoming-invoice":{"get":{"tags":["billing"],"summary":"Get the upcoming (draft) invoice","description":"Returns the org's latest draft invoice with its line items. Requires the `read_billing` permission; 404 when no draft exists.","operationId":"upcoming_invoice","parameters":[{"name":"org","in":"path","description":"Organization wire ID or code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Draft invoice with items","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller lacks read_billing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"No upcoming invoice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/budget":{"get":{"tags":["orgs"],"summary":"Get an organization's hourly budgets","description":"Returns `{hourly_budget, default_project_hourly_budget, hourly_run_rate}` as exact decimal strings. A budget of `-1` means unlimited, which is the default. `hourly_budget` is the ceiling on what the whole organization may be running at once; `default_project_hourly_budget` is the ceiling each of its projects inherits unless it sets its own. Requires the `ReadBudgets` permission.","operationId":"get_org_budget","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The organization's budgets","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this organization's budgets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"put":{"tags":["orgs"],"summary":"Replace an organization's hourly budgets","description":"Sets `hourly_budget` and `default_project_hourly_budget` as exact decimal strings. An absent field, an empty field, and `-1` clear that ceiling — while a field the request does not mention is left exactly as it is, so a client that knows only one of the two ceilings cannot wipe the other. Requires the `ManageBudgets` permission. A budget is the organization's own money ceiling, set by its owners: it is independent of the operator-set org limits, is not refused when an entitlement pins those, and setting it leaves them (and their reported source) untouched. Echoes the accepted budgets on success.","operationId":"update_org_budget","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"`{hourly_budget, default_project_hourly_budget}` decimal strings; absent or `-1` = unlimited","content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Updated budgets","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed budget body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage this organization's budgets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Concurrent update conflict; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"A budget is not a decimal amount of 0 or more","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/":{"get":{"tags":["projects"],"summary":"List an organization's projects","description":"Returns the projects in the organization as a paginated envelope. Requires the `ListOrgProjects` permission.","operationId":"list_projects","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated project list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list this org's projects","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"post":{"tags":["projects"],"summary":"Create a project","description":"Creates a project (name and DNS-label code) with the caller as owner. Requires the `CreateOrgProjects` permission. A code collision returns 409; hitting the org's project limit returns 422.","operationId":"create_project","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectRequest"}}},"required":true},"responses":{"201":{"description":"Project created","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not create projects here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Project code already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid name/code or org project limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}":{"put":{"tags":["projects"],"summary":"Rename a project","description":"Renames a project's name and/or code. Requires the `UpdateProject` permission. A code collision returns 409; invalid name/code returns 422.","operationId":"update_project","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectRequest"}}},"required":true},"responses":{"200":{"description":"Updated name and code","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not update this project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Project code already taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid name or code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/apps-configure":{"post":{"tags":["appconfigs"],"summary":"Configure a project-scoped app","description":"Saves (default), resets, or refreshes version discovery for one app's project-scoped configuration. Sensitive values are tokenized server-side. Requires the `ManageProjectApps` permission. `reset` returns 204.","operationId":"configure_project_apps","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppsConfigureRequest"}}},"required":true},"responses":{"200":{"description":"Saved config (or refresh result)","content":{"application/json":{"schema":{}}}},"204":{"description":"Config reset"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage project apps","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Version discovery is disabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid app, action, param, or locked key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/budget":{"get":{"tags":["projects"],"summary":"Get a project's hourly budget","description":"Returns `{hourly_budget, effective_hourly_budget, hourly_run_rate}` as exact decimal strings. `hourly_budget` is the project's own override and reads `-1` when it has none, in which case it inherits the organization's default for its projects — which is what `effective_hourly_budget` reports. Requires the `ReadBudgets` permission on the organization.","operationId":"get_project_budget","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The project's budget","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this organization's budgets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"put":{"tags":["projects"],"summary":"Replace a project's hourly budget","description":"Sets the project's own `hourly_budget` as an exact decimal string. An absent field, an empty field, and `-1` all clear the override, returning the project to the organization's default for its projects. Requires the `ManageBudgets` permission on the organization. Echoes the accepted budget on success.","operationId":"update_project_budget","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"`{hourly_budget}` decimal string; absent or `-1` = the organization's default","content":{"application/json":{"schema":{}}},"required":true},"responses":{"200":{"description":"Updated budget","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed budget body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage this organization's budgets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Concurrent update conflict; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The budget is not a decimal amount of 0 or more","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/members/":{"get":{"tags":["projects"],"summary":"List project members","description":"Returns the project's members as a paginated envelope, ordered by display name. Requires the `ListProjectMembers` permission.","operationId":"list_members","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated member list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"post":{"tags":["projects"],"summary":"Add a project member","description":"Adds a member to the project by `email` or `user_id` with a role. Requires the `ManageProjectMembers` permission. 400 when neither identifier is supplied; 404 when no user matches; 422 on an invalid role.","operationId":"add_member","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}},"required":true},"responses":{"200":{"description":"Member added","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed body or missing identifier","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Org, project, or user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/members/{user}":{"put":{"tags":["projects"],"summary":"Update a member's role","description":"Changes a project member's role. Requires the `ManageProjectMembers` permission. 422 on an invalid role.","operationId":"update_member","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}},{"name":"user","in":"path","description":"Member user wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberRequest"}}},"required":true},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Org, project, or user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"delete":{"tags":["projects"],"summary":"Remove a project member","description":"Removes a member from the project. Requires the `ManageProjectMembers` permission. Idempotent: succeeds whether or not the user was a member.","operationId":"remove_member","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}},{"name":"user","in":"path","description":"Member user wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Member removed (or already absent)"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not manage members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/networks":{"get":{"tags":["networks"],"summary":"List a project's network","description":"Returns the project's isolated network (VNI, subnets, members) as a list of zero or one entry. Requires the `ReadProject` permission.","operationId":"list_project_networks","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The project's network (0 or 1 entry)","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/pools/":{"get":{"tags":["pools"],"summary":"List a project's pools","description":"Returns the project's live pools as a paginated envelope. Requires the `ListProjectRequests` permission.","operationId":"list_project_pools","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated pool list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list pools here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"post":{"tags":["pools"],"summary":"Create or reconfigure a pool","description":"Creates a pool provisioning request (name, size, provider, spec, `app_data_gib`, app assignments); a name that already exists is reconfigured at the next request version (201 for a fresh pool, 200 for a reconfigure). `app_data_gib` is a top-level pool field, not a spec key — absent, it keeps the size an existing pool already has. Requires the `CreateRequest` permission; core pools require system membership. Sensitive app params are tokenized server-side.","operationId":"create_pool","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePoolRequest"}}},"required":true},"responses":{"200":{"description":"Existing pool reconfigured","content":{"application/json":{"schema":{}}}},"201":{"description":"Pool created","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed pool request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not create requests here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Pool is being removed; retry later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid name, provider, spec, or app param","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Secret replication lacked quorum; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/pools/{pool}":{"get":{"tags":["pools"],"summary":"Get a pool by name","description":"Returns one live pool addressed by name within the project. A cloud pool holding a public address on its site also carries `pool_address`: the address and its hourly price, recorded when it was allocated and charged once per pool. Requires the `ReadPool` permission.","operationId":"get_project_pool","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}},{"name":"pool","in":"path","description":"Pool name","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The pool","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Org, project, or pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/pools/{pool}/agent-tokens/":{"post":{"tags":["pools"],"summary":"Mint a pool agent-registration token","description":"Issues a short-lived registration token that agents redeem at `/api/agent/connect` to enroll into the pool, plus install-script URLs. Requires the `CreatePoolAgentToken` permission. The pool must accept agent-registered nodes.","operationId":"create_agent_token","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}},{"name":"pool","in":"path","description":"Pool name","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Registration token and install URLs","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not mint agent tokens","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Org, project, or pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Pool does not accept agent-registered nodes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/pools/{pool}/restore-points/":{"get":{"tags":["pools"],"summary":"List a pool's App data restore points","description":"Returns every retained restore point the pool holds, slot by slot and newest first within each app's lineage, optionally filtered by slot and app. `bytes` and `files` are what the producing node declared for that point's contents; points share stored layers, so they do not sum to what the pool is charged for. `reference` is the artifact `orc pull` takes (its `tag` within the pool's App-data repository). Requires the `ReadPool` permission. Taking a point's contents — downloading it, or being given the key that opens a pulled one — is a separate permission and a separate route, and no key is ever part of this answer.","operationId":"list_pool_restore_points","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}},{"name":"pool","in":"path","description":"Pool name","required":true,"schema":{"type":"string"}},{"name":"slot","in":"query","description":"Only points of this pool slot","required":false,"schema":{"type":"integer","format":"int64","minimum":0}},{"name":"app","in":"query","description":"Only points of this app","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Restore points, newest first per lineage","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization, project, or pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/orgs/{org}/projects/{proj}/providers/":{"get":{"tags":["providers"],"summary":"List providers visible to a project","description":"Returns the providers visible from the project: its own project-scoped providers, its org's org-scoped providers, and all zone-scoped providers, with per-resource capacity. Requires the `ListProjectProviders` permission.","operationId":"list_project_providers","parameters":[{"name":"org","in":"path","description":"Organization code or wire id","required":true,"schema":{"type":"string"}},{"name":"proj","in":"path","description":"Project code or wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of visible providers","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list providers here","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Organization or project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}":{"put":{"tags":["pools"],"summary":"Update a pool","description":"Resizes, changes lifecycle state (`active`/`blocked`/`rejected`), and/or reconfigures a pool addressed by id. Supplying any of `provider`, `spec`, `app_data_gib`, or `apps` commits the next request version; an absent field keeps its current value, so a provider switch never resets the pool's App-data size. Requires the `UpdatePool` permission; core pools require system membership. Returns the updated pool.","operationId":"update_pool","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePoolRequest"}}},"required":true},"responses":{"200":{"description":"Updated pool","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed pool update","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not update this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Update conflicted or state invalid; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid size, state, provider, spec, or app param","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Secret replication lacked quorum; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"delete":{"tags":["pools"],"summary":"Remove a pool","description":"Removes a pool addressed by id. Requires the `DeletePool` permission. Removal completes once all nodes end: 204 when fully removed (idempotent), 202 while removal is still staged.","operationId":"delete_pool","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Removal staged; completing as nodes end","content":{"application/json":{"schema":{}}}},"204":{"description":"Pool removed"},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not delete this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Removal conflicted; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/app-data-keys/rotate":{"post":{"tags":["pools"],"summary":"Rotate a pool's App data key","description":"Appends a freshly minted App data key to the pool's keyring and commits it as the next request version. Every existing key is kept, so restore points sealed under an older key stay readable; nodes seal new captures under the new key from their next App data login (within 30 minutes). This is a ROLLING REPLACEMENT of the pool: a new request version replaces every node one at a time, each restoring from its newest restore point. Requires the `UpdatePool` and `CreateRequest` permissions. Returns 409 when the pool assigns no app that persists App data.","operationId":"rotate_app_data_key","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Key rotated; the pool rolls onto the new request version","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not update this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Pool has no persisting app, or the update conflicted; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Secret replication lacked quorum; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/endpoints":{"get":{"tags":["pools"],"summary":"List a pool's endpoints","description":"Returns the pool's settled endpoint surface as a paginated envelope: what each endpoint is, how it is exposed, the access name the edge serves it on, and the state of the certificate its TLS depends on. Requires the `ReadPool` permission.","operationId":"list_pool_endpoints","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated endpoint list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/metrics/nodes":{"get":{"tags":["metrics"],"summary":"Read every node of a pool at one instant","description":"One row per node the caller may see in the pool: CPU busy as a share of all its cores, memory used and installed, disk throughput and network throughput, each the newest point inside `[from, to)` — one chart window, so the table under a chart says what the pointed instant looked like on every machine. A node that reported nothing in the window is a row of nulls, not a missing row. Requires `ReadPool` and `ReadNodeStats` in the pool's project.","operationId":"pool_metrics_nodes","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Window start, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"Window end, unix seconds; at most two days after `from`","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The table","content":{"application/json":{"schema":{}}}},"400":{"description":"Empty or oversized window","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool's metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This server has no metrics store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/metrics/range":{"get":{"tags":["metrics"],"summary":"Read one metric family across a pool","description":"One catalog family over a time range for every node of a pool, in one of three shapes. `totals` adds the nodes up at each window, which is what a counter's rate means for a fleet and what makes a latency distribution mergeable (sum the `http_latency` buckets). `distribution` gives the spread across nodes at each window — minimum, nearest-rank p10, median, p90 and maximum of the per-node values, computed from the exact per-node points, so it is a percentile across machines at one instant and never a percentile of percentiles. Within a node the family's devices are summed unless one is named. `app` selects an application's families (`proc_self`, `server`, `http`, `http_latency`); absent it is the host's own. A node that reported nothing in the window is not counted as a zero — every point says how many nodes were behind it. Requires `ReadPool` and `ReadNodeStats` in the pool's project.","operationId":"pool_metrics_range","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}},{"name":"family","in":"query","description":"Catalog family name","required":true,"schema":{"type":"string"}},{"name":"app","in":"query","description":"Application name; absent for the host's own series","required":false,"schema":{"type":"string"}},{"name":"device","in":"query","description":"One device; absent or `all` sums the family's devices per node","required":false,"schema":{"type":"string"}},{"name":"mode","in":"query","description":"`totals` (default) or `distribution`","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","description":"Range start, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"Range end, unix seconds","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"points","in":"query","description":"Point budget per node, 10..=2000 (default 300)","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The aggregated range","content":{"application/json":{"schema":{}}}},"400":{"description":"Unknown family, mode or out-of-bounds range","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool's metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"This server has no metrics store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/requests/{version}":{"get":{"tags":["pools"],"summary":"Get a pool request version","description":"Returns one historical request version of a pool (provider, spec, apps). Requires the `ReadPool` permission.","operationId":"get_pool_request","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","description":"Request version number","required":true,"schema":{"type":"integer","format":"int64","minimum":0}}],"responses":{"200":{"description":"The request version","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this pool","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool or request version not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/pools/{pool}/requests/{version}/nodes":{"get":{"tags":["pools"],"summary":"List nodes for a pool request version","description":"Returns the nodes provisioned under a pool's request version as a paginated envelope, in the shape `/api/nodes/` uses — `baking`, `bake_phase` and `waiting` included, which is how a version's pool-image builder reads. Requires the `ListPoolNodes` permission.","operationId":"list_pool_request_nodes","parameters":[{"name":"pool","in":"path","description":"Pool wire id","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","description":"Request version number","required":true,"schema":{"type":"integer","format":"int64","minimum":0}}],"responses":{"200":{"description":"Paginated node list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list this pool's nodes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Pool or request version not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/support/issues":{"post":{"tags":["support"],"summary":"File a support request","description":"Validates a support request from any authenticated user and durably queues it for asynchronous delivery as a tracked GitHub issue. Returns immediately with an acknowledgement; the issue is created later by the outbox handler. Requires a configured GitHub client (otherwise 503).","operationId":"create_issue","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportRequest"}}},"required":true},"responses":{"200":{"description":"Request accepted and queued","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed request body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"GitHub is not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}":{"get":{"tags":["users"],"summary":"Get a user","description":"Returns the target user's profile. Callable by the user themselves or a system administrator (`ReadUser`).","operationId":"get_user","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The user profile","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/api-keys/":{"get":{"tags":["users"],"summary":"List a user's API keys","description":"Returns the target user's API key metadata (never the secret tokens) as a paginated envelope. Callable by the user themselves or a system administrator (`ListUserApiKeys`).","operationId":"list_api_keys","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated API key metadata","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list this user's keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]},"post":{"tags":["users"],"summary":"Create an API key","description":"Mints a new API key for the target user, optionally with a purpose label and an expiry duration (`30m`, `12h`, `7d`). The plaintext token is returned once. Callable by the user themselves or a system administrator (`CreateUserApiKey`).","operationId":"create_api_key","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}},"required":true},"responses":{"201":{"description":"The created key, including its one-time token","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not create keys for this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Invalid expiry duration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/api-keys/{apikey}":{"delete":{"tags":["users"],"summary":"Delete an API key","description":"Revokes one of the target user's API keys by its numeric id. Callable by the user themselves or a system administrator (`DeleteUserApiKey`).","operationId":"delete_api_key","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}},{"name":"apikey","in":"path","description":"Numeric API key id","required":true,"schema":{"type":"integer","format":"int32","minimum":0}}],"responses":{"200":{"description":"Key revoked","content":{"application/json":{"schema":{}}}},"400":{"description":"Malformed api key id","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not delete this user's keys","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/nodes/":{"get":{"tags":["users"],"summary":"List a user's nodes","description":"Returns the nodes in projects the target user can access, as a paginated envelope. Callable by the user themselves or a system administrator (`ReadUser`).","operationId":"list_user_nodes","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated node list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/oauth-grants/":{"get":{"tags":["users"],"summary":"List a user's connected MCP clients","description":"Returns the OAuth grants the target user gave MCP clients, newest consent first, as a paginated envelope: the client, the consented scopes, when it was connected and last used, and whether the grant is still in force. A grant stops being in force when it is revoked, when the user's password changes, or when its refresh token can no longer be spent (thirty days unused, or ninety days after consent); `ended_reason` then says which (`revoked`, `password_changed`, `expired`), and is null while it is in force. A revoked grant's `revoke_reason` says who ended it: `user`, `administrator` (somebody allowed to manage the user's API keys), `client`, or `refresh_token_reused` / `code_reused` for a replayed credential. Callable by the user themselves or a system administrator (`ListUserApiKeys`).","operationId":"list_oauth_grants","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated grants","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not list this user's grants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/oauth-grants/{grant}":{"delete":{"tags":["users"],"summary":"Revoke a connected MCP client","description":"Revokes one of the target user's OAuth grants: the client's access token fails on its next request and its refresh token buys nothing. Revoking a grant that already ended succeeds. Callable by the user themselves or a system administrator (`DeleteUserApiKey`); an administrator's revocation is recorded as theirs, and the grant list reports it with `revoke_reason` `administrator`.","operationId":"delete_oauth_grant","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}},{"name":"grant","in":"path","description":"Grant id, as the list returns it","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Grant revoked","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not revoke this user's grants","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User or grant not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/orgs/":{"get":{"tags":["users"],"summary":"List a user's organizations","description":"Returns the organizations the target user can see, as a paginated envelope. Callable by the user themselves or a system administrator (`ReadUser`).","operationId":"list_user_orgs","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated organization list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/pools/":{"get":{"tags":["users"],"summary":"List a user's pools","description":"Returns the pools the target user can see, as a paginated envelope. Callable by the user themselves or a system administrator (`ReadUser`).","operationId":"list_user_pools","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated pool list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}},"/api/users/{user}/projects/":{"get":{"tags":["users"],"summary":"List a user's projects","description":"Returns the projects across the target user's visible organizations, as a paginated envelope. Callable by the user themselves or a system administrator (`ReadUser`).","operationId":"list_user_projects","parameters":[{"name":"user","in":"path","description":"User wire id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated project list","content":{"application/json":{"schema":{}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Caller may not read this user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}},"security":[{"api_key":[]}]}}},"components":{"schemas":{"ActivateRequest":{"type":"object","description":"`POST /api/license/activate` request body.","required":["key","instance_fp"],"properties":{"hostname":{"type":"string","description":"Reported hostname, shown in the console and named in takeover notices."},"instance_fp":{"type":"string","description":"The cluster's fingerprint, minted at bootstrap and stable across member\nreplacement and restore."},"key":{"type":"string","description":"The license key, `ORC-XXXXX-XXXXX-XXXXX-XXXXX`."},"version":{"type":"string","description":"Reported `orc-server` version."}}},"AddAdjustmentRequest":{"type":"object","description":"Body of `POST /api/billing/invoices/{invoice}/adjustments`.","required":["amount","reason","idempotency_key"],"properties":{"amount":{"type":"string","description":"Signed decimal string: negative credits the tenant, positive charges them."},"idempotency_key":{"type":"string","description":"The caller's key for this correction. Required, because a request that\ntimes out somewhere in the middle is the ordinary case and a retry must not\ncredit a tenant twice. Repeating it with the same money is accepted and\nwrites nothing; repeating it with different money is a conflict."},"reason":{"type":"string","description":"Why the adjustment exists; it becomes the line's text on the invoice."}}},"AddMemberRequest":{"type":"object","properties":{"email":{"type":["string","null"],"description":"Target member by email (resolved to a user); one of `email` or `user_id`."},"role":{"type":"string","description":"Project role to grant."},"user_id":{"type":["string","null"],"description":"Target member by user wire id; one of `email` or `user_id`."}}},"AppRef":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"App repository name (bare, or scoped like `org/app`)."},"version":{"type":["string","null"],"description":"Optional version/tag; defaults to the app's `default`."}},"additionalProperties":false},"AppsConfigureRequest":{"type":"object","description":"Body of an app-configure call; `action` omitted means save. Unknown keys are\nrefused rather than ignored, so a payload that spells the values anything other\nthan `params` fails here instead of saving an empty configuration.","required":["app"],"properties":{"action":{"type":["string","null"],"description":"`save` (default), `reset`, or `refresh_versions`."},"app":{"$ref":"#/components/schemas/AppRef","description":"The app this configuration targets."},"disabled":{"type":["boolean","null"],"description":"Whether the app is disabled at this scope."},"keep_params":{"type":"array","items":{"type":"string"},"description":"Parameter names whose value stored at THIS scope is kept as it is — how a page\nthat is never shown a sensitive value saves it untouched. Only this scope's own\nstored value is kept: a name it stores nothing for keeps nothing (an inherited\nvalue is never copied in), and a name also given in `params` takes that value."},"locked":{"type":"array","items":{"type":"string"},"description":"Parameter names locked at this scope."},"params":{"type":"object","description":"Default parameter values; sensitive values are tokenized server-side.","additionalProperties":{},"propertyNames":{"type":"string"}}},"additionalProperties":false},"ChangeScopeRequest":{"type":"object","required":["scope"],"properties":{"scope":{"type":"string","description":"Target visibility scope: `project`, `org`, or `zone`.","example":"org"}}},"CloudAddressPlanBody":{"type":"object","description":"One location's cloud address plan, as the API answers it.","required":["site_id","plan","provider_id","location","scope","in_use","over_cap","edited","currency"],"properties":{"cap":{"type":["integer","null"],"format":"int64","description":"At most this many addresses at once; absent for no cap, 0 for raw public\naddresses switched off in the location.","minimum":0},"currency":{"type":"string"},"current_spend":{"type":["string","null"],"description":"What the location's held addresses cost per hour; absent when nothing\npriced them."},"edited":{"type":"boolean","description":"Whether an operator has set the plan; false for the implicit one."},"in_use":{"type":"integer","format":"int64","description":"Addresses the location holds now; may exceed a cap lowered below it.","minimum":0},"location":{"type":"string"},"over_cap":{"type":"boolean"},"plan":{"type":"string","description":"The plan as people see it named, `Vultr · Frankfurt (fra)`."},"price_per_address":{"type":["string","null"],"description":"One address's user price per hour; absent when unknown or not priced."},"provider_id":{"type":"string"},"scope":{"type":"string","description":"`zone-shared`, or the wire id of the one org it admits."},"scope_org":{"type":["string","null"],"description":"The org it admits, by code; absent when zone-shared."},"site_id":{"type":"string","description":"The plan's key: the site, `{provider id}:{location}`."}}},"CloudAddressPlansBody":{"type":"object","description":"Every cloud address plan of the zone, and each provider's quota.","required":["plans","quotas"],"properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/CloudAddressPlanBody"}},"quotas":{"type":"array","items":{"$ref":"#/components/schemas/ProviderQuotaBody"}}}},"ConnectRequest":{"type":"object","required":["agent","public_key"],"properties":{"agent":{"description":"Opaque agent metadata: hostname, hardware facts, and the local `ip` the agent\nuses for its outbound connection to the server."},"node_id":{"type":["string","null"],"description":"Rebind to an existing node after restart; never creates a node."},"public_key":{"type":"string","description":"base64url raw 32-byte ed25519 public key; the node identity for possession proofs."}}},"CreateApiKeyRequest":{"type":"object","properties":{"expiry":{"type":["string","null"],"description":"Optional lifetime as a duration like `30m`, `12h`, or `7d`."},"purpose":{"type":["string","null"],"description":"Optional label for the key (defaults to `api`)."}}},"CreatePoolRequest":{"type":"object","required":["pool","size","provider"],"properties":{"app_data_gib":{"type":["integer","null"],"format":"int64","description":"App-data volume size each node gets, in whole GiB; `0` is the free volume.\nA platform field, not a provider spec key — posted inside `spec` it is\nrefused. Absent on a name that already anchors a pool keeps that pool's\ncurrent size.","minimum":0},"apps":{"description":"App assignments; sensitive params are tokenized server-side."},"pool":{"type":"string","description":"DNS-label pool name (1-63 lowercase letters, digits, dashes; must not end\nin `-<digits>`, which is reserved for slot names)."},"provider":{"type":"string","description":"Provider: a built-in type, a visible provider name, or a provider pool id."},"size":{"type":"integer","format":"int64","description":"Desired node count (0 for non-scalable providers).","minimum":0},"spec":{"description":"Provider-specific spec object."}}},"CreateProjectRequest":{"type":"object","required":["name","code"],"properties":{"code":{"type":"string","description":"DNS-label code (lowercase letters, digits, dashes)."},"name":{"type":"string","description":"Display name (1-80 characters)."}}},"ExecuteRequest":{"type":"object","description":"A process to run once. Shell expressions require an explicit shell executable.","required":["command"],"properties":{"command":{"type":"array","items":{"type":"string"},"description":"Program and exact arguments, e.g. `[\"sh\", \"-c\", \"pwd && ls\"]`."},"max_output_bytes":{"type":"integer","description":"Combined stdout/stderr byte budget, 1..8192, default 8192.","minimum":0},"stdin":{"type":"string","description":"UTF-8 input, followed by EOF. At most 64 KiB."},"timeout_secs":{"type":"integer","format":"int64","description":"Whole-operation deadline, 1..45 seconds, default 30.","minimum":0}},"additionalProperties":false},"GrantLanBody":{"type":"object","description":"`PUT /api/zones/{zone}/lans/{address}/{prefix_len}/grants/{kind}/{id}`.","properties":{"direction":{"type":["string","null"],"description":"`to-lan` (the default). `both` is not available yet."}}},"LanGrantView":{"type":"object","description":"One grant of a LAN.","required":["kind","id","grantee","direction","since"],"properties":{"direction":{"type":"string","description":"`to-lan`."},"grantee":{"type":"string","description":"`org` or `org/project`."},"id":{"type":"string","description":"The project's or org's wire id."},"kind":{"type":"string","description":"`project` or `org`."},"since":{"type":"string"}}},"LanView":{"type":"object","description":"One LAN.","required":["range","label","gateway","gateway_name","state","state_message","warnings","grants","since"],"properties":{"gateway":{"type":"string","description":"The gateway node's wire id."},"gateway_name":{"type":"string","description":"The gateway node's name; its wire id when the node is unknown."},"grants":{"type":"array","items":{"$ref":"#/components/schemas/LanGrantView"}},"label":{"type":"string","description":"The reserved range's label."},"range":{"type":"string","description":"The reserved range, as `a.b.c.d/len`."},"since":{"type":"string","description":"When the gateway was set."},"state":{"type":"string","description":"`up`, `refused` or `down`."},"state_message":{"type":"string","description":"The state in words: why it is refused or down."},"warnings":{"type":"array","items":{"type":"string"},"description":"What an operator should know beside the state: a LAN granted to two orgs, a\ngateway that reports no address on the LAN, the gateway's own firewall\ndropping what it forwards (with the rule to add)."}}},"LansView":{"type":"object","description":"The zone's LANs as the page and the API show them.","required":["lans","gateway_candidates"],"properties":{"gateway_candidates":{"type":"array","items":{"type":"string"},"description":"The zone's adhoc nodes that have not ended, by name: who may be a gateway."},"lans":{"type":"array","items":{"$ref":"#/components/schemas/LanView"},"description":"In range order."}}},"LeaseResponse":{"type":"object","description":"The answer both endpoints give: one signed lease.","required":["lease"],"properties":{"lease":{"type":"object","description":"`{\"payload\": \"<base64url raw JSON>\", \"sig\": \"<base64url 64-byte Ed25519>\"}`.\nThe signature covers the payload bytes exactly as transmitted."}}},"LicenseUpsell":{"type":"object","description":"Schema mirror of [`orc::LicenseUpsell`], the optional `urn:orc:limit`\nextension. Registered under the component name `LicenseUpsell`; keep the\nfields in sync with `orc::LicenseUpsell`.","required":["limit","settings_path","buy_url"],"properties":{"buy_url":{"type":"string","description":"Public purchase URL for this limit."},"limit":{"type":"string","description":"The limited resource, matching `resource`."},"settings_path":{"type":"string","description":"In-product path to the license settings."}}},"LoginRequest":{"type":"object","required":["email","password"],"properties":{"cf-turnstile-response":{"type":"string","description":"Cloudflare Turnstile token from the widget (browser flow only)."},"email":{"type":"string","description":"Account email address."},"next":{"type":["string","null"],"description":"Optional post-login redirect target (browser flow only)."},"password":{"type":"string","description":"Account password."}}},"LoginWelcomeBody":{"type":"object","required":["login_welcome_html"],"properties":{"login_welcome_html":{"type":"string","description":"Operator-supplied login welcome HTML (sanitized server-side)."}}},"MetricsSettingsBody":{"type":"object","description":"A settings write: any subset of the three groups.","properties":{"profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProfileBody"}]},"retention":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RetentionBody"}]},"store":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StoreBody"}]}},"additionalProperties":false},"NodePathView":{"type":"object","description":"One mesh node's paths.","required":["name","url","project","reported","lan","direct","relay","tls"],"properties":{"direct":{"type":"integer","format":"int32","minimum":0},"lan":{"type":"integer","format":"int32","minimum":0},"name":{"type":"string"},"project":{"type":"string","description":"`org/project`."},"relay":{"type":"integer","format":"int32","minimum":0},"reported":{"type":"boolean","description":"Whether its agent reported; the counts are zero when not."},"tls":{"type":"integer","format":"int32","minimum":0},"url":{"type":"string"}}},"PathTotalsView":{"type":"object","description":"How the zone's mesh nodes reach their peers.","required":["lan","direct","relay","tls","reporting","no_path","not_reporting","nodes","nodes_unlisted"],"properties":{"direct":{"type":"integer","format":"int32","minimum":0},"lan":{"type":"integer","format":"int32","description":"Peers reached on a LAN, directly, through a relay and over TLS, summed over\nevery node that reported.","minimum":0},"no_path":{"type":"integer","format":"int64","minimum":0},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/NodePathView"},"description":"The nodes, by name, at most [`NODES_LISTED_MAX`]."},"nodes_unlisted":{"type":"integer","description":"How many more nodes there are than listed.","minimum":0},"not_reporting":{"type":"integer","format":"int64","description":"Mesh nodes whose agent never reported (an older agent).","minimum":0},"relay":{"type":"integer","format":"int32","minimum":0},"reporting":{"type":"integer","format":"int64","description":"Nodes whose agent reported its paths, and of those how many reach no peer.","minimum":0},"tls":{"type":"integer","format":"int32","minimum":0}}},"PlanLimitsView":{"type":"object","description":"What a plan entitles a zone to.","required":["active_nodes"],"properties":{"active_nodes":{"type":"integer","format":"int64","description":"Active-node ceiling of a zone licensed on this plan; `-1` is unlimited,\nthe same spelling every limit in this product uses."}}},"PlanPriceView":{"type":"object","description":"What a plan costs.","required":["amount","currency","interval"],"properties":{"amount":{"type":"integer","format":"int64","description":"In the **minor unit** of `currency`: `49900` is $499.00. The catalog\nstores and Stripe bills in minor units, and the Pricing page accepts\n`499.50`, so a major-unit amount could not stay a whole number."},"currency":{"type":"string","description":"ISO 4217, upper case."},"interval":{"type":"string","description":"Always [`CATALOG_INTERVAL`]."}}},"PlanView":{"type":"object","description":"One plan as the catalog advertises it.","required":["key","title","price","limits","features","buy_url"],"properties":{"buy_url":{"type":"string","description":"Absolute link into the cloud's own pricing section, carrying the plan\nkey. The API advertises; the cloud sells."},"features":{"type":"array","items":{"type":"string"},"description":"The card's bullets, verbatim and in order."},"key":{"type":"string","description":"The stable plan key a license is sold under (`team`, `organization`)."},"limits":{"$ref":"#/components/schemas/PlanLimitsView"},"price":{"$ref":"#/components/schemas/PlanPriceView"},"title":{"type":"string","description":"The card's heading."}}},"PlansResponse":{"type":"object","description":"`GET /api/license/plans` response body.","required":["plans"],"properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/PlanView"},"description":"The catalog in the landing pane's display order. Empty when nothing is\npriced yet."}}},"PortalRequest":{"type":"object","properties":{"customer_id":{"type":["string","null"],"description":"Stripe customer ID to open the billing portal for."},"org_code":{"type":["string","null"],"description":"Organization code that scopes authorization."},"return_url":{"type":["string","null"],"description":"URL Stripe returns the user to after the portal session."}}},"PrefixRequest":{"type":"object","required":["prefix"],"properties":{"kind":{"type":"string","description":"`allocatable` (default) or `reserved`."},"prefix":{"type":"string","description":"IPv4 CIDR prefix to add or remove, e.g. `10.20.0.0/16`."}}},"PrivateRangeView":{"type":"object","description":"One ledger entry.","required":["range","kind","kind_label","owner","owner_id","since","conflict"],"properties":{"conflict":{"type":"boolean","description":"Whether the entry overlaps another it may not."},"kind":{"type":"string","description":"`project-subnet`, `site-network` or `reserved`."},"kind_label":{"type":"string","description":"The kind in words."},"owner":{"type":"string","description":"The holder in words: `org/project (segment|mesh)`, `Provider · location`, or a\nreserved range's label."},"owner_id":{"type":"string","description":"The holder as the ledger records it: a network reference, a site id\n(`{provider id}:{location}`), or the label."},"range":{"type":"string","description":"The range, as `a.b.c.d/len`."},"since":{"type":"string","description":"When the entry was recorded."}}},"PrivateRangesView":{"type":"object","description":"The zone's ledger of private ranges as the page and the API show it.","required":["ranges","conflicts","suggestions"],"properties":{"conflicts":{"type":"array","items":{"$ref":"#/components/schemas/RangeConflictView"},"description":"Every pair of entries that may not both hold their ranges."},"ranges":{"type":"array","items":{"$ref":"#/components/schemas/PrivateRangeView"},"description":"Every entry: project subnets, then site networks, then reserved ranges."},"suggestions":{"type":"array","items":{"$ref":"#/components/schemas/RangeSuggestionView"},"description":"LAN subnets the zone's nodes report that no entry covers."}}},"Problem":{"type":"object","description":"Schema mirror of [`orc::Problem`] (RFC 9457 Problem Details). The real type lives\nin the `orc` crate, which cannot derive `ToSchema` without taking a utoipa\ndependency; this mirror registers under the component name `Problem` and is the\nbody schema every error response references. Keep the fields in sync with\n`orc::Problem`.","required":["type","title","status","detail","errors"],"properties":{"current":{"type":["integer","null"],"format":"int64","description":"Current resource usage for `urn:orc:limit` problems."},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence."},"errors":{"type":"object","description":"Field-level validation errors, keyed by field name.","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"instance":{"type":["string","null"],"description":"URI reference identifying this specific occurrence."},"license_upsell":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LicenseUpsell","description":"Where to lift the ceiling on a deployment whose ceilings are licensed;\nabsent unless a licensing module is composed."}]},"limit":{"type":["integer","null"],"format":"int64","description":"Configured resource limit for `urn:orc:limit` problems."},"resource":{"type":["string","null"],"description":"Limited resource name for `urn:orc:limit` problems."},"status":{"type":"integer","format":"int32","description":"HTTP status code for this occurrence.","minimum":0},"title":{"type":"string","description":"Short human-readable summary of the problem type."},"type":{"type":"string","description":"URI reference identifying the problem type; `about:blank` when generic."}}},"ProfileBody":{"type":"object","description":"The collection half of a settings write; every field optional, so a client that\nknows one setting cannot silently reset the others.","properties":{"contributors_per_rank":{"type":["integer","null"],"format":"int32","minimum":0},"enabled":{"type":["boolean","null"]},"flush_interval_secs":{"type":["integer","null"],"format":"int32","minimum":0},"include_loopback":{"type":["boolean","null"]},"max_spool_bytes":{"type":["integer","null"],"format":"int32","minimum":0},"process_scan_secs":{"type":["integer","null"],"format":"int32","minimum":0},"sample_interval_secs":{"type":["integer","null"],"format":"int32","minimum":0}},"additionalProperties":false},"ProviderQuotaBody":{"type":"object","description":"A zone-scoped provider's quota across all its locations.","required":["provider","in_use","summary"],"properties":{"in_use":{"type":"integer","format":"int64","minimum":0},"provider":{"type":"string"},"quota":{"type":["integer","null"],"format":"int64","description":"Absent for a provider declaring no `pool_ipv4` quota.","minimum":0},"summary":{"type":"string","description":"The same, in a sentence."}}},"RangeConflictView":{"type":"object","description":"Two entries that may not both hold their ranges, and what to do about it.","required":["first","second","message","action"],"properties":{"action":{"type":"string","description":"What the operator can do about it."},"first":{"$ref":"#/components/schemas/RangeSideView"},"message":{"type":"string","description":"Both sides in words."},"release":{"type":["string","null"],"description":"The reserved range to release, when releasing it resolves the conflict."},"second":{"$ref":"#/components/schemas/RangeSideView"}}},"RangeSideView":{"type":"object","description":"One side of a conflict.","required":["range","kind","owner"],"properties":{"kind":{"type":"string"},"owner":{"type":"string"},"range":{"type":"string"}}},"RangeSuggestionView":{"type":"object","description":"A LAN subnet some of the zone's nodes sit on that no entry covers.","required":["range","nodes","label","message"],"properties":{"label":{"type":"string","description":"A label to reserve it under."},"message":{"type":"string","description":"\"192.168.50.0/24 seen on clover, dover: reserve it?\""},"nodes":{"type":"array","items":{"type":"string"},"description":"The nodes reporting it, by name."},"range":{"type":"string"}}},"RegistrationPolicyBody":{"type":"object","required":["user_registration_enabled"],"properties":{"user_registration_enabled":{"type":"boolean","description":"Whether public self-registration is open in this zone."}}},"RelayLimitsBody":{"type":"object","description":"The uplink setting, as `PUT /api/zones/{zone}/network/relay-limits` takes it.","properties":{"uplink_mbps":{"type":["integer","null"],"format":"int64","description":"The uplink of each server in Mbit/s, 1 to 1000000; `null` or absent to\nclear it, so the default cap applies.","minimum":0}}},"RelayLimitsView":{"type":"object","description":"The rate limits of the zone's server relays: relayed and TLS traffic share each\nserver's uplink with raft, the API and the apps.","required":["relay_cap_mbps","node_mbps","default_cap_mbps"],"properties":{"default_cap_mbps":{"type":"integer","format":"int64","description":"The cap while no uplink is set, in Mbit/s.","minimum":0},"node_mbps":{"type":"integer","format":"int64","description":"What one node's traffic through a server relay may be, both ways\ntogether, in Mbit/s.","minimum":0},"relay_cap_mbps":{"type":"integer","format":"int64","description":"What each server's relay carries for all its nodes together, in Mbit/s:\nhalf the uplink, else 200.","minimum":0},"uplink_mbps":{"type":["integer","null"],"format":"int64","description":"The uplink of each server in Mbit/s, as the operator set it; `None` when\nnot set.","minimum":0}}},"RelayRowView":{"type":"object","description":"One relay as the Network page lists it.","required":["kind","kind_label","name","this_server","status","serving","reason","candidates","served","standby","probed","reached","behind_own_address","hints","idle"],"properties":{"attached":{"type":["integer","null"],"description":"How many served nodes this server's relay has heard from; this server's\nrow only.","minimum":0},"behind_own_address":{"type":"integer","format":"int64","description":"Of the probing nodes that did not reach it, how many sit behind one of its\nown public addresses, where a router would have to loop their packets back.","minimum":0},"candidates":{"type":"array","items":{"type":"string"},"description":"Where nodes may reach it."},"hints":{"type":"array","items":{"type":"string"},"description":"What the operator can do about it, in plain words; empty when all is well."},"idle":{"type":"boolean","description":"A site with neither nodes nor a gateway: no relay at all, only holding its\naddress for a while. The page folds these apart from the relays."},"kind":{"type":"string","description":"`server`, `node`, `gateway` (a site gateway running the relay core) or\n`site_gateway` (an older gateway: a port of its own per node)."},"kind_label":{"type":"string","description":"The kind in words: `Server`, `Relay node`, `Site gateway`."},"name":{"type":"string","description":"The node running it, by name; empty for a site with no gateway."},"probed":{"type":"integer","format":"int64","minimum":0},"reached":{"type":"integer","format":"int64","minimum":0},"reached_by":{"type":["string","null"],"description":"What the probing nodes found, in words: \"2 of 3 probing nodes, median\n12.5 ms\". `None` while no node reports probing it."},"reason":{"type":"string","description":"Why it is not serving, since when it is down, or which of its addresses\nfailed; empty when there is nothing to say."},"rtt_median_ms":{"type":["number","null"],"format":"double"},"served":{"type":"integer","format":"int64","description":"Live nodes holding it as their primary relay, and as their warm standby.","minimum":0},"serving":{"type":"boolean","description":"Whether the status is the healthy one."},"site":{"type":["string","null"],"description":"The site a gateway fronts, as `Provider · location`, with its owner when a\ntenant brought it."},"standby":{"type":"integer","format":"int64","minimum":0},"status":{"type":"string","description":"`Serving`, `Disabled`, `Offline`, `Not ready`, `Idle`, `No report` or\n`No gateway` (a site being opened, whose gateway is not assigned yet)."},"this_server":{"type":"boolean","description":"Whether it is the server answering this request."},"tunnelled":{"type":["integer","null"],"description":"How many nodes have a TLS stream attached to this server, because UDP to\nevery relay is blocked where they are; this server's row only.","minimum":0},"url":{"type":["string","null"],"description":"Its node page; `None` for a site with no gateway."}}},"RenewRequest":{"type":"object","description":"`POST /api/license/renew` request body.","required":["key","instance_fp"],"properties":{"instance_fp":{"type":"string"},"key":{"type":"string"},"lease_id":{"type":"string","description":"The lease the caller is currently running on. Recorded, but not what\nauthorizes the renewal: the fingerprint is the session identity, so a\nholder renewing with a stale lease id is still the holder."}}},"ReserveRangeBody":{"type":"object","description":"`POST /api/zones/{zone}/private-ranges`.","required":["range","label"],"properties":{"label":{"type":"string","description":"What the range is, such as `home-lan`."},"range":{"type":"string","description":"The IPv4 network, as `a.b.c.d/len`."}}},"RetentionBody":{"type":"object","description":"The retention half: the four tier lifetimes and the contributors' own.","properties":{"contrib_hours":{"type":["integer","null"],"format":"int32","minimum":0},"day_days":{"type":["integer","null"],"format":"int32","minimum":0},"hour_days":{"type":["integer","null"],"format":"int32","minimum":0},"minute_days":{"type":["integer","null"],"format":"int32","minimum":0},"raw_hours":{"type":["integer","null"],"format":"int32","minimum":0}},"additionalProperties":false},"SetCloudAddressPlanBody":{"type":"object","description":"What `PUT` sets: the whole plan.","required":["scope"],"properties":{"cap":{"type":["integer","null"],"format":"int64","description":"At most this many addresses at once; absent or null for no cap, 0 to switch\nraw public addresses off in the location.","minimum":0},"scope":{"type":"string","description":"`zone-shared`, or the id or code of the one org the location admits."}}},"SetLanGatewayBody":{"type":"object","description":"`PUT /api/zones/{zone}/lans/{address}/{prefix_len}`.","required":["gateway"],"properties":{"gateway":{"type":"string","description":"The gateway node: its wire id, or its name."}}},"SetStrategyRequest":{"type":"object","required":["provider_id","strategy_type"],"properties":{"params":{"description":"Strategy-specific parameters (shape depends on `strategy_type`)."},"provider_id":{"type":"string","description":"Provider pool ID (`p_…`) the strategy applies to."},"strategy_type":{"type":"string","description":"Strategy discriminant, e.g. `flat` or `none`."}}},"SignupRequest":{"type":"object","required":["email","password"],"properties":{"cf-turnstile-response":{"type":"string","description":"Cloudflare Turnstile token from the widget (browser flow only)."},"email":{"type":"string","description":"Email address to register."},"name":{"type":["string","null"],"description":"Optional display name; defaults to the email local part."},"password":{"type":"string","description":"Password (8-128 characters)."}}},"StoreBody":{"type":"object","description":"The store ceilings: how much room the store may take and how many series it admits.","properties":{"contrib_max_bytes":{"type":["integer","null"],"format":"int64","minimum":0},"max_bytes":{"type":["integer","null"],"format":"int64","minimum":0},"max_series_per_node":{"type":["integer","null"],"format":"int32","minimum":0},"max_series_per_org":{"type":["integer","null"],"format":"int32","minimum":0},"max_series_total":{"type":["integer","null"],"format":"int32","minimum":0},"reserve_bytes":{"type":["integer","null"],"format":"int64","minimum":0}},"additionalProperties":false},"SupportRequest":{"type":"object","properties":{"description":{"type":"string","description":"Body of the request (20–5000 characters)."},"kind":{"type":"string","description":"Issue kind: `bug` or `feature`."},"org_code":{"type":"string","description":"Optional organization code for context."},"project_code":{"type":"string","description":"Project code the request relates to (required; reported as `project`)."},"title":{"type":"string","description":"Short title (5–120 characters)."}}},"TerminateNodeBody":{"type":"object","description":"Optional body of `POST /api/nodes/{node}/terminate`. Every field is optional and an\nempty body is the ordinary graceful terminate.","properties":{"force":{"type":"boolean","description":"Stop waiting for the node's apps to finish stopping. Only meaningful on a node\nthat is already terminating; `false` (the default) is the graceful request."},"reason":{"type":["string","null"],"description":"Operator narrative recorded on the termination."}}},"UpdateAccountRequest":{"type":"object","properties":{"billing_interval":{"type":["string","null"],"description":"Billing interval; updates are not yet supported (rejected with 400)."},"plan":{"type":["string","null"],"description":"New plan to switch the account to (e.g. `demo`, `standard`)."}}},"UpdateMemberRequest":{"type":"object","required":["role"],"properties":{"role":{"type":"string","description":"New project role."}}},"UpdatePoolRequest":{"type":"object","properties":{"app_data_gib":{"type":["integer","null"],"format":"int64","description":"New App-data volume size in whole GiB (presence triggers a reconfigure).\nAbsent keeps the size the pool holds, the way an absent `apps` keeps its\nassignments — a provider switch never resets the volume its data sits on.","minimum":0},"apps":{"description":"New app assignments (presence triggers a reconfigure)."},"provider":{"type":["string","null"],"description":"New provider (presence triggers a reconfigure)."},"reason":{"type":"string","description":"Human-readable reason for a state change."},"size":{"type":["integer","null"],"format":"int64","description":"New desired node count.","minimum":0},"spec":{"description":"New provider spec (presence triggers a reconfigure)."},"state":{"type":["string","null"],"description":"Lifecycle state: `active`, `blocked`, or `rejected`."}}},"UpdateProjectRequest":{"type":"object","required":["name","code"],"properties":{"code":{"type":"string","description":"New DNS-label code (lowercase letters, digits, dashes)."},"name":{"type":"string","description":"New display name (1-80 characters)."}}},"ZoneLimitsBody":{"type":"object","required":["max_orgs_per_zone","max_nodes_per_zone"],"properties":{"max_nodes_per_zone":{"type":"integer","format":"int64","description":"Nodes allowed in the zone across every org, counted from creation until they\nend; `-1` is unlimited."},"max_orgs_per_zone":{"type":"integer","format":"int64","description":"Organizations allowed in the zone, system org excluded; `-1` is unlimited."}}},"ZoneNetworkView":{"type":"object","description":"Everything the Network page shows, and what its JSON twin answers.","required":["relays","paths","relay_limits"],"properties":{"add_relay_url":{"type":["string","null"],"description":"Where **Add a relay** leads: the pool composer on the zone's system project\nwith the `mesh-relay` app chosen. `None` while the zone's system project is\nnot known."},"paths":{"$ref":"#/components/schemas/PathTotalsView"},"relay_limits":{"$ref":"#/components/schemas/RelayLimitsView","description":"What a server relay carries, per node and for all its nodes together."},"relays":{"type":"array","items":{"$ref":"#/components/schemas/RelayRowView"}}}}},"securitySchemes":{"api_key":{"type":"http","scheme":"bearer","description":"Bearer API key issued from a user's API keys, or a session-derived token."}}},"tags":[{"name":"auth","description":"Sign in, sign up, and read the current user."},{"name":"nodes","description":"Node inventory, live logs, interactive terminals, and termination."},{"name":"orgs","description":"Organization identity and resource limits."},{"name":"projects","description":"Projects within an organization and their memberships."},{"name":"pools","description":"Pool provisioning requests: create, reconfigure, resize, and remove."},{"name":"providers","description":"Infrastructure providers visible to a project and their visibility scope."},{"name":"metrics","description":"Host metrics for a node, a pool's fleet, the zone's pipeline, and the collection and retention settings."},{"name":"networks","description":"Per-organization address plans and per-project networks."},{"name":"users","description":"User profiles, API keys, and the resources a user can see."},{"name":"appconfigs","description":"Scoped application configuration at zone, org, and project scope."},{"name":"downloads","description":"Public catalog of files published for download by this zone."},{"name":"billing","description":"Per-organization billing accounts, invoices, and the billing cycle."},{"name":"licensing","description":"On-prem license activation and lease renewal for self-hosted clusters."},{"name":"support","description":"File support requests as tracked issues."}]}