openapi: 3.1.0
info:
  title: TokenTriage Management API
  version: "2.13"
  description: |-
    Generated from the internal/dashapi resource registry (plan §2.1) — do not edit by hand; regenerate with UPDATE_GOLDEN=1 at the wave checkpoint. Two gates keep this document provably true in both directions: the byte drift golden, and the live-mux contract that issues every documented operation against the real server and strict-decodes the response into its documented type.

    SCOPE: the management CONTROL PLANE only, on the admin listener. The LLM data plane (/v1/*, /model/*, /v1beta/*, and the data-plane self-read /tt/v1/me) is a separate byte-faithful passthrough surface — never registered, re-documented, or wrapped here (CA9).

    HONESTY: every dollar is a typed Money whose usd is null when unpriced — NEVER 0; savings values always carry basis + verdict_ref (the server refuses to marshal them otherwise); every dollar-bearing response embeds its Provenance (pricing_snapshots, estimated_rows, unpriced_rows, truncated); exports are stamped with ExportMeta and CSV renders the literal UNPRICED. No read surface returns a secret or a hash: config secrets exist only as *_env references, and the shown-once mint/rotate responses are the closed exceptions.

    ERRORS: every non-2xx is application/problem+json (RFC 9457) with a registered type, a stable code, and request_id (matching the TT-Request-Id header, which every response carries). The framework problems bad_request (400), unauthenticated (401), forbidden (403), not_found (404), method_not_allowed (405), payload_too_large (413), rate_limited (429), and internal (500) apply to every operation — documented once here and machine-readable as each operation's default response; per-operation catalogs list domain problems only. The type registry is served at /tt/api/v1/problems and each /tt/api/problems/<slug> page documents one type.

    REQUESTS: JSON bodies are strict-decoded — an unknown field is a 400 bad_request, deliberately: a silently ignored field is a lie about what the server heard. Counts are int64 JSON integers (JavaScript consumers should use BigInt-safe parsing where values may exceed 2^53); timestamps are RFC 3339 UTC strings. Session-authenticated unsafe requests additionally carry the x-tt-csrf header; token principals never use CSRF. Rate limits are disclosed on every response via X-RateLimit-Limit/-Remaining/-Reset (and Retry-After on 429).

    SERVING: this API binds on the ADMIN listener (the dashboard's port, default :9090), never the data-plane listener — hence no servers block; paths are absolute under the admin origin.

    EXTENSIONS (the x- vocabulary, emitted from the registry): x-role-floor (the session-ladder floor: viewer|operator|admin|public) · x-required-scope (the token scope the operation demands — also the scope in its security entry) · x-read-only (a heavy read served over POST; read rate budget) · x-pagination (cursor|offset styles the operation answers) · x-idempotent (honors Idempotency-Key) · x-preview-op (the operationId of this write's dry-run counterpart) · x-delegation (subtree: the real gate is a fail-closed in-handler delegation check) · x-stability (preview operations are exempt from the additive promise and say so) · x-credential (this parameter's value is a live credential; redacted from audits and logs) · x-deprecated-since/x-sunset/x-successor (the B.2 deprecation record).

    VERSIONING: The /v1 path segment is a permanent namespace, not the contract version; compatibility is promised by the leading number of info.version (the epoch, currently 2). Within the epoch, evolution is additive-only; deprecations are machine-readable (RFC 9745 Deprecation + RFC 8594 Sunset headers, spec-marked, changelogged) and a sunset operation returns 410 gone naming its successor — never a bare 404.

    CREDENTIALS: ttv_ virtual keys drive the data plane; tta_ management tokens drive this API; tts_ share/tenant tokens grant read-only scoped views.

    GETTING STARTED (the bootstrap, for a reader who has yet to obtain a credential): POST /tt/api/v1/tokens mints the tta_ token this API wants, and it requires a credential already holding the privileged tokens:mint scope to do it — so the first credential comes from the HOST rather than from this surface. Run `tokentriage token new --role admin --state-dir <state-dir>`; it prints a ttk_ bootstrap bearer once and stores its SHA-256 in <state-dir>/tokens.json. That bearer authorizes the mint (a ttk_ carries its role's scope expansion), so POST /tt/api/v1/tokens with {"label","role","scopes"} — the field is label, not name — returns the scoped tta_ to use from then on. POST /tt/api/v1/auth/login is the BROWSER path under dashboard.auth.mode: users; under token mode the cookie it returns does not authenticate this API. The walkthrough, with a table of which credential kind to reach for, is served beside this document at /tt/api/docs/getting-started.html.
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
tags:
  - name: alerts
    description: |-
      Alert rule states, the append-only fired/resolved timeline, and operator silences.

      Every alert carries the honest-copy triple — a message, a deep link that reproduces the query behind it, and a provenance line naming what was measured — so an alert can always be checked rather than merely believed.

      `GET /alerts` also surfaces `delivery_failures`: deliveries that exhausted their retries. An alerting system whose notifications silently fail to arrive is worse than none, so a retry-exhausted delivery is a first-class queryable state here rather than a swallowed log line. It names channel and rule ids and the env-ref-safe error only — never an endpoint value, which is a secret reference this plane never resolves.

      Silences are WRITES and are audited (`alert.silence`). Every silence must carry an expiry — see `silenceCreate`.
  - name: analytics
    description: |-
      Read the decision ledger: aggregate it, page it, facet it, open one record's cost arithmetic, walk a trace's cost tree, and see what measurement itself cost. Every operation here is a READ — three of them are served over POST because their query is a structured body, and those carry `x-read-only: true` and meter against the READ rate budget.

      BYTE EGRESS IS NOT HERE, AND THE FENCE ON IT DEPENDS ON YOUR CREDENTIAL CLASS. These operations return METADATA — costs, counts, decisions, hashes. The verbatim prompt and completion bytes live on the separate `content` resource behind the privileged `content:read` scope, and a decision record here never inlines one. Note the asymmetry that scope conceals: `content:read` constrains `tta_` MANAGEMENT TOKENS, which are authorized by scope; legacy `ttk_` bearers and dashboard sessions are authorized by ROLE alone and reach the bytes at the viewer floor without it. See the `content` tag for the full statement and the revisit trigger.

      Every dollar-bearing response embeds `provenance`: the pricing snapshots the numbers were priced under, the row count, and the counts of estimated / UNPRICED / demo rows. An unpriced row is EXCLUDED from every dollar total and is disclosed in `provenance.unpriced_rows`; its own `usd` is `null`, never `0`.
  - name: annotations
    description: |-
      Time-anchored chart markers: the four classes of event worth overlaying on any cost or latency time series — alerts firing and resolving, config applies, pricing-snapshot changes, and mode/verdict transitions.

      This resource exists to answer "what CHANGED at the moment the graph did?" without a human correlating four surfaces by timestamp. It is a DERIVED read over sources that are each their own truth — the alert engine's history, the audit trail, and the evidence provider's verdict timeline — so an annotation is never a second record of anything: deleting this endpoint would lose no information.

      Every source is independently optional. A deployment with no alerting, or no evidence provider, simply contributes no markers of that class; the overlay is honestly thinner rather than fabricated.

      Reads on this resource are granted by `alerts:read` (Appendix B.4).
  - name: anomalies
    description: |-
      Detected cost anomalies with ranked root causes — and, when nothing fired, the AFFIRMATIVE NEGATIVE that says what was actually evaluated.

      ★ THE EMPTY RESPONSE IS THE INTERESTING ONE. A findings array of length zero means one of two very different things — "we watched these dimensions over this window and nothing was anomalous" or "we watched nothing" — and this resource refuses to let them look alike. `evaluation` always reports the window, how many series were evaluated, how many were UNEVALUABLE for want of baseline, and how many rows were excluded as unpriced; `scope` renders the watching / not-watching disclosure in words. A clean check here is self-describing, never a silent "all good".

      The detection method id travels on the response, on every finding, and on the evaluation, and every finding embeds a reproducibility block carrying every parameter — so an operator can recompute the flag from the ledger by hand and disagree with it if the evidence warrants.

      Reads on this resource are granted by `alerts:read` (Appendix B.4).
  - name: audit
    description: 'The append-only audit trail (§2.5): one row per attempted mutation AND per denied request, carrying the actor, its role, the origin, the action, the resource, a content HASH of any diff, the config generation, the result and any decision badges. It is the underived truth other surfaces reconcile against. Rows never carry a secret value — diffs are hashes, actors are names, and a credential that appeared in a request PATH is redacted before the row is written, so it cannot be harvested back out through this read.'
  - name: auth
    description: |-
      The SESSION surface: exchange a password or a bearer token for a server-side session, read the principal that session resolves to, and end it. Session ids and CSRF values live server-side as hashes; the only opaque values that ever reach the wire are the `Set-Cookie` session id and the session-bound CSRF token, and the CSRF token is emitted ONLY for a principal that actually has a session — never for a `tta_` management token or a `tts_` share bearer, which have no session for it to bind to.

      `ttv_` virtual keys drive the data plane; `tta_` management tokens drive this API; `tts_` share tokens grant read-only scoped views.
  - name: budgets
    description: |-
      Spend budgets: burn against a limit over a period, with a method-labeled forecast and an honest enforcement posture.

      Budgets are READ-ONLY here. They are declared in configuration and change through the config-as-contract write path, so this resource reports state and never mutates it.

      ★ `burn` EXCLUDES rows the pricing lookup missed, and `unpriced_excluded` counts them. That is the whole honesty contract of this surface: an unpriced row is never summed as $0.00, so a budget can never look comfortable because the rows that would have breached it were the ones nobody could price. Read the count beside the dollar, always. `overrun_bound` discloses what the total does not yet include (tee lag, dropped rows) and is ALWAYS marked approximate — a budget total is a bound, not a settlement.
  - name: cache
    description: |-
      The response cache: what it caught, what that was worth, and how to invalidate it.

      ★ THE ONE THING TO UNDERSTAND BEFORE READING A DOLLAR HERE. This resource reports THREE different money figures and they are deliberately never merged. `savings` is the LICENSED figure — real hits, repriced from the source row's recorded usage, and the only one that describes money genuinely not spent. `shadow_savings` is the would-hit counterfactual: those requests WERE forwarded and WERE paid for, so nothing was avoided and the number is an instrument reading, not a saving. `preview` is a labeled pre-enablement ESTIMATE over historical rows for a cache that may never have run at all. Reading the second or third as the first overstates what the cache did, which is why each carries its own basis string and the preview carries three approximation labels it cannot be marshaled without.

      Every licensed savings figure points its `verdict_ref` at `GET /cache/evidence` — the cache's own authority, NOT the routing `/evidence` surface, because cache savings are licensed by replay evidence and routing skill is a different question with a different verdict.
  - name: config
    description: 'Config-as-contract: read the running configuration, preview a change, and apply it atomically with a compare-and-swap and an audit row. Two shapes of the same discipline — a granular JSON path the dashboard drives (`POST /config/apply`) and a declarative raw-YAML path for GitOps (`GET /config/export` → git → `PUT /config`) — over ONE pipeline: validate → diff → atomic file-first apply → reload → audit. The config file''s bytes are returned VERBATIM by both READS and written verbatim by `PUT /config`, never re-marshaled from a struct, so comments, key order and formatting survive that round trip. (The one operation that does NOT preserve them is `POST /control/mode`, which re-serializes from the parsed config — it says so on itself.) Secrets are not in this surface and cannot be put into it: credential fields hold environment-variable NAMES, and the loader refuses an inline value at validation time.'
  - name: content
    description: |-
      The captured request and response payloads: the VERBATIM prompt and completion bytes a deployment chose to capture (`dashboard.capture.*`, off by default). This is the API's one bulk-egress surface for user text, so it is fenced accordingly — a dedicated privileged scope that no wildcard expands to, and one audit row per read attempt, refusals included. A decision record never inlines a body; it carries only a content hash and an availability flag, and this operation is the only way to the bytes.

      ★ TWO CREDENTIAL CLASSES, TWO AUTHORIZATION MODELS, AND THIS RESOURCE IS WHERE THE DIFFERENCE BITES. `tta_` management tokens are authorized BY SCOPE — fine-grained, and `content:read` constrains them. Legacy `ttk_` bearers and interactive dashboard SESSIONS are authorized BY ROLE ALONE — coarse, predating the scope vocabulary — and the scope layer deliberately does not govern them, so a VIEWER-role `ttk_` bearer reads these bytes WITHOUT holding `content:read`, because the route matrix's shipped viewer floor is its whole authority. That is a deliberate, recorded position and not an oversight: raising the floor would revoke a capability the dashboard has shipped with since DW4-06 from every existing deployment, silently, on upgrade. If your threat model requires the fine-grained fence on ALL credentials, do not issue legacy `ttk_` bearers: mint `tta_` management tokens, which is what the scope vocabulary exists for. [Revisit trigger: the first deployment that needs byte-egress withheld from a role-authorized principal, or the retirement of the `ttk_` class. Response: raise the matrix floor for this route to admin and publish it as a breaking change with its own epoch note.]
  - name: control
    description: 'The engine''s mode ladder — `ledger` (observe only), `shadow` (decide and record, route nothing) and `route` (act) — and the shadow-evidence gate that guards the last rung. Moving the ladder compiles to a config apply through the same atomic pipeline as `POST /config/apply`, so a mode change has a diff, a generation and an audit row like any other config change — with ONE difference that matters if your config is under version control: this is the only operation in the API that re-serializes the config file from the parsed document, so it does NOT preserve comments or formatting. It says so on itself. The gate is honest about its own reach too: it guards THIS operation, not the config path, and every route-mode config is badged regardless of where it came from.'
  - name: costanalytics
    description: |-
      Spend projection, chargeback reporting, and the FOCUS cost export — what the traffic in the ledger cost, split the way an organisation actually bills it, and rendered into files that keep their provenance after they leave this process.

      ★ AN UNPRICED DOLLAR IS `usd: null`, AND IT IS THE MOST IMPORTANT THING ON THIS SURFACE. A `Money` whose `usd` is null means the row was counted but could not be priced — a model with no entry in the price table, a request predating a pricing snapshot, a provider whose rate card is absent. It is deliberately distinguishable from `usd: 0`, which means a priced figure that came to zero. In CSV the same fact renders as the literal `UNPRICED` in the cell, so a spreadsheet that sums the column gets a text value it refuses rather than a silent zero it adds. A consumer that coerces null to 0 before charging somebody has built the error this envelope exists to prevent.

      ★ PROVENANCE TRAVELS WITH THE FIGURE, NOT BESIDE IT. A costed response carries the counts that make it auditable: `unpriced_rows` (counted, unpriceable), `estimated_rows` (priced by inference rather than by a recorded rate), `pricing_snapshots` (which rate cards were in force over the window), and `truncated` (the answer hit a bound and is a floor rather than a total). Reading a total without reading these four is reading a number with its error bars removed. `truncated: true` in particular means the figure understates.

      ★ A SAVINGS CLAIM CARRIES ITS BASIS AND ITS VERDICT REFERENCE. `Savings` is not a bare dollar amount: it carries `basis` (what the counterfactual was) and `verdict_ref` (the recorded measurement it rests on). The verdict vocabulary is closed — `PROVEN` (measured against a real control), `ESTIMATED` (modelled from observed rates), `UNPROVABLE` (the counterfactual is not recoverable from what was recorded). A savings figure quoted without its verdict is a marketing number; these operations decline to serve one.

      ★ THESE SURFACES ARE OPT-IN, AND THEIR ABSENCE IS REPORTED AS ABSENCE. Forecasting needs `analytics.forecast.enabled` over `dashboard.index.enabled`; chargeback needs its report set configured; the FOCUS export needs `analytics.enabled` and the derived index. Where a dependency is missing the answer names the missing dependency rather than serving an empty shape that reads as "nothing was spent".
  - name: events
    description: |-
      The event log: a poll-able, cursor-paged, bounded feed of this instance's SUCCESSFUL management mutations, carrying the same payloads webhook subscribers receive (§2.8).

      ★ WHAT IT DOES NOT COVER, stated first because it is what a reader would otherwise assume. This feed carries the closed set of `resource.action` types THIS SERVER CAN EMIT — read them from `GET /capabilities` → `management_api.events.types` — and only the attempts that SUCCEEDED. It is narrower than the audit trail in two directions at once: an audited action outside that vocabulary produces no event, and a refused or failed attempt produces no event either. `POST /audit` is the complete trail and is where a caller goes for both.

      ★ THE TWO CLASSES A SUBSCRIBER IS LIKELIEST TO ASSUME ARE HERE AND ARE NOT, named rather than left to be discovered from an empty feed. (1) ALERTING — there is no `alert.fired` and no `alert.delivery_failed`. The alert engine drives its fired/resolved state machine and its retry-exhausted delivery buffer entirely off the audit trail, so an alert event would have to be invented here rather than derived, which is the one thing this resource may not do. Read `GET /alerts` and `GET /alerts/history` for that surface; both are first-class and both survive restart. (2) ANOMALY DETECTION — there is no `anomaly.detected`, for the same reason; read `GET /anomalies`. Neither absence is a gap in this feed's coverage of what it does carry, and neither will be filled by guessing: an event type this server cannot emit would be a subscription nobody could ever receive.

      ★ THIS LOG IS DERIVED AND DISPOSABLE. It is a projection of the audit trail, and the audit trail (`POST /audit`) stays the one truth. The emission tap fires AFTER the audit row is durably appended, so `events ⊆ audit` holds by construction — the same discipline that makes the realtime stream a subset of the decision ledger. Two surfaces answering the same question can therefore never disagree, and push is an OPTIMIZATION: polling this resource is always sufficient.

      ★ THE RING IS BOUNDED, SO EXPIRY IS SPECIFIED RATHER THAN SILENT. Every event carries a monotonic per-instance `seq`. A cursor whose position has fallen below the ring's tail is answered **410 `cursor_expired`** carrying `oldest_seq` — the consumer KNOWS there is a gap and reconciles it from `POST /audit` (the underived truth), rather than being silently restarted at the tail and discovering a hole later. Being silently restarted is precisely the dishonesty this design forbids.

      ★ EVERY PAYLOAD IS TYPED. `data` is a CLOSED per-type union of declared schemas, never a free-form object, so a subscriber can generate a handler from this document. It also means event bodies fall under the same money pin as every REST response: a dollar in an event is a `Money`, and an unpriced one is `usd: null` rather than a confident $0.00.

      Retention, ring size, the live epoch and the closed type vocabulary are advertised in `GET /capabilities` under `management_api.events`.
  - name: evidence
    description: 'What the ledger lets you CONCLUDE, and what it refuses to: the trust-ladder verdicts and the promote gate, a draft policy re-decided over logged rows, the calibration artifact lifecycle with its threshold sweep, and the alert-rule backtest. Every number here is a labeled SIMULATION or a measured verdict — never a quality claim — and every one of them refuses rather than fabricates: a quality curve below the labeled-row floor is null with its reason, a sweep a v2 policy cannot honestly model is a stated refusal rather than a silently absent field, and a simulated dollar delta with no priced row behind it is UNPRICED, not $0.00.'
  - name: exports
    description: Stamped, streamed exports of the canonical decision log. Every export leads with an `ExportMeta` provenance stamp — the query that actually ran, the window, the instance, the pricing snapshots, the row count, and the count of UNPRICED rows EXCLUDED from every dollar in the body. The CSV form carries the same stamp as leading `# ` comment lines (strip the prefix and the block is the verbatim stamp JSON) and renders an unpriced cost as the literal `UNPRICED`, never `0.00`. `ExportMeta.v` is the EXPORT SCHEMA version and is 1; it is not the API epoch and does not move with it.
  - name: feedback
    description: 'Capture a human or API verdict on a single request. Feedback is an APPEND-ONLY companion row teed to the same sink the proxy writes through and joined onto its ledger record on read — it never mutates a stored line, so a verdict can be added but never rewritten. Label provenance is first-class: the server derives `source` from the authentication channel, so a dashboard session is `human` and a bearer token is `api`, and evaluation never silently blends them.'
  - name: fleet
    description: |-
      The replica roster and its coverage denominator N — who is in this deployment, which members answered recently, and how to take a member out of the count.

      ★ MEMBERSHIP IS OBSERVED, DECOMMISSION IS DECLARED, AND THE TWO ARE DELIBERATELY DIFFERENT MECHANISMS. A replica joins the roster by writing a heartbeat cell; it leaves the coverage denominator by an explicit, audited `POST /fleet/decommission`. Silence on its own does not evict a member, and that is a decision rather than an omission: a replica that goes quiet during a network partition has not left the fleet, and a denominator that shrank by itself would turn a partial answer into one that looks complete. Silence shows up as a stale card here and as `is_partial` on the surfaces that fold the fleet; it does not quietly change N.

      ★ A SINGLE-NODE DEPLOYMENT IS A FLEET OF ONE, REPORTED HONESTLY. With no fleet provider wired, the roster answers 200 with one card for this process and a `note` saying so — not a 404, and not an empty list. An empty list would read as "the fleet is gone"; the honest degenerate reads as "there is one of me". Consumers that fold coverage get the same `basis` vocabulary they get from a real fleet, so the single-node path is a case of the general shape rather than a special one.

      ★ TWO BOUNDS ARE PUBLISHED, AND THEY ARE NOT THE SAME KIND OF NUMBER (CF10). `liveness_window_ms` is DECLARED — the configured window inside which a heartbeat counts as live. `max_abs_skew_ms` is MEASURED — the largest absolute clock disagreement observed across the cells actually read. Reading the measured one as a configuration knob, or the declared one as an observation, is the mistake this pairing exists to prevent: a large `max_abs_skew_ms` is a report about the deployment's clocks, and it is what makes `liveness_window_ms` interpretable.

      ★ `degraded: true` MEANS THE SHARED STORE WAS UNREACHABLE AND THIS IS A FROZEN SNAPSHOT. The response is still served, `as_of` discloses how old the observation is, and the cards are whatever was last known. Treat `as_of` as the fact and the card contents as its age-stamped payload; a consumer that renders a degraded roster as current is the failure mode the flag exists to make visible.
  - name: governance
    description: |-
      Tenancy, virtual keys, limits and overrides, refusal audit, usage rollups, preview, and delegated self-serve — the governance control plane. Every dollar is typed Money (usd null when unpriced); no read returns a secret or hash; every write is audited.

      THIS PLANE IS OPT-IN. The subsystems behind these operations are constructed only when the deployment's governance block is active (`governance.enabled`, a non-empty `governance.limits`, or `governance.require_key` set to `required`/`optional`). On an instance where it is not, the READ surfaces answer 200 with an honestly EMPTY view — never a fabricated one — while the operations that cannot answer at all return 404 `feature_absent` carrying an `enable_hint` that names the cure. A 404 on a governance write therefore means "this deployment has no governance plane", not "your object does not exist", and it is deliberately not a 503: no amount of retrying cures a condition only a config change can.

      SUBTREE SCOPING IS AUTHORIZATION, NOT FILTERING. Every read takes an optional `node`, and the reporter behind it returns only that subtree — so a constrained reader cannot name, let alone read, another tenant's node, key, limit or refusal. The delegated self-serve operations go further: their route floor is a viewer pre-filter and their real gate is a fail-closed binding check (`x-delegation: subtree`).
  - name: instances
    description: 'The multi-instance federation roster: one health card per instance this deployment can answer across. ONLY REFERENCES cross this boundary — a peer''s URL and the NAME of the environment variable its bearer token is read from, never a token value. A dead peer is a LABELED DEGRADE, never a silent drop: its card carries the failure status and reason, and it is excluded from every federated total with the exclusion named on the result.'
  - name: meta
    description: 'The API''s description of ITSELF: the generated OpenAPI document this server actually obeys, the closed problem-type registry every error body points into, the self-hosted air-gapped reference that renders both, and the capability advertisement that tells a client what this deployment has wired and which cross-cutting policies (rate limits, idempotency, pagination) are in force. Nothing here reads deployment data beyond what a caller can already see; the reference, its assets and the served specification are viewer-floor unless `dashboard.docs_public` opens them.'
  - name: pricing
    description: 'What a token costs, where that number came from, and what this deployment could NOT price. The embedded price table with its vendoring stamp; a dry-run matcher that names which source would price a provider/model pair and why; the unpriced triage — the distinct pricing keys behind every `usd: null` on the rest of the API; and the effective-dated override artifact, which is forward-only and retire-not-delete so a row priced last month stays explicable next year. Every dollar-shaped value here is either a typed `Money` or a per-token RATE that says so; nothing on this surface renders an unpriced cost as 0.'
  - name: providers
    description: The configured upstreams, and the load-bearing invariant that a credential appears here only as a REFERENCE. A provider card carries the env var's NAME (`OPENAI_API_KEY`) and whether that variable is set — never its value, and there is no field on any type in this resource capable of holding one. The cards also report each upstream's dialect, base URL, provider-native prompt-cache posture and CONFIGURED cooldown policy; live health and cooldown counters live in the request path and are not reachable from the management plane, so `health` is honestly `unknown` rather than a fabricated green.
  - name: recommendations
    description: |-
      Detected configuration opportunities — what this deployment could change to spend less or behave better — each with the evidence behind it, and a governed path to applying or dismissing it.

      ★ A RECOMMENDATION IS AN ARTIFACT WITH A LIFECYCLE, NOT A SUGGESTION IN A LIST. It is detected by a scheduled scan, it carries the proof it was derived from, it has a status, and applying or dismissing it is an audited transition that is recorded ON the artifact. That is why there is a `GET /recommendations/{id}` and an `/evidence` drill-through rather than a single list endpoint: the identity is durable and worth linking to.

      ★ THE DETECTION SCAN AND THE MEASUREMENT SWEEP ARE INDEPENDENT CLOCKS, and the responses report both rather than one blended freshness. A scan that has run recently over a ledger that was indexed a while ago is a different situation from the reverse, and collapsing them into one "last updated" would hide which half is stale. Read them separately before treating an empty list as "there is nothing to do".

      ★ NOTHING HERE APPLIES ITSELF. Detection is automatic; the config change is an explicit, operator-floor, CSRF-gated, audited write. There is deliberately no auto-apply mode: a product that rewrites its own routing config on a heuristic is one whose incidents have no human decision to reconstruct.

      ★ APPLYING IS A CONFIG WRITE AND IT TRAVELS THE ORDINARY CONFIG PATH. The candidate is compiled, serialized and REVALIDATED through the same strict pipeline `POST /config/apply` uses, and the same restart-required classifier decides whether it can be hot-applied. A candidate that would not load is refused before anything is written, so the file on disk does not disagree with the running config.

      ★ THIS SURFACE IS OPT-IN AND ITS GATE HAS TWO TERMS. It answers when the config leaf asks for it AND the scheduler is actually wired — the leaf says what the operator requested, the scheduler says what the process can do. Advertising on the leaf alone is how a surface comes to serve an empty list that looks like "no opportunities" when it means "nothing ever scanned".
  - name: reconcile
    description: 'Reconcile a provider''s invoice against the ledger, model by model and day by day. The store never fudges the ledger to agree with the invoice: each slice greedily absorbs its delta into NAMED explanations and whatever is left is reported as an honest `unexplained` dollar rather than hidden. Unpriced rows are EXCLUDED from the ledger total and counted separately, so a reconciliation can never be made to balance by treating an unpriceable row as $0.'
  - name: schedules
    description: 'Scheduled reports: a saved view rendered on a cadence and delivered to a configured alert channel. The list response also advertises the channels a schedule MAY target — id and type only, never an endpoint value, because an endpoint is an env reference that never reaches this plane. Reads are viewer-floor; writes are operator-floor and audited.'
  - name: share
    description: |-
      Share tokens (`tts_`): a read-only link to ONE frozen saved view or inline query, for someone who has no account here. Two planes live under this tag and they are governed differently.

      The ADMIN plane (`/share-tokens`) is admin-floor and carries the privileged `share:admin` scope, which the `*:write` wildcard never expands to. Minting reveals the secret EXACTLY ONCE; only its SHA-256 is persisted, and no read ever returns it again.

      The READ plane (`/share/{token}`) is PUBLIC at the authorization boundary because the token IS the credential: the handler verifies it in constant time and a revoked or expired token verifies false. A bearer gets exactly the frozen scope and nothing else — any attempt to substitute a filter, column set, sort, window, grouping or metric is refused 403 rather than ignored. The token travels in the URL, so it is redacted from every audit row and log line before the write.

      `ttv_` virtual keys drive the data plane; `tta_` management tokens drive this API; `tts_` share tokens grant read-only scoped views.
  - name: stream
    description: 'The realtime decision feed: a Server-Sent Events stream of ledger rows as they are appended, plus periodic health events. It is fed by a post-write tee — the stream can only ever emit rows the log file already contains, never a read-ahead and never a synthesized row — and a slow consumer is dropped and TOLD (an in-stream `dropped` event carrying the real per-connection count), never silently backpressured onto the request path.'
  - name: tokens
    description: |-
      Management tokens (`tta_`): the scoped, optionally subtree-bound programmatic credential for this API. Secrets are shown exactly once at mint and rotate, stored only as a SHA-256, and never returned by any read. Every operation here is audited.

      `ttv_` virtual keys drive the data plane; `tta_` management tokens drive this API; `tts_` share/tenant tokens grant read-only scoped views. A `tta_` token reaches ONLY operations published in this document — any OTHER `/tt/api/v1` route is refused 403 regardless of the token's role — and a node-bound token is confined to its bound subtree on every operation that takes a node parameter.
  - name: users
    description: Local user accounts for session (`users`-mode) authentication. Passwords are argon2id-at-rest and NO read returns the hash or the plaintext — the read shape has no field capable of holding either. Every mutation is audited, and any role or password change (and any deletion) revokes that user's live sessions IMMEDIATELY rather than at session expiry, so a demotion takes effect on the very next request. The whole resource is admin-floor and carries the privileged `users:admin` scope, which the `*:write` wildcard never expands to.
  - name: views
    description: 'Saved views: a named, diffable query artifact under `state_dir/views/`. A view can be pinned to the dashboard navigation and frozen into a share token, so authorship is SERVER-stamped from the authenticated actor and a client-claimed `created_by`/`created_at` is always discarded. Reads are viewer-floor; writes are operator-floor and audited.'
  - name: webhooks
    description: |-
      Webhook delivery: the PUSH half of the event log (§2.8), and the record of what this instance tried to send.

      ★ SUBSCRIPTIONS ARE CONFIG, NOT AN API RESOURCE, and that is a decision rather than an omission. There is no `POST /webhooks` here: a subscription lives in the `webhooks:` section of the config file, so it is validated, diffed and audited like every other policy this product enforces, and it travels the GitOps path (`GET /config/export` → git) with the rest of the file. A parallel mutable subscription store would be a second config plane that the config plane could not see.

      ★ ADDING OR CHANGING A SUBSCRIPTION IS RESTART-REQUIRED, and both write endpoints REFUSE it rather than half-applying it. `webhooks.*` is not on the hot-reload subtree, so `POST /config/apply` and `PUT /config` both answer **422 `restart_required`** listing the exact paths and WRITE NOTHING — the refusal happens before any write, so the file on disk never disagrees with the running config. Edit the config file and restart the process. `POST /config/validate` and `POST /config/diff` do work normally, so the change can be checked and reviewed first; it is the APPLY that is refused. This is stated here because the alternative — implying a hot path that 422s in practice — is the kind of documentation that costs an operator an afternoon.

      ★ THE DESTINATION AND THE SIGNING SECRET ARE ENV-REFS. `webhooks[].url_env` and `webhooks[].secret_env` NAME environment variables; they never carry the values. That is enforced at config validation, not merely intended, because the config file is served verbatim by `GET /config/running` and `GET /config/export` — a pasted `whsec_…` would not be "stored insecurely", it would be PUBLISHED to every principal holding `config:read`. There is no hash-not-value escape for a signing key: you must USE it, not verify a hash of it.

      ★ SIGNING IS STANDARD WEBHOOKS (standardwebhooks.com), EXACTLY. Every delivery carries `webhook-id`, `webhook-timestamp` (unix seconds) and `webhook-signature`. The signature is `v1,<base64(HMAC-SHA256(key, "{id}.{timestamp}.{payload}"))>`, where `key` is the raw bytes the `whsec_` secret's base64 DECODES to — signing the ASCII of the secret is self-consistent and is rejected by every off-the-shelf verifier. Use an existing Standard Webhooks library; you do not need ours.

      ★ VERIFY THE REPLAY WINDOW, AND DEDUPE ON `webhook-id`. A signature is valid forever — HMAC has no notion of time — so a captured delivery replays until something refuses it on AGE. Enforce a tolerance on `webhook-timestamp` (`GET /capabilities` → `management_api.webhooks.tolerance_seconds` is what we enforce and recommend), and dedupe on `webhook-id`, which is constant across every retry and every operator-initiated redelivery of one message. Delivery is AT LEAST ONCE; the id is the half of exactly-once the receiver owns.

      ★ THE BODY IS THE POLL BODY. A delivered payload is byte-for-byte the `Event` shape `GET /events` serves, with `data` narrowed to the one union member the type selects. Every payload is documented under this document's top-level `webhooks` element. Push is an OPTIMIZATION: polling `GET /events` is always sufficient, and is the correct recovery path whenever delivery is failing.

      ★ RETRIES ARE BOUNDED, EXPONENTIAL, AND JITTERED, and the ladder is advertised in `GET /capabilities` rather than described in prose that can drift. Each attempt is signed FRESH (a new timestamp) under the SAME message id, because a retry carrying the original timestamp is refused as stale by any receiver enforcing a window — which would make retries useless exactly when they matter.

      ★ A PERMANENT REFUSAL IS NOT RETRIED. `408`, `425`, `429` and every `5xx` are retried; every other `4xx` stops the message on the spot. Answering `401` or `422` once is enough — a subscriber that rejects a delivery should not then receive four more copies of it, and a client that re-sends through a permanent refusal is a client that gets rate-limited or blocked. A `429`'s `Retry-After` is honoured in both forms (delta-seconds and HTTP-date) and overrides our own backoff, capped so an endpoint cannot park a delivery indefinitely. Redirects are NOT followed at all: the `webhook-*` headers and the body would survive a cross-host hop, so a 3xx is reported as the terminal status it is.
paths:
  /tt/api/docs:
    get:
      operationId: docsGet
      summary: 'The self-hosted, air-gapped API reference: this contract rendered in the browser, fetching nothing external.'
      description: 'Renders the document served at `/tt/api/v1/openapi.yaml` with a vendored renderer that ships inside the binary — no CDN, no font host, no analytics, under a self-only Content-Security-Policy. Deep links are stable: `#<operationId>` addresses an operation and `#<operationId>-<paramName>` addresses one of its parameters, which is what the RFC 8288 `Link: …; rel="deprecation"` header on a deprecated operation points at. Availability follows `dashboard.docs_public`: viewer floor by default, public when an operator opens it. A binary built without the UI build step carries no reference bundle and says so with a 404 naming the cure, rather than serving an empty page.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            text/html:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/docs" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: text/html"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/docs",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "text/html",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/docs`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "text/html",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/docs/{asset}:
    get:
      operationId: docsAsset
      summary: One static file of the embedded reference bundle (the renderer, its stylesheet, its script, its licences).
      description: 'A FLAT namespace: `{asset}` is a single path segment, so a name can never contain a separator and the bundle cannot be escaped — the traversal defence is the route shape rather than a sanitiser that has to be right. Only files actually present in the embedded bundle are served; anything else is 404. Includes the vendored renderer''s licence texts, so the attribution ships with the bytes it covers rather than only in the repository.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            text/css:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
            text/html:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
            text/javascript:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
            text/plain:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/docs/$TOKENTRIAGE_ASSET" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: text/javascript"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ASSET = os.environ["TOKENTRIAGE_ASSET"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/docs/{ASSET}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "text/javascript",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const asset: string = process.env.TOKENTRIAGE_ASSET ?? "";

            const res: Response = await fetch(`${base}/tt/api/docs/${asset}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "text/javascript",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
    parameters:
      - name: asset
        in: path
        required: true
        description: The bundle-relative file name, e.g. `rapidoc-min.js`, `docs.css`, `docs.js`.
        schema:
          type: string
  /tt/api/problems:
    get:
      operationId: problemTypesIndex
      summary: The problem-type registry as a page — the parent of every `type` URI this server emits.
      description: 'The human half of the registry (`GET /tt/api/v1/problems` is the machine half, and both render the same closed table). This namespace is deliberately VERSION-FREE: a `type` URI is a stable identifier that appears in clients'' logs and alert rules, so it must not move when the contract epoch does. Self-hosted and air-gapped — the page fetches nothing.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            text/html:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/problems" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: text/html"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/problems",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "text/html",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/problems`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "text/html",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/problems/{slug}:
    get:
      operationId: problemTypeDoc
      summary: One problem type's documentation page — what it means, what it carries, and how to remediate it.
      description: The dereference target of the `type` member in every error body this API returns. An unregistered slug is 404 `not_found` rather than a rendered stub, because a page for a type the server can never emit would be documentation of something that does not exist.
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            text/html:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/problems/$TOKENTRIAGE_SLUG" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: text/html"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            SLUG = os.environ["TOKENTRIAGE_SLUG"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/problems/{SLUG}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "text/html",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const slug: string = process.env.TOKENTRIAGE_SLUG ?? "";

            const res: Response = await fetch(`${base}/tt/api/problems/${slug}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "text/html",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
    parameters:
      - name: slug
        in: path
        required: true
        description: The stable type identifier — the last segment of a Problem body's `type`, and the same string as its `code`.
        schema:
          type: string
  /tt/api/v1/alerts:
    get:
      operationId: alertsList
      summary: Live alert rule states, plus any retry-exhausted delivery failures.
      description: |-
        Each rule reports `state` (`ok` or `firing`), `since` for the current state, whether it is `silenced`, and the channels it would `notify`. An unwired alert engine returns an empty `alerts` array with a 200 rather than an error — a deployment that configured no alerting is not a failure.

        `delivery_failures` is ABSENT when there are none, which is the difference between "delivery is healthy" and "delivery is not wired" being visible in the same shape: a wired, healthy deployment and an unwired one both omit it, and `alerts` being populated is what tells them apart.
      tags:
        - alerts
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/alerts" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/alerts",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/alerts`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/alerts/backtest:
    get:
      operationId: alertsBacktest
      summary: Replay the configured alert rules over the historical ledger and report where each WOULD have fired.
      description: |-
        A read-only lens over history: the markers are produced by the SAME rule evaluator that fires live alerts, not a parallel re-implementation, so a marker here and a firing in production mean the same thing. Rule types that cannot be honestly replayed (budget-threshold and anomaly rules, whose inputs are runtime state rather than ledger facts) report `backtestable: false` with the reason on the rule AND in the response-level `disclosures`, rather than contributing a fabricated marker or silently disappearing. Alerting that is not configured answers 200 with an empty rule set and a disclosure saying so.

        It carries the `alerts:read` scope, matching the rest of the alert surface, even though it is documented under the `evidence` tag with the other ledger derivations.

        **Cost of the read, stated:** the rules are replayed over the JOINED ledger, which is loaded WHOLE. The per-request deadline and row cap bound admission to this operation, not the scan itself, so a large ledger makes this a long call rather than a truncated one — and a full concurrency budget is refused `budget_exceeded` rather than served a partial history.
      tags:
        - evidence
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BacktestResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/alerts/backtest" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/alerts/backtest",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/alerts/backtest`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/alerts/history:
    get:
      operationId: alertHistoryList
      summary: The append-only fired/resolved transition timeline, newest first.
      description: |-
        History survives restart, which is what makes it the record rather than a view of memory. Each row carries the same honest-copy triple as the live state plus the `transition` polarity, so a timeline can be read without joining back to the rule set.

        The feed is bounded by the engine's own retention rather than by a caller-supplied page, so there is no `limit` parameter to pass and no cursor to follow. An unwired engine returns an empty `history` array with a 200.
      tags:
        - alerts
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AlertHistoryResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/alerts/history" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/alerts/history",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/alerts/history`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/alerts/silences:
    get:
      operationId: silencesList
      summary: Operator silences, including expired ones.
      description: |-
        `active` is DERIVED against the server clock at read time rather than stored, so a silence that has lapsed is reported as inactive without anything having to sweep it. Expired silences are still listed: an operator asking "why did this rule not page anyone last night?" needs to see the silence that has since lapsed, and deleting it from the read would erase the answer.

        `actor` names who installed the silence — the same actor recorded in the `alert.silence` audit row the write produced.
      tags:
        - alerts
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SilencesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/alerts/silences" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/alerts/silences",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/alerts/silences`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: silenceCreate
      summary: Install an operator silence on one rule. The expiry is MANDATORY, and the write is audited.
      description: |-
        ★ A SILENCE WITHOUT AN EXPIRY IS REFUSED — 422 `silence_requires_expiry`, never accepted and never defaulted. This is anti-noise by construction and it is the one rule of this endpoint: a forever-silence installed during an incident is how an alerting system dies quietly, six months later, with everyone assuming it still works. An absent, malformed, or already-past `expires_at` all take the same refusal. The bound is the caller's to choose; having one is not.

        `rule_id` must name a rule this deployment actually has: an unknown id is a 404 rather than a silence stored against nothing, which would report success while silencing no alert at all.

        ★ EVERY BRANCH OF THIS WRITE IS AUDITED as `alert.silence` — accepted, rejected AND failed. Until this operation was registered it wrote NO audit row on any branch (plan §1.2 gap 5): silencing an alert was the one privileged action on this surface that left no trace, which is precisely backwards, because suppressing a signal is exactly the act an investigation later needs to find. The audit row records the actor, the rule and the expiry, and never the note's content beyond what the operator wrote.

        The response ECHOES the engine's stored record, so `created_at` is the real stored timestamp and the shape matches `GET /alerts/silences` exactly — a client can write and then render without a re-read.
      tags:
        - alerts
      x-role-floor: operator
      x-required-scope: alerts:write
      security:
        - managementToken:
            - alerts:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SilenceRequest'
            example:
              rule_id: $TOKENTRIAGE_RULE_ID
              expires_at: "2099-12-31T23:59:59Z"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Silence'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: silence_requires_expiry.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/alerts/silences" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "rule_id": "$(json_str "$TOKENTRIAGE_RULE_ID")",
              "expires_at": "2099-12-31T23:59:59Z"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            RULE_ID = os.environ["TOKENTRIAGE_RULE_ID"]

            payload = {
              "rule_id": RULE_ID,
              "expires_at": "2099-12-31T23:59:59Z"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/alerts/silences",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const ruleId: string = process.env.TOKENTRIAGE_RULE_ID ?? "";

            const payload = {
              "rule_id": ruleId,
              "expires_at": "2099-12-31T23:59:59Z"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/alerts/silences`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/annotations:
    get:
      operationId: annotationsList
      summary: Chart-marker events over a window, across the four annotation classes.
      description: |-
        Markers are time-ordered and each carries its `class`, a human `label`, an optional sub-`kind` (fired/resolved, mode/verdict) and, where one exists, a `deep_link` that reproduces the underlying event.

        Both the resolved `since` bound and the resolved `classes` set are ECHOED on every response. That is not redundancy: a client that defaulted either one must label its overlay with the window and classes the server actually applied, or the chart legend becomes a claim nobody verified.
      tags:
        - annotations
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      parameters:
        - name: since
          in: query
          description: 'The window''s lower bound, in either of two forms: a Go duration applied as a lookback from now (`24h`, `168h`) or an absolute RFC 3339 timestamp. ABSENT takes the documented default of 24h — the resolved bound is echoed back as `since` on every response, so a client always renders the window it actually received rather than the one it assumed. A value that parses as neither form is a 400 naming the parameter, never a silent fallback to the default, because an overlay drawn over the wrong window is worse than one that refused to draw.'
          schema:
            type: string
        - name: types
          in: query
          description: 'Comma-separated class filter. ABSENT means all four classes, and the resolved set is echoed back as `classes`. An unrecognised class name is a 400 rather than being skipped: a typo that silently narrowed an overlay would hide exactly the events the caller asked to see. Comma-separated list.'
          schema:
            type: string
            enum:
              - alert
              - config
              - snapshot
              - transition
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnnotationsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/annotations" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/annotations",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/annotations`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/anomalies:
    get:
      operationId: anomaliesList
      summary: Current anomaly findings, dollar-impact ranked with root causes, plus the affirmative-negative evaluation summary.
      description: |-
        Findings are ordered most-impactful-first. Each carries its robust-z peak, its typed dollar `impact`, and ranked `causes` — each cause a (dimension, value) contribution with a ready-made filter expression that reproduces it in one click, so "why did this spike" is answerable without composing a query.

        ★ WHERE THE UNPRICED SPEND WENT — and the honest answer is not the one this shape might suggest. `impact` and every cause `contribution` are typed money, but they are ALWAYS PRICED: the detector EXCLUDES an unpriced row before a finding is computed rather than carrying it in as a null, so `usd` is never null on this response. The disclosure that actually matters follows from that: **a spike that happened entirely in spend nobody could price is not reported as a finding at all** — it is not ranked last, it is absent. `evaluation.unpriced_excluded` counts the rows dropped over the evaluated window and is the bound on what this feed could have missed, so read it before concluding a quiet period was quiet. A deployment with unpriced models should expect this endpoint to under-report, and that count is how much by.

        An unwired detector returns 200 with no findings, `clean: true`, empty watched/not-watched sets and the method still disclosed — the honest "detection is not configured" state, which the `scope` line renders in words so it cannot be mistaken for a clean bill of health.
      tags:
        - anomalies
      x-role-floor: viewer
      x-required-scope: alerts:read
      security:
        - managementToken:
            - alerts:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AnomaliesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/anomalies" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/anomalies",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/anomalies`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/audit:
    post:
      operationId: auditQuery
      summary: Read the audit trail, most-recent first, filtered and cursor-paginated.
      description: |-
        Served over POST because the filter is a body rather than a query string, and marked read-only (`x-read-only`) so it meters against the READ budget and no intermediary mistakes it for a mutation.

        ORDERING IS FIXED at most-recent-first and there is no `sort` parameter, so a cursor can never be presented under an ordering it was not minted for. The cursor's position is the boundary row's timestamp plus a content-derived tiebreaker: timestamps repeat (two appends inside one clock tick are ordinary on Windows), and an ordering key with no tiebreaker skips or repeats rows across a page boundary — the exact defect DA8 rejects offset pagination for.

        PAGINATION POLICY (B.1 rule 4, stated here because `cursor`/`limit` are shared blocks with no per-op schema of their own): `limit` absent or 0 takes the default page of 100; above the maximum of 1000 the request is REFUSED with a 400 naming the cap, never silently clamped — a clamped limit is a lie about what the server heard. `cursor` is OPAQUE: echo a previous page's `next_cursor` back verbatim and never construct one; it encodes the boundary row's timestamp and a content-derived tiebreaker and nothing else — no identity, no credential, nothing a caller could not already see in the page it came from.

        `next_cursor` is ABSENT at the end of the feed, never an empty string, and `has_more` is derived from it. Both are additive: this operation previously returned the entire trail whenever `limit` was omitted, which is an unbounded read of the one collection that grows without end; it now returns the documented default page and TELLS the caller there is more. A deployment with no audit reader wired answers 404 `feature_absent` rather than an empty page — "nothing was recorded here" and "nothing happened" are different facts. The standard binary always wires one (it aborts boot if the trail cannot be opened), so that state is reachable only when this server is embedded.
      tags:
        - audit
      x-role-floor: viewer
      x-required-scope: audit:read
      x-read-only: true
      x-pagination:
        - cursor
      security:
        - managementToken:
            - audit:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuditQueryRequest'
            example:
              limit: 50
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/audit" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "limit": 50
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "limit": 50
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/audit",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "limit": 50
            };

            const res: Response = await fetch(`${base}/tt/api/v1/audit`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/auth/login:
    post:
      operationId: authLogin
      summary: Exchange a username+password or a bearer token for a session cookie, and receive the session's CSRF token.
      description: |-
        The ONLY public route on this surface. Send either `user`+`password` or `token`; when a user is named the password form wins. On success the session id is set as an HttpOnly, SameSite=Strict cookie (it is never in the body) and the response carries the CSRF token to echo in the `x-tt-csrf` header on every mutating request.

        ★ THE SESSION THIS MINTS AUTHENTICATES SUBSEQUENT REQUESTS ONLY WHERE THE DEPLOYMENT ACCEPTS SESSIONS. With `dashboard.auth.mode: users` the cookie is the credential for every later call. With `auth.mode: token` the management plane resolves the `Authorization` header and NOTHING ELSE, so this operation still succeeds and still returns a CSRF token, but the cookie it set will not authenticate anything — use a `tta_` management token instead. Stated here because the operation's 200 is otherwise indistinguishable between the two deployments, and a client would reasonably conclude it had logged in.

        Invalid credentials are a 401 with no distinction between an unknown user and a wrong password — no existence oracle. This route is fronted by a per-SOURCE fixed-window limiter that runs BEFORE the argon2id verification, so a spray cannot turn a login endpoint into a CPU-and-memory amplifier; exhausting it is a 429 `rate_limited` whose `retry_after_seconds` equals the `Retry-After` header. That limiter is keyed on the connection's remote host and never on a forwarded header, which would be attacker-controlled.
      tags:
        - auth
      x-role-floor: public
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
            example:
              token: $TOKENTRIAGE_BOOTSTRAP_TOKEN
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/auth/login" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "token": "$(json_str "$TOKENTRIAGE_BOOTSTRAP_TOKEN")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            BOOTSTRAP_TOKEN = os.environ["TOKENTRIAGE_BOOTSTRAP_TOKEN"]

            payload = {
              "token": BOOTSTRAP_TOKEN
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/auth/login",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const bootstrapToken: string = process.env.TOKENTRIAGE_BOOTSTRAP_TOKEN ?? "";

            const payload = {
              "token": bootstrapToken
            };

            const res: Response = await fetch(`${base}/tt/api/v1/auth/login`, {
              method: "POST",
              headers: {
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/auth/logout:
    post:
      operationId: authLogout
      summary: End the caller's own session and clear the session cookie.
      description: 'Revokes EXACTLY the session named by the caller''s own cookie and nothing else — it reaches no other principal''s session, which is why it carries the read-class `auth:read` scope rather than a write scope (see this resource''s description and ops_admin.go''s decision 2). Calling it without a session cookie succeeds and clears the cookie: the caller''s intent ("I should not be logged in") is already true, and answering an error would make a logout button fail on a session that had already expired.'
      tags:
        - auth
      x-role-floor: viewer
      x-required-scope: auth:read
      security:
        - managementToken:
            - auth:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LogoutResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/auth/logout" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/auth/logout",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/auth/logout`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/auth/session:
    get:
      operationId: authSessionGet
      summary: The principal this credential resolves to — user, role, and how it authenticated.
      description: |-
        `via` names the credential form — `session` (a cookie), `bearer` (a `tta_` or `ttk_` token), or `off` (the loopback no-auth posture) — so a client can tell a cookie-authenticated browser from a token-authenticated integration without guessing.

        `csrf` is present ONLY when `via` is `session`: it is the live value bound to that session record, re-issued on every call rather than minted once. A token or share principal receives NO `csrf` field at all — there is no session to bind it to, and a CSRF value handed to a bearer caller would be credential material with no purpose. This is the one response on the management surface that carries credential material outside a mint, which is why it is argued in the CA5 shown-once allowlist under its own `re-issued` class.
      tags:
        - auth
      x-role-floor: viewer
      x-required-scope: auth:read
      security:
        - managementToken:
            - auth:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/auth/session" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/auth/session",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/auth/session`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/budgets:
    get:
      operationId: budgetsList
      summary: 'Every configured budget''s live state: burn, limit, forecast, and enforcement posture.'
      description: |-
        Returns all budgets; the set is operator-configured and small, so it is not paged.

        A deployment with no budget evaluator wired returns an empty `budgets` array with a 200 — the honest "nothing is configured" state, distinguishable from "budgets exist and none is breaching" by the array being empty rather than by a status code.

        `enforcement` is one of three postures and the third is the one to watch: `alert_only` never stops spending; `enforcing` refuses requests at the limit; `suspended` means an enforcing budget's evaluator is stale or dead, so enforcement HAS FAILED OPEN and spending is not being stopped despite the configuration saying it should be. That state is reported rather than hidden.

        `forecast` carries the method that produced it, and says `insufficient` rather than projecting from too little data.
      tags:
        - budgets
      x-role-floor: viewer
      x-required-scope: budgets:read
      security:
        - managementToken:
            - budgets:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/budgets" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/budgets",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/budgets`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/budgets/{id}:
    get:
      operationId: budgetGet
      summary: One budget's live state, by its configured id.
      description: Identical in shape to a single element of `GET /budgets`, wrapped in a `budget` member. Every disclosure the list carries — the unpriced exclusion count, the enforcement posture, the approximate overrun bound — travels here too, because a client that drilled into one budget must not receive a less honest answer than the one that listed them all.
      tags:
        - budgets
      x-role-floor: viewer
      x-required-scope: budgets:read
      security:
        - managementToken:
            - budgets:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/budgets/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/budgets/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/budgets/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The budget's configured id, as listed by `GET /budgets`. Budgets are declared in configuration, not created through this API, so an id this deployment does not define is 404 rather than a creatable resource.
        schema:
          type: string
  /tt/api/v1/cache/evidence:
    get:
      operationId: cacheEvidenceGet
      summary: 'The surface a licensed CacheSavings.verdict_ref points at: per-kind licensing posture, the reason for any refusal, and the stored-entry age distribution.'
      description: |-
        This is the cache's verdict authority. Every `savings` figure on this resource references it, and following that reference lands here.

        Each hit kind reports one of two postures. `licensed` means the savings are backed by replay evidence — a canonically-identical request's stored real response, repriced. `cannot_measure` is an HONEST REFUSAL with its reason: either no verifier is configured, or a verified policy is active but has not yet accumulated enough labeled samples to bound the wrong-hit probability, so the static threshold stands and no dollar is licensed. A calibrated semantic row additionally reports its effective threshold, the Wilson lower bound on the correct-serve rate, and the labeled-sample count, so the licensing decision can be re-derived rather than trusted.

        `age_distribution` renders all four buckets in a fixed order including empty ones, so a client can tell "no entries in this band" from "this band was not reported".
      tags:
        - cache
      x-role-floor: viewer
      x-required-scope: cache:read
      security:
        - managementToken:
            - cache:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CacheEvidenceResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/cache/evidence" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/cache/evidence",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/cache/evidence`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/cache/preview:
    get:
      operationId: cachePreviewGet
      summary: A LABELED pre-enablement estimate of what an exact response cache would have saved over historical rows. Not a measurement, and not a preview of the purge.
      description: |-
        ★ READ THIS BEFORE USING THE NUMBER. This operation answers "what would turning the cache ON have saved me?" over rows already in the ledger. It is the SECONDARY instrument: shadow mode is the primary one, and every response says so in `preview.primary_instrument`. It scans the whole log — including requests where the cache never engaged — precisely because it is answering a question about a cache that may never have run.

        ★ IT IS NOT THE DRY-RUN OF `POST /cache/purge`, and no `x-preview-op` links the two. It accepts no `by`/`value`, it cannot see which entries a purge would remove, and nothing here estimates the effect of an invalidation. The purge has no preview operation; see its description.

        Three approximation labels travel on every response and cannot be omitted (the value refuses to marshal without them): `fixed_usage` — the would-save reprices each repeat row's LOGGED tokens, not the tokens a fresh call would generate; `recorded_runtime_state` — it reads recorded ledger facts, not live state; `fingerprint_approximated` — the ledger stores a messages-only prompt hash, NOT the full fingerprint, so two requests identical in messages but differing in system prompt or tools collide here and would NOT share an entry in production. The exact-tier repeat counts are therefore an UPPER BOUND on real hits, and the estimate should be read as a ceiling.

        The semantic tier reports capture COVERAGE and then refuses: re-embedding needs the captured bodies, which live in the content store rather than the ledger, so a ledger-only preview says `can_estimate: false` with the reason instead of guessing.
      tags:
        - cache
      x-role-floor: viewer
      x-required-scope: cache:read
      security:
        - managementToken:
            - cache:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CachePreviewResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/cache/preview" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/cache/preview",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/cache/preview`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/cache/purge:
    post:
      operationId: cachePurge
      summary: Invalidate cached entries by entry, scope, or rule. Destructive, audited on every branch, and returns the number of entries actually deleted.
      description: |-
        `by` is a CLOSED dimension — `entry`, `scope` or `rule` — and an unrecognised value is refused rather than treated as a no-op, because a purge that silently matched nothing would look exactly like a purge that worked. `value` is the EntryID, ScopeKey or rule id to purge along that dimension.

        `deleted` is the count of entries PHYSICALLY removed, which is the only honest answer to "did that do anything?" — it is frequently smaller than an operator expects, and seeing 0 is the point.

        ★ THIS WRITE HAS NO DRY-RUN, stated plainly rather than left to be discovered. `GET /cache/preview` is NOT it: that operation estimates what enabling a cache would have saved over historical rows and cannot see the entry set a purge would touch. There is currently no operation that reports which entries a given `by`/`value` would remove. The mitigation this surface DOES offer is the audit trail: every attempt writes exactly one `cache.purge` row — accepted, rejected or failed — carrying the actor, the dimension, the value and the resulting count, so a purge is always ATTRIBUTABLE after the fact even though it cannot be rehearsed before it. Attributable, not reconstructable: the row proves WHO purged along WHICH dimension and HOW MANY entries died, and it does not record WHICH ones — `by: scope` and `by: rule` are bulk dimensions whose match set no read on this surface can show you, before or after.

        An unwired purge seam answers 404 `feature_absent` naming the config that enables it — never a fabricated success, and deliberately not a 503 a retry layer would retry forever against a condition only a config change cures.
      tags:
        - cache
      x-role-floor: operator
      x-required-scope: cache:write
      security:
        - managementToken:
            - cache:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CachePurgeRequest'
            example:
              by: rule
              value: $TOKENTRIAGE_CACHE_RULE
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CachePurgeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES cached responses. Cache hits stop being served until the
            # !! cache refills.
            # !! Read it before you paste it.

            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/cache/purge" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "by": "rule",
              "value": "$(json_str "$TOKENTRIAGE_CACHE_RULE")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES cached responses. Cache hits stop being served until the
            # !! cache refills.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            CACHE_RULE = os.environ["TOKENTRIAGE_CACHE_RULE"]

            payload = {
              "by": "rule",
              "value": CACHE_RULE
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/cache/purge",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES cached responses. Cache hits stop being served until the
            // !! cache refills.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const cacheRule: string = process.env.TOKENTRIAGE_CACHE_RULE ?? "";

            const payload = {
              "by": "rule",
              "value": cacheRule
            };

            const res: Response = await fetch(`${base}/tt/api/v1/cache/purge`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/cache/stats:
    get:
      operationId: cacheStatsGet
      summary: Cache outcome counts, per-kind hits, and the hit-only licensed savings beside the separate shadow figure.
      description: |-
        `outcomes` tallies the closed vocabulary (hit, would_hit, miss, bypass, error) and every field is always present — a concrete 0 is a fact, not a gap.

        `savings` counts HITS ONLY. When hits occurred but none could be repriced it reports `usd: null` with `unpriced_hits` naming the population, because a confident $0.00 over rows nobody could price is the single most misleading number this surface could emit. `shadow_savings` is the would-hit counterfactual under its own basis string and is never folded into the licensed figure.

        Semantic hits are EXCLUDED-AND-COUNTED from the licensed dollar unless a calibrated verified policy licenses them; `excluded_semantic` discloses how many, and `GET /cache/evidence` says why.

        The read streams the canonical log under this deployment's query budget. A deadline or semaphore refusal returns a valid response with `provenance.truncated: true` rather than an error — a partial answer that says so, never a complete-looking one that isn't.
      tags:
        - cache
      x-role-floor: viewer
      x-required-scope: cache:read
      security:
        - managementToken:
            - cache:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CacheStatsResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/cache/stats" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/cache/stats",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/cache/stats`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/calibrations:
    get:
      operationId: calibrationsList
      summary: 'The calibration lifecycle board: the active/shadow manifest pointers and every versioned artifact.'
      description: 'Deliberately complete rather than paged: the board is bounded by operator action (one artifact per fit) and the question it answers — "what could I activate?" — has no correct partial answer. A deployment with no `classifier.calibration_file` configured answers 200 with `configured: false` and an EMPTY artifact list, which is an honest disabled state rather than a fabricated empty board; the per-artifact operations report `feature_absent` in that same state, because there is no artifact to name.'
      tags:
        - evidence
      x-role-floor: viewer
      x-required-scope: evidence:read
      security:
        - managementToken:
            - evidence:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalibrationList'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/calibrations" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/calibrations",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/calibrations`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/calibrations/{id}:
    get:
      operationId: calibrationGet
      summary: One calibration artifact, its threshold diff against the RUNNING config, cross-check warnings, and — with sweep=true — the threshold sweep.
      description: |-
        The diff and the warnings are computed against the live running config, so they answer "what would activating this change?" rather than "what did this artifact contain?".

        `sweep=true` additionally reprices the logged usage at each candidate threshold, and it is the expensive form: the joined ledger is loaded WHOLE, so the per-request deadline and row cap bound admission to the sweep rather than the scan itself. Without it this is a cheap read. Each sweep point's `cost` is UNPRICED unless EVERY row at that threshold priced — a partial sum would silently drop the unpriced rows and can invert the cost curve — and `unpriced_rows` discloses the exclusion. The quality curve is computed only above the oracle-labeled row floor; below it every point's `quality` is null and `quality_refusal` says how many labels were found and how many are needed. Where the sweep cannot honestly model the running policy at all (a v2 `routing.policy` document, whose routing can depend on structure a single-threshold re-decision cannot reproduce), `sweep_refusal` states that instead of omitting the field, so an absent sweep is never mistaken for "no data".
      tags:
        - evidence
      x-role-floor: viewer
      x-required-scope: evidence:read
      security:
        - managementToken:
            - evidence:read
        - session: []
      parameters:
        - name: sweep
          in: query
          description: Compute the threshold sweep (`sweep=true`, or the legacy `sweep=1`). Absent or false omits it. It is opt-in because it re-reads and reprices the joined ledger under the query budget.
          schema:
            type: boolean
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalibrationDetail'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/calibrations/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/calibrations/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/calibrations/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: A calibration artifact version id as listed by `GET /calibrations` (`artifacts[].id`, or the `active`/`shadow` pointers). Only manifest-tracked ids are ever resolved to a file, so an id this deployment does not track is 404 rather than a path the server tries to open.
        schema:
          type: string
  /tt/api/v1/calibrations/{id}/activate:
    post:
      operationId: calibrationActivate
      summary: Point the calibration manifest at this artifact and audit it. Live routing does not change until the next config reload.
      description: |-
        Every attempt is audited, including the REFUSED ones, because "who tried to activate what" is a question the trail exists to answer.

        **What "atomic" does and does not mean here.** Each file the swap touches is replaced by an atomic rename, and concurrent activations are serialised WITHIN this server process, so two callers cannot interleave the read-modify-write and leave two artifacts marked active. That guarantee is process-scoped: a second `tokentriage` process sharing the same calibration directory is outside it, as it is for every other file the single-node engine owns. The response's `note` states the one thing an operator most needs to know and that the status code cannot say: activation does not touch live routing. The running proxy adopts the newly active artifact on its NEXT config reload, so a 200 here is not a claim that traffic moved.

        It takes no request body: the artifact is the path parameter, and the effect is a pointer swap.
      tags:
        - evidence
      x-role-floor: operator
      x-required-scope: evidence:write
      security:
        - managementToken:
            - evidence:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CalibrationActivateResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! ACTIVATES a calibration artifact, changing how live requests are
            # !! classified.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/calibrations/$TOKENTRIAGE_ID/activate" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! ACTIVATES a calibration artifact, changing how live requests are
            # !! classified.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/calibrations/{ID}/activate",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! ACTIVATES a calibration artifact, changing how live requests are
            // !! classified.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/calibrations/${id}/activate`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: A calibration artifact version id as listed by `GET /calibrations` (`artifacts[].id`, or the `active`/`shadow` pointers). Only manifest-tracked ids are ever resolved to a file, so an id this deployment does not track is 404 rather than a path the server tries to open.
        schema:
          type: string
  /tt/api/v1/capabilities:
    get:
      operationId: capabilitiesGet
      summary: What this deployment has wired, what contract it serves, and the rate-limit / idempotency / pagination policies in force.
      description: 'The one pre-authentication read: the dashboard''s login page needs it to discover the auth mode, and a client needs it to discover the contract revision and the policy numbers BEFORE it writes a retry loop. Every feature flag reports what is actually wired on this instance — an unwired surface advertises `false` rather than a teaser — and `management_api` carries the §2.5 policy disclosures read from the constants the server enforces, so the advertised numbers and the enforced numbers cannot disagree. `badges` is recomputed from the LIVE running mode on every read, so a `route` mode applied by SIGHUP or at boot is badged exactly like one applied through the dashboard.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Capabilities'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/capabilities" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/capabilities",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/capabilities`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/chargeback/reports:
    get:
      operationId: chargebackReportsList
      summary: The configured chargeback reports, rendered for one period, each carrying its split rules and its export stamp.
      description: |-
        A chargeback report attributes spend to the units an organisation bills — team, tenant, project — using the split rules in configuration. This lists the configured reports rendered for the selected period.

        ★ THE REPORT SET IS CONFIG, NOT AN API RESOURCE. There is no `POST /chargeback/reports` here: a report definition lives in configuration, so it is validated, diffed and audited like the rest of the policy this product enforces, and it travels the GitOps path with the config file. A parallel mutable report store would be a second config plane the config plane could not see. The consequence for a caller is that the set returned here changes through a config apply rather than through a write to this surface.

        ★ THE SPLIT RULES ARE SERVED WITH THE NUMBERS. A chargeback figure without the rule that produced it is not auditable, and "why is my team's number what it is" is the question this endpoint exists to answer. The rules travel on the report rather than being something a caller has to correlate back to a config file version.

        ★ THE UNPRICED COHORT IS REPORTED, NOT ABSORBED. Rows that could not be priced are counted in the report's provenance and their money is `usd: null`, rather than being dropped from the denominator or folded in at zero. A report whose unpriced count is large is telling you the attribution is over a subset; dropping those rows silently would make the splits sum to a clean total that is wrong.

        ★ EACH REPORT CARRIES THE SAME STAMP ITS EXPORT WILL CARRY, so a downloaded file and the report it came from can be shown to be the same rendering rather than assumed to be.
      tags:
        - costanalytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      parameters:
        - name: at
          in: query
          description: 'Select the period by naming an instant inside it — the report is rendered for the period CONTAINING this timestamp. Absent ⇒ the current period. An unparseable value is 400 rather than a fall back to now: "last month''s report" quietly answered for this month is the worst failure available on this surface, and it is the one a retry would not reveal.'
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChargebackReportsResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/chargeback/reports" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/chargeback/reports",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/chargeback/reports`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/chargeback/reports/{id}:
    get:
      operationId: chargebackReportGet
      summary: One configured chargeback report, rendered for the selected period.
      description: |-
        The single-report form of the list above, with the same period selection, the same split rules, the same unpriced cohort accounting and the same export stamp.

        ★ THE `id` IS THE CONFIGURED REPORT NAME, not a generated handle, so it is stable across renders and across periods and can be held in a dashboard URL. An id naming a report that is not configured is a 404 — distinct from a configured report that happens to have no rows in the period, which is a 200 with an empty split and a provenance block explaining the window.

        ★ RENDERING IS PER-REQUEST, so two calls at different `at` values return different numbers under the same id. The id identifies the DEFINITION; the response identifies the rendering, and the stamp is what ties a particular set of numbers to a particular period.
      tags:
        - costanalytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      parameters:
        - name: at
          in: query
          description: The instant whose containing period is rendered. Absent ⇒ the current period. Unparseable is 400, for the reason the list operation's `at` gives.
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChargebackReport'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/chargeback/reports/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/chargeback/reports/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/chargeback/reports/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The configured report name, as it appears in the list response. Path parameter.
        schema:
          type: string
  /tt/api/v1/chargeback/reports/{id}/export:
    get:
      operationId: chargebackReportExport
      summary: Download one chargeback report as a stamped CSV or JSONL file.
      description: |-
        The file form of the report. It is the SAME rendering the JSON read serves, carrying the SAME stamp, so a file in somebody's downloads folder can be tied back to the period and the rules that produced it.

        ★ THE LICENSING GATE IS RE-IMPOSED BEFORE THE FIRST BYTE IS WRITTEN. The CSV renderer is a string formatter and would happily print an incoherent report; so the report is marshalled through the JSON path first, and a report that could not be served as JSON is refused as a file too. This matters because a refusal after the first byte would arrive as a truncated download rather than as a status code — there is no way to turn a half-written file back into an error.

        ★ CSV WRITES THE LITERAL `UNPRICED`, NOT AN EMPTY CELL AND NOT `0`. An empty cell reads as missing data and a zero reads as free; the literal is a text value in a numeric column, so a spreadsheet surfaces it instead of summing past it. This is the CSV half of the `usd: null` rule and it is the half that reaches a finance team.

        ★ THE JSON FORM IS JSONL, AND THE FIRST LINE IS THE STAMP. One JSON value per line: the export header first, then a line per row. It is newline-terminated throughout, which is what makes it streamable and what lets a reader parse the stamp without buffering the file. Concatenating the header onto the first row would produce an unparseable line, and that is a real bug this format was corrected for rather than a hypothetical.

        ★ THE RESPONSE BODY IS OPAQUE BYTES. The typed shapes documented elsewhere in this document describe the JSON read; this operation serves a file, and its `Content-Disposition` names it after the report and the format.
      tags:
        - costanalytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      parameters:
        - name: format
          in: query
          description: File format. Absent ⇒ `csv`. An unrecognised value is 400 naming the two accepted forms rather than falling back to a default the caller did not choose. `json` here means JSONL — one value per line, stamp first.
          schema:
            type: string
            enum:
              - csv
              - json
        - name: at
          in: query
          description: The instant whose containing period is exported. Absent ⇒ the current period. Unparseable is 400.
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                description: JSON body with no published schema yet — the acknowledged §2.1-rule-2 exemption class (a dashsrv-local shape awaiting promotion); typed no later than MS-B.
            text/csv:
              schema:
                type: string
                description: 'CSV twin of the JSON shape: ExportMeta as leading #-comment lines, the literal UNPRICED for an unpriced dollar (never 0), csvSafe-neutralized cells.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/chargeback/reports/$TOKENTRIAGE_ID/export" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/chargeback/reports/{ID}/export",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/chargeback/reports/${id}/export`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
    parameters:
      - name: id
        in: path
        required: true
        description: The configured report name. Path parameter.
        schema:
          type: string
  /tt/api/v1/config:
    put:
      operationId: configReplace
      summary: Declaratively replace the whole configuration with a raw application/yaml document. If-Match generation CAS, ?dry_run=true preview, byte-exact round trip with GET /config/export.
      description: |-
        **The declarative operation, and the contract's only `PUT`** — this API reserves the verb for whole-document replacement, which is exactly what this is. The request body is the config FILE, byte-for-byte as it will be written to disk: no JSON envelope, no string escaping, no re-encoding. `Content-Type: application/yaml`.

        **The loop.** `GET /config/export` (dump, with the generation as its `ETag`) → git → edit → `PUT /config?dry_run=true` (the full diff and the restart-required verdict, nothing written, nothing audited) → `PUT /config` with `If-Match: "<generation>"` (atomic apply + audit). What a `decK`-style tool cannot offer, and this does: an all-or-nothing apply, a restart-required refusal disclosed BEFORE any write, and an audit row with `diff_sha256` and `audit_id`.

        **`If-Match` is REQUIRED and its absence is a refusal, not a force.** A missing header answers `412 precondition_failed` exactly as a stale one does — an unconditional declarative write is how a stale checkout silently reverts an hour of someone else's work. The value is the quoted reload generation (`If-Match: "7"`); the weak form (`W/"7"`) is accepted because a generation is not a byte-hash and the distinction cannot mean anything here. The problem body carries `current_generation` — the winner's number — so the loser can re-export, rebase and retry. (`POST /config/apply` signals the same race as `409 generation_conflict` from its body-carried `expected_generation`: one conflict signal per operation.)

        **`?dry_run=true` writes nothing and audits nothing**, and it does not require `If-Match` — a preview changes nothing, so there is nothing to guard, and requiring the header would make the cheapest safety check the most annoying one. The response is the same shape as an apply's with `applied: false`, `dry_run: true`, no `audit_id`, and the full `changes` / `restart_required` / `badges` a real apply would have produced.

        **What this replaces, and what it does not.** It replaces the config FILE — the engine plane. It does not touch runtime-plane state (minted keys and tokens, per-node overrides, saved views, schedules, pricing overrides, share tokens), which lives in its own stores with its own resources. Applying an exported document will not delete any of it. This boundary is the one declarative tools blur; it is disclosed on both halves of the pair.

        **Ordering, failure and audit are identical to `POST /config/apply`** — same validate → diff → refuse-restart-required → atomic file-first write → reload → audit pipeline, same guarantees about what a refused write leaves on disk (nothing changed), and one `config.apply` audit row on every branch except the dry run.

        **THE PROMOTE GATE DOES NOT GUARD THIS OPERATION, AND THAT IS PUBLISHED RATHER THAN DISCOVERED.** The shadow-evidence promote gate is a *dashboard-path* gate: promoting to `route` through `POST /control/mode` requires either a satisfied gate or an explicit `acknowledge_no_evidence`, but a config that simply *says* `mode: route` — applied here, by `PUT /config`, by SIGHUP, or at boot — is applied without any evidence check at all. This API publishes that path, so the bypass is a first-class, documented operation instead of a fact you would otherwise learn by reading the source.

        What makes it honest rather than a hole: the `route_without_shadow_evidence` badge is recomputed from the LIVE running mode on EVERY config origin, so an evidence-free promote through this operation is badged in this response, badged in `GET /capabilities`, and stamped into the audit row — identically to one made from the dashboard. There is no origin from which a route-mode promotion is unbadged. If you want the gate enforced, use `POST /control/mode`, which is the operation that asks.
      tags:
        - config
      x-role-floor: operator
      x-required-scope: config:apply
      security:
        - managementToken:
            - config:apply
        - session: []
      parameters:
        - name: dry_run
          in: query
          description: |-
            `true` previews: the full diff, the restart-required verdict and the would-be badges, with NOTHING written and NOTHING audited. Absent means a real apply.

            **A value this server cannot read is a 400, never a real apply.** `true`/`false` are matched case-insensitively — so a client that renders a boolean as `True` is fine — and `1`/`0` are also accepted; anything else (`yes`, `on`, an empty `?dry_run=`) is refused with `400 bad_request` naming this parameter. The strictness is load-bearing rather than pedantic: a lenient parse takes an unrecognised value as `false` and performs a real whole-document config replacement for a caller who believed they were previewing — and the `If-Match` on the same request would let it succeed. `POST /config/apply` carries the same flag as a JSON boolean, which has one spelling and cannot be mis-parsed; if you generate requests programmatically, that asymmetry is a safety difference worth knowing.
          schema:
            type: boolean
        - name: If-Match
          in: header
          description: |-
            The quoted reload generation this write is conditional on — the `ETag` from `GET /config/export`, or `reload_generation` from `GET /config/running`, quoted (`If-Match: "7"`). REQUIRED on a real apply: missing and stale are both `412 precondition_failed`, because an unconditional declarative write is how a stale checkout reverts someone else's change. `*` is refused for the same reason, and so is a comma-separated LIST of tags — the validator here is a monotonic counter rather than an opaque content hash, so `"1","2","3"` would let a caller enumerate their way past the precondition.

            It is marked OPTIONAL in the machine-readable contract and REQUIRED in this prose, deliberately: it is required on a real apply and NOT required on `?dry_run=true`, and OpenAPI cannot express a conditionally-required parameter. Marking it required would make every generated client demand an argument for the PREVIEW call — making the safe path harder to invoke than the dangerous one. The server is the enforcer either way: a real apply without it is a 412.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/yaml:
            schema:
              type: string
              description: Raw body — not a reflected schema; the operation description documents its content.
      responses:
        "200":
          description: OK
          headers:
            ETag:
              description: |-
                The running config's reload generation, quoted (`"7"`). It is an ENTITY TAG in the RFC 9110 sense — an opaque validator — but its opacity is a courtesy, not a secret: it is the same integer `GET /config/running` reports as `reload_generation` and the same one `POST /config/apply` takes as `expected_generation`, so a client may compare the two. Feed it back to `PUT /config` as `If-Match` to make the write conditional. It is NOT a content hash: two different files can carry the same generation only if one was written outside this API without a reload, which is the state `GET /config/running` and `GET /config/export` are byte-equal precisely to make visible.

                **Conditional requests are NOT honoured on the read: `If-None-Match` is ignored and `GET /config/export` always answers 200.** That is a contract decision rather than an omission, and it is the same one `GET /openapi.yaml` publishes: a 304 is a response this document has no way to describe — an operation declares a success shape and its problem types, and nothing else — so a client generated from this contract would route a real 304 into the problem+json arm and decode an empty body as an error. A GitOps loop polling this endpoint should compare the tag it already holds against the one it just received and skip its own work, rather than expect the transfer to be saved. On the WRITE the precondition IS enforced (`If-Match` on `PUT /config`), which is the direction that protects data.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigApplyResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "412":
          description: 'Problem: precondition_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: restart_required, validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REPLACES THE RUNNING CONFIGURATION with the whole document in
            # !! $TOKENTRIAGE_BODY_FILE. Under If-Match it refuses a stale write;
            # !! without a correct If-Match it refuses entirely.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X PUT "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "If-Match: $TOKENTRIAGE_IF_MATCH" \
              -H "Accept: application/json" \
              -H "Content-Type: application/yaml" \
              --data-binary "@$TOKENTRIAGE_BODY_FILE"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REPLACES THE RUNNING CONFIGURATION with the whole document in
            # !! $TOKENTRIAGE_BODY_FILE. Under If-Match it refuses a stale write;
            # !! without a correct If-Match it refuses entirely.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            IF_MATCH = os.environ["TOKENTRIAGE_IF_MATCH"]

            with open(os.environ["TOKENTRIAGE_BODY_FILE"], "rb") as fh:
                body = fh.read()

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config",
                method="PUT",
                data=body,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "If-Match": IF_MATCH,
                    "Accept": "application/json",
                    "Content-Type": "application/yaml",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! REPLACES THE RUNNING CONFIGURATION with the whole document in
            // !! $TOKENTRIAGE_BODY_FILE. Under If-Match it refuses a stale write;
            // !! without a correct If-Match it refuses entirely.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            import { readFile } from "node:fs/promises";

            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const ifMatch: string = process.env.TOKENTRIAGE_IF_MATCH ?? "";

            const body: string = await readFile(process.env.TOKENTRIAGE_BODY_FILE ?? "", "utf8");

            const res: Response = await fetch(`${base}/tt/api/v1/config`, {
              method: "PUT",
              headers: {
                Authorization: `Bearer ${token}`,
                "If-Match": ifMatch,
                Accept: "application/json",
                "Content-Type": "application/yaml",
              },
              body,
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/config/apply:
    post:
      operationId: configApply
      summary: Apply a config document through the atomic validate → diff → file-first apply → reload → audit pipeline, with a body-carried generation CAS and an in-band dry run.
      description: |-
        The GRANULAR path — a JSON envelope carrying the document as a string, which is what the dashboard posts from an editor. **If you are driving configuration declaratively or from git, use `PUT /config` instead**: it takes the file as raw `application/yaml`, so an operator's comments and key order survive the round trip that a JSON string field's re-encoding puts at risk, and its `If-Match` header is the standard conditional-write spelling.

        **`dry_run: true` writes nothing and audits nothing.** It returns the exact `changes`, the `restart_required` verdict, and the badges the applied config WOULD carry — computed by the same code path that would have applied it. `applied` is false, `dry_run` is true, and `audit_id` is ABSENT, which is the wire signal that this call left no trace in the trail. (The declarative twin spells the same thing `PUT /config?dry_run=true`, because its body is the document and has no room for a flag.)

        **`expected_generation` is a required compare-and-swap, not an optimization.** Read it from `GET /config/running`. If another editor applied first, this operation writes NOTHING and answers `409 generation_conflict` carrying `current_generation` — re-read, re-diff against that generation, re-apply. A generation of 0 against a running generation of 1 is a conflict like any other, so an omitted field is a refusal rather than a silent overwrite. (`PUT /config` signals the same race as `412 precondition_failed` because its CAS is an `If-Match` header — one conflict signal per operation, both carrying `current_generation`.)

        **Ordering, and what a failure leaves behind.** Validation and the restart-required refusal happen BEFORE any write, so a rejected apply leaves the file byte-unchanged and traffic untouched. Past that point the FILE is written first (backup → temp-in-same-dir → rename, crash-atomic) and only then is the engine reloaded, so the file on disk is always a config the loader accepted. If the reload itself fails, the engine keeps serving the OLD configuration and the failure is returned verbatim — the file then holds valid bytes that are not yet live, and a restart applies them.

        **Every branch writes exactly one audit row (`config.apply`) carrying the actor, the `diff_sha256`, the generation, the badges and the outcome — EXCEPT a dry run, which audits nothing by contract.** That includes the refusals: a malformed body, an unwired config surface, a lost CAS, an invalid document, a restart-required refusal and a failed reload are all in the trail, because "who tried to change the config, and what did the server say" is the question the trail exists to answer.

        **THE PROMOTE GATE DOES NOT GUARD THIS OPERATION, AND THAT IS PUBLISHED RATHER THAN DISCOVERED.** The shadow-evidence promote gate is a *dashboard-path* gate: promoting to `route` through `POST /control/mode` requires either a satisfied gate or an explicit `acknowledge_no_evidence`, but a config that simply *says* `mode: route` — applied here, by `PUT /config`, by SIGHUP, or at boot — is applied without any evidence check at all. This API publishes that path, so the bypass is a first-class, documented operation instead of a fact you would otherwise learn by reading the source.

        What makes it honest rather than a hole: the `route_without_shadow_evidence` badge is recomputed from the LIVE running mode on EVERY config origin, so an evidence-free promote through this operation is badged in this response, badged in `GET /capabilities`, and stamped into the audit row — identically to one made from the dashboard. There is no origin from which a route-mode promotion is unbadged. If you want the gate enforced, use `POST /control/mode`, which is the operation that asks.
      tags:
        - config
      x-role-floor: operator
      x-required-scope: config:apply
      security:
        - managementToken:
            - config:apply
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigApplyRequest'
            example:
              yaml: |
                version: 1
                mode: shadow
                upstreams:
                  anthropic:
                    base_url: https://api.anthropic.com
                    dialect: anthropic
                    api_key_env: ANTHROPIC_API_KEY
                tiers:
                  - name: cheap
                    upstream: anthropic
                    model: claude-haiku-4-5
                  - name: frontier
                    upstream: anthropic
                    model: claude-sonnet-4-6
                routing:
                  default_tier: frontier
              expected_generation: 1
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigApplyResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "409":
          description: 'Problem: generation_conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: restart_required, validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REPLACES THE RUNNING CONFIGURATION of this deployment. The body below
            # !! is a complete config, not a patch, and `expected_generation: 1`
            # !! matches a deployment that has never reloaded — so this succeeds on
            # !! a fresh install and 409s on a busy one. Add "dry_run": true to see
            # !! the diff without applying it.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config/apply" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "expected_generation": 1
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REPLACES THE RUNNING CONFIGURATION of this deployment. The body below
            # !! is a complete config, not a patch, and `expected_generation: 1`
            # !! matches a deployment that has never reloaded — so this succeeds on
            # !! a fresh install and 409s on a busy one. Add "dry_run": true to see
            # !! the diff without applying it.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "expected_generation": 1
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config/apply",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! REPLACES THE RUNNING CONFIGURATION of this deployment. The body below
            // !! is a complete config, not a patch, and `expected_generation: 1`
            // !! matches a deployment that has never reloaded — so this succeeds on
            // !! a fresh install and 409s on a busy one. Add "dry_run": true to see
            // !! the diff without applying it.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "expected_generation": 1
            };

            const res: Response = await fetch(`${base}/tt/api/v1/config/apply`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/config/diff:
    post:
      operationId: configDiff
      summary: The exact semantic diff between the running config and a candidate, with the paths that cannot hot-apply.
      description: |-
        Every changed path with its old and new rendering and whether the engine can apply it without a restart. The change set is the SAME one an apply computes and hashes into the audit row's `diff_sha256`, so a diff you reviewed is the diff that was applied.

        `restart_required` is the subset of paths that cannot be hot-applied. It is advisory HERE and binding on the write: `POST /config/apply` and `PUT /config` REFUSE a change set with a non-empty `restart_required` (422 `restart_required`) before writing anything, because a hot-reload endpoint that wrote the file and then failed the reload would leave the file and the running engine disagreeing with a hidden pending restart.

        **An invalid candidate is `422 validation_failed`, not an empty diff** — there is nothing to diff against, and returning `changes: []` would read as "no changes". The loader's verbatim messages ride the problem's `errors[]`, each with an RFC 6901 pointer at the offending field where the message named one.

        Served over POST because its input is a whole document, not because it writes: it is marked `x-read-only` and touches nothing.
      tags:
        - config
      x-role-floor: viewer
      x-required-scope: config:read
      x-read-only: true
      security:
        - managementToken:
            - config:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigDiffRequest'
            example:
              yaml: |
                version: 1
                mode: shadow
                upstreams:
                  anthropic:
                    base_url: https://api.anthropic.com
                    dialect: anthropic
                    api_key_env: ANTHROPIC_API_KEY
                tiers:
                  - name: cheap
                    upstream: anthropic
                    model: claude-haiku-4-5
                  - name: frontier
                    upstream: anthropic
                    model: claude-sonnet-4-6
                routing:
                  default_tier: frontier
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigDiffResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config/diff" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config/diff",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/config/diff`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/config/export:
    get:
      operationId: configExport
      summary: The running config file's bytes, verbatim, as raw application/yaml with the reload generation as the ETag — the dump half of the declarative GitOps loop.
      description: |-
        **The canonical export, where "canonical" means VERBATIM.** This is the file on disk, byte-for-byte: comments, key order, blank lines, indentation and any `!!` tags exactly as the operator wrote them. It is deliberately NOT re-marshaled from the parsed `Config` struct. A re-marshaled export would look canonical and would silently delete every comment an operator wrote — on the one path (`export → git → PUT`) where those comments are the operator's documentation — and it would make this response disagree with `GET /config/running`.

        **The GitOps loop.** `GET /config/export > config.yaml`; commit it; edit it; `PUT /config?dry_run=true` to see the exact diff and the restart-required verdict without writing anything; then `PUT /config` with `If-Match:` set to the `ETag` you got here. Export → PUT round-trips byte-exactly, pinned with a checksum on both sides by a test in this repository.

        **What is NOT in here, stated because a declarative tool that blurs this is the failure mode.** The config file governs the ENGINE plane — upstreams, tiers, routing, budgets, the mode ladder. Runtime-plane state lives in its own stores and has its own resources: minted governance keys, management tokens, per-node limit overrides, saved views, schedules, pricing overrides and share tokens are NOT here and applying an exported file will not delete them. A tool that presented one document as "the whole configuration" would be promising an all-or-nothing sync it cannot deliver.

        **No secret can appear in these bytes** — see `GET /config/running` for why that is structural rather than a scrub.
      tags:
        - config
      x-role-floor: viewer
      x-required-scope: config:read
      security:
        - managementToken:
            - config:read
        - session: []
      responses:
        "200":
          description: OK
          headers:
            ETag:
              description: |-
                The running config's reload generation, quoted (`"7"`). It is an ENTITY TAG in the RFC 9110 sense — an opaque validator — but its opacity is a courtesy, not a secret: it is the same integer `GET /config/running` reports as `reload_generation` and the same one `POST /config/apply` takes as `expected_generation`, so a client may compare the two. Feed it back to `PUT /config` as `If-Match` to make the write conditional. It is NOT a content hash: two different files can carry the same generation only if one was written outside this API without a reload, which is the state `GET /config/running` and `GET /config/export` are byte-equal precisely to make visible.

                **Conditional requests are NOT honoured on the read: `If-None-Match` is ignored and `GET /config/export` always answers 200.** That is a contract decision rather than an omission, and it is the same one `GET /openapi.yaml` publishes: a 304 is a response this document has no way to describe — an operation declares a success shape and its problem types, and nothing else — so a client generated from this contract would route a real 304 into the problem+json arm and decode an empty body as an error. A GitOps loop polling this endpoint should compare the tag it already holds against the one it just received and skip its own work, rather than expect the transfer to be saved. On the WRITE the precondition IS enforced (`If-Match` on `PUT /config`), which is the direction that protects data.
              schema:
                type: string
          content:
            application/yaml:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config/export" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/yaml"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config/export",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/yaml",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/config/export`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/yaml",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/v1/config/running:
    get:
      operationId: configRunning
      summary: 'The running configuration as a JSON envelope: the file''s bytes verbatim, its path, its reload generation and the loader''s warnings.'
      description: |-
        The read the dashboard uses. `yaml` is the config FILE's bytes, byte-for-byte — not a re-serialization of the parsed config — so what you read is what an operator wrote, comments included. `GET /config/export` returns the SAME bytes raw, and the two are pinned byte-identical by a test in this repository — because two surfaces answering the same question and disagreeing is the failure this API is built to make impossible.

        `reload_generation` is the compare-and-swap token. Send it back as `expected_generation` on `POST /config/apply`, or as `If-Match: "<generation>"` on `PUT /config`.

        `warnings` are re-derived from the RUNNING config by the loader on every read, so they describe what is live rather than what was true at boot. `restart_required` is always `[]` here and that is not a stub: the running config has no pending change by definition — the field exists so this response and `POST /config/diff`'s have the same shape.

        **No secret can appear in `yaml`.** Credential fields in this config schema are `*_env` references holding an environment-variable NAME, and `internal/config`'s `validateEnvRef` rejects an inline value at load time — so a config carrying a secret never became the running config in the first place.
      tags:
        - config
      x-role-floor: viewer
      x-required-scope: config:read
      security:
        - managementToken:
            - config:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigRunningResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config/running" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config/running",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/config/running`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/config/validate:
    post:
      operationId: configValidate
      summary: 'Run the strict loader pipeline over a candidate config and return its errors and warnings VERBATIM. Stateless: reads nothing, writes nothing.'
      description: |-
        The CI-callable pre-flight. It runs the loader's front half over the bytes you send — strict decode (an unknown key is an error, never a silent ignore), defaults, then the STRUCTURAL validation matrix — and returns its findings verbatim.

        **It is necessary but NOT sufficient, and the boundary is exact.** The structural matrix is everything this operation can run without a running engine. Three classes of check happen only at apply, because they need registries this operation does not have: the descriptor-aware provider checks, the routing-policy semantic matrix, and the governance/limit registries. A document that passes here can therefore still be refused by `POST /config/apply` or `PUT /config` with `422 validation_failed` carrying the engine's own message — that is a real branch of those operations, not a theoretical one. Treat a green result here as "this will not fail for a structural reason", and use `?dry_run=true` on a write when you need the whole answer, since a dry run runs the same pre-checks the real apply runs.

        **An invalid draft is a 200, not an error.** "Is this config valid?" has a true answer in both directions, and `{"valid": false, "errors": [...]}` IS that answer — so this operation returns no problem document for an invalid input. (The two operations that ACT on a config do return `422 validation_failed` for the same input, because there the invalidity prevented a thing you asked for.)

        **`errors` are the loader's own strings, unreworded and YAML-path-named** (`tiers[0].upstream: "nope" does not name a defined upstream`), one per line of the joined error. Rewording them would cost the path, which is the part you act on.

        This is the ONE operation on this resource that needs no control plane and no config path: it validates the bytes you sent, so it works on a server that has no configuration of its own.
      tags:
        - config
      x-role-floor: viewer
      x-required-scope: config:read
      x-read-only: true
      security:
        - managementToken:
            - config:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigValidateRequest'
            example:
              yaml: |
                version: 1
                mode: shadow
                upstreams:
                  anthropic:
                    base_url: https://api.anthropic.com
                    dialect: anthropic
                    api_key_env: ANTHROPIC_API_KEY
                tiers:
                  - name: cheap
                    upstream: anthropic
                    model: claude-haiku-4-5
                  - name: frontier
                    upstream: anthropic
                    model: claude-sonnet-4-6
                routing:
                  default_tier: frontier
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConfigValidateResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/config/validate" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/config/validate",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/config/validate`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/control/mode:
    post:
      operationId: controlModeSet
      summary: Move the engine's mode ladder. Promotion to `route` is gated on shadow evidence (or an explicit acknowledgement); demotion never is.
      description: |-
        Compiles the mode change to a config apply: the running config is copied, `mode` is set, and the result flows through the SAME atomic validate → diff → file-first apply → reload → audit pipeline `POST /config/apply` uses, with the running generation as its CAS. So a mode change is a config change: it has a diff, it bumps the generation, and it leaves a `mode.set` audit row carrying `diff_sha256`, the generation and the badges.

        **⚠ THIS OPERATION REWRITES YOUR CONFIG FILE FROM THE PARSED DOCUMENT, AND THAT IS DESTRUCTIVE.** It is the ONE operation in this API that does not preserve the file's bytes: the config that lands on disk is a re-serialization of the running configuration with `mode` changed, so **every comment, blank line, inline flow map and key ordering an operator wrote is gone permanently** — including from `GET /config/export` afterwards, because that read is honest about what is now on disk. Nothing is silently lost from the CONFIGURATION (the semantics are identical and the diff shows exactly one changed path); what is lost is the file's authorship.

        **If your config is under version control, change the mode declaratively instead**: `GET /config/export`, edit the `mode:` line, `PUT /config` with `If-Match`. That path is byte-exact, and it is also the path that BYPASSES the promote gate below — so the honest summary is: use this operation when you want the gate to be asked, and `PUT /config` when you want your file back unchanged. The verbatim guarantee the `config` resource publishes covers its two reads and `PUT /config`; it has never covered this operation, and saying so here is the difference between a documented behaviour and a surprise.

        **The gate fires only on PROMOTION to `route`, and only from here.** If the shadow-evidence gate is unsatisfied and you did not send `acknowledge_no_evidence: true`, this operation refuses with `403 promote_gate_refused` whose `reasons` are the gate's own findings, rendered VERBATIM and never reworded — including the case where the gate is unsatisfied because no evidence provider is configured at all, which is stated in those words rather than as a bare "not satisfied". An absent `reasons` member means the gate produced NO findings; it is never an invented reason and never an empty array.

        **Demotion (`route` → `shadow` → `ledger`) never checks evidence.** Stepping DOWN the ladder is always safe and must never be blocked by the machinery that guards stepping up — the gate would otherwise be able to trap a deployment in `route`.

        **The gate is a dashboard-path gate and this is the operation that says so.** A config that simply says `mode: route`, applied through `POST /config/apply`, `PUT /config`, SIGHUP or at boot, is applied with no evidence check. That path is published on both write operations. What holds the two together is the badge: `route_without_shadow_evidence` is recomputed from the LIVE running mode on every origin, so an acknowledged promotion made HERE is badged exactly as a bypassing one is — the acknowledgement records who took the risk, it does not clear the badge.

        **Every branch writes exactly one `mode.set` audit row** — the refusals included (a malformed body, an unknown mode, an unwired control plane, a gate refusal, a lost race and a failed reload), because a refused promotion is the interesting half of the trail.

        **A lost race is a `409 generation_conflict`, not a validation failure.** Another operator applying between this handler reading the running generation and the engine taking its reload lock is a CONCURRENCY outcome: it answers 409 carrying `current_generation`, so a retry policy keyed on conflict statuses retries it and one keyed on 4xx-means-bad-input does not permanently give up on a mode change that was never wrong.
      tags:
        - control
      x-role-floor: operator
      x-required-scope: config:apply
      security:
        - managementToken:
            - config:apply
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ControlModeRequest'
            example:
              mode: shadow
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ControlModeResponse'
        "403":
          description: 'Problem: promote_gate_refused.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "409":
          description: 'Problem: generation_conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! CHANGES THE ENFORCEMENT MODE for live traffic. Moving off `route`
            # !! stops requests being routed; moving onto it starts enforcing
            # !! decisions on real requests.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/control/mode" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "mode": "shadow"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! CHANGES THE ENFORCEMENT MODE for live traffic. Moving off `route`
            # !! stops requests being routed; moving onto it starts enforcing
            # !! decisions on real requests.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "mode": "shadow"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/control/mode",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! CHANGES THE ENFORCEMENT MODE for live traffic. Moving off `route`
            // !! stops requests being routed; moving onto it starts enforcing
            // !! decisions on real requests.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "mode": "shadow"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/control/mode`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/cost-exports:
    post:
      operationId: costExportCreate
      summary: Render a FOCUS-shaped cost or savings export over a window, as a stamped CSV or JSONL file.
      description: |-
        Renders the ledger into the FOCUS cost specification's column shape, so spend from this deployment lands in a FinOps pipeline beside spend from everything else.

        ★ POST BECAUSE IT IS A HEAVY READ WITH A STRUCTURED REQUEST, NOT BECAUSE IT CREATES ANYTHING. Nothing is stored and nothing is mutated; the response IS the file. The verb is POST because the window, grain, timezone and kind do not belong in a query string at this size, and the operation is classified into the READ rate-limit budget accordingly. Retrying is therefore safe in the way a GET is safe — there is no partially-created export to clean up.

        ★ IT COMPUTES NO NUMBER OF ITS OWN. The handler asks the shipped query engine the same questions the `/query` surface answers and renders what comes back, so a FOCUS export and a dashboard reading of the same window cannot disagree about the same figure. That is a structural property of routing both through one engine rather than a claim about two implementations being kept in step.

        ★ THE TWO KINDS ANSWER DIFFERENT QUESTIONS. `focus_cost` renders what was spent. `focus_savings` renders what was avoided — and it carries the basis and verdict reference described on this resource, because a savings row without its counterfactual is not a measurement. A savings row that could not be licensed against a recorded verdict fails the render with the violated rule named, rather than being quietly omitted to produce a shorter, cleaner file.

        ★ REFUSAL ORDER IS DELIBERATE: surface-absent, then request-invalid, then substrate-unavailable. A malformed grain is malformed in every deployment, so it is reported as a bad request rather than masked by a message about the index being off — an operator told "the index is unavailable" for a request that was never valid will enable the index, retry, and only then learn the grain was wrong.

        ★ THE RESPONSE BODY IS OPAQUE BYTES, and `Content-Disposition` names the file after the kind and format.
      tags:
        - costanalytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      x-read-only: true
      security:
        - managementToken:
            - analytics:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CostExportRequest'
            example:
              kind: focus_cost
              grain: 1d
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                description: JSON body with no published schema yet — the acknowledged §2.1-rule-2 exemption class (a dashsrv-local shape awaiting promotion); typed no later than MS-B.
            text/csv:
              schema:
                type: string
                description: 'CSV twin of the JSON shape: ExportMeta as leading #-comment lines, the literal UNPRICED for an unpriced dollar (never 0), csvSafe-neutralized cells.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/cost-exports" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "kind": "focus_cost",
              "grain": "1d"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "kind": "focus_cost",
              "grain": "1d"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/cost-exports",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "kind": "focus_cost",
              "grain": "1d"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/cost-exports`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/v1/events:
    get:
      operationId: eventsList
      summary: 'The derived, bounded event feed: cursor-paged, filterable by type, with an explicit 410 when a cursor falls off the ring.'
      description: |-
        Events are returned OLDEST FIRST, which is the only ordering a resume-from-cursor feed can offer honestly: a consumer follows `next_cursor` forward and never has to reason about a page boundary moving underneath it.

        `oldest_seq`, `latest_seq` and `ring_size` travel on EVERY response, not only on the 410. A consumer that watches the distance between its own position and `oldest_seq` can widen its poll interval or reconcile BEFORE it falls off, instead of discovering the loss afterwards.

        ★ THE 410 IS THE CONTRACT, NOT AN EDGE CASE. Two conditions produce it. (1) The cursor's sequence is below the ring's oldest live sequence — events between the two are gone. (2) The cursor was minted by a DIFFERENT ring incarnation (this instance restarted, or the log was rebuilt). The second matters more than it looks: without it, a cursor at `seq: 500` presented to a freshly-restarted instance whose sequence has returned to 1 would be satisfied with an empty page forever — a silent, permanent stall that reads exactly like "nothing has happened". Both conditions answer 410 `cursor_expired` with `oldest_seq`; reconcile from `POST /audit` and resume.

        An absent `cursor` IS page one, and page one starts at the ring's oldest live event.

        ★ HOW TO POLL THIS FEED, because it differs from every finite list in this API in one way and the difference is what makes a poll loop possible. `next_cursor` IS PRESENT ON EVERY RESPONSE THAT HAS A POSITION TO REPORT — including when `has_more` is `false`, and including when the page is EMPTY. On a finite list, an absent `next_cursor` means "the end"; a ring has no end, only a tail that grows, so an absent cursor there would leave a caught-up consumer with no duplicate-free way to ask again. The two members answer DIFFERENT questions here: `has_more` means "there are further matching events RIGHT NOW, fetch again immediately", and `next_cursor` means "resume from here whenever you next poll". Echo the last `next_cursor` you received, forever; re-presenting it when nothing has happened is idempotent and returns an empty page with the same position back.

        The cursor advances past events your filter EXCLUDED, not merely past the ones you were given. That is why a `types`-filtered consumer that keeps polling is never expired for a gap it did not care about.

        `types` filters to a subset of the closed vocabulary. An UNRECOGNISED type — or an empty element, such as a trailing comma — is a 400, never a silently-ignored filter: a filter the server did not apply would return a superset the caller believes is a subset.
      tags:
        - events
      x-role-floor: viewer
      x-required-scope: events:read
      x-pagination:
        - cursor
      security:
        - managementToken:
            - events:read
        - session: []
      parameters:
        - name: types
          in: query
          description: Comma-separated subset of the closed `resource.action` vocabulary (see `GET /capabilities` → `management_api.events.types`). Absent ⇒ every type. An unrecognised value is refused with 400 rather than dropped. Comma-separated list.
          schema:
            type: string
        - name: since
          in: query
          description: 'Return only events created at or after this instant (RFC 3339 UTC). It is a CONVENIENCE filter over the same ring, not a second retention promise: it can never reach further back than `oldest_seq`, and pairing it with a cursor keeps the cursor''s position authoritative.'
          schema:
            type: string
            format: date-time
        - name: cursor
          in: query
          description: Opaque page position, echoed back verbatim from a previous response's `next_cursor` — never constructed client-side. Absent ⇒ the first page, starting at the ring's oldest live event. A cursor below the tail (or from a previous ring incarnation) is 410 `cursor_expired`, never a silent restart.
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 ⇒ 100; the maximum is 1000, and a larger value is REFUSED naming the cap rather than silently clamped (a clamped limit is a lie about what the server heard).
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EventsResponse'
        "410":
          description: 'Problem: cursor_expired.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated over a BOUNDED RING, which pages differently from the
            # finite lists in this API: next_cursor is present on EVERY response that
            # has a position to report, including beside has_more: false and on an
            # empty page, so its absence is NOT an end-of-feed signal. has_more means
            # "fetch again now"; next_cursor means "resume from here whenever you next
            # poll" — echo the last one you received, forever. A cursor that fell
            # off the ring's tail is answered 410 cursor_expired with oldest_seq;
            # reconcile from POST /audit rather than restarting. Cursors are opaque
            # — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/events?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated over a BOUNDED RING, which pages differently from the
            # finite lists in this API: next_cursor is present on EVERY response that
            # has a position to report, including beside has_more: false and on an
            # empty page, so its absence is NOT an end-of-feed signal. has_more means
            # "fetch again now"; next_cursor means "resume from here whenever you next
            # poll" — echo the last one you received, forever. A cursor that fell
            # off the ring's tail is answered 410 cursor_expired with oldest_seq;
            # reconcile from POST /audit rather than restarting. Cursors are opaque
            # — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/events?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated over a BOUNDED RING, which pages differently from the
            // finite lists in this API: next_cursor is present on EVERY response that
            // has a position to report, including beside has_more: false and on an
            // empty page, so its absence is NOT an end-of-feed signal. has_more means
            // "fetch again now"; next_cursor means "resume from here whenever you next
            // poll" — echo the last one you received, forever. A cursor that fell
            // off the ring's tail is answered 410 cursor_expired with oldest_seq;
            // reconcile from POST /audit rather than restarting. Cursors are opaque
            // — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/events?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/evidence:
    get:
      operationId: evidenceGet
      summary: 'The trust-ladder evidence state: per-source verdicts with their provenance, plus the promote gate and its reasons.'
      description: |-
        The read surface behind the promote decision. Each quality source reports one of four verdicts and nothing else: `BEAT` (a judged counterfactual win), `NOT_BEAT`, `ON_FRONTIER` (served-model labels can only place routing on or below the frontier, so the signed delta is shown and the note says why it is not a win), or `CANNOT_MEASURE` — the REFUSAL, returned with its reason instead of a fabricated number whenever the labeled-row floor is not met.

        An unwired evidence engine answers **200**, not 404: `promote_gate.satisfied` is false and `promote_gate.reasons` says the engine is not configured. "The gate is not satisfied, and here is why" is a real answer to the question this endpoint is asked, so reporting the feature absent would replace a true answer with a missing one. The same applies when the query budget is exhausted mid-read: the response is a 200 whose `provenance.truncated` is true.
      tags:
        - evidence
      x-role-floor: viewer
      x-required-scope: evidence:read
      security:
        - managementToken:
            - evidence:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvidenceState'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/evidence" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/evidence",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/evidence`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/exports:
    post:
      operationId: exportCreate
      summary: Export matching decision-log rows as a stamped JSON stream or CSV (the body's `format` selects; unpriced costs render null / UNPRICED, never 0).
      description: |-
        A READ served over POST because its input is a filter AST. Two representations, selected by the request body's `format` field: `json` (the DEFAULT) and `csv`.

        **Selecting one.** The body's `format` WINS OUTRIGHT — it is an instruction, not a preference. When it is ABSENT the `Accept` request header is honoured: a header that names `text/csv` and names no json type returns CSV; everything else, including a `*/*` wildcard, returns the documented `json` default. `q=` weights are not interpreted, and an unmatched `Accept` is not a `406` — this operation has a default representation, so an unrecognised preference falls back to it rather than failing. [Both media types have appeared on this 200 since the operation was registered, which in OpenAPI IS a statement that the response is content-negotiated; until AP2-06 no handler read `Accept` at all, so a client that asked for CSV received JSON with no error at all. AP1 integration review finding 2a.]

        **The JSON form is a stream, not a document.** It emits the `ExportMeta` header object, then the row array, then — only if the query budget tripped during the stamp scan — a trailing `{"tokentriage_export_truncated":true,…}` object. Read it with a streaming decoder (`json.Decoder`/`ijson`), not a whole-body parse. That framing is what keeps an export of any size bounded in server memory: the row set is never materialised.

        **The stamp is computed over the same rows the body carries, and says so when it is not.** Pass one streams the matching set to count it; pass two re-streams the append-only log and emits exactly that many rows. The two passes open the log separately, so a rotation between them is possible — and because the header is already on the wire by then, the status code cannot be taken back. A short or failed second pass therefore appends the same trailing `{"tokentriage_export_truncated":true,…}` object (a `# truncated: true (…)` line in CSV) that a budget breach does, naming which of the two happened. An export that reaches you without that tail carries every row its stamp counted.

        **Honesty.** A row whose authoritative `cost.unpriced` flag is set renders `usd: null` in JSON and the literal `UNPRICED` in CSV — never the ledger's raw pre-flag `0`, and never `0.00`. `excluded.unpriced` in the stamp is how many such rows the export contains, and `estimated_rows` how many carried an estimate. Every request- or operator-influenceable CSV cell is neutralised against spreadsheet formula evaluation.
      tags:
        - exports
      x-role-floor: viewer
      x-required-scope: exports:read
      x-read-only: true
      security:
        - managementToken:
            - exports:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExportRequest'
            example: {}
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                description: JSON body with no published schema yet — the acknowledged §2.1-rule-2 exemption class (a dashsrv-local shape awaiting promotion); typed no later than MS-B.
            text/csv:
              schema:
                type: string
                description: 'CSV twin of the JSON shape: ExportMeta as leading #-comment lines, the literal UNPRICED for an unpriced dollar (never 0), csvSafe-neutralized cells.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/exports" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {}
            JSON
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {}

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/exports",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {};

            const res: Response = await fetch(`${base}/tt/api/v1/exports`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/v1/facets:
    post:
      operationId: analyticsFacets
      summary: Count the distinct values of one field under a filter, for faceted navigation (a heavy READ served over POST).
      description: |-
        `field` is required and is validated against the same closed field registry the filter grammar uses (served at `/capabilities.filter_fields`). `top` bounds the returned buckets; when buckets were dropped the response says so in `truncated`, which is why this is the one analytics response with no `provenance` block — it carries no dollars, and its only partiality signal is its own.

        QUERY BUDGET: this is a heavy read under the §2.1 budget (a per-request deadline, a row cap, and a concurrency semaphore). When the budget bites, the answer is a PARTIAL 200 whose `provenance.truncated` is `true` — never a silently complete-looking result, and never a 503. Always read `provenance` before trusting a total.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      x-read-only: true
      security:
        - managementToken:
            - analytics:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FacetsRequest'
            example:
              field: http.provider
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FacetsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/facets" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "field": "http.provider"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "field": "http.provider"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/facets",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "field": "http.provider"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/facets`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/feedback:
    post:
      operationId: feedbackSubmit
      summary: File a verdict on one request as an append-only companion row.
      description: |-
        `req_id` names the ledger record the verdict is about and `verdict` is `up`, `down`, or a free-form label of at most 64 bytes; a longer label is a 400 naming the limit, never a silent trim. `note` is an optional 2000-byte annotation.

        The `actor` and `source` on the stored row are derived from the authenticated principal and are NOT caller-supplied — a client cannot file a verdict as `human` over a bearer token. All API-sourced feedback shares the actor name `token`, because a bearer principal carries no per-token name; that granularity limit is stated here rather than left to be discovered from the data.

        Capture is rate-limited per actor (60 verdicts per minute), which answers 429 with `retry_after_seconds`. A deployment with no decision-log sink wired answers 404 `feature_absent` carrying the configuration cure — not a 503, because no amount of retrying wires a sink.

        AUDIT: every accepted verdict writes an audit row (`feedback.submit`). The append-only companion row is the DATA; the audit row is the accountability trail, and the two are separate because the companion row rides a drop-on-full queue and an audit trail that can be silently dropped is not an audit trail.
      tags:
        - feedback
      x-role-floor: operator
      x-required-scope: feedback:write
      security:
        - managementToken:
            - feedback:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeedbackRequest'
            example:
              req_id: $TOKENTRIAGE_REQ_ID
              verdict: up
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/feedback" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "req_id": "$(json_str "$TOKENTRIAGE_REQ_ID")",
              "verdict": "up"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            REQ_ID = os.environ["TOKENTRIAGE_REQ_ID"]

            payload = {
              "req_id": REQ_ID,
              "verdict": "up"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/feedback",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const reqId: string = process.env.TOKENTRIAGE_REQ_ID ?? "";

            const payload = {
              "req_id": reqId,
              "verdict": "up"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/feedback`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/fleet:
    get:
      operationId: fleetGet
      summary: 'The replica roster: one card per member of the coverage denominator, with the declared liveness window and the measured clock skew.'
      description: |-
        Answers "who is in this deployment right now, and how much do I trust that answer?" The card list is the roster UNION the observed membership, so a replica that is rostered but silent and a replica that is heartbeating but not yet rostered both appear, distinguishably.

        ★ `beat_age_ms: -1` IS A SENTINEL MEANING "NO HEARTBEAT CELL", AND IT IS NOT THE SAME AS A LARGE AGE. A stale-but-present member carries its real (large) age; `-1` means this member has written nothing for us to age. Render it as "no heartbeat", not as fresh and not as green — a UI that sorted ascending on this field would put the members it knows least about at the top of the healthy list. The sentinel is also not unique to a single-node self: a booting member whose first cell has not landed shows `live: true` with `-1` transiently, so the value means "no cell" rather than "one node".

        ★ `basis` NAMES HOW THE DENOMINATOR WAS ESTABLISHED, and it travels with the same vocabulary the query-fold coverage envelope uses. Two responses with the same replica count and different `basis` values are making different claims about how firm that count is. Read it before comparing counts across responses.

        ★ THIS READ REPORTS; IT DOES NOT RECONCILE. Seeing a stale card here is not a reason to decommission the member — see the decommission operation for why silence and departure are kept apart.
      tags:
        - fleet
      x-role-floor: viewer
      x-required-scope: fleet:read
      security:
        - managementToken:
            - fleet:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FleetResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/fleet" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/fleet",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/fleet`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/fleet/decommission:
    post:
      operationId: fleetDecommission
      summary: Remove one replica from the coverage denominator N. Explicit, audited, and idempotent.
      description: |-
        ★ THIS CHANGES N. IT DOES NOT DELETE DATA. The named replica stops counting toward the coverage denominator, so folds over the fleet stop waiting for it and stop marking themselves partial on its account. Its ledger volume stays queryable wherever it lives, and its history stays attributable to it. "Decommission" here is a statement about MEMBERSHIP, not a retention action, and there is deliberately no data-removal side effect to discover later.

        ★ THE OUTCOME IS A CLOSED `status` VOCABULARY, AND THE DISTINCTION IS WORTH READING. `decommissioned` means this call recorded it. `already_decommissioned` means it was already out of the denominator and this call changed nothing — a 200, because the requested end state holds. `not_found` means the roster has no row under that name, which is a different fact from "already gone": it is usually a typo or a name from another environment, and `decommissioned` is empty on that branch. Dispatching on the HTTP status alone collapses three outcomes into one; dispatch on `status`.

        ★ RETRYING IS SAFE, AND THE SECOND CALL TELLS YOU IT WAS THE SECOND CALL. The write is idempotent by end state rather than by request identity, so a retry after a timeout answers `already_decommissioned` with the ORIGINAL `decommissioned` timestamp rather than restamping it. That timestamp is the audit-linked fact; a client that treats the retry's response as the moment of decommission records the wrong time.

        ★ RE-ADMISSION IS NOT ON THIS SURFACE. A decommissioned replica that heartbeats again is a deliberate operational question, not an automatic one, and it is settled by the deployment rather than by a route here. Ask for the roster afterwards to see what the denominator actually became.

        ★ ADMIN FLOOR, AND THE REJECTED AND FAILED BRANCHES ARE AUDITED TOO. A refused attempt leaves a row behind, which is usually what an incident review is looking for.
      tags:
        - fleet
      x-role-floor: admin
      x-required-scope: fleet:write
      x-idempotent: true
      security:
        - managementToken:
            - fleet:write
        - session: []
      parameters:
        - name: Idempotency-Key
          in: header
          description: 'Optional idempotency key (§2.5): 24 h retention; a replay returns the stored first response verbatim (errors included); reuse with a different payload is 422 idempotency_payload_mismatch; a concurrent in-flight duplicate is 409 idempotency_in_flight.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecommissionRequest'
            example:
              replica: $TOKENTRIAGE_REPLICA
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecommissionResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/fleet/decommission" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "replica": "$(json_str "$TOKENTRIAGE_REPLICA")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            REPLICA = os.environ["TOKENTRIAGE_REPLICA"]

            payload = {
              "replica": REPLICA
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/fleet/decommission",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const replica: string = process.env.TOKENTRIAGE_REPLICA ?? "";

            const payload = {
              "replica": replica
            };

            const res: Response = await fetch(`${base}/tt/api/v1/fleet/decommission`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/forecast:
    get:
      operationId: forecastGet
      summary: Project spend forward from the recent history in the derived index, with the method and confidence that produced the projection.
      description: |-
        Fits the recent per-hour series and projects it over a horizon. The history window is the trailing eight weeks; the projection begins at the next whole hour.

        ★ THE PARTIAL CURRENT HOUR IS EXCLUDED FROM THE PROJECTION AND INCLUDED IN THE ACTUAL-TO-DATE TERM, which is the subtle part of reading a month-end figure here. The hour in progress is not yet a data point worth fitting — it is a fraction of one, and fitting it drags the slope toward zero near the top of every hour. It does belong in what has been spent so far, so that is where it is counted. A reader reconciling "actual + projected" against a wall-clock total should expect them to agree; a reader who assumed the current hour was dropped entirely will find a small excess that is not an error.

        ★ A PROJECTION IS A METHOD PLUS A CONFIDENCE, AND BOTH ARE PUBLISHED. The response names which method produced it and how much the recent series supports it. A short or erratic history yields a projection with low stated confidence rather than a refusal — the figure and its trustworthiness are separate fields, so a consumer can render a wide band instead of a false point estimate.

        ★ THE PROJECTION INHERITS THE MONEY ENVELOPE. Its provenance block reports the same `unpriced_rows` / `pricing_snapshots` counts the ledger reads carry, because a forecast fitted over partially-unpriced history is itself partially unpriced. A projection built on a window with a large unpriced cohort is projecting the priced fraction; the counts are how a reader sees that.

        ★ ABSENT RATHER THAN EMPTY. With `analytics.forecast.enabled` off, or with no derived index to read a series from, this answers 404 naming the configuration that would turn it on. A zeroed forecast would be indistinguishable from a genuine prediction of no spend.
      tags:
        - costanalytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      parameters:
        - name: horizon
          in: query
          description: How far forward to project, as a Go duration (`72h`, `168h`). Absent ⇒ `168h` (seven days). The ceiling is `744h` (thirty-one days); a longer horizon is REFUSED naming the cap rather than silently clamped, because a silently-shortened projection answers a question the caller did not ask. A zero or negative value is refused the same way.
          schema:
            type: string
        - name: tz
          in: query
          description: IANA timezone name (`America/New_York`) used to place hour and day boundaries. Absent ⇒ the deployment default. This changes where the day breaks fall and therefore changes a daily or month-end figure; it is not a display-side concern. An unknown zone is 400.
          schema:
            type: string
        - name: filter
          in: query
          description: Restrict the fitted history to a slice of the ledger, in the same §2.2 filter text the query surface accepts. Absent ⇒ the whole ledger. The projection is fitted over the filtered series, so a filter here narrows what is being predicted rather than post-filtering a global projection. The q= text filter grammar (see the FilterNode schema description).
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Forecast'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/forecast" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/forecast",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/forecast`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/exports/refusals:
    get:
      operationId: governanceRefusalsExport
      summary: Full subtree/window-scoped refusal-feed export (JSON or CSV via format=), provenance-stamped, secret-free.
      description: |-
        The archive twin of `governanceRefusals`: the FULL subtree- and window-scoped refusal feed, not the 1000-row live view. Same secret-free row shape, same no-key-existence-oracle rule (a 401 `invalid_key` refusal names no limit), plus the ExportMeta provenance stamp.

        An export is COMPLETE BY DEFAULT. `offset` and `limit` here are a RESUME position and a row cap for a caller who wants to pull a very large export in pieces — they are not a pagination contract, and the response carries no `next_offset` or `next_cursor` to follow. That is why this operation declares no `x-pagination` while `governanceRefusals` declares two styles: one is a paged feed, the other is a whole file you may fetch in chunks.

        REPRESENTATION IS SELECTED BY `format=` FIRST, AND BY `Accept` WHEN `format=` IS ABSENT — see that parameter.

        A 404 `feature_absent` means no canonical decision log is readable on this instance (with the cure named); a 503 `budget_exceeded` means the query budget was exhausted and is genuinely transient.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the export to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: since
          in: query
          description: Window start, INCLUSIVE. Absent ⇒ from the beginning of the log.
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          description: Window end, EXCLUSIVE. Absent ⇒ to the end of the log.
          schema:
            type: string
            format: date-time
        - name: offset
          in: query
          description: 'RESUME position: rows to skip. An export is complete by default, so this exists for chunked retrieval, not for paging — no cursor or next-offset is returned to follow.'
          schema:
            type: integer
            format: int64
        - name: limit
          in: query
          description: Row cap for this chunk. 0 or absent ⇒ ALL matching rows (this export is deliberately uncapped, unlike the 1000-row live feed).
          schema:
            type: integer
            format: int64
        - name: format
          in: query
          description: |-
            The representation to return: `json` (default) or `csv`. ★ THIS PARAMETER WINS OUTRIGHT. It is an INSTRUCTION, not a preference, so it beats any `Accept` header; if you know which representation you want, send it here and stop reading.

            `Accept` IS HONOURED WHEN `format=` IS ABSENT, and the rule is deliberately coarse rather than a full RFC 9110 negotiation: a header that names `text/csv` and names NO json type selects csv. Everything else — including `Accept: application/json, text/csv`, a `*/*` wildcard, and a media type this operation does not serve — selects the documented `json` default. Two things this server deliberately does NOT do: it does not interpret `q=` weights (a negotiation that reads `q=0` as a weak preference rather than a refusal is wrong in a way the caller cannot see), and it does not answer `406` for an unmatched `Accept` (these operations have a default representation, so an unmatched header means "no preference this server can honour", not "no acceptable response exists").

            Both media types appear on the 200 response because both are genuinely served AND genuinely negotiated. The csv form carries the ExportMeta provenance as leading `#` comment lines and the literal `UNPRICED` for an unpriced dollar (never `0`), with every cell csvSafe-neutralized against formula injection.
          schema:
            type: string
            enum:
              - json
              - csv
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRefusalExportResponse'
            text/csv:
              schema:
                type: string
                description: 'CSV twin of the JSON shape: ExportMeta as leading #-comment lines, the literal UNPRICED for an unpriced dollar (never 0), csvSafe-neutralized cells.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/exports/refusals" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/exports/refusals",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/exports/refusals`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/exports/usage:
    get:
      operationId: governanceUsageExport
      summary: Periodized per-node spend/usage rollup over the ledger (JSON or CSV via format=), provenance-stamped, unpriced+unattributed excluded and disclosed.
      description: |-
        The billing-shaped read: per (node, period-bucket) request counts, token counts and spend, rolled up over the canonical decision log.

        **WHAT IT EXCLUDES**, AND WHY THAT IS THE POINT. Two classes of row cannot honestly be placed in a per-node spend rollup, and neither is silently dropped. UNPRICED rows — rows the pricing layer could not price at all — are excluded from every dollar figure and counted in `excluded.unpriced` plus per-row `unpriced_rows`. UNATTRIBUTED rows — rows with no resolvable governance node — are excluded and counted in `excluded.unattributed`. A group with zero priced rows reports its spend as UNPRICED (`usd: null`), never as `$0.00`: a fabricated zero is indistinguishable from a real zero, and the whole reason this API exists is that those two must never look the same.

        Every response is stamped with the same ExportMeta provenance envelope the /exports surface uses — pricing snapshots, unpriced-excluded, estimated, demo, window, instance — so a figure can always be traced to the pricing data that produced it. `tokentriage_export.v` is the EXPORT SCHEMA version and stays 1 independently of the API epoch.

        REPRESENTATION IS SELECTED BY `format=` FIRST, AND BY `Accept` WHEN `format=` IS ABSENT — see that parameter.

        **THIS OPERATION CAN RETURN A PARTIAL 200, AND YOU MUST CHECK FOR IT.** The house convention across this API is that a read which trips the query budget answers a PARTIAL 200 rather than a 503 — an operator gets the rows that were scanned plus a disclosure, never an error that discards work already done. This operation follows it, and the disclosure is carried OUT OF BAND because the report shape has no member for it:

        - JSON: the body is TWO top-level objects separated by a newline. The first is the report. A SECOND object `{"tokentriage_export_truncated":true,"reason":"…"}` follows it if and only if the scan was cut short. A whole-body `JSON.parse` FAILS on a truncated export (two values, not one), and a decoder that reads only the first value silently drops the disclosure — so decode as a JSON STREAM and read until EOF.
        - CSV: a trailing `# truncated: true` comment line after the data rows.

        **ABSENCE OF THE SECOND OBJECT IS THE COMPLETENESS SIGNAL** — there is no positive `truncated: false` marker to look for, so a client that never checks cannot tell a complete rollup from a partial one, and the failure mode is a billing figure that is silently short. `tokentriage_export.excluded` counts rows the scan CHOSE to exclude (unpriced, unattributed); it does not count rows the scan never reached.

        **THIS OPERATION CAN RETURN A PARTIAL 200, AND YOU MUST CHECK FOR IT.** The house convention across this API is that a read which trips the query budget answers a PARTIAL 200 rather than a 503 — an operator gets the rows that were scanned plus a disclosure, never an error that discards work already done. This operation follows it, and the disclosure is carried OUT OF BAND because the report shape has no member for it:

        - JSON: the body is TWO top-level objects separated by a newline. The first is the report. A SECOND object `{"tokentriage_export_truncated":true,"reason":"…"}` follows it if and only if the scan was cut short. A whole-body `JSON.parse` FAILS on a truncated export (two values, not one), and a decoder that reads only the first value silently drops the disclosure — so decode as a JSON STREAM and read until EOF.
        - CSV: a trailing `# truncated: true` comment line after the data rows.

        **ABSENCE OF THE SECOND OBJECT IS THE COMPLETENESS SIGNAL** — there is no positive `truncated: false` marker to look for, so a client that never checks cannot tell a complete rollup from a partial one, and the failure mode is a billing figure that is silently short. `tokentriage_export.excluded` counts rows the scan CHOSE to exclude (unpriced, unattributed); it does not count rows the scan never reached.

        A 404 `feature_absent` means no canonical decision log is readable on this instance (with the cure named); a 503 `budget_exceeded` means the query budget was exhausted and is genuinely transient — retry it, with the disclosed retry delay.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the rollup to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: period
          in: query
          description: 'Rollup bucket. DEFAULT `total` (one row per node, `bucket: "all"`); otherwise each row''s `bucket` is the RFC 3339 UTC start of its period. A value outside this enum is refused 400.'
          schema:
            type: string
            enum:
              - day
              - week
              - month
              - total
        - name: since
          in: query
          description: Window start, INCLUSIVE. Absent ⇒ from the beginning of the log.
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          description: Window end, EXCLUSIVE. Absent ⇒ to the end of the log. Exclusive so adjacent windows tile without double-counting a boundary row.
          schema:
            type: string
            format: date-time
        - name: format
          in: query
          description: |-
            The representation to return: `json` (default) or `csv`. ★ THIS PARAMETER WINS OUTRIGHT. It is an INSTRUCTION, not a preference, so it beats any `Accept` header; if you know which representation you want, send it here and stop reading.

            `Accept` IS HONOURED WHEN `format=` IS ABSENT, and the rule is deliberately coarse rather than a full RFC 9110 negotiation: a header that names `text/csv` and names NO json type selects csv. Everything else — including `Accept: application/json, text/csv`, a `*/*` wildcard, and a media type this operation does not serve — selects the documented `json` default. Two things this server deliberately does NOT do: it does not interpret `q=` weights (a negotiation that reads `q=0` as a weak preference rather than a refusal is wrong in a way the caller cannot see), and it does not answer `406` for an unmatched `Accept` (these operations have a default representation, so an unmatched header means "no preference this server can honour", not "no acceptable response exists").

            Both media types appear on the 200 response because both are genuinely served AND genuinely negotiated. The csv form carries the ExportMeta provenance as leading `#` comment lines and the literal `UNPRICED` for an unpriced dollar (never `0`), with every cell csvSafe-neutralized against formula injection.
          schema:
            type: string
            enum:
              - json
              - csv
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceUsageReport'
            text/csv:
              schema:
                type: string
                description: 'CSV twin of the JSON shape: ExportMeta as leading #-comment lines, the literal UNPRICED for an unpriced dollar (never 0), csvSafe-neutralized cells.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/exports/usage" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/exports/usage",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/exports/usage`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/health:
    get:
      operationId: governanceHealth
      summary: 'Distributed-governance health: shared-state reachability, breaker state, suspensions, convergence lag (honest air-gapped when single-node).'
      description: |-
        Whether the machinery behind distributed governance is actually working: the shared-state tier's reachability, the circuit-breaker state, which limits have had enforcement SUSPENDED, and how far behind each scope's convergence is running.

        **AIR-GAP HONESTY IS VISIBLE**, NOT INFERRED. On a single-node instance with no shared-state tier configured, this does NOT 404 and does not return an empty carcass that looks like a healthy system with nothing to report. It returns `capability.shared_state_enabled: false` with a plain note saying this is a single-node instance and that distributed limits, cross-replica convergence and reconcile are not active. The difference between "nothing is wrong" and "nothing is watching" is the entire value of this endpoint.

        `suspensions` is the loud one: a suspended limit is armed to enforce but its evaluator heartbeat is stale, so it has failed OPEN and traffic is flowing past the cap.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceHealthResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/health" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/health",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/health`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/health/fleet:
    get:
      operationId: governanceHealthFleet
      summary: Per-replica governance health across the fleet, riding the same coverage envelope as the federated query fold.
      description: |-
        The fan-out form of the health read above: one card per replica in the coverage denominator, plus the envelope that says how much of the fleet actually answered.

        **AN UNREACHABLE REPLICA IS A TYPED CARD, NOT A GAP.** A replica that fails to answer appears in `replicas` with a non-ok `status` and a `health` of null, and it counts in the coverage denominator. It is not dropped from the list and it is not given a fabricated healthy posture. Dropping it would make a partial answer look complete — the fleet would appear entirely healthy because the sick member was the one that did not reply, which is precisely backwards.

        **`replicas` HAS ONE ENTRY PER REPLICA IN THE DENOMINATOR**, so `len(replicas)` matches `coverage.replicas_total` and the two halves of the response cannot disagree about who is in the fleet. The server re-verifies that alignment before serializing and refuses with a 500 rather than serve a self-inconsistent envelope — the same refusal the federated query fold makes for the same reason.

        **READ `coverage.is_partial` BEFORE COMPARING POSTURES.** With `is_partial: true` the answer describes the members that responded, not the fleet; `replicas_answered` over `replicas_total` is the fraction it rests on. Treating a partial fan-out as a fleet-wide verdict is the failure this envelope exists to prevent.

        **SINGLE-NODE AND AIR-GAPPED IS THE HONEST DEGENERATE**, not a special case and not a 404: one card, a denominator of one, and the local-only basis. The shape is the same one a real fleet returns, so a consumer needs no branch for it.

        This surface is READ-ONLY by design, like the health read it fans out — there is no adopt or write affordance here.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FleetGovernanceHealth'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/health/fleet" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/health/fleet",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/health/fleet`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/keys:
    get:
      operationId: governanceKeysList
      summary: List virtual keys (kid+stub+label+state+scopes only — never a secret).
      description: |-
        Virtual-key metadata for the requested subtree. The read shape carries the public key id (`kid`), a display-only 4-character `stub`, the label, the owning tenancy node, state (active|revoked|expired), timestamps, rotation lineage, and the key's allowed surface — and it has NO field capable of holding a secret or a hash. That is structural, not a convention: "a key's stored value" is unrepresentable on this shape, so no bug in this handler can leak one. The secret exists on exactly one response in this entire resource, the shown-once mint reveal.

        `last_used` is deliberately COARSE (hour granularity): a precise last-use timestamp on a credential list is a traffic-analysis side channel, and hour resolution is what an operator actually needs to spot a dormant key.

        Empty `scopes` slices mean UNRESTRICTED for that dimension, not "denied" — an empty `models` list is a key that may call any model.

        An instance with no governance armed answers 200 with an empty `keys` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the listing to keys owned within this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceKeysResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: governanceKeyMint
      summary: Mint a virtual key; the secret is revealed exactly once.
      description: |-
        Create a virtual key owned by a tenancy node.

        **THE SECRET IS SHOWN ONCE AND IS NOT RECOVERABLE.** The `secret` field of this response is the only time the plaintext `ttv_` credential exists outside the caller: the store persists only its SHA-256, it is never logged, it is never written to an audit row (the row names the `kid`), and there is no endpoint that can return it again. A caller that loses it must rotate the key. This is one of a closed, allowlisted set of responses in the entire API permitted to carry a credential.

        Requires the PRIVILEGED `keys:mint` scope, which the `*:write` wildcard deliberately never expands to — minting a credential is not an ordinary write, so a broadly-scoped token cannot do it implicitly.

        `scopes` bounds what the key may call; an EMPTY list in any dimension means unrestricted for that dimension, not denied. `expires_at` is RFC 3339 UTC; absent means non-expiring.

        Every branch of this endpoint — including a malformed body — writes exactly one audit row, so a probing client leaves a complete trail rather than a gap.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
              scopes:
                models: []
                providers: []
                endpoints: []
                allow_passthrough: false
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": False
              }
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/keys/{kid}:
    get:
      operationId: governanceKeyGet
      summary: One virtual key's read shape by kid (subtree-scoped; no cross-scope existence oracle).
      description: |-
        One key's metadata, in the same secret-free shape the list returns.

        **NO EXISTENCE ORACLE.** When `node` constrains the read, a kid that exists OUTSIDE that subtree answers with exactly the same 404 as a kid that does not exist at all. The two cases are indistinguishable by design: if they differed, a caller scoped to one tenant could enumerate another tenant's key ids by watching which 404s came back differently.

        A 404 on this route is one of two things and the problem type tells them apart: `not_found` means no such key is visible to you, while `feature_absent` means this deployment has no key store at all and names the config that would create one.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: The subtree scope for the read; an out-of-scope kid answers exactly like a missing one (no existence oracle). A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceKeyResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The key's PUBLIC id, as listed by `GET /governance/keys` and returned at mint. It is a name for the key, not key material.
        schema:
          type: string
  /tt/api/v1/governance/keys/{kid}/revoke:
    post:
      operationId: governanceKeyRevoke
      summary: Revoke a virtual key.
      description: |-
        Move a key to `revoked`. The record is retained — revocation is a state transition, not a deletion — so the key remains visible on the read surfaces with its history intact, which is what makes it auditable after the fact.

        Revocation carries the COMMON `governance:write` scope rather than a privileged one, because destroying access to the DATA PLANE is the SAFE direction: a virtual key governs an application's model access, not the operator's own reach into this API, so an operator locked out of minting during an incident must still be able to kill a leaked key. Requiring an authority the incident may have taken away would make the containment step the one step you cannot perform.

        **The credential resources DIFFER on this, deliberately, and AP2-06 took the wave-level decision this paragraph used to defer.** The line is the PLANE the credential governs, not the verb: destroying a CONTROL-PLANE credential is privileged everywhere — `tokenRevoke` (`tokens:write`), `tokenRotate` (`tokens:mint`), `shareTokenRevoke` (`share:admin`) and `governanceKeyRotate` (`keys:mint`) — because those credentials are how an operator reaches this API at all, and a `*:write` wildcard that reaches them can lock the operator out of the plane they would use to recover. This operation is the single DECLARED exception, and it is held as one by a gate rather than left to decay back into an accident.

        A 404 `feature_absent` means this deployment has no key store.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRevokeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            # !! reaching models immediately — which is the point during an
            # !! incident, and a surprise otherwise.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID/revoke" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            # !! reaching models immediately — which is the point during an
            # !! incident, and a surprise otherwise.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}/revoke",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            // !! reaching models immediately — which is the point during an
            // !! incident, and a surprise otherwise.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}/revoke`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The public id of the key to revoke.
        schema:
          type: string
  /tt/api/v1/governance/keys/{kid}/rotate:
    post:
      operationId: governanceKeyRotate
      summary: Rotate a key, minting a successor; the successor secret is revealed exactly once.
      description: |-
        Mint a SUCCESSOR to an existing key, recording the rotation lineage so the old and new key can be correlated in the audit trail and in `rotated_from` on the key read shape.

        The successor's secret is revealed exactly once, under the same shown-once discipline as mint: SHA-256 at rest, never logged, never audited, never re-fetchable.

        Rotation is a SEPARATE surface from mint rather than an alias for it — it reads an existing credential's grant, mints under it, and touches the predecessor — which is why it carries its own row in the shown-once allowlist rather than sheltering under the mint's.

        Requires the privileged `keys:mint` scope. A 404 `feature_absent` means this deployment has no key store.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      security:
        - managementToken:
            - keys:mint
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor key after its grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID/rotate" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor key after its grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}/rotate",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! REVOKES the predecessor key after its grace window. Deploy the
            // !! successor first.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}/rotate`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The public id of the key to rotate. The successor is a NEW key with its own kid; this one is recorded as its `rotated_from`.
        schema:
          type: string
  /tt/api/v1/governance/limits:
    get:
      operationId: governanceLimitsList
      summary: Compiled limit definitions (armed) + persisted per-node overrides (effective on next reload).
      description: |-
        The two halves of "what is the cap", kept deliberately separate because they are not the same fact.

        `definitions` are the ARMED compiled limits — what is enforcing RIGHT NOW. `overrides` are persisted per-node writes that are COMPILE-BOUND: they are durable and audited, but they do not take effect until the next governance reload, and every one of them is returned with `pending_reload: true` and a note saying so. No override is ever reported here as live-enforced. That is the whole point of the split: lowering a cap through this API cannot show "applied" while traffic still flows against the old ceiling — the definition keeps showing the amount actually enforced.

        A definition attaches either as a `template` (the config-plane catalog, attached at a level: org/team/user) or `pinned` to one node. Subtree scoping applies to PINNED definitions only: templates are the catalog, not node-private, so they are always shown. Overrides are node-scoped and filter normally.

        Spend-class amounts render as typed Money; count-class as `amount_count`. `override_allowed` plus `override_max` are the ceiling a delegated override must fit under.

        An instance with no governance armed answers 200 with both arrays empty and a note saying why.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine PINNED definitions and overrides to this subtree; template definitions are the config-plane catalog and are always shown. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceLimitsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/limits/overrides:
    post:
      operationId: governanceOverrideSet
      summary: Set a per-node limit override (persisted + audited; effective on next governance reload).
      description: |-
        Raise or lower one limit's amount for one tenancy node. The write is durable and audited immediately.

        **IT DOES NOT ENFORCE YET**, AND THE RESPONSE SAYS SO. Overrides are COMPILE-BOUND: the value lands in the store but the armed limit set is unchanged until the next governance reload. Every success returns `pending_reload: true` with a note. This surface will never tell you a cap is applied while traffic is still being measured against the old one — read `governanceLimitsList`'s `definitions` for what is enforcing right now.

        `amount` is the limit's STORED unit: NANODOLLARS for `budget_usd` (integer, so money never rounds through a float — $1.00 is 1000000000), a raw count otherwise. An `amount` ≤ 0 is REFUSED 400 rather than accepted as "unlimited": zero would DISABLE the cap, which is the exact inversion of what someone setting a limit meant.

        A 422 `unsupported_override_class` means the limit's TYPE is structurally incapable of honouring a per-node amount override: `rpm`, `tpm` and `concurrency` bake their cap into a frozen per-window store when governance compiles, so the reload would silently DROP it. It is refused at the boundary with the same verdict the reload would reach, instead of returning a cheerful 200 and a row that reports `pending_reload` forever. Overrides apply only to the ledger-settled types (`budget_usd`, `quota_tokens`, `quota_requests`).

        KNOWN DIVERGENCE FROM THE DELEGATED TWIN, stated because it will surprise you: an UNKNOWN `limit_id` is accepted HERE with a 200 and `pending_reload: true` (this plane defers unknown-limit validation to the reload), while `governanceSelfServeOverride` refuses the same input 400. So a typo'd `limit_id` looks like a successful write on this route and then never takes effect. Confirm the id against `governanceLimitsList`'s `definitions` before relying on the 200.

        A 404 `feature_absent` means this deployment has no governance plane to override.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideRequest'
            example:
              limit_id: $TOKENTRIAGE_LIMIT_ID
              node: $TOKENTRIAGE_NODE
              amount: 150000000000
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: unsupported_override_class.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits/overrides" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limit_id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "amount": 150000000000
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limit_id": LIMIT_ID,
              "node": NODE,
              "amount": 150000000000
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits/overrides",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limit_id": limitId,
              "node": node,
              "amount": 150000000000
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits/overrides`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/limits/overrides/{limit_id}/{node}:
    delete:
      operationId: governanceOverrideDelete
      summary: Delete a per-node limit override (persisted + audited; effective on next governance reload).
      description: |-
        Remove one node's override, returning that limit to its compiled definition at that node. Like setting one, deleting one is COMPILE-BOUND: the removal is durable and audited immediately but does not change what is enforcing until the next governance reload, and the response says so.

        The node is a PATH REMAINDER, not a single segment, because a tenancy node id contains '/' — the full path is `…/overrides/{limit_id}/org:acme/team:ml`. The OpenAPI parameter is documented under its clean name `node`.

        Deleting an override that is not set is 404 `not_found` — this operation is not idempotent-by-silence, because "there was nothing there" is information the caller asked for. A 404 `feature_absent` is the different case: this deployment has no governance plane at all.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideDeleteResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a limit override, so the inherited limit applies again.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits/overrides/$TOKENTRIAGE_LIMIT_ID/$TOKENTRIAGE_NODE" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a limit override, so the inherited limit applies again.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits/overrides/{LIMIT_ID}/{NODE}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a limit override, so the inherited limit applies again.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits/overrides/${limitId}/${node}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: limit_id
        in: path
        required: true
        description: The compiled limit's id, as it appears in `governanceLimitsList`'s definitions.
        schema:
          type: string
      - name: node
        in: path
        required: true
        description: 'The tenancy node whose override is being removed. A path REMAINDER: it may contain ''/'' (e.g. `org:acme/team:ml`). A path remainder: the value may itself contain ''/'' (e.g. a tenancy node id like org:acme/team:ml).'
        schema:
          type: string
  /tt/api/v1/governance/nodes:
    get:
      operationId: governanceNodesList
      summary: The compiled tenancy tree (flat, parent-pointed).
      description: |-
        The tenancy tree as a FLAT, parent-pointed list rather than a nested document: each node carries its own id, its `parent` id (empty for a root org), a label and a kind (org|team|user). Flat because a subtree-scoped read returns a SLICE of the tree, and a nested shape would have to either fabricate the ancestors the caller may not see or return a forest of orphans; a parent pointer says exactly what is known and nothing more.

        Node ids are '/'-joined paths (`org:acme/team:ml`), which is why the delete-override route takes the node as a path REMAINDER rather than a single segment.

        An instance with no governance armed answers 200 with an empty `nodes` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the tree to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: governanceNodeCreate
      summary: Create a runtime tenancy node.
      description: |-
        Add a node to the tenancy tree at RUNTIME, without a config edit or a restart — the runtime plane's counterpart to the config-declared tree.

        `id` is the full '/'-joined path (`org:acme/team:ml`) and `kind` is org|team|user; `parent` is the containing node. The tree shape is what every subtree scope in this resource is evaluated against, so creating a node is what makes a scope able to name it.

        A 404 `feature_absent` means this deployment has no governance plane.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceNodeCreateRequest'
            example:
              id: $TOKENTRIAGE_NEW_NODE
              kind: team
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "id": "$(json_str "$TOKENTRIAGE_NEW_NODE")",
              "kind": "team"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NEW_NODE = os.environ["TOKENTRIAGE_NEW_NODE"]

            payload = {
              "id": NEW_NODE,
              "kind": "team"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const newNode: string = process.env.TOKENTRIAGE_NEW_NODE ?? "";

            const payload = {
              "id": newNode,
              "kind": "team"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/nodes/{id}:
    delete:
      operationId: governanceNodeDelete
      summary: Delete a runtime tenancy node (overrides cascade).
      description: |-
        Remove a runtime tenancy node. ★ PER-NODE OVERRIDES ATTACHED TO IT CASCADE — they are removed with it. That is deliberate and it is stated here because the alternative is worse: an orphaned override on a node that no longer exists is unreachable through every read and delete route in this API, and would silently re-arm if a node with the same id were ever created again.

        The id is a PATH REMAINDER, not a single segment, because a tenancy node id contains '/' — the full path is `…/nodes/org:acme/team:ml`. The OpenAPI parameter is documented under its clean name `id`.

        A 404 `feature_absent` means this deployment has no governance plane.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodeDeleteResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a tenancy node. Limits and grants attached to it go with it.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a tenancy node. Limits and grants attached to it go with it.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes/{ID}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a tenancy node. Limits and grants attached to it go with it.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes/${id}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: 'The node id to delete. A path REMAINDER: it may contain ''/'' (e.g. `org:acme/team:ml`). A path remainder: the value may itself contain ''/'' (e.g. a tenancy node id like org:acme/team:ml).'
        schema:
          type: string
  /tt/api/v1/governance/preview:
    post:
      operationId: governancePreview
      summary: Dry-run a proposed limit set over ledger history + live counters (approximation-labeled).
      description: |-
        Answers "what would this limit set have done?" against real ledger history and the live counters, WITHOUT arming anything. It mutates no state, refuses no live traffic, and reveals no secret. It is a POST because the proposed limit set is a body, not because it writes — hence `x-read-only` and the viewer floor.

        **THE ANSWER IS LABELED**, PER SECTION, AND THE LABELS ARE STRUCTURAL. Ledger-settled limits (budget_usd, quota_tokens, quota_requests) can be replayed EXACTLY against recorded history. Rate-class limits (rpm/tpm/concurrency) cannot: their windows are ephemeral and are gone, so that section carries a mandatory `rate_windows_approximated` label. Where neither is possible the response says CANNOT-ESTIMATE rather than producing a number. The labels are stamped by the response types themselves, so no handler bug can strip them and leave a bare figure looking authoritative.

        A proposed set that does not compile — an unknown limit type, an unknown algorithm, an incoherent attachment — is refused 422 `validation_failed` carrying the compiler's own message. It is never answered with a fabricated preview.

        A 404 `feature_absent` means the dry-run seam is not wired on this instance; the response explicitly implies NO verdict about the proposed limits.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-read-only: true
      security:
        - managementToken:
            - governance:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernancePreviewRequest'
            example:
              limits:
                - id: $TOKENTRIAGE_LIMIT_ID
                  type: budget_usd
                  attach:
                    node: $TOKENTRIAGE_NODE
                  amount_usd: 1
                  period: day
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernancePreviewResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/preview" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limits": [
                {
                  "id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
                  "type": "budget_usd",
                  "attach": {
                    "node": "$(json_str "$TOKENTRIAGE_NODE")"
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limits": [
                {
                  "id": LIMIT_ID,
                  "type": "budget_usd",
                  "attach": {
                    "node": NODE
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/preview",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limits": [
                {
                  "id": limitId,
                  "type": "budget_usd",
                  "attach": {
                    "node": node
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/preview`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/reconcile:
    get:
      operationId: governanceReconcile
      summary: Reconcile drift report (display-and-alert only; never an adopt affordance).
      description: |-
        Per-scope drift between the canonical, ledger-recomputed burn and whatever a distributed rollup claims.

        **THIS SURFACE IS DISPLAY-AND-ALERT ONLY**, AND THAT IS STRUCTURAL. There is no write route here and `adopted` is always false — the response shape cannot express an adopt. Reconciliation tells you the two numbers disagree and by how much; it never resolves the disagreement by overwriting the canonical ledger with a rollup, because the ledger is the system of record and a drift report is evidence, not authority.

        On a single-node / air-gapped instance it returns an honest empty report saying there is no distributed rollup to reconcile against and that the canonical ledgers are the sole system of record.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceReconcileResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/reconcile" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/reconcile",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/reconcile`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/refusals:
    get:
      operationId: governanceRefusals
      summary: Curated refusal-audit feed (newest first), cursor- or offset-paginated within the node subtree.
      description: |-
        Why requests were refused by governance, newest first: the refusal type, the limit that caused it, the node and key it was attributed to, the enforcement posture at the time, and the HTTP status the caller actually received.

        **ORDERING: NEWEST FIRST BY `ts`, AND ROWS SHARING A TIMESTAMP HAVE NO DEFINED RELATIVE ORDER.** That second half is a real limitation, not a formality: the feed is produced by walking the canonical decision log in reverse, so rows recorded in the same second come back in whatever order they were appended, and nothing sorts them afterwards. Two refusals in the same second are ordinary under load.

        This is WHY the cursor carries `req_id` alongside the timestamp: to IDENTIFY the boundary row, not to order it. A position that can only compare timestamps cannot tell two same-second rows apart and will skip one or repeat one across a page boundary while returning 200 — so the cursor locates the exact row it was minted from instead of inferring where it sits. The consequence for you: page boundaries are safe, but do not assume the relative order of same-timestamp rows is stable across calls, and do not derive a sequence number from it.

        **PAGINATION: TWO FORMS**, ONE POSITION. This operation answers the house cursor contract (`cursor` in, `page.next_cursor` + `page.has_more` out) AND the original offset block (`offset` in, `page.total` + `page.next_offset` out). They describe the same position in the same set, so you may follow either and may switch between them mid-feed. **`cursor` WINS when both are supplied** — a request carrying both has a fresh cursor and a stale offset, and honoring the offset would silently re-deliver or skip rows. `page.offset` in the response always reports the position the server actually paged from, so the body never claims a position it did not use.

        THE `offset` PARAMETER IS DEPRECATED (RFC 9745/8594) — it alone; `limit` is not, and neither is the `next_offset` response member. It still answers, and will keep answering until the sunset date; a response to a request that used one carries `Deprecation`, `Sunset` and a `Link; rel="deprecation"` header. Prefer `cursor`: an offset into a live, newest-first feed is read against a set that is still growing at the head, so rows shift under a paging client. Send no cursor for the first page and echo `page.next_cursor` back VERBATIM for each subsequent one; an unreadable cursor is a 400 naming the parameter, never a silent restart from the top.

        THIS IS THE CAPPED LIVE VIEW, not the archive. The feed is bounded at 1000 rows and `page.total` SATURATES there — it is the count within the bound, not the true subtree count. The full, uncapped, window-scoped feed is `governanceRefusalsExport`.

        NO KEY-EXISTENCE ORACLE: a 401 `invalid_key` refusal names no limit, even if the upstream record carried one. An authentication failure may not disclose which limits exist.

        An instance with no governance armed answers 200 with an empty `refusals` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-pagination:
        - cursor
        - offset
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: The subtree the feed — and its cursors and offsets — are scoped to. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Page size. DEFAULT 100; MAXIMUM 1000. A larger value is CLAMPED to the maximum rather than refused — and the clamp is OBSERVABLE, because `page.limit` in the response reports the size the server actually applied, so a client can always read back what it got. (Note the deliberate divergence from the newer list operations in this API, which REFUSE an over-cap limit with a 400: this feed shipped with the clamp and has live consumers, so changing it would be a breaking behaviour change on a published route.) A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default rather than being refused.'
          schema:
            type: integer
            format: int64
        - name: cursor
          in: query
          description: |-
            An OPAQUE page position: echo back a previous response's `page.next_cursor` VERBATIM, never construct one. Absent ⇒ the first page. It encodes the boundary row's timestamp AND its request id, because a position that cannot break a timestamp tie is not a total order and would skip or repeat rows. Opaque is an interop promise, not confidentiality — a cursor carries only the position, never a credential or a principal identity. An unreadable cursor is a 400 `bad_request` naming the parameter, never a silently-restarted-from-the-top page. Supplying `cursor` makes `offset` ignored.

            **A CURSOR IS SCOPED TO THE `node` IT WAS MINTED UNDER.** It records a POSITION, not the SET the position was taken in — so replaying one under a different `node` resolves it against the new subtree and returns a plausible page that means nothing in the sequence you were walking. RESTART the sequence (send no cursor) whenever you change `node`. This is not a confinement hole: rows are always re-fetched under the CURRENT request's `node`, so a cursor can never widen your scope or reach another tenant's rows — the failure mode is a wrong page for you, not somebody else's page. The server cannot yet detect the mistake on your behalf, which is why it is stated here.

            A STALE cursor — one whose boundary row has aged out of this bounded live view — does NOT fail. It resolves to the nearest surviving position at or older than the boundary instant and answers 200, which may RE-DELIVER a row you have already seen. That is deliberate and it is the opposite trade from an UNREADABLE cursor (a named 400): re-delivering a row is a duplicate you can detect by `req_id`, whereas skipping one is silent data loss. There is no header or member signalling that a repositioning happened, so a consumer that must not double-process should key on `req_id`.
          schema:
            type: string
        - name: offset
          in: query
          deprecated: true
          description: 'DEPRECATED — use `cursor`. Rows to skip within the already-subtree-scoped set (never the global pre-filter feed). It still answers until the sunset date and responses carry the RFC 9745 `Deprecation` and RFC 8594 `Sunset` headers when you use it. It is deprecated because an offset into a live newest-first feed is unstable: rows arriving at the head shift every later page, so a client paging by offset silently re-reads or misses rows. Ignored when `cursor` is supplied.'
          schema:
            type: integer
            format: int64
          x-deprecated-since: "2026-07-25T00:00:00Z"
          x-sunset: "2027-02-01T00:00:00Z"
          x-successor: governanceRefusals
      responses:
        "200":
          description: OK
          headers:
            Deprecation:
              description: RFC 9745 structured-field Date, e.g. `@1784937600` — when this operation's deprecated `offset` parameter was announced. Present ONLY on a response to a request that USED a deprecated parameter; a request paging by `cursor` carries none of these three headers.
              schema:
                type: string
            Link:
              description: RFC 8288 link with `rel="deprecation"`, anchored at the specific parameter (`#governanceRefusals-offset`) rather than at the document — a bare docs link on an operation with four parameters and one deprecation says something is going away and refuses to say what.
              schema:
                type: string
            Sunset:
              description: RFC 8594 HTTP-date, e.g. `Mon, 01 Feb 2027 00:00:00 GMT` — the date after which the deprecated `offset` parameter becomes removal-eligible. Sunset is always >= Deprecation (the RFC 9745 ordering rule, validated when the descriptor is registered). Absent when no removal date is set.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRefusalsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/refusals?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/refusals?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/refusals?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve:
    get:
      operationId: governanceSelfServeRead
      summary: A delegated admin's own subtree view (nodes/status/keys/refusals), scoped to their bindings; a read outside the subtree is refused 403.
      description: |-
        What a DELEGATED org- or team-admin sees: the same governance facts an operator sees, narrowed to the subtree(s) they actually administer.

        **READ THE AUTHORIZATION CAREFULLY** — THE FLOOR IS NOT THE GATE. The route's viewer floor is only a pre-filter. The real decision is a FAIL-CLOSED check against an admin-authored role binding: a caller with no binding administers nothing and is refused 403, and that refusal is audited so a probe is on the record. Bindings are admin-authored only — there is no route in this API by which a delegated admin can grant themselves one, or widen the one they have.

        `bindings` lists every subtree the caller administers, so a UI can offer a switcher. `node` drills into one of them; a node outside every binding is a WIDEN-403, never a 404 — hiding the attempt behind "not found" would also hide it from whoever reads the audit trail. With no `node`, the response covers the caller's first bound subtree.

        `keys` is the same metadata-only shape the operator surface serves; a delegated admin sees no more of a key than an operator does.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-delegation: subtree
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Drill into a sub-node WITHIN one of the caller's bindings; a node outside every binding is a widen-403, never a 404. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Cap the refusal-feed rows included in the view. DEFAULT 100; MAXIMUM 1000. A larger value is clamped to the maximum SILENTLY: this response carries no page block, so nothing on the wire reports the size the server actually applied. A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default.'
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceSelfServeResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve",
                method="GET",
                data=None,
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve`, {
              method: "GET",
              headers: {
                Cookie: `tt_session=${session}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve/keys:
    post:
      operationId: governanceSelfServeMint
      summary: Mint a virtual key within the caller's delegated subtree (secret revealed once); an owner node outside the subtree is refused 403.
      description: |-
        The delegated mint: an org- or team-admin creates a virtual key owned by a node INSIDE the subtree they administer.

        **THE VIEWER FLOOR ON THIS ROUTE IS A PRE-FILTER**, NOT THE AUTHORIZATION. Read `x-delegation: subtree` beside it: the real gate is a fail-closed check that the caller holds an admin-authored role binding whose subtree CONTAINS the requested owner node. A viewer with no binding clears the floor, reaches the handler, and is refused 403 there — and the privileged `keys:mint` scope this operation requires is not granted by a viewer-role token's expansion in the first place. Every denial is audited with the REAL acting user.

        The subtree check is prefix-safe: a binding on `org:acme` does not authorize `org:acme-evil`, and a traversal like `../` is rejected before the prefix comparison rather than after it.

        **THE SECRET IS SHOWN ONCE**, under exactly the same discipline as the operator mint: SHA-256 at rest, never logged, never audited (the row names the kid), never re-fetchable.

        The delegated write seam stamps the REAL acting principal into the key store's own audit trail — never a generic "dashboard" actor — so a delegated mint is attributable end to end to the human who performed it.

        A 404 `feature_absent` means the delegated write seam is not wired on this instance.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: keys:mint
      x-delegation: subtree
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
              scopes:
                models: []
                providers: []
                endpoints: []
                allow_passthrough: false
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve/keys" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "x-tt-csrf: $TOKENTRIAGE_CSRF" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]
            CSRF = os.environ["TOKENTRIAGE_CSRF"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": False
              }
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve/keys",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "x-tt-csrf": CSRF,
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";
            const csrf: string = process.env.TOKENTRIAGE_CSRF ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve/keys`, {
              method: "POST",
              headers: {
                Cookie: `tt_session=${session}`,
                "x-tt-csrf": csrf,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve/overrides:
    post:
      operationId: governanceSelfServeOverride
      summary: Set a bounded per-node override within the caller's delegated subtree, under the inherited template ceiling; out-of-subtree or over-ceiling is refused 403.
      description: |-
        The delegated override: an org- or team-admin adjusts a limit for a node INSIDE the subtree they administer, and only up to a ceiling somebody else set.

        **THE VIEWER FLOOR IS A PRE-FILTER** — read `x-delegation: subtree`. The real gate is the same fail-closed role-binding check the delegated mint uses, and every denial is audited with the real acting user.

        **BOUNDED MEANS BOUNDED, AND FOUR DIFFERENT THINGS ARE REFUSED 403.** The status alone cannot tell them apart, so check the definition in `governanceLimitsList`: (1) the target node is outside your delegated subtree; (2) the definition's `override_allowed` is FALSE, so this limit admits no per-node override from anyone; (3) the definition declares NO `override_max`, and an unbounded delegated raise is refused rather than treated as unlimited, because "no ceiling declared" is not a grant; (4) your `amount` exceeds that `override_max`. The ceiling is the one the compiler enforces, read from the armed definitions, so the delegated path can never exceed what the operator path would allow.

        A 422 `unsupported_override_class` is a DIFFERENT refusal with a different cause, and the two are easy to confuse: 403 case (2) means this limit is CONFIGURED not to accept overrides (`override_allowed: false`), while the 422 means its limit TYPE is structurally incapable of honouring one — `rpm`, `tpm` and `concurrency` freeze their cap into a per-window store when governance compiles, so a reload would silently drop the override. Overrides apply only to the ledger-settled types (`budget_usd`, `quota_tokens`, `quota_requests`). Refusing at the boundary matches the verdict the reload would reach, instead of a 200 that never applies.

        Like every override in this API, a success is COMPILE-BOUND and returns `pending_reload: true` — durable and audited now, enforcing after the next governance reload.

        A 404 `feature_absent` means the delegated write seam is not wired on this instance.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:write
      x-delegation: subtree
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideRequest'
            example:
              limit_id: $TOKENTRIAGE_LIMIT_ID
              node: $TOKENTRIAGE_NODE
              amount: 150000000000
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: unsupported_override_class.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve/overrides" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "x-tt-csrf: $TOKENTRIAGE_CSRF" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limit_id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "amount": 150000000000
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]
            CSRF = os.environ["TOKENTRIAGE_CSRF"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limit_id": LIMIT_ID,
              "node": NODE,
              "amount": 150000000000
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve/overrides",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "x-tt-csrf": CSRF,
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";
            const csrf: string = process.env.TOKENTRIAGE_CSRF ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limit_id": limitId,
              "node": node,
              "amount": 150000000000
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve/overrides`, {
              method: "POST",
              headers: {
                Cookie: `tt_session=${session}`,
                "x-tt-csrf": csrf,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/shadow:
    get:
      operationId: governanceShadow
      summary: Shadow-mode would-refuse counts per limit.
      description: |-
        The trust-ladder instrument: for each limit running in shadow mode, how many requests it WOULD have refused had it been enforcing. Every figure on this surface is counterfactual and is labeled shadow — none of these requests was refused, and no traffic was affected. It is the evidence an operator uses to decide whether a proposed cap is safe to arm.

        An instance with no previewer wired answers 200 with an honest empty (still shadow-labeled) set rather than an error: "no shadow limits are running" is a true and useful answer, where a 404 would not be.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the counts to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceShadowResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/shadow" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/shadow",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/shadow`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/status:
    get:
      operationId: governanceStatus
      summary: Per-node governance status (spend/quota/rate posture, forecast, overrun bound). Filterable by node subtree, posture, class, limit_type.
      description: |-
        The live per-node view of what governance is doing right now: for each tenancy node, every compiled limit with its posture, its consumption, a burn forecast where one is computable, and an honest bound on how far the figure may already be wrong.

        POSTURE is the four-state honesty ladder, and the node's headline posture is the LOUDEST across its limits: `alert_only` (a soft/shadow limit — it observes and never refuses), `enforcing` (a hard limit refusing at the cap), `fail_closed` (hard, and it refuses when the limiter itself is unavailable), `suspended` (armed to enforce, but the evaluator heartbeat is stale so enforcement has FAILED OPEN and traffic is flowing past the cap). `suspended` is the state an operator most needs to see, which is why it outranks the others for the badge.

        SPEND-CLASS limits report `burn`/`limit` as typed Money and count what they could not price in `unpriced_excluded`: unpriced rows are EXCLUDED from the dollar figure rather than folded in as $0, so burn is an understatement of a known size rather than a confident wrong number. Count-class limits report `observed`/`ceiling` instead; the irrelevant representation is absent, never a misleading zero.

        `overrun` is the B.6 approximate bound — tee lag, dropped rows, in-flight reservations, estimate skew, convergence lag — and `approximate: true` on it means exactly what it says: spend admitted by in-flight requests may not be reflected yet.

        The filters narrow at the LIMIT level, and a node whose limits all fail the filter is dropped entirely; each surviving node's headline posture is RECOMPUTED over the limits that were kept, so the badge can never reflect a limit the caller filtered out.

        An instance with no governance armed answers 200 with an empty `nodes` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the view to this tenancy subtree (e.g. org:acme or org:acme/team:ml). A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: posture
          in: query
          description: Keep only limits whose posture matches. Single-valued; absent ⇒ no constraint. A value outside this enum is refused `400 bad_request` naming the parameter and the legal values — never answered with an empty node list, which would be indistinguishable from an honest "nothing is in that posture".
          schema:
            type: string
            enum:
              - alert_only
              - enforcing
              - fail_closed
              - suspended
        - name: class
          in: query
          description: Keep only limits of this representation class — `spend` (dollar-denominated, reports Money) or `count` (tokens/requests/rate, reports raw counts). A value outside this enum is refused `400 bad_request` naming the parameter and the legal values.
          schema:
            type: string
            enum:
              - spend
              - count
        - name: limit_type
          in: query
          description: Keep only limits of this type. `budget_usd` is the sole spend-class type; the rest are count class. A value outside this enum is refused `400 bad_request` naming the parameter and the legal values.
          schema:
            type: string
            enum:
              - budget_usd
              - quota_tokens
              - quota_requests
              - rpm
              - tpm
              - concurrency
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceStatusResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/status" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/status",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/status`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/tenant-tokens:
    post:
      operationId: governanceTenantTokenMint
      summary: Mint a tenant-scoped viewer token (subtree-frozen; shown once).
      description: |-
        Issue a read-only credential that a tenant can use to see THEIR OWN subtree and nothing else — the mechanism behind giving a customer or an internal team visibility into their spend without giving them an account on this control plane.

        The token is FROZEN to one node subtree at mint. That freeze is not a filter applied at read time that a clever caller might widen: it is baked into the credential, and the read route refuses any node outside it with a 403.

        **THE TOKEN IS SHOWN ONCE.** The `token` field carries a live `tts_` credential, returned exactly once and never persisted (only its SHA-256 is stored), never logged, and never written to an audit row — the mint's audit row names the token's PUBLIC id and its frozen subtree, nothing more. `record` is the redacted register entry and has no field capable of holding the secret. Requires the privileged `keys:mint` scope.

        The request body is reflected into this document (`GovernanceTenantTokenMintRequest`) rather than described here, so the schema and the handler cannot drift apart. `expires_in` is a GO duration string, not an ISO 8601 one — see its field description.

        A 404 `feature_absent` means the share-token store is not open on this instance.

        Every branch of this endpoint audits exactly once, rejections included.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceTenantTokenMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceTenantTokenMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/tenant-tokens" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/tenant-tokens",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/tenant-tokens`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/tenant/{token}:
    get:
      operationId: governanceTenantRead
      summary: Read a tenant's own subtree via a tenant-scoped token (the token is the credential).
      description: |-
        The tenant-facing read: nodes, per-node limit status, keys (metadata only) and the recent refusal feed for the subtree the presented token is frozen to.

        This operation is PUBLIC at the authorization boundary because the token IS the credential — it is verified by the handler itself, and there is no session or management token involved. An unknown, revoked, expired, or wrong-kind token all return the SAME 404, so no probe can tell them apart — and so does a deployment whose share-token store is not open at all, deliberately: a distinct answer for that case would let an unauthenticated caller fingerprint the deployment before presenting any credential. This is the ONE governance operation that does not answer an absent dependency with `feature_absent` and a cure, because on a public route that cure is written for an operator and read by a stranger. The admin-authenticated mint still does.

        **THE WIDEN GUARD.** `node` drills DOWN within the token's frozen subtree. A node above it, beside it, or in another tenant entirely is refused 403 — deliberately NOT a 404, because a 404 would hide the attempt from the operator reading the audit trail. A blank `node` resolves to the frozen root. `scope_node` in the response always reports the freeze, so a tenant can see the boundary they are reading within.

        `read_only` is structurally always true: there is no write route on this surface.
      tags:
        - governance
      x-role-floor: public
      security: []
      parameters:
        - name: node
          in: query
          description: Drill into a sub-node WITHIN the token's frozen subtree. A node outside it is refused 403, never silently widened and never hidden behind a 404. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Cap the refusal-feed rows included in the view. DEFAULT 100; MAXIMUM 1000. A larger value is clamped to the maximum SILENTLY: this response carries no page block, so nothing on the wire reports the size the server actually applied. A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default.'
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceTenantViewResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/tenant/$TOKENTRIAGE_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/tenant/{TOKEN}",
                method="GET",
                data=None,
                headers={
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/tenant/${token}`, {
              method: "GET",
              headers: {
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: token
        in: path
        required: true
        description: 'The tenant-scoped viewer token''s SECRET value (`tts_…`), as returned once by `governanceTenantTokenMint`. This route is PUBLIC at the authorization boundary because the token IS the credential — it is verified by the handler itself and carries its own frozen subtree scope. It is redacted from every audit row and log line BEFORE the write, it is never echoed in a response or a problem body, and an unknown, revoked, expired or wrong-kind token all return the SAME 404 so no probe can distinguish them. This parameter''s value is a LIVE CREDENTIAL: it is redacted before any audit or log write and never echoed in a problem body (CA5).'
        schema:
          type: string
        x-credential: true
  /tt/api/v1/instances:
    get:
      operationId: instancesList
      summary: List the federation roster with each instance's reachability, staleness and pricing snapshot.
      description: |-
        `federated` reports whether any REMOTE peer is configured. When it is false the roster is this instance alone and `note` says so — never a fabricated fleet.

        COVERAGE AND PARTIALITY: each card carries `status` (`ok`, `unreachable`, `timeout`, `auth_failed`), `reachable`, `last_seen` and the peer's reported `pricing_snapshots`. When any peer is not `ok`, `note` names it and states that it is excluded from federated totals — the roster twin of `federation.exclusion_note` on a merged query, so partiality reads the same way on both surfaces.

        SECRETS: `token_env` is the NAME of an environment variable, never its value, and `token_present` is a presence flag computed on THIS server. There is no field on this response through which a bearer token can travel.

        A peer's pricing snapshots are surfaced here because a federated total spanning more than one snapshot trips the mixed-snapshot disclosure on the query result; this read is how an operator sees which instance is responsible.
      tags:
        - instances
      x-role-floor: viewer
      x-required-scope: instances:read
      security:
        - managementToken:
            - instances:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InstancesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/instances" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/instances",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/instances`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/metacost:
    get:
      operationId: metacostGet
      summary: 'The cost OF measurement: what this platform spent to measure its own routing quality.'
      description: |-
        The meter's own meter. Each source line is a REAL dollar computed from the canonical log — today the shadow duplicate spend joined from shadow-grade companions, and the response cache's own metered embedding spend. A source with no real spend produces NO line rather than a synthetic `$0`, and an unpriced component is excluded from the sum rather than folded in as zero. There is no oracle line because the oracle companion records a score and a label but no dollar: an absent line is the honest answer where an invented one would make an unmeasured spend look bounded.

        QUERY BUDGET: this is a heavy read under the §2.1 budget (a per-request deadline, a row cap, and a concurrency semaphore). When the budget bites, the answer is a PARTIAL 200 whose `provenance.truncated` is `true` — never a silently complete-looking result, and never a 503. Always read `provenance` before trusting a total.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MetacostResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/metacost" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/metacost",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/metacost`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/openapi.yaml:
    get:
      operationId: specGet
      summary: The generated OpenAPI 3.1 document this server obeys — the same bytes committed at docs/api/management.yaml.
      description: 'Served straight from the registry the router mounts from, so the contract you generate a client against is the contract the server enforces; two gates hold that in both directions (a byte-identical drift golden, and a live-mux test that issues every documented operation and strict-decodes the response into its documented type). The response carries an `ETag` derived from the document''s own bytes, so a poller can detect that the contract changed by comparing tags instead of re-parsing 100+ KB of YAML. Conditional requests are NOT honoured: `If-None-Match` is ignored and this operation always answers 200. That is a contract decision, not an omission — a 304 is a response this document has no way to describe (an operation declares a success shape and its problem types, and nothing else), so a client generated from it would route a real 304 into the problem+json arm and decode an empty body as an error. Availability follows `dashboard.docs_public`: viewer floor by default, public when an operator opens the reference.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          headers:
            ETag:
              description: 'A strong validator over the served document''s bytes: it changes if and only if the document does. Use it to detect a changed contract. It does NOT save the transfer — this operation does not answer conditional requests (see the description).'
              schema:
                type: string
          content:
            application/yaml:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/openapi.yaml" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/yaml"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/openapi.yaml",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/yaml",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/openapi.yaml`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/yaml",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
  /tt/api/v1/pricing/match:
    post:
      operationId: pricingMatch
      summary: 'Dry-run the price resolution for a provider/model pair: which source wins, which key, which stamp would be written, and the steps that got there.'
      description: |-
        The honesty affordance behind every priced row. It answers "if a request for this model arrived now, what would it be priced at, by WHICH source, and what provenance stamp would land on the ledger row?" — and it answers it by running the SAME resolver the request path runs, not a parallel re-implementation.

        **It mutates nothing** and is served over POST only because its input is a body rather than a path.

        **`winner` is one of three states and the third one matters.** `override` — an effective-dated override entry won. `table` — the base catalogue priced it. `miss` — NOTHING priced it, `priced` is false, `rates` is ABSENT rather than zero, and a real request for this pair would be recorded UNPRICED. That third state is the one this endpoint exists to make visible before it shows up as a `usd: null` in a report.

        **`steps` is the trace, and it is honest about degradation.** On a deployment with no overrides configured the first step says so in those words; the resolution then runs against the base table only, and no fabricated override branch is reported. It is always present, never null.

        **`at` selects the instant** because overrides are effective-dated: the same pair can resolve differently next Tuesday, and asking about next Tuesday is the point of a forward-dated override. RFC 3339 or `YYYY-MM-DD`; absent means now (UTC), which makes the response non-deterministic — pass `at` explicitly if you are comparing two runs.
      tags:
        - pricing
      x-role-floor: viewer
      x-required-scope: pricing:read
      x-read-only: true
      security:
        - managementToken:
            - pricing:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PricingMatchRequest'
            example:
              model: gpt-4o
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingMatchResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/pricing/match" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "model": "gpt-4o"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "model": "gpt-4o"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/pricing/match",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "model": "gpt-4o"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/pricing/match`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/pricing/overrides:
    get:
      operationId: pricingOverridesList
      summary: The effective-dated pricing-override artifact — active AND retired entries, with whether the listed artifact is the one the engine is actually pricing against.
      description: |-
        Lists the override artifact as it exists ON DISK, which is deliberately not the same thing as what the engine is pricing with. A create through `POST /pricing/overrides` appears here immediately, so a write→list round-trip is consistent; `overlay_active` is what tells you whether the entries are LIVE. When it is false the artifact is configured but the running overlay predates it, and a `note` says so — `pricing.overrides_file` is read at boot.

        **Retired entries are listed, and that is the contract.** The artifact is retire-not-delete: an entry that priced rows in March still explains those rows in December, so retiring flips `status` and keeps the record. A client filtering for current pricing should filter on `status == "active"` rather than expect the server to have forgotten.

        **An unconfigured deployment answers 200, not 404.** `configured: false` with an empty list and a note is the honest disabled state — "no overrides are configured" is a true and complete answer to "what overrides exist", so this operation does not report the feature absent. Its write twin does, because there is nothing for a write to land in.
      tags:
        - pricing
      x-role-floor: viewer
      x-required-scope: pricing:read
      security:
        - managementToken:
            - pricing:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingOverridesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/pricing/overrides" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/pricing/overrides",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/pricing/overrides`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: pricingOverrideWrite
      summary: Create or retire one effective-dated pricing override. Forward-only, retire-not-delete, audited on every branch — and it does not move live pricing until the next restart.
      description: |-
        A config-as-contract write: it emits an entry into the override ARTIFACT and audits it. The artifact is the record; the running overlay is built at boot, so **a 200 here is not a claim that live pricing changed** — `pending_restart` is always true and the response `note` says which restart is required. This is stated on the operation because the status code cannot say it and it is the single thing an operator most needs to know after a successful write.

        **`action: "create"` (the default) is forward-only.** `effective_from` is required, must be `YYYY-MM-DD`, and must not be in the past: the engine already refuses to apply an override retroactively, and this endpoint additionally refuses to AUTHOR a backdated record, so the artifact never contains a claim about a price that was not in force at the time. Both base rates must be present and greater than zero — a half-specified override would author a confident dollar out of a missing number, which is the failure this whole surface exists to prevent. Each refusal is a 422 `validation_failed` carrying an RFC 6901 pointer at the offending field.

        **`action: "retire"` does not delete.** It flips the matching ACTIVE entry's status, keeping the record so rows it already priced stay explicable. `effective_from` is optional here and disambiguates when one key carries several entries; no matching active entry is a 404.

        **Every branch writes an audit row, including the refused ones** (`pricing.override`, with the outcome as its result) — "who tried to change a price, and what did the server say" is a question the trail exists to answer, and a rejected attempt is the interesting half of it. All NINE branches: a malformed body, a missing key, an unreadable or unwritable artifact, each validation refusal, an unknown action, a retire with no match, and the success.

        **Concurrency, stated because this endpoint does not serialise it.** The write is a read-modify-write over one artifact and there is no lock: two creates issued at the same instant can both read the pre-write file, and the second write wins — the first entry is LOST. What cannot happen is a torn or partial artifact: the file is replaced by an atomic temp-file-plus-rename and nothing is persisted before that single operation, so a concurrent reader always sees one complete version and a REJECTED write leaves the file byte-unchanged. Treat override authoring as a single-operator activity, or read back with `GET /pricing/overrides` and confirm your entry is present.

        **There is no dry-run counterpart and the reason is structural**, not an omission: a preview of this write would report an artifact append, and the write's effect on pricing is already deferred to the next restart, which is a stronger safety property than a preview. `GET /pricing/snapshot?view=diff` shows the override-vs-table position before and after, and `POST /pricing/match` resolves any key at any instant.
      tags:
        - pricing
      x-role-floor: operator
      x-required-scope: pricing:write
      security:
        - managementToken:
            - pricing:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PricingOverrideRequest'
            example:
              key: $TOKENTRIAGE_PRICING_KEY
              action: create
              effective_from: "2099-01-01"
              in: 0.000001
              out: 0.000004
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingOverrideResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/pricing/overrides" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "key": "$(json_str "$TOKENTRIAGE_PRICING_KEY")",
              "action": "create",
              "effective_from": "2099-01-01",
              "in": 0.000001,
              "out": 0.000004
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            PRICING_KEY = os.environ["TOKENTRIAGE_PRICING_KEY"]

            payload = {
              "key": PRICING_KEY,
              "action": "create",
              "effective_from": "2099-01-01",
              "in": 0.000001,
              "out": 0.000004
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/pricing/overrides",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const pricingKey: string = process.env.TOKENTRIAGE_PRICING_KEY ?? "";

            const payload = {
              "key": pricingKey,
              "action": "create",
              "effective_from": "2099-01-01",
              "in": 0.000001,
              "out": 0.000004
            };

            const res: Response = await fetch(`${base}/tt/api/v1/pricing/overrides`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/pricing/snapshot:
    get:
      operationId: pricingSnapshot
      summary: The embedded price table's provenance stamp and size, optionally the table itself or its diff against the configured overrides.
      description: |-
        The cheap read by default: the `pricing_snapshot` string this deployment stamps onto every priced ledger row (`litellm@2026-07-14#179affb`), the vendoring provenance behind it — upstream catalogue, fetch date, the SHA-256 of the raw vendored file, and the generator that compiled it — and how many models it prices.

        **Two opt-in expansions, because the default answer should stay small.** `models=true` returns the WHOLE compiled table, which is thousands of entries; it is the form to use when you are diffing catalogues, not the form to poll. `view=diff` returns every override entry rendered against the base table it layers over, including the retired ones.

        **What `view=diff` does NOT do, stated so its emptiness is never misread.** It diffs OVERRIDES against the TABLE. It is not a table-vs-table diff between two catalogue snapshots: a second snapshot is not resident at runtime, so there is nothing to diff against. Asking for the diff ALWAYS produces the `diff` key — as `[]` when there is nothing to show, never as an absent field that reads identically to not having asked — and an empty one always carries a `note` naming which kind of empty it is: no overrides configured at all, or a live overlay whose artifact holds no entries. The second is the state a freshly-configured `pricing.overrides_file` is in.

        `overlay_active` is the field to check before trusting the diff as a description of LIVE pricing: the override overlay is built at boot, so an artifact written since the last restart is listed and diffed but is not yet what the engine prices against.
      tags:
        - pricing
      x-role-floor: viewer
      x-required-scope: pricing:read
      security:
        - managementToken:
            - pricing:read
        - session: []
      parameters:
        - name: models
          in: query
          description: Include the full compiled model table under `models` (`models=true`, or the legacy `models=1` the shipped dashboard sends). Absent or false omits it — the table is thousands of entries and the stamp is what most callers want.
          schema:
            type: boolean
        - name: view
          in: query
          description: '`view=diff` adds the override-vs-table diff under `diff`. Any other value is ignored rather than refused, because this parameter selects an ADDITIVE section of an otherwise complete response — there is no partial answer for it to produce.'
          schema:
            type: string
            enum:
              - diff
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingSnapshotResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/pricing/snapshot" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/pricing/snapshot",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/pricing/snapshot`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/pricing/unpriced:
    get:
      operationId: pricingUnpriced
      summary: 'The unpriced triage: the distinct pricing keys this deployment could not price, with the row and token counts behind each (every cost here is UNPRICED, never 0).'
      description: |-
        The surface behind every `usd: null` on the rest of the API. It groups the ledger by `cost.pricing_key` over the rows that need pricing and reports, per key, how many rows and how many tokens are affected — most-affected first, so the list doubles as a work queue.

        **Every `cost_usd` on this response is the UNPRICED Money value — `usd` null — and that is a deliberate choice, not an artifact of empty data.** The cohort has two halves. The first is authoritative: rows whose `cost.unpriced` flag is set, written since the flag existed. The second is HEURISTIC: rows written BEFORE that flag existed, identified as `estimated && usd == 0` — a pricing miss recorded at the time as a confident-looking estimated $0. Aggregating the second half faithfully would emit `{usd: 0, estimated: true}`, which would render a pricing MISS as a real, nearly-free dollar on the one surface built to surface misses. So both halves render UNPRICED, and `cohort` states the heuristic caveat in prose on every response rather than leaving it to be inferred from row counts.

        **`rows` and `tokens` are the numbers to act on.** They are real counts, not dollars, and they are what tells you whether a missing price is a rounding error or the reason your cost report is wrong.

        **Partiality is disclosed, never silent.** When the query budget is exhausted — the deadline, the row cap, or a full concurrency semaphore — the response is a 200 whose `provenance.truncated` is true and whose `rows` is an empty array, not a 503 and not a short list presented as complete.
      tags:
        - pricing
      x-role-floor: viewer
      x-required-scope: pricing:read
      security:
        - managementToken:
            - pricing:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricingUnpricedResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/pricing/unpriced" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/pricing/unpriced",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/pricing/unpriced`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/problems:
    get:
      operationId: problemTypesList
      summary: 'The CLOSED problem-type registry as JSON: every `type` this API can emit, with its status and its permitted extension members.'
      description: 'Closed by construction — types are added by registration in the server''s source, never at runtime — so a client may treat an unrecognised `type` as "a server I do not understand" rather than as a transient condition, and may exhaustively switch on this list. Each entry''s `type` is the exact relative URI an error body carries; dereferencing it on this host renders the same row as a page. Deliberately unpaginated: the registry is closed and small, and the question it answers ("what can this API return?") has no correct partial answer.'
      tags:
        - meta
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemTypesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/problems" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/problems",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/problems`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/providers:
    get:
      operationId: providersList
      summary: 'The configured upstreams: dialect, base URL, credential REFERENCE and presence, prompt-cache posture, and the configured cooldown policy.'
      description: |-
        One card per upstream, sorted by id, read from the RUNNING config where a control plane is wired (so a hot reload is reflected) and otherwise from the configured config file — `source` says which.

        **No secret crosses this boundary, structurally.** `api_key_env` is the env var's NAME; `configured` and `key_status` report only whether that variable is SET, from the boolean `os.LookupEnv` returns — its string return is never bound on this path, so there is no value to leak, log or audit. `key_status` is `configured` (named and set), `missing` (named and unset — the actionable state) or `inbound` (no key configured; the caller's own Authorization header is forwarded as-is). Multi-part credentials (`aws_sigv4`, `gcp_oauth`) project the same way under `auth_refs`: one NAME and one boolean per part.

        **`auth_scheme` is INFERRED from the config block's shape and is absent when it cannot be.** A credentials-file reference means `gcp_oauth`; AWS key parts mean `aws_sigv4`. The bearer-vs-header distinction for a single-key upstream is not inferable from config alone, so the field is omitted rather than guessed — `key_status` already reports that credential's presence.

        **`health` is always `unknown`, and that is the honest value.** Live health and cooldown counters live in the proxy's request path, which the management plane cannot reach; `cooldown` is therefore the CONFIGURED policy, not a live state. Reporting a green health this server cannot observe would be the most consequential lie on this surface.

        **An unreachable config answers 200 with an empty list and a note**, not an error: "which upstreams are configured?" has a true empty answer, and inventing providers is the one thing worse than reporting none.
      tags:
        - providers
      x-role-floor: viewer
      x-required-scope: providers:read
      security:
        - managementToken:
            - providers:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvidersResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/providers" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/providers",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/providers`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/providers/{id}:
    get:
      operationId: providerGet
      summary: One upstream's card, by its configured name.
      description: |-
        The same C9-safe projection `GET /providers` returns, for a single upstream — see that operation for what every field means and for why no credential value can appear in any of them.

        Unlike the list, this operation reports `feature_absent` when the running config cannot be read at all: asked about a SPECIFIC upstream in a deployment that cannot see its own config, there is no true answer to give, and an empty one would read as "that upstream does not exist". An upstream the config genuinely does not name is `not_found`. The two are different problem types on purpose.
      tags:
        - providers
      x-role-floor: viewer
      x-required-scope: providers:read
      security:
        - managementToken:
            - providers:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderCard'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/providers/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/providers/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/providers/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: An upstream's configured name — the key under `upstreams:` in the running config, which is the same value `GET /providers` reports as `providers[].id`. A name this deployment does not configure is 404 `not_found`; a deployment whose running config cannot be read at all is 404 `feature_absent`, which is a different problem type precisely so a client can tell "no such upstream" from "this server cannot see its own config".
        schema:
          type: string
  /tt/api/v1/providers/{id}/probe:
    post:
      operationId: providerProbe
      summary: Operator-triggered upstream probe. In this build it performs NO network call and reads no key value — it reports what a real probe would use, and audits the attempt.
      description: |-
        **This operation does not do what its name promises, and it says so rather than pretending.** A live probe would issue a request to the upstream using the referenced credential; that is not implemented in this build. What this endpoint does is report the env var a real probe WOULD use and whether it is currently set (`probed: false`, with the full disclosure in `note`), and record an audit row for the operator who asked. No upstream request is made and no key VALUE is read — not even here, where an argument for reading one would be easiest to make.

        It is a write in the authorization sense rather than the state sense: it is floored at operator and scoped `providers:write` because triggering upstream contact is an operator action, and because the audit row is the point. When a real probe lands, the contract it lands into is already the honest one — `probed` becomes true and the note goes away.

        It takes no request body: the upstream is the path parameter.
      tags:
        - providers
      x-role-floor: operator
      x-required-scope: providers:write
      security:
        - managementToken:
            - providers:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderProbeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/providers/$TOKENTRIAGE_ID/probe" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/providers/{ID}/probe",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/providers/${id}/probe`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: An upstream's configured name — the key under `upstreams:` in the running config, which is the same value `GET /providers` reports as `providers[].id`. A name this deployment does not configure is 404 `not_found`; a deployment whose running config cannot be read at all is 404 `feature_absent`, which is a different problem type precisely so a client can tell "no such upstream" from "this server cannot see its own config".
        schema:
          type: string
  /tt/api/v1/query:
    post:
      operationId: analyticsQuery
      summary: 'Aggregate the ledger: group-by and/or time-bucketed metrics over a filtered window (a heavy READ served over POST).'
      description: |-
        The request body is the query, not a side effect: `filter` is the closed filter AST, `group_by` takes at most two dimensions natively, `bucket` turns the result into a time series, and `metrics` names values from the pinned vocabulary (`cost_usd`, `requests`, `tokens_by_type`, `savings`, `p95_cost`, `p99_ttft`). A metric, dimension or filter field outside its documented domain is a 400 naming the failing input — never a silently dropped clause.

        Dollar metrics resolve to typed `Money`/`Savings`, so an unpriced group's `usd` is `null` rather than `0`, and `savings` cannot be marshalled at all without its `basis` and `verdict_ref`.

        QUERY BUDGET: this is a heavy read under the §2.1 budget (a per-request deadline, a row cap, and a concurrency semaphore). When the budget bites, the answer is a PARTIAL 200 whose `provenance.truncated` is `true` — never a silently complete-looking result, and never a 503. Always read `provenance` before trusting a total.

        FEDERATION: when this deployment is configured with peer instances, this operation fans out and MERGES the fleet's answers, and the response carries an additional `federation` member. It is ABSENT on a single-instance deployment — its absence is the signal that the result came from one instance — and every group key gains an `instance` dimension when it is present.

        ★ READ `federation.excludes` BEFORE TRUSTING ANY TOTAL. `provenance.truncated` reports only THIS instance's budget state and CANNOT see a dead peer, so a merged result whose unreachable peer contributed nothing still reports `truncated: false`. The peers dropped from the totals are named in `federation.excludes`, the reason is rendered in `federation.exclusion_note`, and each peer's own status is on its card in `federation.peers`. A total with a non-empty `excludes` is a PARTIAL total, however complete the provenance block looks.

        An EMPTY `excludes` is not by itself proof of completeness. In one state — a fan-out refused by the query budget BEFORE any peer was contacted — no peer can be named because none was reached, so `excludes` and `peers` are both empty while NOTHING is included, local or remote. That state sets `exclusion_note` and `provenance.truncated: true`, which is why `truncated` remains a required read even though it cannot see a dead peer. The two fields answer different questions: `excludes` says WHICH instances are missing, `truncated` says THIS answer is incomplete.

        An INVALID query is a 400 on a federated deployment exactly as it is on a single-instance one: a remote peer failing is a fleet degrade disclosed through `federation`, but this instance refusing the request is a client error and is answered as one. A fault in this instance's own leg is neither — it is a 500, and its peer card carries `local_error` rather than a network status that could not honestly describe an in-process query.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      x-read-only: true
      security:
        - managementToken:
            - analytics:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/QueryRequest'
            example:
              group_by:
                - http.provider
              metrics:
                - cost_usd
                - requests
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/query" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "group_by": [
                "http.provider"
              ],
              "metrics": [
                "cost_usd",
                "requests"
              ]
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "group_by": [
                "http.provider"
              ],
              "metrics": [
                "cost_usd",
                "requests"
              ]
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/query",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "group_by": [
                "http.provider"
              ],
              "metrics": [
                "cost_usd",
                "requests"
              ]
            };

            const res: Response = await fetch(`${base}/tt/api/v1/query`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/recommendations:
    get:
      operationId: recommendationsList
      summary: The detected recommendations, with the scan and index watermarks that say how current the answer is.
      description: |-
        ★ AN EMPTY LIST IS AMBIGUOUS UNLESS YOU READ THE WATERMARKS, and this response is shaped so you can. `recommendations: []` beside a scan that has run means "nothing was detected"; the same empty list beside a scan that has not run yet means "nobody has looked". Those are different facts and a caller acting on the first when the second is true concludes the deployment is optimal. The list is an empty ARRAY rather than null, so a strict client parser does not throw on the honest empty case.

        ★ PROVENANCE NAMES THE INDEX POSITION THE SCAN READ THROUGH. A recommendation is derived from ledger volume, so "which rows did this see" is part of the answer. Two responses with the same artifacts and different watermarks are making different claims about how much traffic supports them.

        ★ THE MONEY ON AN ARTIFACT OBEYS THE ENVELOPE. A projected saving is a `Money` — `usd` is null where the underlying rows could not be priced — and it carries its basis and verdict reference rather than standing as a bare figure. A recommendation whose saving is unprovable says so; it does not quote a number it cannot support.
      tags:
        - recommendations
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecommendationsResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/recommendations" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/recommendations",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/recommendations`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/recommendations/{id}:
    get:
      operationId: recommendationGet
      summary: One recommendation artifact by id, with its status, its projected effect and its applied history.
      description: |-
        The durable single-artifact read. The id is stable across scans, so it can be held in a dashboard URL, quoted in a ticket, or carried by an automation between the preview and the apply.

        ★ THE ARTIFACT CARRIES ITS OWN HISTORY. A recommendation that has been applied retains the version that recorded it — the audit id, the config generation, the diff hash, and the tier that was in force beforehand. The prior tier is frozen at apply time on purpose: once the config has moved, the realized-measurement path can no longer recover what the deployment used to be doing, so the value has to be captured at the moment it is still true.

        ★ AN UNKNOWN ID IS 404, INCLUDING FOR AN ARTIFACT THAT HAS AGED OUT. Recommendation artifacts are bounded, so a very old id can stop resolving. That is reported as a plain not-found rather than as a distinct "expired" state, because the two are the same fact from a caller's position: there is nothing here to act on.
      tags:
        - recommendations
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Recommendation'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/recommendations/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/recommendations/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/recommendations/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The artifact id, as served in the list response. Path parameter.
        schema:
          type: string
  /tt/api/v1/recommendations/{id}/apply:
    post:
      operationId: recommendationApply
      summary: Compile this recommendation into a config candidate and apply it — or, with preview, stop at the diff and write nothing.
      description: |-
        ★ SEND `{"preview": true}` FIRST. THE TWO CALLS RETURN THE SAME SHAPE. A preview compiles the candidate, validates it, diffs it against the running config and then writes nothing; the response is the committed-apply shape with `applied: false`. So a client can render the preview and the result through one code path, and an operator sees the exact diff before authorising it.

        ★ PASS BACK THE `generation` THE PREVIEW REPORTED, as `expected_generation`. That turns the apply into a compare-and-swap: if the config moved between the preview and the apply, the write is refused as a generation conflict carrying the current generation, rather than committing a diff nobody looked at. Omitting it is supported and means "apply at whatever generation you read" — which is the right choice for an unattended caller and the wrong one behind a human who was shown a diff.

        ★ THE REFUSALS HAPPEN BEFORE ANY WRITE, AND THEY ARE ANSWERS RATHER THAN FAULTS. A recommendation in a state that does not admit an apply is refused naming the state. An advisory artifact with no compilable candidate is refused carrying its recipe, so the operator gets the manual steps rather than a dead end. A candidate identical to the running config is refused rather than applied — an apply that writes an identical file still bumps a generation and files an audit row claiming a change, and the trail keeps that forever. A candidate touching a restart-required path is refused listing those paths, because hot-applying it would leave the running process disagreeing with the file on disk.

        ★ ONE OUTCOME IS DELIBERATELY REPORTED AS A 500 THAT SAYS THE CHANGE DID LAND. If the config is applied and the artifact then fails to record it, the response says so in both halves — the change is live, at the named generation and audit id, and the artifact did not update. Reporting a plain failure there would be the opposite of what happened and would invite a retry that applies the change twice.

        ★ OPERATOR FLOOR, CSRF-GATED, AND AUDITED ON THE REFUSED BRANCHES TOO — a rejected apply leaves a row behind, which is usually what a later review is looking for.
      tags:
        - recommendations
      x-role-floor: operator
      x-required-scope: config:apply
      security:
        - managementToken:
            - config:apply
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecommendationApplyRequest'
            example:
              preview: false
              expected_generation: 0
              ttl: ""
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecommendationApplyResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "409":
          description: 'Problem: conflict, generation_conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: restart_required, validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/recommendations/$TOKENTRIAGE_ID/apply" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "preview": false,
              "expected_generation": 0,
              "ttl": ""
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            payload = {
              "preview": False,
              "expected_generation": 0,
              "ttl": ""
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/recommendations/{ID}/apply",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const payload = {
              "preview": false,
              "expected_generation": 0,
              "ttl": ""
            };

            const res: Response = await fetch(`${base}/tt/api/v1/recommendations/${id}/apply`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: |-
          The artifact id to apply, as served in the list response. Path parameter.

          It is the SAME id the preview was rendered against, and passing a different one is the mistake this parameter's description exists to prevent: the diff an operator approved belongs to one artifact, and applying it under another id applies a change nobody looked at.
        schema:
          type: string
  /tt/api/v1/recommendations/{id}/dismiss:
    post:
      operationId: recommendationDismiss
      summary: Dismiss this recommendation with a required reason, recorded on the artifact and in the audit trail.
      description: |-
        ★ THE REASON IS REQUIRED, AND A BLANK ONE IS REFUSED WITH 400. It is retained on the artifact, so it is what a later reader — or the same operator in three months — has to go on when the detector surfaces the same opportunity again. A dismissal with no reason is indistinguishable from one nobody remembers making. The refusal happens here rather than at the store's own contract check, so the caller gets an actionable 400 instead of a 500, and so the trail does not have to record an attempt that could not have succeeded.

        ★ DISMISSING IS A LIFECYCLE TRANSITION, SO IT CAN BE REFUSED AS A CONFLICT. An artifact in a state the lifecycle does not admit a dismissal from — one already APPLIED, or SUPERSEDED by a later scan — answers 409 naming the state it is in. The refusal is audited alongside the successes.

        ★ RE-DISMISSING AN ALREADY-DISMISSED ARTIFACT IS ACCEPTED, AND IT REPLACES THE RECORDED REASON. This is stated because the sentence above invites the opposite guess, and because the difference is one a caller can act on: a dismissal that timed out client-side is safe to repeat, but a second dismissal carrying a DIFFERENT reason overwrites the first rather than being refused, and the artifact then reads as though the later reason were the one the original decision was made on. Repeat the same reason on a retry.

        ★ DISMISSING SUPPRESSES THIS ARTIFACT, NOT THE DETECTION. If the underlying condition persists, a later scan can raise the opportunity again as a new artifact — which is why the reason matters, and why this is not spelled "ignore forever". A permanent suppression is a configuration decision rather than a per-artifact one.

        ★ THE RESPONSE CARRIES THE POST-TRANSITION STATUS, read back from the store rather than assumed from the request, so a client can reconcile without a follow-up read.
      tags:
        - recommendations
      x-role-floor: operator
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecommendationDismissRequest'
            example:
              reason: not worth taking on this workload — the slice is being retired next sprint
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecommendationDismissResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "409":
          description: 'Problem: conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/recommendations/$TOKENTRIAGE_ID/dismiss" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "reason": "not worth taking on this workload — the slice is being retired next sprint"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            payload = {
              "reason": "not worth taking on this workload — the slice is being retired next sprint"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/recommendations/{ID}/dismiss",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const payload = {
              "reason": "not worth taking on this workload — the slice is being retired next sprint"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/recommendations/${id}/dismiss`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: |-
          The artifact id to dismiss, as served in the list response. Path parameter.

          Dismissing suppresses THIS artifact, not the detection: if the underlying condition persists, a later scan raises a NEW artifact with a new id rather than reviving this one.
        schema:
          type: string
  /tt/api/v1/recommendations/{id}/evidence:
    get:
      operationId: recommendationEvidence
      summary: 'The drill-through: the proof this recommendation was derived from, the sample rows behind it, and a link to the slice''s own evidence.'
      description: |-
        Answers "why does the product think this?" — which is the question that decides whether an operator applies a config change or ignores it.

        ★ THE PROOF TRAVELS VERBATIM OFF THE ARTIFACT RATHER THAN BEING RECOMPUTED. Recomputing it at read time would show the proof as it is NOW, against traffic that has moved on, while the recommendation was derived from the traffic as it WAS. Those diverge, and a drill-through that quietly showed the current version would make a stale recommendation look freshly justified. What you read here is what the detector saw.

        ★ THE SAMPLE ROWS ARE A SAMPLE, AND THEY ARE LABELLED AS ONE. They are enough to recognise the pattern, not the full population the figure was computed over; the deep link to the slice's own evidence is the path to the whole set. Summing the samples will not reproduce the artifact's totals and is not meant to.

        ★ THE MONEY ENVELOPE APPLIES HERE TOO. Sampled rows that could not be priced carry `usd: null`, so a drill-through over a partly-unpriced slice looks partly unpriced rather than partly free.
      tags:
        - recommendations
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecommendationEvidenceResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/recommendations/$TOKENTRIAGE_ID/evidence" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/recommendations/{ID}/evidence",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/recommendations/${id}/evidence`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The artifact id. Path parameter.
        schema:
          type: string
  /tt/api/v1/reconcile/imports:
    post:
      operationId: reconcileImport
      summary: Import a provider export, match it against the ledger, and return the delta report.
      description: |-
        The request is `multipart/form-data` with a `format` field naming a registered parser and a `file` part carrying the export. An upload over 25 MiB is refused **413 `payload_too_large`** naming the limit — never a partial parse. The registered parser names are advertised by `reconcileReportsList` so a client never has to guess one.

        The import is stored as a provenance-stamped artifact — its `file_sha256` pins exactly which bytes produced the report — and the delta report is returned in the same response.

        HONESTY, and read this before trusting the residual: the report never forces the ledger to agree with the invoice. Each (model, day) slice absorbs what it can into NAMED explanations and reports the remainder as `unexplained`. Two properties of that figure are not visible from its own fields. When `ledger_total` carries `estimated: true`, the residual is arithmetic over ESTIMATED dollars even though `unexplained` itself reads `estimated: false` — read the totals it was derived from, not only its own flag. And a model/day the invoice never mentions yields `invoice_usd: {usd: 0}`, a REAL zero meaning "billed nothing", not an UNPRICED. Unpriced ledger rows are EXCLUDED from `ledger_total` and counted in `unpriced_rows`, so a reconciliation can never be made to balance by treating an unpriceable row as $0.

        BUDGET: matching reads the WHOLE ledger, so ADMISSION is bounded by the same query budget every heavy off-path read uses. A request that cannot acquire a slot before its deadline is refused 503 `budget_exceeded` and audited as rejected; it is genuinely transient and correctly retriable. Note the bound is on admission, not on computation: once a slot is held the scan runs to completion.

        AUDIT: every import ATTEMPT writes exactly one audit row, on EVERY branch — accepted, refused for budget, or rejected for size, a missing format, an empty upload or a parse failure — so an attempted import is on the record whether or not it succeeded.
      tags:
        - reconcile
      x-role-floor: operator
      x-required-scope: reconcile:write
      security:
        - managementToken:
            - reconcile:write
        - session: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              description: Multipart form — parts documented in the operation description.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconcileArtifact'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IMPORTS an invoice into the reconciliation store, changing what
            # !! reconciliation reports.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/reconcile/imports" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -F "file=@$TOKENTRIAGE_BODY_FILE"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IMPORTS an invoice into the reconciliation store, changing what
            # !! reconciliation reports.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            # multipart/form-data: one file part named "file".
            BOUNDARY = "----tokentriage-sample"
            with open(os.environ["TOKENTRIAGE_BODY_FILE"], "rb") as fh:
                part = fh.read()
            body = (
                f"--{BOUNDARY}\r\n"
                'Content-Disposition: form-data; name="file"; filename="upload.csv"\r\n'
                "Content-Type: text/csv\r\n\r\n"
            ).encode() + part + f"\r\n--{BOUNDARY}--\r\n".encode()

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/reconcile/imports",
                method="POST",
                data=body,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": f"multipart/form-data; boundary={BOUNDARY}",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! IMPORTS an invoice into the reconciliation store, changing what
            // !! reconciliation reports.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            import { readFile } from "node:fs/promises";

            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const form = new FormData();
            form.set(
              "file",
              new Blob([await readFile(process.env.TOKENTRIAGE_BODY_FILE ?? "")], { type: "text/csv" }),
              "upload.csv",
            );

            const res: Response = await fetch(`${base}/tt/api/v1/reconcile/imports`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
              body: form,
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/reconcile/reports:
    get:
      operationId: reconcileReportsList
      summary: List the stored reconciliations and the import formats this deployment accepts.
      description: |-
        `reports` holds every stored reconciliation artifact with its full delta report; `formats` holds the registered parser names `reconcileImport` will accept here, so an import picker is built from this one read rather than from a hard-coded list that can drift from the deployment.

        A deployment with no reconciliation store answers 404 `feature_absent` carrying the cure. It does NOT answer an empty list: `reports: []` means "nothing has been imported", and returning that for "this deployment cannot import anything" would be a lie a client has no way to detect.
      tags:
        - reconcile
      x-role-floor: viewer
      x-required-scope: reconcile:read
      security:
        - managementToken:
            - reconcile:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconcileReportsResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/reconcile/reports" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/reconcile/reports",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/reconcile/reports`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/reconcile/reports/{id}:
    get:
      operationId: reconcileReportGet
      summary: Read one stored reconciliation artifact and its delta report.
      description: '`id` is an artifact id from `reconcileReportsList`. An unknown id is a 404 `not_found`; a deployment with no reconciliation store at all is a 404 `feature_absent` carrying the cure, so a client can tell "this report does not exist" from "this deployment does not reconcile" — a distinction the shipped surface collapsed into one `not_found`.'
      tags:
        - reconcile
      x-role-floor: viewer
      x-required-scope: reconcile:read
      security:
        - managementToken:
            - reconcile:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReconcileArtifact'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/reconcile/reports/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/reconcile/reports/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/reconcile/reports/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The stored reconciliation artifact id, as returned by reconcileReportsList.
        schema:
          type: string
  /tt/api/v1/replay:
    post:
      operationId: replayRun
      summary: Re-decide logged rows under a draft policy and return the aggregated decision/cost diff (a labeled simulation, never a quality claim).
      description: |-
        The draft is parsed through the SAME strict loader an apply uses, so a replay previews exactly what an apply would accept and an invalid draft returns the loader's own messages verbatim as `validation_failed` field errors.

        **It is a simulation and says so structurally.** `kind` is always `decision_cost_simulation` and `approximations` always carries both `fixed_usage` (the logged token usage is replayed unchanged — a rerouted request would in reality produce different output) and `recorded_runtime_state`. Neither can be dropped: the response type refuses to marshal without them.

        **Two refusals rather than a degenerate answer.** A well-formed draft that references an input replay cannot reconstruct, and a draft whose replayed rows were PREDOMINANTLY unevaluable, are both 422 `validation_failed` naming the counts and the missing inputs — not a confident aggregate over rows that could not be re-decided.

        **The cost delta is typed, and there are TWO of it.** `would_cost_delta` is a Money value whose `usd` is null when no replayed row priced, so an all-unpriced simulation reads as UNPRICED rather than as "$0.00, no change"; `would_cost_delta_usd` is the bare-number form the shipped dashboard still reads, and it reports 0 in exactly that case because a float cannot express it. **Prefer `would_cost_delta`** — at the response level and inside `by_rule`. Pinned, degraded and unpriced rows never contribute to either, and are each disclosed by count.

        **The response is a labeled SIMULATION, never a quality claim.** `kind` and both `approximations` are stamped by the server on every response and cannot be absent. (That sentence belongs on the response SCHEMA and is carried here instead: a pinned custom-marshaled type's schema description is generated from the pin, so a `RegisterSchemaDoc` on it is dropped — see the AP1-03 spec-patch note in docs/ap_wave1_report.md.)

        **Cost of the read, stated:** the draft is replayed over the JOINED ledger, which is loaded WHOLE. The per-request deadline and row cap bound admission to this operation, not the scan itself, so a large ledger makes this a long call rather than a truncated one.
      tags:
        - evidence
      x-role-floor: operator
      x-required-scope: evidence:write
      x-read-only: true
      security:
        - managementToken:
            - evidence:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReplayRequest'
            example:
              policy_yaml: |
                version: 1
                mode: shadow
                upstreams:
                  anthropic:
                    base_url: https://api.anthropic.com
                    dialect: anthropic
                    api_key_env: ANTHROPIC_API_KEY
                tiers:
                  - name: cheap
                    upstream: anthropic
                    model: claude-haiku-4-5
                  - name: frontier
                    upstream: anthropic
                    model: claude-sonnet-4-6
                routing:
                  default_tier: frontier
              since: "2026-01-01T00:00:00Z"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReplayResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/replay" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "policy_yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "since": "2026-01-01T00:00:00Z"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "policy_yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "since": "2026-01-01T00:00:00Z"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/replay",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "policy_yaml": "version: 1\nmode: shadow\nupstreams:\n  anthropic:\n    base_url: https://api.anthropic.com\n    dialect: anthropic\n    api_key_env: ANTHROPIC_API_KEY\ntiers:\n  - name: cheap\n    upstream: anthropic\n    model: claude-haiku-4-5\n  - name: frontier\n    upstream: anthropic\n    model: claude-sonnet-4-6\nrouting:\n  default_tier: frontier\n",
              "since": "2026-01-01T00:00:00Z"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/replay`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/requests/{req_id}:
    get:
      operationId: requestGet
      summary: One decision record with its companions joined and its cost arithmetic broken out term by term.
      description: |-
        `cost.terms` is the credibility asset: each billable quantity times the per-unit rate that actually priced it, summing to `cost.total`. The terms are emitted ONLY when they reconcile to the recorded dollar within a nanodollar under the row's own pricing snapshot — an UNPRICED row yields `total.usd: null` and NO terms, and a row whose snapshot no longer resolves yields the total with no breakdown. No arithmetic is ever fabricated to fill the block.

        `content_refs` reports whether the verbatim request/response bodies were captured, by hash and availability only — the bodies themselves are never inlined here; they are a separate, separately-scoped byte-egress read.

        QUERY BUDGET: unlike the other analytics reads, this operation has no partial form to degrade to (it returns one record or none), so a budget breach is an honest 503 `budget_exceeded` rather than a truncated 200.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordResponse'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/requests/$TOKENTRIAGE_REQ_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            REQ_ID = os.environ["TOKENTRIAGE_REQ_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/requests/{REQ_ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const reqId: string = process.env.TOKENTRIAGE_REQ_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/requests/${reqId}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: req_id
        in: path
        required: true
        description: The decision log's request id, as returned in `rows[].req_id` or in the data plane's `x-tt-request-id` response header. It is NOT the management plane's `TT-Request-Id` (a different identifier for a different plane — see the document description).
        schema:
          type: string
  /tt/api/v1/requests/{req_id}/content:
    get:
      operationId: contentGet
      summary: 'Read one captured request or response body verbatim (privileged: `content:read`, audited per read).'
      description: |-
        Returns the stored bytes exactly as captured, as `text/plain` with `Cache-Control: no-store` and `X-Content-Type-Options: nosniff` — the payload is never re-interpreted as an active content type.

        AUTHORIZATION IS NOT THE ORDINARY READ RULE. `content:read` is PRIVILEGED (§2.1 rule 3): `*:read` never expands to it and it must be named explicitly on the token, because this is bulk egress of prompts and completions rather than of metadata. Since a management token's scopes are bounded by its role's expansion at mint, that means only an ADMIN-role `tta_` can hold it. The route matrix keeps its shipped VIEWER floor for dashboard SESSIONS and pre-existing `ttk_` bearers, which the scope layer deliberately does not govern — so the honest statement is: this operation is scope-fenced for programmatic credentials and role-fenced for interactive ones.

        EVERY ATTEMPT IS AUDITED, including the refused ones, and the row is written BEFORE any byte is served — "who read which stored prompt" is the question the trail exists to answer, and a trail written after the egress could be lost to the failure that interrupted it. The row records the request id, which side was asked for, and the byte count; it never records the bytes.

        An absent or expired body is 404 `not_found` — a body dropped by the capture sampler or swept by the TTL is genuinely gone, never fabricated. A deployment with capture switched off answers 404 `feature_absent` with the config key that turns it on.
      tags:
        - content
      x-role-floor: viewer
      x-required-scope: content:read
      security:
        - managementToken:
            - content:read
        - session: []
      parameters:
        - name: which
          in: query
          required: true
          description: 'Which side of the exchange to read: `request` (the prompt as it reached the upstream) or `response` (the completion as it came back). There is no default — a body this sensitive is not served on a guess.'
          schema:
            type: string
            enum:
              - request
              - response
      responses:
        "200":
          description: OK
          content:
            text/plain:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/requests/$TOKENTRIAGE_REQ_ID/content?which=request" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: text/plain"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import sys
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            REQ_ID = os.environ["TOKENTRIAGE_REQ_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/requests/{REQ_ID}/content?which=request",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "text/plain",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    sys.stdout.buffer.write(resp.read())
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const reqId: string = process.env.TOKENTRIAGE_REQ_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/requests/${reqId}/content?which=request`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "text/plain",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(await res.text());
    parameters:
      - name: req_id
        in: path
        required: true
        description: 'The decision record''s request id, as returned in `req_id` by the record and rows surfaces. It is a single path SEGMENT: a percent-encoded separator does not widen it into a sub-path.'
        schema:
          type: string
  /tt/api/v1/rows:
    post:
      operationId: analyticsRows
      summary: Page verbatim ledger rows under a filter and sort, cursor-paginated (a heavy READ served over POST).
      description: |-
        Rows are the canonical artifact — the same bytes the reader would produce, with the oracle, shadow-grade and feedback companions joined onto the returned page. This layer strips nothing, so a row's own `cost.unpriced` flag survives to the client.

        PAGINATION: `limit` and `cursor` in the request body, `next_cursor` and `has_more` in the response. `next_cursor` is ABSENT at the end of the feed — absence IS the end-of-feed signal, and `has_more` is derived from it so the two can never disagree. A `limit` above this deployment's maximum is REFUSED with a 400 naming the maximum; it is never silently clamped, because a clamped limit is a lie about what the server heard (Appendix B.1 rule 3's reasoning applied to a parameter).

        ORDERING: `sort` takes `field` or `-field` over `ts`, `cost.usd`, `latency.total_ms`, `latency.ttft_ms`, `usage.total_tokens`, with `req_id` as the stable tiebreaker; the default is `-ts`. An unknown field is a 400, so a typo never silently reorders the page.

        QUERY BUDGET: this is a heavy read under the §2.1 budget (a per-request deadline, a row cap, and a concurrency semaphore). When the budget bites, the answer is a PARTIAL 200 whose `provenance.truncated` is `true` — never a silently complete-looking result, and never a 503. Always read `provenance` before trusting a total.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      x-read-only: true
      x-pagination:
        - cursor
      security:
        - managementToken:
            - analytics:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RowsRequest'
            example:
              limit: 50
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RowsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/rows" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "limit": 50
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "limit": 50
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/rows",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "limit": 50
            };

            const res: Response = await fetch(`${base}/tt/api/v1/rows`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/schedules:
    get:
      operationId: schedulesList
      summary: List scheduled reports, plus the alert channels a schedule may deliver to.
      description: Ordered by `id` ascending, which is stable and unique, so the cursor describes a total order. Cursor-paged; `channels` is NOT paged — it is a small, complete advertisement of what this deployment has configured, and a partial one would let a create form offer a channel while hiding a valid alternative.
      tags:
        - schedules
      x-role-floor: viewer
      x-required-scope: schedules:read
      x-pagination:
        - cursor
      security:
        - managementToken:
            - schedules:read
        - session: []
      parameters:
        - name: cursor
          in: query
          description: 'An OPAQUE page position: echo back a previous response''s `next_cursor` VERBATIM, never construct one. Absent means the first page. An unreadable cursor is a 400 `bad_request` naming the parameter — never a silently-restarted-from-the-top page, which would re-deliver rows the caller already saw and call it success.'
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 takes the documented default (100); a value above the maximum (1000) is REFUSED 400 rather than silently clamped — a clamped limit is a lie about what the server heard (B.1 rule 3).
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SchedulesPage'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/schedules?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/schedules?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/schedules?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: scheduleSave
      summary: Create or replace a scheduled report, taking the id from the body.
      description: '`view_id` MUST resolve to an existing saved view and `channel_id` to a configured alert channel: a schedule that renders nothing, or delivers nowhere, is a silent no-op we refuse to create. An unknown channel or a cadence outside `hourly|daily|weekly` is a 422 `validation_failed` carrying a JSON pointer at the field.'
      tags:
        - schedules
      x-role-floor: operator
      x-required-scope: schedules:write
      security:
        - managementToken:
            - schedules:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Schedule'
            example:
              v: 1
              id: $TOKENTRIAGE_SCHEDULE_ID
              view_id: $TOKENTRIAGE_VIEW_ID
              cadence: daily
              channel_id: $TOKENTRIAGE_CHANNEL_ID
              created_by: ""
              created_at: ""
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Schedule'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/schedules" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "v": 1,
              "id": "$(json_str "$TOKENTRIAGE_SCHEDULE_ID")",
              "view_id": "$(json_str "$TOKENTRIAGE_VIEW_ID")",
              "cadence": "daily",
              "channel_id": "$(json_str "$TOKENTRIAGE_CHANNEL_ID")",
              "created_by": "",
              "created_at": ""
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            SCHEDULE_ID = os.environ["TOKENTRIAGE_SCHEDULE_ID"]
            VIEW_ID = os.environ["TOKENTRIAGE_VIEW_ID"]
            CHANNEL_ID = os.environ["TOKENTRIAGE_CHANNEL_ID"]

            payload = {
              "v": 1,
              "id": SCHEDULE_ID,
              "view_id": VIEW_ID,
              "cadence": "daily",
              "channel_id": CHANNEL_ID,
              "created_by": "",
              "created_at": ""
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/schedules",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const scheduleId: string = process.env.TOKENTRIAGE_SCHEDULE_ID ?? "";
            const viewId: string = process.env.TOKENTRIAGE_VIEW_ID ?? "";
            const channelId: string = process.env.TOKENTRIAGE_CHANNEL_ID ?? "";

            const payload = {
              "v": 1,
              "id": scheduleId,
              "view_id": viewId,
              "cadence": "daily",
              "channel_id": channelId,
              "created_by": "",
              "created_at": ""
            };

            const res: Response = await fetch(`${base}/tt/api/v1/schedules`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/schedules/{id}:
    get:
      operationId: scheduleGet
      summary: One scheduled report, with its run history.
      tags:
        - schedules
      x-role-floor: viewer
      x-required-scope: schedules:read
      security:
        - managementToken:
            - schedules:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Schedule'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/schedules/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/schedules/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/schedules/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: scheduleUpdate
      summary: Create or replace the scheduled report named by the path.
      description: Identical to `scheduleSave` except that the PATH id wins over any `id` in the body. On an update the stored author and creation time are preserved.
      tags:
        - schedules
      x-role-floor: operator
      x-required-scope: schedules:write
      security:
        - managementToken:
            - schedules:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Schedule'
            example:
              v: 1
              id: $TOKENTRIAGE_SCHEDULE_ID
              view_id: $TOKENTRIAGE_VIEW_ID
              cadence: daily
              channel_id: $TOKENTRIAGE_CHANNEL_ID
              created_by: ""
              created_at: ""
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Schedule'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/schedules/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "v": 1,
              "id": "$(json_str "$TOKENTRIAGE_SCHEDULE_ID")",
              "view_id": "$(json_str "$TOKENTRIAGE_VIEW_ID")",
              "cadence": "daily",
              "channel_id": "$(json_str "$TOKENTRIAGE_CHANNEL_ID")",
              "created_by": "",
              "created_at": ""
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]
            SCHEDULE_ID = os.environ["TOKENTRIAGE_SCHEDULE_ID"]
            VIEW_ID = os.environ["TOKENTRIAGE_VIEW_ID"]
            CHANNEL_ID = os.environ["TOKENTRIAGE_CHANNEL_ID"]

            payload = {
              "v": 1,
              "id": SCHEDULE_ID,
              "view_id": VIEW_ID,
              "cadence": "daily",
              "channel_id": CHANNEL_ID,
              "created_by": "",
              "created_at": ""
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/schedules/{ID}",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";
            const scheduleId: string = process.env.TOKENTRIAGE_SCHEDULE_ID ?? "";
            const viewId: string = process.env.TOKENTRIAGE_VIEW_ID ?? "";
            const channelId: string = process.env.TOKENTRIAGE_CHANNEL_ID ?? "";

            const payload = {
              "v": 1,
              "id": scheduleId,
              "view_id": viewId,
              "cadence": "daily",
              "channel_id": channelId,
              "created_by": "",
              "created_at": ""
            };

            const res: Response = await fetch(`${base}/tt/api/v1/schedules/${id}`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    delete:
      operationId: scheduleDelete
      summary: Delete a scheduled report. The next cadence tick renders nothing for it.
      tags:
        - schedules
      x-role-floor: operator
      x-required-scope: schedules:write
      security:
        - managementToken:
            - schedules:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResult'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a schedule. It stops firing.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/schedules/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a schedule. It stops firing.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/schedules/{ID}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a schedule. It stops firing.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/schedules/${id}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The schedule's slug. On POST it is AUTHORITATIVE over any `id` in the body.
        schema:
          type: string
  /tt/api/v1/share-tokens:
    get:
      operationId: shareTokensList
      summary: List share tokens (id, frozen scope, authorship, expiry, revocation — never a secret or hash).
      description: Ordered by `id` ascending, which is stable and unique, so the cursor describes a total order. `expired` is DERIVED against the server clock at read time; a token can be dead by revocation, by expiry, or because the view it froze was deleted, and the list shows the first two.
      tags:
        - share
      x-role-floor: admin
      x-required-scope: share:admin
      x-pagination:
        - cursor
      security:
        - managementToken:
            - share:admin
        - session: []
      parameters:
        - name: cursor
          in: query
          description: 'An OPAQUE page position: echo back a previous response''s `next_cursor` VERBATIM, never construct one. Absent means the first page. An unreadable cursor is a 400 `bad_request` naming the parameter — never a silently-restarted-from-the-top page, which would re-deliver rows the caller already saw and call it success.'
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 takes the documented default (100); a value above the maximum (1000) is REFUSED 400 rather than silently clamped — a clamped limit is a lie about what the server heard (B.1 rule 3).
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShareTokensResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/share-tokens?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/share-tokens?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/share-tokens?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: shareTokenMint
      summary: Mint a share token. The secret is returned EXACTLY ONCE and can never be retrieved again.
      description: |-
        Exactly ONE of `view_id` or `query` freezes the scope. A `view_id` is resolved LIVE at every read, so editing the view changes what the bearer sees and deleting it revokes the link; an inline `query` is fully self-contained. `expires_in` is a Go duration — ABSENT means non-expiring, but a PRESENT non-positive value is refused rather than silently minting an eternal credential for a caller who explicitly asked for a bound.

        `token` is the ONE field on this whole surface that carries credential material. Store it when you receive it: the server keeps only its SHA-256, and there is no read that can show it to you again.
      tags:
        - share
      x-role-floor: admin
      x-required-scope: share:admin
      security:
        - managementToken:
            - share:admin
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShareMintRequest'
            example:
              view_id: $TOKENTRIAGE_VIEW_ID
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShareMintResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/share-tokens" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "view_id": "$(json_str "$TOKENTRIAGE_VIEW_ID")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            VIEW_ID = os.environ["TOKENTRIAGE_VIEW_ID"]

            payload = {
              "view_id": VIEW_ID
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/share-tokens",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const viewId: string = process.env.TOKENTRIAGE_VIEW_ID ?? "";

            const payload = {
              "view_id": viewId
            };

            const res: Response = await fetch(`${base}/tt/api/v1/share-tokens`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/share-tokens/{id}:
    delete:
      operationId: shareTokenRevoke
      summary: Revoke a share token immediately.
      description: Revocation takes effect on the next read — there is no cache to expire. The token is addressed by its public `id`; the secret is neither accepted nor needed.
      tags:
        - share
      x-role-floor: admin
      x-required-scope: share:admin
      security:
        - managementToken:
            - share:admin
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResult'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a share token. Any link handed out with it stops
            # !! working immediately.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/share-tokens/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a share token. Any link handed out with it stops
            # !! working immediately.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/share-tokens/{ID}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! IRREVERSIBLY REVOKES a share token. Any link handed out with it stops
            // !! working immediately.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/share-tokens/${id}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The share token's PUBLIC id, as returned in `record.id` at mint. NEVER the `tts_` secret — a secret is not accepted here and cannot be looked up, which is why a lost share link is re-minted rather than recovered.
        schema:
          type: string
  /tt/api/v1/share/{token}:
    get:
      operationId: shareRead
      summary: 'PUBLIC, token-self-gated: resolve a share token to the read-only query it froze.'
      description: |-
        No authentication header is required — the token in the path IS the credential. The response carries the resolved query plus the minimum display context needed to render it: no navigation, no other data, and `read_only` is always true.

        An unknown, revoked, expired or unresolvable token all return the SAME 404 body, deliberately: no probe can distinguish "my token was revoked" from "the view behind it was deleted", and a holder of an arbitrary string cannot use this endpoint to confirm that it is a live token.
      tags:
        - share
      x-role-floor: public
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShareReadResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/share/$TOKENTRIAGE_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/share/{TOKEN}",
                method="GET",
                data=None,
                headers={
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/share/${token}`, {
              method: "GET",
              headers: {
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: token
        in: path
        required: true
        description: 'The share token''s SECRET value — this route is PUBLIC at the authorization boundary because the token IS the credential, verified in constant time by the handler itself. It is redacted from every audit row and log line before the write, and it is never echoed in a response or a problem body. An unknown, revoked, expired or unresolvable token all return the SAME 404, so no probe can distinguish them (no existence oracle). This parameter''s value is a LIVE CREDENTIAL: it is redacted before any audit or log write and never echoed in a problem body (CA5).'
        schema:
          type: string
        x-credential: true
  /tt/api/v1/share/{token}/rows:
    post:
      operationId: shareRows
      summary: 'PUBLIC, token-self-gated: replay EXACTLY the frozen query and page its rows.'
      description: |-
        The only body this accepts is paging. Presenting `filter`, `columns`, `group_by`, `sort`, `since` or `metrics` is a 403 `forbidden` — the scope is frozen and a substitution is REFUSED, not ignored. Those fields are documented precisely so the refusal can be a refusal: bodies are strict-decoded, so without them a scope-widening attempt would come back as a parse complaint about an unknown field.

        `cursor` here is the SHIPPED `POST /rows` cursor — a decimal record offset — not the opaque cursor the other list operations use; the two forms coexist until the card that owns `POST /rows` reconciles them. `limit` is clamped to this deployment's query row cap with `provenance.truncated` disclosing it, which is the sanctioned alternate form of the limit policy: a clamp is honest only where the response carries the disclosure.
      tags:
        - share
      x-role-floor: public
      x-read-only: true
      x-pagination:
        - cursor
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShareRowsRequest'
            example:
              limit: 50
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RowsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/share/$TOKENTRIAGE_TOKEN/rows" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "limit": 50
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_TOKEN"]

            payload = {
              "limit": 50
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/share/{TOKEN}/rows",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_TOKEN ?? "";

            const payload = {
              "limit": 50
            };

            const res: Response = await fetch(`${base}/tt/api/v1/share/${token}/rows`, {
              method: "POST",
              headers: {
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: token
        in: path
        required: true
        description: 'The share token''s SECRET value — this route is PUBLIC at the authorization boundary because the token IS the credential, verified in constant time by the handler itself. It is redacted from every audit row and log line before the write, and it is never echoed in a response or a problem body. An unknown, revoked, expired or unresolvable token all return the SAME 404, so no probe can distinguish them (no existence oracle). This parameter''s value is a LIVE CREDENTIAL: it is redacted before any audit or log write and never echoed in a problem body (CA5).'
        schema:
          type: string
        x-credential: true
  /tt/api/v1/stream:
    get:
      operationId: streamSubscribe
      summary: Subscribe to the realtime decision feed (Server-Sent Events).
      description: |-
        Emits `hello` first (carrying the resume coordinate a future reconnect should present), then `record` events — each one a VERBATIM line of the decision ledger, identical to a row from `POST /rows` — plus `companion` (oracle/shadow/feedback rows), `health` every 15 seconds, `alert` on a rule transition, and `dropped` when this connection's bounded queue overflowed.

        `Last-Event-ID` resumes: the server replays the exact byte gap after the coordinate and de-duplicates the overlap, so a briefly-disconnected consumer heals without inventing rows. When the coordinate predates a log rotation the gap is NOT seekable, and the server says so with a `dropped` event instead of silently skipping or replaying misaligned bytes.

        The response body is OPAQUE BY DESIGN rather than by omission: an event stream is a framing, not a document, and its `data:` payloads are four different shapes selected by `types`. A single published 200 schema would be a shape a generated client would decode into and be wrong about. Errors raised BEFORE the stream opens are ordinary `application/problem+json`; once the 200 and `text/event-stream` are on the wire, failures stay SSE-framed events.

        Concurrency is capped (`dashboard.stream.max_clients`); a connection refused at the cap is a 503 `budget_exceeded` carrying `retry_after_seconds` — genuinely transient, and the ledger has everything the stream missed. A build with no stream seam wired answers 404 `feature_absent` naming the composition step; the standard binary always wires one, so that state is reachable only when this server is embedded directly.
      tags:
        - stream
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      parameters:
        - name: types
          in: query
          description: 'Which event classes to receive, comma-separated: `record`, `companion`, `health`, `alert`. Absent means all four. An UNKNOWN token is a 400 naming it, never a silently empty stream — `?types=recrod` must not look like "no traffic yet". Comma-separated list.'
          schema:
            type: string
        - name: filter
          in: query
          description: 'A `q=`-grammar text filter (Appendix B.3) applied to `record` events only, over the same closed field registry `/capabilities` publishes. Control events are never filtered: a consumer that asked for a subset still needs to hear that rows were dropped. The q= text filter grammar (see the FilterNode schema description).'
          schema:
            type: string
        - name: Last-Event-ID
          in: header
          description: The `id:` of the last `record` event this consumer saw, in the server's `<file>:<offset>` form. Only `record` events carry an id, so a browser EventSource resumes from the last ledger row rather than from a control event.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            text/event-stream:
              schema:
                type: string
                description: Opaque bytes (ByteEgress class, §2.1 rule 3) — covered by its own byte-level honesty test, not a reflected schema; see the operation description.
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -N --max-time 10 \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/stream" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Last-Event-ID: $TOKENTRIAGE_LAST_EVENT_ID" \
              -H "Accept: text/event-stream"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LAST_EVENT_ID = os.environ["TOKENTRIAGE_LAST_EVENT_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/stream",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Last-Event-ID": LAST_EVENT_ID,
                    "Accept": "text/event-stream",
                },
            )

            try:
                with urllib.request.urlopen(req, timeout=30) as resp:
                    # Server-Sent Events: read ONE complete frame, then stop. The stream
                    # has no end by design — a reader waiting for EOF waits forever.
                    for raw in resp:
                        line = raw.decode("utf-8", "replace").rstrip("\n")
                        print(line)
                        if line == "":
                            break
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const lastEventId: string = process.env.TOKENTRIAGE_LAST_EVENT_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/stream`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                "Last-Event-ID": lastEventId,
                Accept: "text/event-stream",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            // Server-Sent Events: read ONE chunk, then cancel. The stream has no end by
            // design — awaiting res.text() on it never resolves.
            const reader = res.body!.getReader();
            const { value } = await reader.read();
            await reader.cancel();
            console.log(new TextDecoder().decode(value));
  /tt/api/v1/tokens:
    get:
      operationId: tokensList
      summary: List management tokens (id, stub, label, role, scopes, node, state, expiry, last use — never a secret or hash).
      description: 'Returns every token record in full: this is the credential-surface audit read, so it is deliberately complete and unpaginated rather than cursor-paged. `last_used_day` is a UTC DAY, not a timestamp — day granularity is enough to answer "is this credential still in use?" without turning the token store into a telemetry channel.'
      tags:
        - tokens
      x-role-floor: admin
      x-required-scope: tokens:read
      security:
        - managementToken:
            - tokens:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokensResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/tokens" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/tokens",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/tokens`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: tokenMint
      summary: Mint a management token. The secret is returned EXACTLY ONCE and can never be retrieved again.
      description: 'The grant is validated before any secret is generated, so a refused mint leaves no credential material anywhere. A grant naming scopes the requested role does not imply is refused 422 rather than persisted: a token can never carry more authority than its role. Scopes are required — a scope-less token reads as unrestricted and behaves as useless. Two ceilings bound the grant: it may not exceed what the requested role implies, and it may not exceed what the CALLING credential itself holds — a credential may only issue authority it already has. A `tta_` token also reaches only operations published in this document, so a scope naming an unregistered surface grants nothing.'
      tags:
        - tokens
      x-role-floor: admin
      x-required-scope: tokens:mint
      security:
        - managementToken:
            - tokens:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenMintRequest'
            example:
              label: ci-reader
              role: viewer
              scopes:
                - analytics:read
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenMintResponse'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/tokens" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "label": "ci-reader",
              "role": "viewer",
              "scopes": [
                "analytics:read"
              ]
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            payload = {
              "label": "ci-reader",
              "role": "viewer",
              "scopes": [
                "analytics:read"
              ]
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/tokens",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const payload = {
              "label": "ci-reader",
              "role": "viewer",
              "scopes": [
                "analytics:read"
              ]
            };

            const res: Response = await fetch(`${base}/tt/api/v1/tokens`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/tokens/{id}:
    get:
      operationId: tokenGet
      summary: One management token's read shape by id (never a secret or hash).
      description: The `id` is the token's PUBLIC handle (`mk_` + 12 base58), never its secret — a `tta_` secret is not accepted here and cannot be looked up, which is why a lost secret is rotated rather than recovered.
      tags:
        - tokens
      x-role-floor: admin
      x-required-scope: tokens:read
      security:
        - managementToken:
            - tokens:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/tokens/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/tokens/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/tokens/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: 'The token''s PUBLIC handle: "mk_" + 12 base58 characters, as returned in `token.id` at mint. NEVER the `tta_` secret — a secret is not accepted here and cannot be looked up, which is why losing one means rotating rather than recovering.'
        schema:
          type: string
  /tt/api/v1/tokens/{id}/revoke:
    post:
      operationId: tokenRevoke
      summary: 'Revoke a management token immediately. Idempotent in effect: revoking an already-revoked token succeeds.'
      description: |-
        Revocation takes effect on the next authentication — there is no cache to expire.

        **`tokens:write` is PRIVILEGED, so it is never granted by a `*:write` wildcard and must be named on the token.** It is a scope of its own rather than being folded into `tokens:mint`, and the two halves of that are separate decisions. Privileged, because this operation IRREVERSIBLY DESTROYS A CREDENTIAL: a token granted `*:write` — which an operator reads as "may change things" — could otherwise turn off every other management credential, including itself, while being unable to create one. Its own scope rather than `tokens:mint`, because revoke-only is the grant an incident-response credential actually wants, and folding the two together would make "may turn a leaked key off" unobtainable without "may issue new keys".

        **The credential resources still DIFFER, and the difference is now a decided position rather than an accident.** Destroying a CONTROL-PLANE credential is privileged everywhere — this operation (`tokens:write`), `tokenRotate` (`tokens:mint`) and `shareTokenRevoke` (`share:admin`) — because those credentials are how an operator reaches this API at all, and a wildcard that reaches them can lock the operator out of the plane they would use to recover. Revoking a DATA-PLANE virtual key (`governanceKeyRevoke`) deliberately stays on the COMMON `governance:write`: that key governs an application's model access, not the operator's own, so killing a leaked one during an incident is the SAFE direction and must not require an authority the incident may have taken away. The two rules are the same rule applied to different planes, and that operation states its half.

        **A token may revoke ITSELF**, deliberately, and this is the one place the rule is stated: there is no self-check on this path. A credential that can turn itself off is good incident hygiene — the holder of a leaked secret is often the fastest party to act, and a self-revocation guard would mean a compromised credential can be used for everything EXCEPT the one call that limits the damage. The audit row names the actor either way.

        [Because `tokens:write` is privileged, the lowest role whose expansion grants it is `admin` rather than `operator`. That matches this operation's own admin floor, so no reachable grant is lost: an operator-role token could never have called this route. A mint that names it under a lower role is refused 422 naming the role that would work.]
      tags:
        - tokens
      x-role-floor: admin
      x-required-scope: tokens:write
      security:
        - managementToken:
            - tokens:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a management credential. Anything using it
            # !! starts answering 401 immediately.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/tokens/$TOKENTRIAGE_ID/revoke" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a management credential. Anything using it
            # !! starts answering 401 immediately.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/tokens/{ID}/revoke",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! IRREVERSIBLY REVOKES a management credential. Anything using it
            // !! starts answering 401 immediately.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/tokens/${id}/revoke`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: 'The token''s PUBLIC handle: "mk_" + 12 base58 characters, as returned in `token.id` at mint. NEVER the `tta_` secret — a secret is not accepted here and cannot be looked up, which is why losing one means rotating rather than recovering.'
        schema:
          type: string
  /tt/api/v1/tokens/{id}/rotate:
    post:
      operationId: tokenRotate
      summary: 'Rotate a token: mint a successor with the same grant, record lineage both ways, and put the predecessor into a bounded grace period.'
      description: 'The successor inherits the predecessor''s role, scopes, node binding and REMAINING life — never a fresh full term, so repeated rotation cannot extend a credential someone deliberately time-boxed. `grace_seconds` can only bring the predecessor''s expiry FORWARD: rotating a token that expires in 30 seconds with a one-hour grace still leaves it expiring in 30 seconds. `grace_seconds: 0` revokes the predecessor immediately. Rotating a revoked or expired token is refused 409 — a successor must not inherit authority from a credential that no longer has any.'
      tags:
        - tokens
      x-role-floor: admin
      x-required-scope: tokens:mint
      security:
        - managementToken:
            - tokens:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRotateRequest'
            example: {}
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenMintResponse'
        "409":
          description: 'Problem: conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor credential after the grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/tokens/$TOKENTRIAGE_ID/rotate" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {}
            JSON
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor credential after the grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            payload = {}

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/tokens/{ID}/rotate",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! REVOKES the predecessor credential after the grace window. Deploy the
            // !! successor first.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const payload = {};

            const res: Response = await fetch(`${base}/tt/api/v1/tokens/${id}/rotate`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: 'The token''s PUBLIC handle: "mk_" + 12 base58 characters, as returned in `token.id` at mint. NEVER the `tta_` secret — a secret is not accepted here and cannot be looked up, which is why losing one means rotating rather than recovering.'
        schema:
          type: string
  /tt/api/v1/traces/{trace_id}:
    get:
      operationId: traceGet
      summary: 'One agent trace''s cost tree: nodes, fan-out edges, and totals that disclose the rows they could not cost.'
      description: |-
        `totals` separates own cost from children cost and carries `uncosted_rows` — the count of nodes the pricing missed, disclosed rather than folded into the total as zero. Where a node carries a shadow-grade companion its `shadow` block reports what the OTHER tier would have cost and the judge's verdict; a node with no companion carries NO `shadow` at all, because a counterfactual that was never measured is absent, never inferred. The counterfactual total likewise sums only the measurable nodes and discloses how many were not.

        QUERY BUDGET: this is a heavy read under the §2.1 budget (a per-request deadline, a row cap, and a concurrency semaphore). When the budget bites, the answer is a PARTIAL 200 whose `provenance.truncated` is `true` — never a silently complete-looking result, and never a 503. Always read `provenance` before trusting a total.
      tags:
        - analytics
      x-role-floor: viewer
      x-required-scope: analytics:read
      security:
        - managementToken:
            - analytics:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TraceResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/traces/$TOKENTRIAGE_TRACE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            TRACE_ID = os.environ["TOKENTRIAGE_TRACE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/traces/{TRACE_ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const traceId: string = process.env.TOKENTRIAGE_TRACE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/traces/${traceId}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: trace_id
        in: path
        required: true
        description: The agent trace id (`trace.trace_id` on a ledger row). A trace groups the requests one agent run issued; the response reconciles own-vs-children cost and discloses how many rows it could not cost.
        schema:
          type: string
  /tt/api/v1/users:
    get:
      operationId: usersList
      summary: List user accounts (name, role, timestamps — never a password or its hash).
      description: 'Ordered by `name` ascending, which is stable and unique, so the cursor describes a total order. Cursor-paged: `next_cursor` is ABSENT on the last page, and `has_more` states the same fact explicitly.'
      tags:
        - users
      x-role-floor: admin
      x-required-scope: users:admin
      x-pagination:
        - cursor
      security:
        - managementToken:
            - users:admin
        - session: []
      parameters:
        - name: cursor
          in: query
          description: 'An OPAQUE page position: echo back a previous response''s `next_cursor` VERBATIM, never construct one. Absent means the first page. An unreadable cursor is a 400 `bad_request` naming the parameter — never a silently-restarted-from-the-top page, which would re-deliver rows the caller already saw and call it success.'
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 takes the documented default (100); a value above the maximum (1000) is REFUSED 400 rather than silently clamped — a clamped limit is a lie about what the server heard (B.1 rule 3).
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsersResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/users?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/users?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/users?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: userCreate
      summary: Create a user account. Answers 201 with the created record.
      description: The password is hashed with argon2id before it is stored and is never echoed back. A name that already exists is a 409 `conflict` rather than a silent overwrite — creating over an existing account would reset a colleague's password with a success status.
      tags:
        - users
      x-role-floor: admin
      x-required-scope: users:admin
      security:
        - managementToken:
            - users:admin
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCreateRequest'
            example:
              name: ada
              password: $TOKENTRIAGE_NEW_USER_PASSWORD
              role: viewer
      responses:
        "201":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        "409":
          description: 'Problem: conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/users" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "name": "ada",
              "password": "$(json_str "$TOKENTRIAGE_NEW_USER_PASSWORD")",
              "role": "viewer"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NEW_USER_PASSWORD = os.environ["TOKENTRIAGE_NEW_USER_PASSWORD"]

            payload = {
              "name": "ada",
              "password": NEW_USER_PASSWORD,
              "role": "viewer"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/users",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const newUserPassword: string = process.env.TOKENTRIAGE_NEW_USER_PASSWORD ?? "";

            const payload = {
              "name": "ada",
              "password": newUserPassword,
              "role": "viewer"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/users`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/users/{name}:
    get:
      operationId: userGet
      summary: One user account's read shape (never a password or its hash).
      tags:
        - users
      x-role-floor: admin
      x-required-scope: users:admin
      security:
        - managementToken:
            - users:admin
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/users/$TOKENTRIAGE_NAME" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NAME = os.environ["TOKENTRIAGE_NAME"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/users/{NAME}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const name: string = process.env.TOKENTRIAGE_NAME ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/users/${name}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: userUpdate
      summary: Change a user's role and/or password. Either or both; at least one is required.
      description: |-
        A body carrying neither field is refused rather than answered 200 over a no-op — a silent success for a request that changed nothing is a lie about what the server heard (B.1 rule 3).

        EVERY successful change REVOKES that user's live sessions, so a demotion or a password reset takes effect on the very next request instead of at session expiry. The revocation count is in the audit row.
      tags:
        - users
      x-role-floor: admin
      x-required-scope: users:admin
      security:
        - managementToken:
            - users:admin
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdateRequest'
            example:
              role: operator
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/users/$TOKENTRIAGE_NAME" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<'JSON'
            {
              "role": "operator"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NAME = os.environ["TOKENTRIAGE_NAME"]

            payload = {
              "role": "operator"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/users/{NAME}",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const name: string = process.env.TOKENTRIAGE_NAME ?? "";

            const payload = {
              "role": "operator"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/users/${name}`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    delete:
      operationId: userDelete
      summary: Remove a user account and revoke its live sessions.
      description: '`revoked_sessions` DISCLOSES how many live sessions the removal killed, because an operator removing a compromised account needs to see that the sessions went with it rather than assume it.'
      tags:
        - users
      x-role-floor: admin
      x-required-scope: users:admin
      security:
        - managementToken:
            - users:admin
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserDeleteResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a dashboard user AND drops their live sessions.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/users/$TOKENTRIAGE_NAME" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a dashboard user AND drops their live sessions.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NAME = os.environ["TOKENTRIAGE_NAME"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/users/{NAME}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a dashboard user AND drops their live sessions.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const name: string = process.env.TOKENTRIAGE_NAME ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/users/${name}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: name
        in: path
        required: true
        description: The user's login name, matched case-insensitively (creating `Ada` and reading `ada` reach one record).
        schema:
          type: string
  /tt/api/v1/views:
    get:
      operationId: viewsList
      summary: List saved views.
      description: 'Ordered by `id` ascending, which is stable and unique, so the cursor describes a total order. Cursor-paged: `next_cursor` is ABSENT on the last page, and `has_more` states the same fact explicitly.'
      tags:
        - views
      x-role-floor: viewer
      x-required-scope: views:read
      x-pagination:
        - cursor
      security:
        - managementToken:
            - views:read
        - session: []
      parameters:
        - name: cursor
          in: query
          description: 'An OPAQUE page position: echo back a previous response''s `next_cursor` VERBATIM, never construct one. Absent means the first page. An unreadable cursor is a 400 `bad_request` naming the parameter — never a silently-restarted-from-the-top page, which would re-deliver rows the caller already saw and call it success.'
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 takes the documented default (100); a value above the maximum (1000) is REFUSED 400 rather than silently clamped — a clamped limit is a lie about what the server heard (B.1 rule 3).
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ViewsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/views?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/views?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/views?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: viewSave
      summary: Create or replace a saved view, taking the id from the body.
      description: 'A bogus `query.sort` field is refused HERE, at save time, rather than at read time: a view is replayed by share tokens, so an unsortable field would otherwise produce a token that resolves alive and 500s only when someone opens it.'
      tags:
        - views
      x-role-floor: operator
      x-required-scope: views:write
      security:
        - managementToken:
            - views:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavedView'
            example:
              v: 1
              id: $TOKENTRIAGE_VIEW_ID
              name: Spend by provider
              surface: requests
              query:
                filter: null
                text: ""
              pinned: false
              created_by: ""
              created_at: ""
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SavedView'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/views" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "v": 1,
              "id": "$(json_str "$TOKENTRIAGE_VIEW_ID")",
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": null,
                "text": ""
              },
              "pinned": false,
              "created_by": "",
              "created_at": ""
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            VIEW_ID = os.environ["TOKENTRIAGE_VIEW_ID"]

            payload = {
              "v": 1,
              "id": VIEW_ID,
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": None,
                "text": ""
              },
              "pinned": False,
              "created_by": "",
              "created_at": ""
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/views",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const viewId: string = process.env.TOKENTRIAGE_VIEW_ID ?? "";

            const payload = {
              "v": 1,
              "id": viewId,
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": null,
                "text": ""
              },
              "pinned": false,
              "created_by": "",
              "created_at": ""
            };

            const res: Response = await fetch(`${base}/tt/api/v1/views`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/views/{id}:
    get:
      operationId: viewGet
      summary: One saved view.
      tags:
        - views
      x-role-floor: viewer
      x-required-scope: views:read
      security:
        - managementToken:
            - views:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SavedView'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/views/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/views/{ID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/views/${id}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: viewUpdate
      summary: Create or replace the saved view named by the path.
      description: Identical to `viewSave` except that the PATH id wins over any `id` in the body. On an update the stored author and creation time are preserved — an editor never rewrites who first saved the view.
      tags:
        - views
      x-role-floor: operator
      x-required-scope: views:write
      security:
        - managementToken:
            - views:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SavedView'
            example:
              v: 1
              id: $TOKENTRIAGE_VIEW_ID
              name: Spend by provider
              surface: requests
              query:
                filter: null
                text: ""
              pinned: false
              created_by: ""
              created_at: ""
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SavedView'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/views/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "v": 1,
              "id": "$(json_str "$TOKENTRIAGE_VIEW_ID")",
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": null,
                "text": ""
              },
              "pinned": false,
              "created_by": "",
              "created_at": ""
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]
            VIEW_ID = os.environ["TOKENTRIAGE_VIEW_ID"]

            payload = {
              "v": 1,
              "id": VIEW_ID,
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": None,
                "text": ""
              },
              "pinned": False,
              "created_by": "",
              "created_at": ""
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/views/{ID}",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";
            const viewId: string = process.env.TOKENTRIAGE_VIEW_ID ?? "";

            const payload = {
              "v": 1,
              "id": viewId,
              "name": "Spend by provider",
              "surface": "requests",
              "query": {
                "filter": null,
                "text": ""
              },
              "pinned": false,
              "created_by": "",
              "created_at": ""
            };

            const res: Response = await fetch(`${base}/tt/api/v1/views/${id}`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    delete:
      operationId: viewDelete
      summary: Delete a saved view.
      description: Share tokens frozen to this view resolve it LIVE, so deleting a view makes every token that referenced it return the same 404 as an invalid token — deletion is the revocation.
      tags:
        - views
      x-role-floor: operator
      x-required-scope: views:write
      security:
        - managementToken:
            - views:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeleteResult'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a saved view. Schedules and share tokens pointing at it
            # !! break.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/views/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a saved view. Schedules and share tokens pointing at it
            # !! break.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/views/{ID}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a saved view. Schedules and share tokens pointing at it
            // !! break.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/views/${id}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The saved view's slug. On POST it is AUTHORITATIVE over any `id` in the body, so a view can never be renamed by a path/body mismatch.
        schema:
          type: string
  /tt/api/v1/webhooks/deliveries:
    get:
      operationId: webhookDeliveriesList
      summary: 'The bounded delivery record: what this instance tried to send, to which subscription, how many times, and why it failed.'
      description: |-
        Newest first — the inverse of `GET /events`, and deliberately so. An event feed is resumed forward from a cursor; a failure log is READ BACKWARD from the most recent problem, which is what an operator opening this endpoint during an incident is actually doing.

        ★ WHAT THIS RESPONSE WILL NOT GIVE YOU, listed because the omissions are the design and each has a reason. NO signing secret and no resolved env value — `secret_env` names where it lives, and that name is already public in the config. NO signature: it is not a secret (the receiver has it) but it is derived key material, and `signature_count` answers the only operational question its bytes would have ("is my rotation live?"). NO destination URL: a webhook URL is routinely the credential itself, since a Slack incoming hook, a Zapier catch hook and an EventBridge partner URL each carry their whole authorization in the path. NO payload bytes: `GET /events` already serves that body, and a second copy would be a second egress surface with no second reason to exist — `event_id` correlates and `payload_sha256` proves identity. NO endpoint response body: arbitrary remote content has no business on a registered read of ours.

        ★ THE STORE IS IN-MEMORY AND BOUNDED, AND `durable: false` SAYS SO ON EVERY RESPONSE. An empty page from a store that presented itself as a ledger would read as "nothing failed" when it meant "we restarted". `configured` separates the other conflation: `false` with an empty page means delivery is not set up at all, `true` means nothing has been sent yet.

        ★ THE `error` FIELD IS DRAWN FROM A FIXED VOCABULARY, never from a raw transport error. Go's `*url.Error` renders the FULL endpoint URL in its message, and that URL is often the credential — so transport failures are classified into `timeout`, `dns error`, `tls error`, `connection error`, `transport error`, `endpoint returned <status>`, or a fault naming the env-ref that is unset. A less specific message is the price of not leaking the endpoint, and it is stated here rather than discovered.
      tags:
        - webhooks
      x-role-floor: viewer
      x-required-scope: webhooks:read
      x-pagination:
        - cursor
      security:
        - managementToken:
            - webhooks:read
        - session: []
      parameters:
        - name: subscription_id
          in: query
          description: Only deliveries for this `webhooks[].id`. An id matching no configured subscription returns an empty page rather than a 400 — a subscription REMOVED from config still has history worth reading.
          schema:
            type: string
        - name: status
          in: query
          description: Only deliveries in this state. An unrecognised value is 400, never a silently-ignored filter.
          schema:
            type: string
            enum:
              - retrying
              - succeeded
              - failed
        - name: types
          in: query
          description: |-
            Comma-separated subset of the closed `resource.action` vocabulary (see `GET /capabilities` → `management_api.events.types`). Absent ⇒ every type. An unrecognised value — or an empty element, such as a trailing comma — is refused with 400 rather than dropped.

            It is spelled and typed EXACTLY as `GET /events`'s `types` is, deliberately: these two resources cross-reference each other in every paragraph, and a singular `event_type` here would have made "every budget failure" inexpressible on the surface whose whole job is reporting failures. Comma-separated list.
          schema:
            type: string
            enum:
              - config.applied
              - mode.set
              - governance.key.minted
              - governance.key.rotated
              - governance.key.revoked
              - governance.override.set
              - governance.override.deleted
              - budget.threshold_crossed
              - budget.exceeded
              - token.minted
              - token.revoked
              - calibration.activated
              - cache.purged
        - name: cursor
          in: query
          description: |-
            Opaque page position, echoed verbatim from a previous response's `next_cursor`. Absent ⇒ the first (newest) page. Unlike the event feed there is NO 410 here: this store is a diagnostic buffer, not a resumable feed, so a cursor whose records have been evicted simply yields the records that remain.

            ★ THIS FEED DESCENDS, SO A CURSOR WALKS BACKWARD INTO HISTORY — do NOT carry over `GET /events`'s idiom of echoing the last `next_cursor` forever. There it means "resume from here" on an ASCENDING feed and new events appear after it; here it means "continue older than this", and because newer records take HIGHER sequences they are behind you, not ahead. A monitor built that way reports zero failures forever. To poll for NEW deliveries, re-request the first page (no cursor) and read down until you meet an id you have already seen; use the cursor only to page further back within one pass.
          schema:
            type: string
        - name: limit
          in: query
          description: Page size. Absent or 0 ⇒ 100; the maximum is 1000, and a larger value is REFUSED naming the cap rather than silently clamped.
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveriesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/webhooks/deliveries?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/webhooks/deliveries?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/webhooks/deliveries?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/webhooks/deliveries/{id}/redeliver:
    post:
      operationId: webhookRedeliver
      summary: Re-send one message to its subscription, re-signed with a fresh timestamp under the same message id.
      description: |-
        ★ THIS SCHEDULES A SEND; IT DOES NOT PERFORM ONE. A 200 means the message was accepted onto the delivery worker, not that the endpoint received it — blocking a management request on a third-party endpoint would put an arbitrary remote service on this API's latency path. `accepted: true` says so in the body. Poll `GET /webhooks/deliveries` for the outcome.

        ★ THE MESSAGE ID IS PRESERVED; THE TIMESTAMP AND SIGNATURE ARE FRESH. Preserving `webhook-id` is what lets a receiver dedupe a redelivery it already processed — the same guarantee a retry gives. Re-signing is not optional: the original `webhook-timestamp` is by definition old, and a receiver enforcing a replay window (which every correct receiver does) would refuse the replayed bytes as stale. So the body is identical — `payload_sha256` proves it — and the signature is not.

        ★ REDELIVERY IS A WRITE AND IS AUDITED, once, with the delivery id and the subscription id as its resource. The response carries the `audit_id`.

        ★ IT CAN BE REFUSED, and the refusals are meanings rather than habits. **404 `feature_absent`** — no `webhooks:` section is configured on this instance. **404** — no such delivery record; the store is bounded, so a record old enough to have been evicted is genuinely gone. **409 `conflict`** — either the record no longer retains its payload (read `replayable` on the record BEFORE issuing this, so you see it coming) or the subscription it was addressed to has since been removed from config, in which case there is no destination to re-send to and inventing one would be worse than refusing.

        ★ NO `Idempotency-Key`. This operation is not idempotent and does not pretend to be: "send it again" is the entire request, and two calls SHOULD produce two attempts. Deduplication belongs at the receiver, on `webhook-id`, which is exactly where this operation preserves it.
      tags:
        - webhooks
      x-role-floor: operator
      x-required-scope: webhooks:write
      security:
        - managementToken:
            - webhooks:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookRedeliverResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "409":
          description: 'Problem: conflict.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/webhooks/deliveries/$TOKENTRIAGE_ID/redeliver" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/webhooks/deliveries/{ID}/redeliver",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/webhooks/deliveries/${id}/redeliver`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: The delivery record's id — which IS the Standard Webhooks `webhook-id` the receiver saw in its header, so it can be grepped straight out of a receiver's logs. It is not a credential and is safe in a path.
        schema:
          type: string
webhooks:
  budget.exceeded:
    post:
      summary: A budget's burn reached or passed its limit. Carries the live budget state with typed Money; excluded_spend is UNPRICED (usd null) whenever rows were excluded, never $0.00.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BudgetExceededDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  budget.threshold_crossed:
    post:
      summary: A budget crossed one of its configured ascending alert thresholds. Carries the live budget state with typed Money and the unpriced-exclusion disclosure.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BudgetThresholdCrossedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  cache.purged:
    post:
      summary: Response-cache entries were invalidated along a dimension. The audit trail records who purged what; neither it nor this event records WHICH entries died.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CachePurgedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  calibration.activated:
    post:
      summary: A classifier calibration artifact was activated. Live routing is unchanged until the next config reload — the payload's summary says so.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CalibrationActivatedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  config.applied:
    post:
      summary: A configuration was applied through the config-as-contract pipeline (POST /config/apply or PUT /config).
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigAppliedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  governance.key.minted:
    post:
      summary: A virtual key was minted. The SECRET is not here and never will be — it is shown once, in the mint response.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceKeyMintedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  governance.key.revoked:
    post:
      summary: A virtual key was revoked and stops authenticating immediately.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceKeyRevokedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  governance.key.rotated:
    post:
      summary: A virtual key was rotated, minting a successor secret (shown once at the rotate response).
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceKeyRotatedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  governance.override.deleted:
    post:
      summary: A per-node governance limit override was removed, restoring the compiled limit.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideDeletedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  governance.override.set:
    post:
      summary: A per-node governance limit override was set.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideSetDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  mode.set:
    post:
      summary: The proxy mode was changed (ledger|shadow|route). A promote to route made without shadow evidence carries the route_without_shadow_evidence badge here exactly as it does in the audit trail.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ModeSetDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  token.minted:
    post:
      summary: A tta_ management token was minted. Carries the public token id only; the secret is shown once, at the mint response.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenMintedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
  token.revoked:
    post:
      summary: A tta_ management token was revoked and stops authenticating immediately.
      description: Asynchronous event payload, delivered to configured webhook endpoints with Standard Webhooks signatures (webhook-id / webhook-timestamp / webhook-signature) and always available by polling GET /tt/api/v1/events — push is an optimization, polling is sufficient (§2.8).
      tags:
        - events
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRevokedDelivery'
      responses:
        2XX:
          description: Return any 2XX status to acknowledge receipt.
components:
  schemas:
    AlertHistoryItem:
      type: object
      properties:
        at:
          type: string
        deep_link:
          type: string
        message:
          type: string
        method:
          type: string
        provenance_line:
          type: string
        rule_id:
          type: string
        rule_type:
          type: string
        transition:
          type: string
      required:
        - at
        - deep_link
        - message
        - provenance_line
        - rule_id
        - rule_type
        - transition
    AlertHistoryResponse:
      type: object
      properties:
        history:
          type: array
          items:
            $ref: '#/components/schemas/AlertHistoryItem'
      required:
        - history
    AlertState:
      type: object
      properties:
        deep_link:
          type: string
          description: A ready-made query that reproduces the data behind this alert. It exists so the first question after an alert — "show me" — is one click rather than a query a human has to reconstruct correctly under pressure.
        filter:
          type: string
        fired:
          type: boolean
        message:
          type: string
        notify:
          type: array
          items:
            type: string
        provenance_line:
          type: string
          description: What was actually measured, in words, so an alert can be checked rather than believed. It travels with the message and the deep link as the honest-copy triple this surface guarantees on every alert.
        rule_id:
          type: string
        silenced:
          type: boolean
        since:
          type: string
        state:
          type: string
        type:
          type: string
        window:
          type: string
      required:
        - deep_link
        - filter
        - fired
        - notify
        - rule_id
        - silenced
        - state
        - type
        - window
    AlertsResponse:
      type: object
      properties:
        alerts:
          type: array
          items:
            $ref: '#/components/schemas/AlertState'
        delivery_failures:
          type: array
          items:
            $ref: '#/components/schemas/DeliveryFailure'
      required:
        - alerts
    AnalyticsCaps:
      type: object
      properties:
        chargeback_reports:
          type: integer
          format: int64
        enabled:
          type: boolean
        forecast:
          type: boolean
        measurement:
          type: boolean
        metacost:
          type: boolean
        recommendations:
          type: boolean
      required:
        - chargeback_reports
        - enabled
        - forecast
        - metacost
        - recommendations
    Annotation:
      type: object
      properties:
        actor:
          type: string
        at:
          type: string
        class:
          type: string
        deep_link:
          type: string
        kind:
          type: string
        label:
          type: string
      required:
        - at
        - class
        - label
    AnnotationsResponse:
      type: object
      properties:
        annotations:
          type: array
          items:
            $ref: '#/components/schemas/Annotation'
        classes:
          type: array
          description: 'The RESOLVED class set — all four when unfiltered. Echoed for the same reason as `since`: a chart legend that names classes the server did not actually include is a claim nobody verified.'
          items:
            type: string
        since:
          type: string
          description: The RESOLVED lower bound the server actually applied, echoed so a client that omitted `since` labels its overlay with the real window rather than an assumed one.
      required:
        - annotations
        - classes
        - since
    AnomaliesResponse:
      type: object
      properties:
        correlations:
          anyOf:
            - $ref: '#/components/schemas/AnomalyCorrelations'
            - type: "null"
        evaluation:
          $ref: '#/components/schemas/AnomalyEvaluation'
        findings:
          type: array
          items:
            $ref: '#/components/schemas/AnomalyFinding'
        method:
          type: string
        scope:
          type: string
      required:
        - evaluation
        - findings
        - method
        - scope
    AnomalyCandidate:
      type: object
      properties:
        change:
          $ref: '#/components/schemas/AnomalyChange'
        finding_id:
          type: string
        lag_minutes:
          type: number
        method:
          type: string
        note:
          type: string
        rank:
          type: integer
          format: int64
        score:
          type: number
      required:
        - change
        - finding_id
        - lag_minutes
        - method
        - note
        - rank
        - score
    AnomalyCause:
      type: object
      properties:
        contribution:
          $ref: '#/components/schemas/Money'
        dimension:
          type: string
        filter:
          type: string
          description: A ready-made filter expression that isolates this cause's rows. The point of a ranked cause list is that the next question is already answerable, so each cause ships the query that answers it.
        value:
          type: string
      required:
        - contribution
        - dimension
        - filter
        - value
    AnomalyChange:
      type: object
      properties:
        actor:
          type: string
        at:
          type: string
        evidence_ref:
          type: string
        kind:
          type: string
        ref:
          type: string
        summary:
          type: string
      required:
        - at
        - kind
        - ref
    AnomalyCorrelations:
      type: object
      description: Wire shape of AnomalyCorrelations (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/AnomalyCandidate'
        changes_considered:
          type: integer
          format: int64
        lookback_minutes:
          type: number
        method:
          type: string
        reason:
          type: string
      required:
        - candidates
        - changes_considered
        - lookback_minutes
        - method
    AnomalyEvaluation:
      type: object
      description: 'The AFFIRMATIVE NEGATIVE: what was actually evaluated, present whether or not anything fired. It is what stops an empty findings array meaning either "nothing was anomalous" or "nothing was watched" — two states with opposite operational meaning that would otherwise look identical.'
      properties:
        clean:
          type: boolean
        computed_at:
          type: string
        dimensions_not_watched:
          type: array
          description: Dimensions this detector is NOT watching. Published beside the watched set because the honest reading of a clean result depends entirely on what was in scope to be dirty.
          items:
            type: string
        dimensions_watched:
          type: array
          items:
            type: string
        method:
          type: string
        repro:
          $ref: '#/components/schemas/AnomalyRepro'
        series_evaluated:
          type: integer
          format: int64
        series_unevaluable:
          type: integer
          format: int64
          description: 'Series that could NOT be evaluated for want of baseline history. They are neither clean nor anomalous, and counting them separately is what keeps `clean: true` from over-claiming.'
        unpriced_excluded:
          type: integer
          format: int64
          description: 'Rows the detector DROPPED before computing anything, because their spend could not be priced. This is the bound on what this feed could have missed: an unpriced row never becomes part of a finding''s impact, so a spike occurring entirely in unpriced spend is ABSENT from `findings` rather than ranked low in it. Non-zero means the anomaly surface is under-reporting by an amount this count bounds.'
        window_evaluated:
          $ref: '#/components/schemas/Window'
      required:
        - clean
        - dimensions_not_watched
        - dimensions_watched
        - method
        - repro
        - series_evaluated
        - series_unevaluable
        - unpriced_excluded
        - window_evaluated
    AnomalyFinding:
      type: object
      properties:
        causes:
          type: array
          items:
            $ref: '#/components/schemas/AnomalyCause'
        detected_at:
          type: string
        dimension:
          type: string
        id:
          type: string
        impact:
          $ref: '#/components/schemas/Money'
          description: The summed excess dollars over the finding's window. It is ALWAYS priced — unpriced rows are excluded upstream by the detector, so `usd` is never null here. That is not a guarantee that every dollar was seen; it is the reason `evaluation.unpriced_excluded` has to be read beside it.
        method:
          type: string
        peak_z:
          type: number
        repro:
          $ref: '#/components/schemas/AnomalyRepro'
        value:
          type: string
        window:
          $ref: '#/components/schemas/Window'
      required:
        - causes
        - detected_at
        - dimension
        - id
        - impact
        - method
        - peak_z
        - repro
        - value
        - window
    AnomalyRepro:
      type: object
      properties:
        baseline_population:
          type: integer
          format: int64
        impact_floor_usd:
          type: number
          description: A configured detector THRESHOLD in dollars — the impact below which a deviation is not reported — not a measured cost. It is a parameter of the method, published so the method can be reproduced.
        method:
          type: string
          description: The detection method id, echoed on the response, on every finding and on the evaluation. With the parameters beside it an operator can recompute a flag from the ledger by hand — a detector that cannot be checked is a detector that has to be trusted.
        sigma_min:
          type: number
        w:
          type: integer
          format: int64
        z_threshold:
          type: number
      required:
        - baseline_population
        - impact_floor_usd
        - method
        - sigma_min
        - w
        - z_threshold
    AppliedInfo:
      type: object
      properties:
        at:
          type: string
        audit_id:
          type: string
        diff_sha256:
          type: string
        generation:
          type: integer
          format: int64
        prior_tier:
          type: string
      required:
        - at
        - audit_id
        - diff_sha256
        - generation
    Assessed:
      type: object
      description: Wire shape of Assessed (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        approximations:
          type: array
          items:
            type: string
        baseline:
          $ref: '#/components/schemas/Baseline'
        basis:
          $ref: '#/components/schemas/Basis'
        ci:
          anyOf:
            - $ref: '#/components/schemas/ConfidenceInterval'
            - type: "null"
        disclosure:
          type: string
        estimated:
          type: boolean
        excluded_rows:
          type: integer
          format: int64
        unpriced:
          type: boolean
        unpriced_excluded:
          type: integer
          format: int64
        usd:
          type:
            - number
            - "null"
        verdict:
          type: string
      required:
        - baseline
        - basis
        - estimated
        - unpriced
        - unpriced_excluded
        - verdict
    AuditQueryRequest:
      type: object
      description: An optional filter over the trail plus the house cursor block. Every member is optional; a zero request returns the first page of the whole trail.
      properties:
        action:
          type: string
        actor:
          type: string
        cursor:
          type: string
          description: OPAQUE page position, echoed back VERBATIM from a previous response's `next_cursor`. Absent means the first page. Never construct one. On the operations that embed THIS type it is base64url over the server's own keyset position record — stated for this type rather than for the API, because two operations (`analyticsRows`, `shareRows`) declare their own cursor and encode a scan-window offset instead; echoing the value back is correct everywhere, parsing one is supported nowhere. The encoding is an interop promise rather than confidentiality, and a cursor carries no secret, no credential and no identity, only a position you could already see in the page it came from.
        limit:
          type: integer
          format: int64
          description: Page size. Absent or 0 takes this operation's documented default; above its maximum the request is REFUSED with a 400 naming the cap, never silently clamped — a clamped page is a lie about what the server heard. The pair in force is advertised at `/capabilities.management_api.pagination`, which each operation may override.
        origin:
          type: string
        result:
          type: string
        since:
          type: string
          description: RFC 3339, INCLUSIVE lower bound on a row's `ts`. Malformed input is a 400 naming the parameter, never a silently ignored filter that would return more rows than the caller asked to see.
        until:
          type: string
          description: RFC 3339, EXCLUSIVE upper bound on a row's `ts` — so `since`/`until` pairs tile a period without overlapping at the boundary.
    AuditResponse:
      type: object
      description: One page of the append-only audit trail, most-recent first. `next_cursor` is absent at the end of the feed (never an empty string) and `has_more` is derived from it, so the two can never disagree.
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        rows:
          type: array
          items:
            $ref: '#/components/schemas/AuditRow'
      required:
        - has_more
        - rows
    AuditRow:
      type: object
      properties:
        action:
          type: string
        actor:
          type: string
          description: 'Who acted: a user name for a session, or `token:<public handle>` for a bearer credential — the handle, never the secret. `anonymous` is an unauthenticated attempt, which is itself a row.'
        badges:
          type: array
          description: Disclosures attached to the decision rather than buried in it — today `route_without_shadow_evidence`, set when a promotion to route happened without a satisfied shadow-evidence gate, whatever origin applied it.
          items:
            type: string
        diff_sha256:
          type: string
          description: A content HASH of the applied diff, never the diff itself and never a value from it. It exists so two operators can agree they are talking about the same change without the trail becoming a copy of the configuration — including its secret-bearing environment references.
        generation:
          type:
            - integer
            - "null"
          format: int64
        origin:
          type: string
        resource:
          type: string
          description: What the action was performed on — for a denial, the request's METHOD and PATH. A credential that appeared in that path (the `tts_` segment of a share read) is replaced with `<redacted>` BEFORE the row is written, structurally from the operation's credential parameters and by prefix from the server's own credential vocabulary, so this read cannot be used to harvest one.
        result:
          type: string
          description: '`ok` | `rejected` | `failed`. REJECTED and FAILED rows are the point: a trail that recorded only successes could not answer "who tried", which is the question an incident starts with. Denied READS are recorded too, not only denied writes.'
        role:
          type: string
        summary:
          type: string
        ts:
          type: string
        v:
          type: integer
          format: int64
      required:
        - action
        - actor
        - origin
        - result
        - role
        - ts
        - v
    AuthCaps:
      type: object
      properties:
        mode:
          type: string
        role:
          type: string
      required:
        - mode
        - role
    BacktestMarker:
      type: object
      properties:
        deep_link:
          type: string
        fired_at:
          type: string
        message:
          type: string
        provenance_line:
          type: string
          description: The sample/estimated/unpriced disclosure for the window this marker covers, in the same words the live alert carries, so a backtested marker and a real firing are read the same way.
        resolved_at:
          type: string
      required:
        - deep_link
        - fired_at
        - message
        - provenance_line
    BacktestResponse:
      type: object
      properties:
        disclosures:
          type: array
          items:
            type: string
        provenance:
          $ref: '#/components/schemas/Provenance'
        records:
          type: integer
          format: int64
        rules:
          type: array
          items:
            $ref: '#/components/schemas/BacktestRuleResult'
        window:
          $ref: '#/components/schemas/Window'
      required:
        - provenance
        - records
        - rules
        - window
    BacktestRuleResult:
      type: object
      properties:
        backtestable:
          type: boolean
          description: False for rule types whose firing depends on runtime state rather than ledger facts (budget-threshold and anomaly rules). Such a rule reports its reason in `disclosure` and contributes NO markers — it is not silently omitted from the result, because an absent rule reads as "it never fired".
        disclosure:
          type: string
        markers:
          type: array
          items:
            $ref: '#/components/schemas/BacktestMarker'
        rule_id:
          type: string
        rule_type:
          type: string
        window:
          type: string
      required:
        - backtestable
        - markers
        - rule_id
        - rule_type
        - window
    Baseline:
      type: object
      properties:
        desc:
          type: string
        kind:
          type: string
      required:
        - desc
        - kind
    Basis:
      type: object
      properties:
        evidence_ref:
          type: string
        method:
          type: string
        reason:
          type: string
        rows:
          type: integer
          format: int64
        window:
          $ref: '#/components/schemas/Window'
      required:
        - method
        - rows
        - window
    Bound:
      type: object
      properties:
        convergence_lag_rows:
          type: integer
          format: int64
        dropped_rows:
          type: integer
          format: int64
        estimate_skew_p95:
          type: number
        in_flight_reserved:
          type: integer
          format: int64
        tee_lag_rows:
          type: integer
          format: int64
      required:
        - dropped_rows
        - tee_lag_rows
    BreakerReachability:
      type: object
      properties:
        consecutive_failures:
          type: integer
          format: int64
        err:
          type: string
        reachable:
          type: boolean
        since:
          type: string
        state:
          type: string
      required:
        - consecutive_failures
        - reachable
        - state
    Budget:
      type: object
      properties:
        burn:
          $ref: '#/components/schemas/Money'
        crossed_thresholds:
          type: array
          items:
            type: number
        enforcement:
          type: string
          description: '`alert_only` never stops spending. `enforcing` refuses requests at the limit. `suspended` is the one to watch: an enforcing budget whose evaluator is stale or dead, so enforcement HAS FAILED OPEN — spending is not being stopped even though the configuration says it should be. Reported rather than hidden, because a silently failed-open enforcer is the state most likely to be discovered by an invoice.'
        filter:
          type: string
        forecast:
          $ref: '#/components/schemas/BudgetForecast'
        fraction:
          type: number
        id:
          type: string
        limit:
          $ref: '#/components/schemas/Money'
        overrun_bound:
          $ref: '#/components/schemas/OverrunBound'
        period:
          type: string
        period_start:
          type: string
        resets_at:
          type: string
        thresholds:
          type: array
          items:
            type: number
        unpriced_excluded:
          type: integer
          format: int64
          description: 'In-scope rows EXCLUDED from `burn` because the pricing lookup missed. This is the disclosure that keeps a budget honest: without it, a budget could look comfortable precisely because the rows that would have breached it were the ones nobody could price. Non-zero means the burn is a LOWER bound.'
      required:
        - burn
        - crossed_thresholds
        - enforcement
        - filter
        - forecast
        - fraction
        - id
        - limit
        - overrun_bound
        - period
        - period_start
        - resets_at
        - thresholds
        - unpriced_excluded
    BudgetCaps:
      type: object
      properties:
        defined:
          type: integer
          format: int64
        enforce_granted:
          type: boolean
      required:
        - defined
        - enforce_granted
    BudgetExceededDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/BudgetExceededDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - budget.exceeded
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    BudgetExceededDeliveryData:
      type: object
      properties:
        budget_exceeded:
          $ref: '#/components/schemas/BudgetExceededEvent'
      required:
        - budget_exceeded
    BudgetExceededEvent:
      type: object
      description: A budget's burn reached or passed its limit. `budget` is the SAME shape `GET /budgets` serves, built by the SAME mapper, so the poll surface and the push surface cannot disagree about a dollar.
      properties:
        budget:
          $ref: '#/components/schemas/Budget'
        budget_id:
          type: string
        excluded_spend:
          $ref: '#/components/schemas/Money'
          description: 'What the rows the pricing lookup MISSED were worth — and nobody knows, which is what ''unpriced'' means. So this is an UNPRICED Money (`usd: null`) whenever any row was excluded, never $0.00: a confident zero here would assert that the rows nobody could price cost nothing, and a budget can look comfortable precisely because the rows that would have breached it were the unpriceable ones. It is priced at exactly 0 ONLY when `budget.unpriced_excluded` is 0, where zero rows excluded really is zero dollars excluded. That count is where it already lives; this payload does not repeat it.'
      required:
        - budget
        - budget_id
        - excluded_spend
    BudgetForecast:
      type: object
      properties:
        already_exhausted:
          type: boolean
        exhaust_at:
          type: string
        insufficient:
          type: boolean
        method:
          type: string
          description: The projection method that produced `exhaust_at`, carried so a forecast can be argued with. When there is too little data the forecast reports `insufficient` instead of projecting from noise.
        rate_usd_per_hour:
          type:
            - number
            - "null"
      required:
        - already_exhausted
        - insufficient
        - method
    BudgetResponse:
      type: object
      properties:
        budget:
          $ref: '#/components/schemas/Budget'
      required:
        - budget
    BudgetThresholdCrossedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/BudgetThresholdCrossedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - budget.threshold_crossed
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    BudgetThresholdCrossedDeliveryData:
      type: object
      properties:
        budget_threshold_crossed:
          $ref: '#/components/schemas/BudgetThresholdCrossedEvent'
      required:
        - budget_threshold_crossed
    BudgetThresholdCrossedEvent:
      type: object
      description: A budget crossed one of its configured ascending alert fractions. Carries the same live state and the same unpriced disclosure as `budget.exceeded`.
      properties:
        budget:
          $ref: '#/components/schemas/Budget'
        budget_id:
          type: string
        excluded_spend:
          $ref: '#/components/schemas/Money'
          description: 'Unpriced (`usd: null`) whenever rows were excluded from `budget.burn`, never $0.00 — see the same field on `budget.exceeded` for the full reasoning.'
        threshold:
          type: number
          description: The CONFIGURED ascending fraction that was crossed (0.8, 0.9, …), not the live observed fraction — read `budget.fraction` for that. The two differ whenever burn moved between the crossing and the read.
      required:
        - budget
        - budget_id
        - excluded_spend
        - threshold
    BudgetsResponse:
      type: object
      properties:
        budgets:
          type: array
          items:
            $ref: '#/components/schemas/Budget'
      required:
        - budgets
    BurnRate:
      type: object
      properties:
        exhaust_at:
          type: string
        usd_per_hour:
          type: number
      required:
        - usd_per_hour
    CacheAgeBucket:
      type: object
      properties:
        count:
          type: integer
          format: int64
        label:
          type: string
      required:
        - count
        - label
    CacheEvidenceResponse:
      type: object
      properties:
        age_distribution:
          type: array
          items:
            $ref: '#/components/schemas/CacheAgeBucket'
        excluded_semantic:
          type: integer
          format: int64
          description: 'Semantic hits EXCLUDED-AND-COUNTED from every licensed dollar because nothing bounds their correctness. Reported as a count and never as money: crediting savings whose wrong-hit probability is unbounded is the exact trade this surface refuses to make.'
        kinds:
          type: array
          items:
            $ref: '#/components/schemas/CacheKindEvidence'
        provenance:
          $ref: '#/components/schemas/Provenance'
      required:
        - age_distribution
        - excluded_semantic
        - kinds
        - provenance
    CacheKindEvidence:
      type: object
      properties:
        bound:
          type: number
        calibrated:
          type: boolean
        hits:
          type: integer
          format: int64
        kind:
          type: string
        licensed:
          type: boolean
        posture:
          type: string
        reason:
          type: string
        samples:
          type: integer
          format: int64
        threshold:
          type: number
      required:
        - hits
        - kind
        - licensed
        - posture
    CacheOutcomeCounts:
      type: object
      properties:
        bypass:
          type: integer
          format: int64
        error:
          type: integer
          format: int64
        hit:
          type: integer
          format: int64
        miss:
          type: integer
          format: int64
        would_hit:
          type: integer
          format: int64
      required:
        - bypass
        - error
        - hit
        - miss
        - would_hit
    CachePreview:
      type: object
      description: Wire shape of CachePreview (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        approximations:
          type: array
          description: 'The three mandatory qualifications on every number here. `fixed_usage`: the would-save reprices each repeat row''s LOGGED tokens, not the tokens a fresh call would generate. `recorded_runtime_state`: it reads recorded ledger facts, not live state. `fingerprint_approximated`: the ledger stores a messages-only prompt hash rather than the full fingerprint, so requests differing only in system prompt or tools collide here and would NOT share an entry in production — which makes every repeat count an UPPER BOUND.'
          items:
            type: string
        exact:
          $ref: '#/components/schemas/CachePreviewExact'
        kind:
          type: string
        pricing_snapshot:
          type: string
        primary_instrument:
          type: string
          description: The note that SHADOW MODE, not this estimate, is the primary cache measurement. It is on every response because the failure mode of a preview endpoint is being quoted as a result.
        semantic:
          $ref: '#/components/schemas/CachePreviewSemantic'
        total_rows:
          type: integer
          format: int64
        truncated:
          type: boolean
        unkeyable_rows:
          type: integer
          format: int64
        window:
          $ref: '#/components/schemas/Window'
      required:
        - approximations
        - exact
        - kind
        - pricing_snapshot
        - primary_instrument
        - semantic
        - total_rows
        - truncated
        - unkeyable_rows
        - window
    CachePreviewExact:
      type: object
      properties:
        distinct_keys:
          type: integer
          format: int64
        estimated_repeats:
          type: integer
          format: int64
        keyable_rows:
          type: integer
          format: int64
        repeat_rate:
          type: number
        repeat_rows:
          type: integer
          format: int64
        unpriced_repeats:
          type: integer
          format: int64
        would_save_usd:
          type: number
          description: 'A BARE float, and the one honesty gap on this surface that this contract discloses rather than hides. Read it beside `unpriced_repeats`: when no repeat could be repriced this field reads 0 and ONLY a non-zero `unpriced_repeats` distinguishes that from a genuine "nothing to save". The producing type lives in internal/replay, outside the card that registered this operation, so the bound is published here rather than implied away by a typed wrapper this card could not correctly add.'
      required:
        - distinct_keys
        - estimated_repeats
        - keyable_rows
        - repeat_rate
        - repeat_rows
        - unpriced_repeats
        - would_save_usd
    CachePreviewResponse:
      type: object
      properties:
        preview:
          $ref: '#/components/schemas/CachePreview'
        provenance:
          $ref: '#/components/schemas/Provenance'
      required:
        - preview
        - provenance
    CachePreviewSemantic:
      type: object
      properties:
        can_estimate:
          type: boolean
          description: False on a ledger-only preview, with `reason` saying why. Re-embedding needs the captured BODIES, which live in the content store rather than the ledger — so this tier reports its capture `coverage` and REFUSES, which is the honest answer DC15 mandates over a guess.
        content_rows:
          type: integer
          format: int64
        coverage:
          type: number
        reason:
          type: string
        status:
          type: string
        total_rows:
          type: integer
          format: int64
      required:
        - can_estimate
        - content_rows
        - coverage
        - reason
        - status
        - total_rows
    CachePurgeRequest:
      type: object
      description: 'The purge target: a closed dimension plus the value to purge along it. An unrecognised `by` is refused before the purge seam is touched, because a typo that silently matched nothing would be indistinguishable from a purge that worked.'
      properties:
        by:
          type: string
        value:
          type: string
      required:
        - by
        - value
    CachePurgeResponse:
      type: object
      description: What was purged, and how many entries were PHYSICALLY deleted. `deleted` is the answer to "did that do anything?" and a 0 is a real and frequently correct answer — this operation has no dry-run, so the count is the first honest feedback an operator gets.
      properties:
        by:
          type: string
        deleted:
          type: integer
          format: int64
        note:
          type: string
        value:
          type: string
      required:
        - by
        - deleted
        - value
    CachePurgedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/CachePurgedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - cache.purged
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    CachePurgedDeliveryData:
      type: object
      properties:
        cache_purged:
          $ref: '#/components/schemas/CachePurgedEvent'
      required:
        - cache_purged
    CachePurgedEvent:
      type: object
      properties:
        actor:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
        target:
          type: string
      required:
        - actor
        - origin
    CacheSavings:
      type: object
      description: 'A cache-savings figure (§2.6). HAND-PINNED: marshaling REFUSES without basis + verdict_ref (mirrors Savings); usd is null when unpriced, never 0; excluded/unpriced hit counts are disclosed, never folded into a dollar.'
      properties:
        basis:
          type: string
        estimated:
          type: boolean
        excluded_semantic:
          type: integer
          format: int64
          description: semantic hits EXCLUDED-and-COUNTED (unverified — no policy licenses them); omitted when 0
        unpriced:
          type: boolean
        unpriced_hits:
          type: integer
          format: int64
          description: hits whose reprice MISSED — in the row population, contributing no USD; omitted when 0
        usd:
          type:
            - number
            - "null"
          description: null when unpriced (never 0)
        verdict_ref:
          type: string
      required:
        - basis
        - estimated
        - unpriced
        - usd
        - verdict_ref
    CacheStatsResponse:
      type: object
      properties:
        hits_by_kind:
          type: object
          description: Hit counts per cache kind (exact, normalized, semantic). Semantic hits are excluded from the licensed dollar unless a calibrated verified policy licenses them, so this map and `savings` can legitimately disagree — `GET /cache/evidence` explains why.
          additionalProperties:
            type: integer
            format: int64
        outcomes:
          $ref: '#/components/schemas/CacheOutcomeCounts'
        provenance:
          $ref: '#/components/schemas/Provenance'
        savings:
          $ref: '#/components/schemas/CacheSavings'
          description: 'The LICENSED figure: cache HIT rows only, repriced from each source row''s recorded usage — money genuinely not spent. Its `basis` says so in words and its `verdict_ref` points at `/tt/api/v1/cache/evidence`, the authority that licenses it. `usd` is null (never 0) when hits occurred that nobody could reprice, with `unpriced_hits` naming that population.'
        shadow_savings:
          $ref: '#/components/schemas/CacheSavings'
          description: 'The WOULD-HIT counterfactual, and it is NOT a saving: those requests were forwarded and were PAID FOR, so nothing was avoided. It carries the same type and field names as `savings` and the opposite meaning, which is exactly why `basis` exists — it is the field that stops the two being added together. They are never merged.'
      required:
        - hits_by_kind
        - outcomes
        - provenance
        - savings
        - shadow_savings
    CacheWriteDetail:
      type: object
      properties:
        ephemeral_1h_tokens:
          type: integer
          format: int64
        ephemeral_5m_tokens:
          type: integer
          format: int64
      required:
        - ephemeral_1h_tokens
        - ephemeral_5m_tokens
    CalibrationActivateResponse:
      type: object
      properties:
        active:
          type: string
        audited_at:
          type: string
        diff:
          type: array
          items:
            $ref: '#/components/schemas/ThresholdDiffEntry'
        note:
          type: string
          description: 'States the thing the status code cannot: the manifest pointer moved, but LIVE ROUTING DID NOT. The running proxy adopts the newly active artifact on its next config reload.'
        shadow:
          type: string
        versions:
          type: array
          items:
            type: string
      required:
        - active
        - diff
        - note
        - shadow
        - versions
    CalibrationActivatedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/CalibrationActivatedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - calibration.activated
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    CalibrationActivatedDeliveryData:
      type: object
      properties:
        calibration_activated:
          $ref: '#/components/schemas/CalibrationActivatedEvent'
      required:
        - calibration_activated
    CalibrationActivatedEvent:
      type: object
      properties:
        actor:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
        version:
          type: string
      required:
        - actor
        - origin
    CalibrationDetail:
      type: object
      properties:
        created_at:
          type: string
        dataset_window:
          type: string
        diff:
          type: array
          items:
            $ref: '#/components/schemas/ThresholdDiffEntry'
        id:
          type: string
        is_active:
          type: boolean
        is_shadow:
          type: boolean
        parent_id:
          type: string
        source:
          type: string
        status:
          type: string
        sweep:
          anyOf:
            - $ref: '#/components/schemas/CalibrationSweep'
            - type: "null"
        sweep_refusal:
          type: string
          description: Present when a sweep was requested but cannot be honestly computed — today, under a v2 `routing.policy` document, whose routing can depend on structure a single-threshold re-decision cannot reproduce. It is stated rather than left as an absent `sweep`, so "we will not answer this" is never mistaken for "no data".
        thresholds:
          type: object
          additionalProperties:
            type: number
        training_stats:
          type: object
          additionalProperties:
            type: number
        warnings:
          type: array
          items:
            type: string
      required:
        - created_at
        - diff
        - id
        - is_active
        - is_shadow
        - source
        - status
        - thresholds
        - warnings
    CalibrationList:
      type: object
      properties:
        active:
          type: string
        artifacts:
          type: array
          items:
            $ref: '#/components/schemas/CalibrationSummary'
        configured:
          type: boolean
        dir:
          type: string
          description: The SERVER-SIDE path this deployment reads calibration artifacts from, echoed so an operator can tell which directory a board belongs to. It is an absolute filesystem path on the server and it is visible at the VIEWER floor — deliberately, because it is operator-supplied configuration (`classifier.calibration_file`) rather than a secret, and a board with no way to say where it came from is a provenance gap. Neither CA5 half can see this field (the structural sweep matches field NAMES and the live scan matches credential PREFIXES, and a path is neither), so it is argued here rather than left to a sweep that would never raise it. Absent when no directory is configured.
        shadow:
          type: string
      required:
        - active
        - artifacts
        - configured
        - shadow
    CalibrationSummary:
      type: object
      properties:
        created_at:
          type: string
        dataset_window:
          type: string
        id:
          type: string
        is_active:
          type: boolean
        is_shadow:
          type: boolean
        parent_id:
          type: string
        source:
          type: string
        status:
          type: string
        thresholds:
          type: object
          additionalProperties:
            type: number
        training_stats:
          type: object
          additionalProperties:
            type: number
      required:
        - created_at
        - id
        - is_active
        - is_shadow
        - source
        - status
        - thresholds
    CalibrationSweep:
      type: object
      properties:
        approximations:
          type: array
          description: 'Always `fixed_usage` and `recorded_runtime_state`: the sweep reprices the LOGGED token usage at each candidate threshold, so it models what routing would have cost, not what a differently-routed request would have produced.'
          items:
            type: string
        baseline_threshold:
          type: number
        cheap_tier:
          type: string
        direction:
          type: string
        frontier_tier:
          type: string
        labeled_rows:
          type: integer
          format: int64
        min_labeled_rows:
          type: integer
          format: int64
        points:
          type: array
          items:
            $ref: '#/components/schemas/SweepPoint'
        provenance:
          $ref: '#/components/schemas/Provenance'
        quality_measurable:
          type: boolean
        quality_refusal:
          type: string
        rule_id:
          type: string
        signal:
          type: string
      required:
        - approximations
        - baseline_threshold
        - cheap_tier
        - direction
        - frontier_tier
        - labeled_rows
        - min_labeled_rows
        - points
        - provenance
        - quality_measurable
        - rule_id
        - signal
    Candidate:
      type: object
      properties:
        eligible:
          type: boolean
        score:
          type:
            - number
            - "null"
        tier:
          type: string
      required:
        - eligible
        - tier
    Capabilities:
      type: object
      description: 'The honest advertisement of ONE deployment: what is wired here (never what the product can do in general), which contract epoch and revision this server serves, and the cross-cutting policies in force. An unwired feature reports false rather than a teaser, and `badges` is recomputed from the live running mode on every read so a config change applied outside the dashboard is disclosed identically to one applied through it.'
      properties:
        api_version:
          type: integer
          format: int64
          description: The management wire-contract EPOCH (B.2's six-numbers table), the same number as the leading digit of `management_api.version` and of the served document's `info.version`. It is NOT the `/v1` path segment, which is a permanent namespace.
        badges:
          type: array
          description: Disclosures about the running configuration that a client should surface rather than bury — today `route_without_shadow_evidence`, present whenever traffic is being routed without a satisfied shadow-evidence gate, WHATEVER origin applied that mode (dashboard, SIGHUP, boot, or a raw config write).
          items:
            type: string
        engine:
          $ref: '#/components/schemas/EngineCaps'
        features:
          $ref: '#/components/schemas/Features'
        filter_fields:
          type: array
          description: 'The CLOSED field registry both query forms validate against: the `filter` JSON AST in POST bodies and the `q` text form on GET lists compile to the same predicate over exactly these fields. A field absent here is not filterable, and no third query language exists.'
          items:
            $ref: '#/components/schemas/Field'
        management_api:
          $ref: '#/components/schemas/ManagementAPICaps'
        staleness:
          $ref: '#/components/schemas/Staleness'
      required:
        - api_version
        - badges
        - engine
        - features
        - filter_fields
        - management_api
        - staleness
    CaptureCaps:
      type: object
      properties:
        request:
          type: boolean
        response:
          type: boolean
      required:
        - request
        - response
    CascadeBlock:
      type: object
      properties:
        attempts:
          type: integer
          format: int64
        cascade_unpriced:
          type: integer
          format: int64
        final:
          type: string
        judge_cost:
          anyOf:
            - $ref: '#/components/schemas/StageCost'
            - type: "null"
        judge_ms:
          type: integer
          format: int64
        stages:
          type: array
          items:
            $ref: '#/components/schemas/CascadeStage'
      required:
        - attempts
        - cascade_unpriced
        - final
        - stages
    CascadeStage:
      type: object
      description: Wire shape of CascadeStage (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        accepted:
          type: boolean
        check:
          type: object
          additionalProperties: {}
        cost:
          $ref: '#/components/schemas/StageCost'
        id:
          type: string
        latency_ms:
          type: integer
          format: int64
        model:
          type: string
        tier:
          type: string
        usage:
          $ref: '#/components/schemas/Usage'
      required:
        - accepted
        - cost
        - id
        - latency_ms
        - tier
        - usage
    ChargebackPeriod:
      type: object
      properties:
        end:
          type: string
        start:
          type: string
        tz:
          type: string
      required:
        - end
        - start
        - tz
    ChargebackReport:
      type: object
      description: Wire shape of ChargebackReport (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        dimension:
          type: string
        export:
          $ref: '#/components/schemas/ExportMeta'
        id:
          type: string
        mode:
          type: string
        period:
          $ref: '#/components/schemas/ChargebackPeriod'
        provenance:
          $ref: '#/components/schemas/Provenance'
        reconcile_ref:
          type: string
        rows:
          type: array
          items:
            $ref: '#/components/schemas/ChargebackRow'
        split_rules:
          type: array
          items:
            $ref: '#/components/schemas/SplitRuleDesc'
        total_cost:
          $ref: '#/components/schemas/Money'
      required:
        - dimension
        - export
        - id
        - mode
        - period
        - provenance
        - rows
        - total_cost
    ChargebackReportsResponse:
      type: object
      properties:
        reports:
          type: array
          items:
            $ref: '#/components/schemas/ChargebackReport'
      required:
        - reports
    ChargebackRow:
      type: object
      properties:
        asserted:
          type: boolean
        cost:
          $ref: '#/components/schemas/Money'
        entity:
          type: string
        estimated_rows:
          type: integer
          format: int64
        kind:
          type: string
        requests:
          type: integer
          format: int64
        shared_allocated:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        shared_rules:
          type: array
          items:
            type: string
        snapshots:
          type: array
          items:
            type: string
        tokens:
          type: integer
          format: int64
        unpriced_rows:
          type: integer
          format: int64
      required:
        - cost
        - entity
        - estimated_rows
        - kind
        - requests
        - tokens
        - unpriced_rows
    ConfidenceInterval:
      type: object
      properties:
        hi_usd:
          type: number
        level:
          type: number
        lo_usd:
          type: number
      required:
        - hi_usd
        - level
        - lo_usd
    ConfigAppliedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/ConfigAppliedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - config.applied
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    ConfigAppliedDeliveryData:
      type: object
      properties:
        config_applied:
          $ref: '#/components/schemas/ConfigAppliedEvent'
      required:
        - config_applied
    ConfigAppliedEvent:
      type: object
      properties:
        actor:
          type: string
        badges:
          type: array
          items:
            type: string
        diff_sha256:
          type: string
        generation:
          type:
            - integer
            - "null"
          format: int64
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
      required:
        - actor
        - badges
        - origin
    ConfigApplyRequest:
      type: object
      description: A config write in the granular JSON form. `PUT /config` is the declarative twin and takes the same document as a raw application/yaml body.
      properties:
        dry_run:
          type: boolean
          description: '`true` previews: the exact diff, the restart-required verdict and the would-be badges, with nothing written and NOTHING AUDITED. `expected_generation` is still checked, so a preview also tells you whether you would have lost the race.'
        expected_generation:
          type: integer
          format: int64
          description: 'The reload generation this write is conditional on, from GET /config/running. A mismatch is 409 `generation_conflict` carrying `current_generation` and NOTHING is written. It is effectively required: omitting it sends 0, which conflicts with every running generation, so the write is refused rather than silently forced.'
        yaml:
          type: string
          description: 'The whole config document as a string. Prefer PUT /config for declarative and git-driven workflows: it takes the file as raw bytes, which removes the JSON string round trip that an operator''s comments and formatting have to survive here.'
      required:
        - expected_generation
        - yaml
    ConfigApplyResponse:
      type: object
      description: The outcome of a config write — the response of BOTH POST /config/apply and PUT /config, because the two differ in how the caller carries the CAS and the body, never in what an applied config change IS. Every member is always present except `audit_id`, whose ABSENCE is the honest signal that nothing was written to the trail (i.e. this was a dry run).
      properties:
        applied:
          type: boolean
          description: 'Whether this call changed the running configuration. False exactly when `dry_run` is true — every other outcome is a problem+json response, not a `{applied: false}` success, so a 200 from these operations is either an apply that happened or a preview that says so. It is not a soft-failure channel.'
        audit_id:
          type: string
          description: The correlating id of the `config.apply` audit row this write produced — also embedded in the row's summary, so the id resolves back to the trail through POST /audit. ABSENT exactly when nothing was audited, which is exactly the dry-run case.
        badges:
          type: array
          description: |-
            The honest-state badges this config carries. Today's one badge is `route_without_shadow_evidence`, present whenever the effective mode is `route` and the shadow-evidence gate is unsatisfied, no matter which origin applied it.

            WHICH CONFIG they describe depends on `dry_run`, and the difference is the whole value of the field on a preview. On a real apply they are recomputed from the mode that is now LIVE — the same values `GET /capabilities` reports and the same values stamped into the audit row this call wrote. On a DRY RUN they are computed from the CANDIDATE you sent: they are the badges the apply WOULD produce, which is what lets you see a promotion turning into an unbadged-looking state before you make it. On a dry run they may therefore differ from `GET /capabilities` (which reports the live set), and no audit row exists to carry them.

            Always present; `[]` when there is nothing to disclose, so "no badges" and "badges not reported" can never render the same.
          items:
            type: string
        changes:
          type: array
          description: The change set that was applied — or, on a dry run, that WOULD be applied. Always present (`[]` when the candidate is identical to the running config), so an empty diff can never render like an omitted one.
          items:
            $ref: '#/components/schemas/ConfigChange'
        dry_run:
          type: boolean
          description: True when this call previewed and changed nothing. On a dry run `applied` is false, `generation` is the CURRENT generation (unchanged), `audit_id` is absent, and `changes` / `restart_required` / `badges` describe what an apply WOULD have done.
        generation:
          type: integer
          format: int64
          description: 'The reload generation AFTER this call: the new one on a successful apply, the unchanged current one on a dry run. Use it as the next write''s `expected_generation` / `If-Match`.'
        restart_required:
          type: array
          description: 'Always `[]` on a successful apply — a change set needing a restart is REFUSED before any write, so a 200 here can never be hiding one. On a dry run it is the verdict: non-empty means the real apply would be refused with 422 `restart_required`, and the process must be restarted to make that change.'
          items:
            type: string
      required:
        - applied
        - badges
        - changes
        - dry_run
        - generation
        - restart_required
    ConfigChange:
      type: object
      properties:
        hot_reloadable:
          type: boolean
          description: 'Whether the engine can apply this path without a restart. A change set containing even one false is REFUSED by both write operations (422 `restart_required`) before anything is written — a hot-reload endpoint that wrote the file and then failed the reload would leave the file and the running engine disagreeing. The one exception is a DRY RUN, which reports the verdict as a 200 instead of refusing: the point of a preview is to tell you what would happen.'
        new:
          type: string
          description: The candidate value, rendered the same way as `old`.
        old:
          type: string
          description: 'The running value, rendered as a string. Structured values are rendered, not typed: this member exists to be READ by an operator approving a change, and a heterogeneous JSON value would be harder to display and no more precise about what changed.'
        path:
          type: string
      required:
        - hot_reloadable
        - new
        - old
        - path
    ConfigDiffRequest:
      type: object
      properties:
        yaml:
          type: string
      required:
        - yaml
    ConfigDiffResponse:
      type: object
      description: The exact semantic difference between the running config and a candidate. This is the SAME change set an apply computes and hashes into its audit row's `diff_sha256`, so a diff you reviewed is the diff that was applied.
      properties:
        changes:
          type: array
          items:
            $ref: '#/components/schemas/ConfigChange'
        restart_required:
          type: array
          description: The config paths in this change set that cannot be hot-applied. Advisory on the diff and BINDING on both writes, which refuse a non-empty set with 422 `restart_required` carrying the same list — except on a DRY RUN, which reports the same list in a 200 response instead of refusing.
          items:
            type: string
      required:
        - changes
        - restart_required
    ConfigRunningResponse:
      type: object
      description: The running configuration as a JSON envelope. `yaml` is the config file's bytes VERBATIM — the same bytes GET /config/export returns raw — never a re-serialization of the parsed config.
      properties:
        loaded_at:
          type: string
        path:
          type: string
        reload_generation:
          type: integer
          format: int64
          description: 'The compare-and-swap token. It starts at 1 and increments once per successful reload. Send it back as `expected_generation` on POST /config/apply, or as `If-Match: "<generation>"` on PUT /config; GET /config/export returns the same number as its quoted ETag.'
        restart_required:
          type: array
          description: 'Always `[]` on this response, and that is a fact rather than a stub: the RUNNING config has no pending change to require a restart FOR. The member exists so this response and POST /config/diff''s carry the same shape. A change that would need a restart is reported by the diff and REFUSED by both writes.'
          items:
            type: string
        warnings:
          type: array
          description: The loader's warnings re-derived from the RUNNING config on every read, so they describe what is live now rather than what was true at the last boot.
          items:
            type: string
        yaml:
          type: string
          description: 'The config FILE''s bytes, byte-for-byte, including comments, key order and formatting. It is NOT re-marshaled from the parsed configuration: re-marshaling would silently delete an operator''s comments and would make this field disagree with GET /config/export, which returns the same bytes raw. Secrets are not here and cannot be: credential fields carry an environment-variable NAME and the loader refuses an inline value.'
      required:
        - path
        - reload_generation
        - restart_required
        - warnings
        - yaml
    ConfigValidateRequest:
      type: object
      properties:
        yaml:
          type: string
      required:
        - yaml
    ConfigValidateResponse:
      type: object
      description: The strict loader's verdict on a candidate document. Returned with HTTP 200 in BOTH directions — "is this valid?" has a true answer either way, so an invalid draft is an answer, not an error.
      properties:
        errors:
          type: array
          description: 'The loader''s own messages, VERBATIM and YAML-path-named (`tiers[0].upstream: "nope" does not name a defined upstream`), one entry per line of the joined error. Never reworded — the path is the part you act on. Empty when `valid` is true.'
          items:
            type: string
        valid:
          type: boolean
        warnings:
          type: array
          description: 'Non-fatal findings: the document would load, but something in it is probably not what you meant. Warnings never block an apply.'
          items:
            type: string
      required:
        - errors
        - valid
        - warnings
    Content:
      type: object
      properties:
        request:
          type: boolean
        response:
          type: boolean
      required:
        - request
        - response
    ContentRef:
      type: object
      description: Whether a verbatim captured body exists for this record — by hash and availability ONLY. The bytes are never inlined here; reading them is a separate operation behind its own non-wildcard scope.
      properties:
        available:
          type: boolean
        hash:
          type: string
        which:
          type: string
      required:
        - available
        - hash
        - which
    ControlModeRequest:
      type: object
      description: A move on the engine's mode ladder. Promotion to `route` is gated on shadow evidence; demotion never is.
      properties:
        acknowledge_no_evidence:
          type: boolean
          description: Promote to `route` even though the shadow-evidence gate is unsatisfied. It records WHO took the risk in the audit row; it does NOT clear the `route_without_shadow_evidence` badge, which is recomputed from the live running mode and stays until the evidence exists.
        mode:
          type: string
          description: '`ledger` (observe only), `shadow` (decide and record, route nothing) or `route` (act). Anything else is 422 `validation_failed` naming the offending value.'
      required:
        - mode
    ControlModeResponse:
      type: object
      description: The applied mode with the honest-state badges recomputed from the LIVE running mode — identical in form to what a config-path apply of the same mode returns, because the badge does not depend on the origin.
      properties:
        applied:
          type: boolean
          description: 'Always true on this response. This operation has no preview form, so any outcome other than an applied mode change is a problem+json response rather than a `{applied: false}` success. The member exists for shape parity with the config-write response and is a constant, not a status to branch on.'
        audit_id:
          type: string
        badges:
          type: array
          description: Recomputed from the LIVE running mode, so an acknowledged promotion is badged exactly as a bypassing one is. Always present; `[]` when there is nothing to disclose.
          items:
            type: string
        generation:
          type: integer
          format: int64
        mode:
          type: string
      required:
        - applied
        - audit_id
        - badges
        - generation
        - mode
    Cost:
      type: object
      properties:
        estimated:
          type: boolean
        pricing_key:
          type: string
        pricing_snapshot:
          type: string
        region:
          type: string
        unpriced:
          type: boolean
          description: 'The AUTHORITATIVE flag that no price could be resolved for this row: set exactly when the pricing lookup missed, so an unpriced row is never confusable with a genuinely FREE one (`usd: 0`, `unpriced` absent). When true, the sibling `usd` is not a real dollar amount and `unpriced_reason` names which lookup failed. Additive and omitempty — absent means false — so rows written before the flag existed carry no value here; `GET /pricing/unpriced` identifies that pre-flag cohort heuristically and says so.'
        unpriced_reason:
          type: string
        usd:
          type: number
          description: The row's priced outcome in US dollars, as recorded in the ledger. **Read `unpriced` before reading this number.** When `unpriced` is true this field is NOT a dollar amount — it is the zero a missed price lookup left behind — and every aggregate must EXCLUDE the row and disclose the exclusion rather than silently sum it. It is a bare float rather than the `Money` envelope every COMPUTED dollar on this API carries because this is the frozen ledger-record schema (internal/declog), which is the source rows those computed figures are derived FROM; its honesty envelope is the sibling flag rather than a wrapper type. Every response that aggregates these rows reports the exclusion count as `provenance.unpriced_rows`.
        would_unpriced:
          type: array
          items:
            type: string
        would_usd:
          type: object
          additionalProperties:
            type: number
      required:
        - estimated
        - pricing_key
        - pricing_snapshot
        - usd
        - would_usd
    CostArithmetic:
      type: object
      description: The record's cost re-expressed as the multiplication the pricing engine actually performed. `total` is typed Money; `terms` is EMPTY — not zeroed — whenever no honest breakdown exists (an unpriced row, an unresolvable pricing key, or terms that do not reconcile to the recorded dollar).
      properties:
        pricing_snapshot:
          type: string
          description: The price-table snapshot this row was priced under, as recorded ON THE ROW — not the table loaded now. When the two differ the breakdown is withheld rather than computed under a snapshot the chip does not name.
        terms:
          type: array
          items:
            $ref: '#/components/schemas/CostTerm'
        total:
          $ref: '#/components/schemas/Money'
      required:
        - pricing_snapshot
        - terms
        - total
    CostExportRequest:
      type: object
      description: The FOCUS export request. `kind` and `grain` are both REQUIRED despite `grain`'s omitempty encoding — see the field note.
      properties:
        format:
          type: string
          description: |-
            `csv` (the default) or `jsonl`.

            ★ A COST THAT COULD NOT BE PRICED IS AN EMPTY CELL HERE, never a zero. That is the FOCUS idiom for "no charge could be determined", and the distinction is the point: a spreadsheet SUMS a zero and skips a blank, so rendering an unpriceable row as 0.00 would silently lower the total this file exists to reconcile against an invoice.

            ★ THE CHARGEBACK EXPORT SPELLS THE SAME SITUATION DIFFERENTLY, and the difference is worth knowing before a reader writes a parser for both: `GET /chargeback/reports/{id}/export` writes the literal `UNPRICED` in that cell. Two files, two column conventions — FOCUS here, this product's own there — and neither is a zero.
        grain:
          type: string
          description: |-
            The calendar grain the rows are rolled up to: `1m`, `1h`, `1d`, `1w`, `1mo` or `1q`.

            ★ REQUIRED IN THIS RELEASE, notwithstanding the omitempty encoding. The row-level export (`grain` omitted) is not implemented, and the server REFUSES an absent grain rather than quietly promoting it to a daily bucket — a file whose whole purpose is to be reconciled against an invoice must not answer a different question than the one asked. Send a grain.
        kind:
          type: string
          description: 'Which export to render: `focus_cost` (the FOCUS-mapped cost rows) or `focus_savings` (the realized-savings rows, under the stricter licensing contract). REQUIRED, and an unknown kind is refused rather than defaulted — a caller served a different export than the one they asked for has no way to notice from the file.'
        since:
          type: string
          description: Start of the export window, inclusive. Interpreted in `tz`.
        tz:
          type: string
          description: IANA timezone name for the window and for calendar bucketing. Bucketing is calendar-correct through the shared truncation function, so weeks, quarters and DST transitions land where the invoice puts them rather than on fixed 24-hour offsets.
        until:
          type: string
          description: End of the export window. Interpreted in `tz`.
      required:
        - kind
    CostTerm:
      type: object
      description: 'One line of the cost arithmetic: a billable quantity times the per-unit rate that priced it. Token kinds bill `tokens` x `unit_usd_per_token`; the non-token kinds (image, second, character, request) bill `units` x `unit_usd`. These are the FACTORS of a multiplication whose sum is the typed `total` above, which is why they are plain numbers rather than Money values: a term exists only where a real rate priced a real quantity, so there is no unpriced state for one to represent.'
      properties:
        kind:
          type: string
          description: '`input` | `output` | `cache_read` | `cache_write` | `reasoning` | `image` | `second` | `character` | `request`.'
        tokens:
          type: integer
          format: int64
        unit_usd:
          type: number
        unit_usd_per_token:
          type: number
        units:
          type: number
        usd:
          type: number
      required:
        - kind
        - tokens
        - unit_usd_per_token
        - usd
    Decision:
      type: object
      properties:
        affinity_pin:
          type: boolean
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/Candidate'
        cascade:
          anyOf:
            - $ref: '#/components/schemas/CascadeBlock'
            - type: "null"
        cooldown_skips:
          type: array
          items:
            type: string
        explain:
          anyOf:
            - $ref: '#/components/schemas/ExplainBlock'
            - type: "null"
        fail_safe:
          type: boolean
        governance:
          anyOf:
            - $ref: '#/components/schemas/GovernanceBlock'
            - type: "null"
        mode:
          type: string
        model_rewrite:
          anyOf:
            - $ref: '#/components/schemas/ModelRewrite'
            - type: "null"
        override:
          type: boolean
        policy_rev:
          type: string
        refused_by_budget:
          type: string
        refused_by_limit:
          anyOf:
            - $ref: '#/components/schemas/RefusedByLimitBlock'
            - type: "null"
        refused_by_policy:
          type: string
        response_cache:
          anyOf:
            - $ref: '#/components/schemas/ResponseCacheBlock'
            - type: "null"
        rule:
          type: string
        shadow_tier:
          type:
            - string
            - "null"
        tier:
          type: string
        upstream:
          type: string
      required:
        - affinity_pin
        - candidates
        - cooldown_skips
        - fail_safe
        - mode
        - override
        - rule
        - tier
        - upstream
    DecommissionRequest:
      type: object
      properties:
        replica:
          type: string
      required:
        - replica
    DecommissionResponse:
      type: object
      properties:
        decommissioned:
          type: string
        replica:
          type: string
        status:
          type: string
      required:
        - replica
        - status
    DeleteResult:
      type: object
      description: A deletion acknowledgement. It exists as a typed 200 body rather than an empty 204 because the operation descriptor can express a success SHAPE and problem responses and nothing else — a bodyless success would be documented as an untyped operation, which is the class this contract is retiring.
      properties:
        id:
          type: string
        ok:
          type: boolean
      required:
        - id
        - ok
    DeliveryFailure:
      type: object
      description: A delivery that EXHAUSTED its retries — the alert about the alerting. It names channel and rule ids and the error text only. The channel's endpoint is an env reference this plane never resolves, so no field here is capable of carrying a webhook URL or a token.
      properties:
        at:
          type: string
        attempts:
          type: integer
          format: int64
        channel_id:
          type: string
        channel_type:
          type: string
        error:
          type: string
          description: The delivery error, named in terms of the env-REFERENCE rather than any resolved value. A failure to deliver must be diagnosable without the diagnosis becoming a way to read a secret out of the configuration.
        rule_id:
          type: string
        rule_type:
          type: string
      required:
        - at
        - attempts
        - channel_id
        - channel_type
        - error
        - rule_id
        - rule_type
    Dims:
      type: object
      properties:
        feature:
          type: string
        key:
          type: string
        tags:
          type: object
          additionalProperties:
            type: string
        team:
          type: string
        tenant:
          type: string
        user:
          type: string
    Disclosures:
      type: object
      properties:
        translation:
          anyOf:
            - $ref: '#/components/schemas/TranslationLoss'
            - type: "null"
    DistributedBound:
      type: object
      properties:
        approximate:
          type: boolean
        convergence_lag:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        convergence_lag_rows:
          type: integer
          format: int64
        degraded:
          type: boolean
        note:
          type: string
        remote_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        stale:
          type: boolean
      required:
        - approximate
        - degraded
        - note
        - stale
    EnforcementMode:
      type: object
      properties:
        bound:
          type: string
        exact:
          type: boolean
        live:
          type: integer
          format: int64
        share:
          type: integer
          format: int64
        strategy:
          type: string
      required:
        - exact
        - strategy
    EngineCaps:
      type: object
      properties:
        mode:
          type: string
        pricing_snapshot:
          type: string
        version:
          type: string
      required:
        - mode
        - pricing_snapshot
        - version
    EvalRunSummary:
      type: object
      properties:
        computed_at:
          type: string
        delta:
          type:
            - number
            - "null"
        id:
          type: string
        labeled_rows:
          type: integer
          format: int64
        promote_gate_satisfied:
          type: boolean
        verdict:
          type: string
          enum:
            - BEAT
            - NOT_BEAT
            - ON_FRONTIER
            - CANNOT_MEASURE
      required:
        - computed_at
        - id
        - labeled_rows
        - promote_gate_satisfied
        - verdict
    EvaluatedType:
      type: object
      properties:
        opportunities:
          type: integer
          format: int64
        reason:
          type: string
        rows:
          type: integer
          format: int64
        type:
          type: string
      required:
        - opportunities
        - rows
        - type
    Event:
      type: object
      description: One row of the DERIVED event log (§2.8). Every event is a projection of an audit row that was durably appended FIRST, so `events ⊆ audit` holds by construction rather than by test — the audit trail stays the one truth and this log can be rebuilt from it at any time.
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/EventData'
          description: 'The closed per-type payload union: EXACTLY ONE member is set, and it is the member whose name is `type` with `.` replaced by `_` (`budget.exceeded` ⇒ `data.budget_exceeded`). The union is closed at the server''s package init — a type with no member, or a member with no type, takes the binary down at boot rather than shipping an event whose `data` a client finds empty.'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - config.applied
            - mode.set
            - governance.key.minted
            - governance.key.rotated
            - governance.key.revoked
            - governance.override.set
            - governance.override.deleted
            - budget.threshold_crossed
            - budget.exceeded
            - token.minted
            - token.revoked
            - calibration.activated
            - cache.purged
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    EventActor:
      type: object
      description: |-
        The audited principal the event is attributable to, read from the audit row that begot it — one mapping, not two, so the trail and the feed cannot name different actors for the same act.

        ONE DELIBERATE DIFFERENCE, stated because 'verbatim' would have been false: every string that leaves the audit row for an event body is swept for credential-shaped literals first. The audit trail is local to an operator who already has it; an event body travels to webhook endpoints, so the feed NEVER carries a credential-shaped literal even in the case where the trail recorded one. When that sweep fires, `actor` reads `[redacted]` where the trail reads the value.
      properties:
        actor:
          type: string
        origin:
          type: string
        role:
          type: string
      required:
        - actor
        - origin
    EventData:
      type: object
      description: 'The CLOSED per-type payload union (§2.8). EXACTLY ONE member is present on any event, and it is the member whose name is the event''s `type` with `.` replaced by `_` (`budget.exceeded` ⇒ `data.budget_exceeded`). minProperties/maxProperties state that in JSON Schema rather than in prose, so a validator enforces it: neither an empty object nor two members at once is a legal event body. It is a union of DECLARED SCHEMAS rather than a free-form object on purpose — a free-form `data` would make this document''s `webhooks` element document nothing, and would leave event bodies as the one cost surface where a bare float could still reach a consumer.'
      properties:
        budget_exceeded:
          anyOf:
            - $ref: '#/components/schemas/BudgetExceededEvent'
            - type: "null"
        budget_threshold_crossed:
          anyOf:
            - $ref: '#/components/schemas/BudgetThresholdCrossedEvent'
            - type: "null"
        cache_purged:
          anyOf:
            - $ref: '#/components/schemas/CachePurgedEvent'
            - type: "null"
        calibration_activated:
          anyOf:
            - $ref: '#/components/schemas/CalibrationActivatedEvent'
            - type: "null"
        config_applied:
          anyOf:
            - $ref: '#/components/schemas/ConfigAppliedEvent'
            - type: "null"
        governance_key_minted:
          anyOf:
            - $ref: '#/components/schemas/GovernanceKeyEvent'
            - type: "null"
        governance_key_revoked:
          anyOf:
            - $ref: '#/components/schemas/GovernanceKeyEvent'
            - type: "null"
        governance_key_rotated:
          anyOf:
            - $ref: '#/components/schemas/GovernanceKeyEvent'
            - type: "null"
        governance_override_deleted:
          anyOf:
            - $ref: '#/components/schemas/GovernanceOverrideEvent'
            - type: "null"
        governance_override_set:
          anyOf:
            - $ref: '#/components/schemas/GovernanceOverrideEvent'
            - type: "null"
        mode_set:
          anyOf:
            - $ref: '#/components/schemas/ModeSetEvent'
            - type: "null"
        token_minted:
          anyOf:
            - $ref: '#/components/schemas/TokenEvent'
            - type: "null"
        token_revoked:
          anyOf:
            - $ref: '#/components/schemas/TokenEvent'
            - type: "null"
      minProperties: 1
      maxProperties: 1
    EventEnvelope:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - config.applied
            - mode.set
            - governance.key.minted
            - governance.key.rotated
            - governance.key.revoked
            - governance.override.set
            - governance.override.deleted
            - budget.threshold_crossed
            - budget.exceeded
            - token.minted
            - token.revoked
            - calibration.activated
            - cache.purged
      required:
        - created_at
        - id
        - request
        - seq
        - type
    EventLogCaps:
      type: object
      description: The event log's retention advertisement (§2.8). Read from the SAME constants the server enforces, so the advertised bound and the real bound are one object rather than two copies that can drift.
      properties:
        durable:
          type: boolean
          description: 'false ⇒ the ring lives only in this process''s memory and a restart begins a NEW epoch. That is not a silent data loss: every cursor from the previous epoch is answered 410 `cursor_expired`, so a consumer reconciles from `POST /audit` rather than stalling on an empty page forever.'
        epoch:
          type: string
          description: Names this ring incarnation. A cursor minted under a different epoch is expired BY DEFINITION, whatever its sequence — which is what makes a post-restart resume a loud 410 instead of a silent stall.
        expired_problem:
          type: string
        notes:
          type: array
          items:
            type: string
        resource:
          type: string
        retention:
          type: string
          description: 'Stated in words because this is a COUNT bound, not a time bound: a busy control plane fills the ring in an afternoon and a quiet one holds a month, so no honest retention DURATION exists to advertise. The count, the live `oldest_seq` and the 410 are what a consumer can actually plan against.'
        ring_size:
          type: integer
          format: int64
        types:
          type: array
          items:
            type: string
            enum:
              - config.applied
              - mode.set
              - governance.key.minted
              - governance.key.rotated
              - governance.key.revoked
              - governance.override.set
              - governance.override.deleted
              - budget.threshold_crossed
              - budget.exceeded
              - token.minted
              - token.revoked
              - calibration.activated
              - cache.purged
      required:
        - durable
        - epoch
        - expired_problem
        - notes
        - resource
        - retention
        - ring_size
        - types
    EventRequest:
      type: object
      description: 'Correlates the event with the management call that caused it. BOTH MEMBERS ARE OPTIONAL AND THE OMISSION IS THE HONESTY: an event emitted by a background tap (a budget breach observed on a timer) has no request, and a block of empty strings would claim a correlation that does not exist.'
      properties:
        idempotency_key:
          type: string
        request_id:
          type: string
    EventsResponse:
      type: object
      description: |-
        One page of the bounded event ring, oldest first, plus the three ring coordinates. `oldest_seq`, `latest_seq` and `ring_size` travel on EVERY response, not only on the 410, so a consumer can widen its poll interval or reconcile BEFORE it falls off the tail instead of discovering the loss afterwards. All three are read under ONE lock with the page itself, so `latest_seq` can never be below a `seq` in the same body.

        ★ `next_cursor` HERE MEANS SOMETHING SLIGHTLY DIFFERENT FROM EVERY OTHER LIST IN THIS API, and the difference is deliberate. Elsewhere its ABSENCE is the end-of-feed signal. A ring has no end, so it is present on every response that has a position to report — including beside `has_more: false` and including on an empty page. `has_more` means "more matching events exist right now"; `next_cursor` means "resume from here". Without that split a caught-up consumer had no duplicate-free way to poll again, which made "polling this resource is always sufficient" false.
      properties:
        events:
          type: array
          items:
            $ref: '#/components/schemas/Event'
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        latest_seq:
          type: integer
          format: int64
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        oldest_seq:
          type: integer
          format: int64
        ring_size:
          type: integer
          format: int64
      required:
        - events
        - has_more
        - latest_seq
        - oldest_seq
        - ring_size
    EvidenceState:
      type: object
      description: 'The trust-ladder state: what each quality source concluded, with the labeled-row counts and the policy fingerprint that license the conclusion, plus the promote gate those verdicts feed. A source that cannot measure says CANNOT_MEASURE and gives its reason; it never reports a delta it could not compute.'
      properties:
        history:
          type: array
          items:
            $ref: '#/components/schemas/EvalRunSummary'
        promote_gate:
          $ref: '#/components/schemas/GateState'
        provenance:
          $ref: '#/components/schemas/Provenance'
        quality_sources:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/QualitySource'
      required:
        - promote_gate
        - provenance
        - quality_sources
    ExplainBlock:
      type: object
      properties:
        fail_safe_why:
          type: string
        overridden:
          anyOf:
            - $ref: '#/components/schemas/ExplainOverride'
            - type: "null"
        path:
          type: array
          items:
            type: string
        rolls:
          type: array
          items:
            $ref: '#/components/schemas/ExplainRoll'
        trace:
          type: array
          items:
            $ref: '#/components/schemas/ExplainTrace'
      required:
        - fail_safe_why
        - path
        - trace
    ExplainOverride:
      type: object
      properties:
        by:
          type: string
        from:
          type: string
        stages_suppressed:
          type: boolean
        to:
          type: string
      required:
        - by
        - from
        - stages_suppressed
        - to
    ExplainRoll:
      type: object
      properties:
        key_sha7:
          type: string
        node:
          type: string
        outcome:
          type: string
        scope:
          type: string
        value:
          type: number
      required:
        - key_sha7
        - node
        - outcome
        - scope
        - value
    ExplainTrace:
      type: object
      description: Wire shape of ExplainTrace (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        ctx_ver:
          type: string
        event:
          type: string
        filtered:
          type: array
          items:
            $ref: '#/components/schemas/FilteredCand'
        node:
          type: string
        read:
          type: object
          additionalProperties: {}
        scores:
          type: array
          items:
            $ref: '#/components/schemas/ScoredCand'
        selector:
          type: string
        why:
          type: string
      required:
        - event
        - node
    ExportExcluded:
      type: object
      properties:
        unpriced:
          type: integer
          format: int64
      required:
        - unpriced
    ExportMeta:
      type: object
      properties:
        demo:
          type: boolean
        estimated_rows:
          type: integer
          format: int64
        excluded:
          $ref: '#/components/schemas/ExportExcluded'
        generated_at:
          type: string
        instance:
          type: string
        pricing_snapshots:
          type: array
          items:
            type: string
        query:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        rows:
          type: integer
          format: int64
        v:
          type: integer
          format: int64
          description: The EXPORT SCHEMA version — stays 1, independent of the API epoch in info.version; it bumps only when the export format itself changes (plan §4 row 1).
        window:
          $ref: '#/components/schemas/Window'
      required:
        - demo
        - estimated_rows
        - excluded
        - generated_at
        - instance
        - pricing_snapshots
        - rows
        - v
        - window
    ExportRequest:
      type: object
      properties:
        format:
          type: string
        query:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        text:
          type: string
        window:
          $ref: '#/components/schemas/Window'
    FacetValue:
      type: object
      properties:
        count:
          type: integer
          format: int64
        value:
          type: string
      required:
        - count
        - value
    FacetsRequest:
      type: object
      description: Value counts for ONE field under a filter — the faceted-navigation query.
      properties:
        field:
          type: string
          description: REQUIRED. A field from the closed registry served at `/capabilities.filter_fields`.
        filter:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        since:
          type: string
        top:
          type: integer
          format: int64
          description: Keep only the N most frequent values; the rest are counted as dropped, not hidden.
      required:
        - field
    FacetsResponse:
      type: object
      properties:
        field:
          type: string
        truncated:
          type: boolean
          description: TRUE when buckets were dropped to honour `top` or the query budget. The one partiality signal on this response — it carries no dollars, so it carries no provenance envelope.
        values:
          type: array
          items:
            $ref: '#/components/schemas/FacetValue'
      required:
        - field
        - truncated
        - values
    Features:
      type: object
      properties:
        alerts:
          type: boolean
        analytics:
          anyOf:
            - $ref: '#/components/schemas/AnalyticsCaps'
            - type: "null"
        anomaly:
          type: boolean
        auth:
          $ref: '#/components/schemas/AuthCaps'
        budgets:
          $ref: '#/components/schemas/BudgetCaps'
        cache:
          type: boolean
        capture:
          $ref: '#/components/schemas/CaptureCaps'
        control_plane:
          type: boolean
        demo:
          type: boolean
        dimensions:
          type: boolean
        governance:
          type: boolean
        index:
          type: boolean
        instances:
          type: integer
          format: int64
        otlp:
          type: boolean
        reconcile:
          type: boolean
        replay:
          type: boolean
        stream:
          type: boolean
      required:
        - alerts
        - anomaly
        - auth
        - budgets
        - capture
        - control_plane
        - demo
        - dimensions
        - index
        - instances
        - otlp
        - reconcile
        - replay
        - stream
    Federation:
      type: object
      description: 'Present on a POST /query result ONLY when the answer was merged across peer instances. It is the merge''s honesty record: which instances the total spans, which were DROPPED from it, and why.'
      properties:
        excludes:
          type: array
          description: 'The names of peers DROPPED from the merged totals — the field that says rows are MISSING. Read it before trusting any dollar or count in this response: `provenance.truncated` reports only THIS instance''s budget state and cannot see a dead peer, so a merged result missing an unreachable peer''s rows still reports `truncated: false`. A non-empty `excludes` means the totals are PARTIAL however complete the provenance block looks. ABSENT when nothing was excluded — but see `exclusion_note`, which is also set in the one state where NOTHING was included: a fan-out refused before any peer was contacted names no individual peer, because none was reached to be named.'
          items:
            type: string
        exclusion_note:
          type: string
          description: The rendered human annotation for `excludes` ("excludes prod-2 (unreachable)"). It is ALSO set, with an empty `excludes` and an empty `peers`, when a federated fan-out was refused before contacting any peer — in that state `provenance.truncated` is true and NO instance's rows are included, local or remote.
        mixed_snapshot:
          type: boolean
          description: True when the merged total spans more than one pricing snapshot, or when rows-bearing peers reported divergent snapshot sets. A dollar summed across differing snapshots is disclosed, never silently blended.
        peers:
          type: array
          description: 'One health card per instance the merge spanned, INCLUDING this one (`local: true`). A card whose `status` is not `ok` contributed no rows to the totals above.'
          items:
            $ref: '#/components/schemas/PeerHealth'
      required:
        - mixed_snapshot
        - peers
    Feedback:
      type: object
      properties:
        actor:
          type: string
        note:
          type: string
        source:
          type: string
        ts:
          type: string
        verdict:
          type: string
      required:
        - actor
        - source
        - ts
        - verdict
    FeedbackRequest:
      type: object
      properties:
        note:
          type: string
        req_id:
          type: string
        verdict:
          type: string
      required:
        - req_id
        - verdict
    FeedbackResponse:
      type: object
      description: The capture acknowledgement. It echoes the stored companion row so a caller can see the provenance the SERVER derived — in particular `source`, which the caller does not get to choose.
      properties:
        accepted:
          type: boolean
          description: 'Always true on a 2xx; a refusal is a problem+json, never `accepted: false`. It means the companion row was ENQUEUED to the append-only sink, which is an acceptance receipt and NOT a durability guarantee — the sink drops and counts on a full queue, exactly like every other ledger row.'
        feedback:
          $ref: '#/components/schemas/Feedback'
        req_id:
          type: string
      required:
        - accepted
        - feedback
        - req_id
    Field:
      type: object
      properties:
        dynamic:
          type: boolean
        name:
          type: string
        note:
          type: string
        type:
          type: string
      required:
        - name
        - type
    FieldError:
      type: object
      properties:
        issue:
          type: string
        pointer:
          type: string
      required:
        - issue
        - pointer
    FilterNode:
      type: object
      description: 'A filter AST node (internal/filter). HAND-PINNED as an opaque object — reflecting the recursive AST would document less than the grammar does. JSON form: {"and":[…]} · {"or":[…]} · {"not":{…}} · leaf {"f":"<field>","op":"<op>","v":<value>} with ops eq|neq|lt|lte|gt|gte|in|glob|exists; fields are the closed registry served in /capabilities.filter_fields. The equivalent q= text form is the frozen filter.ParseText grammar (plan Appendix B.3).'
    FilteredCand:
      type: object
      properties:
        tier:
          type: string
        why:
          type: string
      required:
        - tier
        - why
    FleetCoverage:
      type: object
      properties:
        basis:
          type: string
        is_partial:
          type: boolean
        max_abs_skew_ms:
          type: integer
          format: int64
        replicas:
          type: array
          items:
            $ref: '#/components/schemas/ReplicaCoverage'
        replicas_answered:
          type: integer
          format: int64
        replicas_total:
          type: integer
          format: int64
        trace_linked:
          type: integer
          format: int64
      required:
        - basis
        - is_partial
        - max_abs_skew_ms
        - replicas
        - replicas_answered
        - replicas_total
    FleetGovernanceHealth:
      type: object
      properties:
        coverage:
          $ref: '#/components/schemas/FleetCoverage'
        kind:
          type: string
        replicas:
          type: array
          items:
            $ref: '#/components/schemas/ReplicaGovernanceHealth'
      required:
        - coverage
        - kind
        - replicas
    FleetReplicaCard:
      type: object
      properties:
        beat_age_ms:
          type: integer
          format: int64
        config_drift:
          type: boolean
        config_sha:
          type: string
        conflict:
          type: boolean
        dash_url:
          type: string
        decommission_pending:
          type: boolean
        decommissioned:
          type: boolean
        first_seen:
          type: string
        last_seen:
          type: string
        live:
          type: boolean
        name:
          type: string
        skew_ms:
          type: integer
          format: int64
        source:
          type: string
        stable:
          type: boolean
        version:
          type: string
      required:
        - beat_age_ms
        - live
        - name
        - skew_ms
        - source
        - stable
    FleetResponse:
      type: object
      properties:
        as_of:
          type: string
        basis:
          type: string
        degraded:
          type: boolean
        kind:
          type: string
        liveness_window_ms:
          type: integer
          format: int64
        max_abs_skew_ms:
          type: integer
          format: int64
        note:
          type: string
        replicas:
          type: array
          items:
            $ref: '#/components/schemas/FleetReplicaCard'
      required:
        - as_of
        - basis
        - kind
        - liveness_window_ms
        - max_abs_skew_ms
        - replicas
    Forecast:
      type: object
      description: Wire shape of Forecast (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/BurnRate'
            - type: "null"
        insufficient:
          type: boolean
        insufficient_reason:
          type: string
        method:
          type: string
        month_end:
          $ref: '#/components/schemas/Assessed'
        provenance:
          $ref: '#/components/schemas/Provenance'
        series:
          type: array
          items:
            $ref: '#/components/schemas/ForecastPoint'
        unpriced_note:
          anyOf:
            - $ref: '#/components/schemas/UnpricedTrendNote'
            - type: "null"
      required:
        - insufficient
        - method
        - month_end
        - provenance
    ForecastPoint:
      type: object
      properties:
        p10:
          type: number
        p50:
          type: number
        p90:
          type: number
        ts:
          type: string
      required:
        - p10
        - p50
        - p90
        - ts
    GateState:
      type: object
      properties:
        reasons:
          type: array
          description: Always populated when `satisfied` is false, so a refused promotion explains itself rather than presenting as a disabled button with no cause.
          items:
            type: string
        satisfied:
          type: boolean
      required:
        - reasons
        - satisfied
    GateUS:
      type: object
      properties:
        a:
          type: integer
          format: int64
        b:
          type: integer
          format: int64
      required:
        - a
        - b
    GovernanceBlock:
      type: object
      properties:
        gate_us:
          $ref: '#/components/schemas/GateUS'
        kid:
          type: string
        node:
          type: string
        reserved_tokens:
          type: integer
          format: int64
        rotation_grace:
          type: boolean
        serving_key:
          anyOf:
            - $ref: '#/components/schemas/ServingKeyRef'
            - type: "null"
        suspended:
          type: array
          items:
            type: string
        user_asserted:
          type: boolean
        verdict:
          type: string
        would_refuse:
          type: array
          items:
            type: string
      required:
        - gate_us
        - kid
        - node
        - rotation_grace
        - user_asserted
        - verdict
    GovernanceConvergence:
      type: object
      properties:
        bound:
          $ref: '#/components/schemas/DistributedBound'
        cap:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        class:
          type: string
        cold_start:
          type: boolean
        degraded:
          type: boolean
        limit_id:
          type: string
        local_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        node:
          type: string
        remote_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        replicas:
          type: integer
          format: int64
        scope_label:
          type: string
        stale:
          type: boolean
      required:
        - bound
        - class
        - cold_start
        - degraded
        - limit_id
        - node
        - replicas
        - scope_label
        - stale
    GovernanceForecast:
      type: object
      properties:
        already_exhausted:
          type: boolean
        exhaust_at:
          type: string
        insufficient:
          type: boolean
        method:
          type: string
        rate_usd_per_hour:
          type:
            - number
            - "null"
      required:
        - already_exhausted
        - insufficient
        - method
    GovernanceHealthResponse:
      type: object
      properties:
        capability:
          $ref: '#/components/schemas/SharedStateDisclosure'
        convergence:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceConvergence'
        kind:
          type: string
        postgres:
          anyOf:
            - $ref: '#/components/schemas/PostgresReachability'
            - type: "null"
        redis:
          anyOf:
            - $ref: '#/components/schemas/BreakerReachability'
            - type: "null"
        suspensions:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceSuspension'
      required:
        - capability
        - convergence
        - kind
        - suspensions
    GovernanceKey:
      type: object
      properties:
        created_at:
          type: string
        expires_at:
          type: string
        kid:
          type: string
        label:
          type: string
        last_used:
          type: string
        node:
          type: string
        rotated_from:
          type: string
        scopes:
          $ref: '#/components/schemas/GovernanceKeyScopes'
        state:
          type: string
        stub:
          type: string
      required:
        - kid
        - node
        - scopes
        - state
        - stub
    GovernanceKeyEvent:
      type: object
      properties:
        actor:
          type: string
        kid:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
      required:
        - actor
        - origin
    GovernanceKeyMintedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/GovernanceKeyMintedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - governance.key.minted
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    GovernanceKeyMintedDeliveryData:
      type: object
      properties:
        governance_key_minted:
          $ref: '#/components/schemas/GovernanceKeyEvent'
      required:
        - governance_key_minted
    GovernanceKeyResponse:
      type: object
      properties:
        key:
          $ref: '#/components/schemas/GovernanceKey'
      required:
        - key
    GovernanceKeyRevokedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/GovernanceKeyRevokedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - governance.key.revoked
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    GovernanceKeyRevokedDeliveryData:
      type: object
      properties:
        governance_key_revoked:
          $ref: '#/components/schemas/GovernanceKeyEvent'
      required:
        - governance_key_revoked
    GovernanceKeyRotatedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/GovernanceKeyRotatedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - governance.key.rotated
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    GovernanceKeyRotatedDeliveryData:
      type: object
      properties:
        governance_key_rotated:
          $ref: '#/components/schemas/GovernanceKeyEvent'
      required:
        - governance_key_rotated
    GovernanceKeyScopes:
      type: object
      properties:
        allow_passthrough:
          type: boolean
        endpoints:
          type: array
          items:
            type: string
        models:
          type: array
          items:
            type: string
        providers:
          type: array
          items:
            type: string
      required:
        - allow_passthrough
        - endpoints
        - models
        - providers
    GovernanceKeysResponse:
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
      required:
        - keys
    GovernanceLimit:
      type: object
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        forecast:
          anyOf:
            - $ref: '#/components/schemas/GovernanceForecast'
            - type: "null"
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        observed:
          type:
            - number
            - "null"
        overrun_bound:
          $ref: '#/components/schemas/GovernanceOverrunBound'
        period:
          type: string
        posture:
          type: string
        resets_at:
          type: string
        unit:
          type: string
        unpriced_excluded:
          type: integer
          format: int64
      required:
        - class
        - fraction
        - limit_id
        - limit_type
        - overrun_bound
        - posture
    GovernanceLimitDef:
      type: object
      properties:
        amount:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        amount_count:
          type:
            - integer
            - "null"
          format: int64
        attach:
          type: string
        class:
          type: string
        enforcement:
          anyOf:
            - $ref: '#/components/schemas/EnforcementMode'
            - type: "null"
        level:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        override_allowed:
          type: boolean
        override_max:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        override_max_count:
          type:
            - integer
            - "null"
          format: int64
        period:
          type: string
        posture:
          type: string
        unit:
          type: string
      required:
        - attach
        - class
        - limit_id
        - limit_type
        - override_allowed
        - posture
    GovernanceLimitsResponse:
      type: object
      properties:
        definitions:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceLimitDef'
        note:
          type: string
        overrides:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceOverride'
      required:
        - definitions
        - note
        - overrides
    GovernanceMintRequest:
      type: object
      properties:
        expires_at:
          type: string
        label:
          type: string
        node:
          type: string
        scopes:
          $ref: '#/components/schemas/GovernanceKeyScopes'
      required:
        - node
        - scopes
    GovernanceMintResponse:
      type: object
      description: 'The SHOWN-ONCE mint/rotate reveal: secret is streamed exactly once and never persisted, logged, or re-fetchable (the store holds only its SHA-256). The ONE governance schema that carries a secret; every read surface is secret-free.'
      properties:
        kid:
          type: string
        label:
          type: string
        node:
          type: string
        secret:
          type: string
        stub:
          type: string
        warning:
          type: string
      required:
        - kid
        - node
        - secret
        - stub
        - warning
    GovernanceNode:
      type: object
      properties:
        label:
          type: string
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceLimit'
        node:
          type: string
        posture:
          type: string
      required:
        - limits
        - node
        - posture
    GovernanceNodeCreateRequest:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
        label:
          type: string
        parent:
          type: string
      required:
        - id
        - kind
    GovernanceNodeDeleteResponse:
      type: object
      properties:
        deleted:
          type: boolean
        id:
          type: string
      required:
        - deleted
        - id
    GovernanceNodeResponse:
      type: object
      properties:
        id:
          type: string
      required:
        - id
    GovernanceNodesResponse:
      type: object
      properties:
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
      required:
        - nodes
    GovernanceOverride:
      type: object
      properties:
        amount:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        amount_count:
          type:
            - integer
            - "null"
          format: int64
        class:
          type: string
        limit_id:
          type: string
        node:
          type: string
        note:
          type: string
        pending_reload:
          type: boolean
        raw_amount:
          type: integer
          format: int64
          description: The exact stored int64 (nanodollars for budget_usd, a count otherwise) — for lossless round-trip back through the set endpoint.
      required:
        - limit_id
        - node
        - note
        - pending_reload
        - raw_amount
    GovernanceOverrideDeleteResponse:
      type: object
      properties:
        deleted:
          type: boolean
        limit_id:
          type: string
        node:
          type: string
        note:
          type: string
      required:
        - deleted
        - limit_id
        - node
        - note
    GovernanceOverrideDeletedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/GovernanceOverrideDeletedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - governance.override.deleted
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    GovernanceOverrideDeletedDeliveryData:
      type: object
      properties:
        governance_override_deleted:
          $ref: '#/components/schemas/GovernanceOverrideEvent'
      required:
        - governance_override_deleted
    GovernanceOverrideEvent:
      type: object
      properties:
        actor:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
        target:
          type: string
      required:
        - actor
        - origin
    GovernanceOverrideRequest:
      type: object
      properties:
        amount:
          type: integer
          format: int64
          description: The limit's STORED unit — NANODOLLARS for budget_usd (integer, so money never rounds through a float; $1.00 == 1000000000), a raw count (tokens/requests) otherwise. amount <= 0 is refused (it would disable the cap).
        limit_id:
          type: string
        node:
          type: string
      required:
        - amount
        - limit_id
        - node
    GovernanceOverrideResponse:
      type: object
      properties:
        note:
          type: string
        override:
          $ref: '#/components/schemas/GovernanceOverride'
      required:
        - note
        - override
    GovernanceOverrideSetDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/GovernanceOverrideSetDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - governance.override.set
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    GovernanceOverrideSetDeliveryData:
      type: object
      properties:
        governance_override_set:
          $ref: '#/components/schemas/GovernanceOverrideEvent'
      required:
        - governance_override_set
    GovernanceOverrunBound:
      type: object
      properties:
        approximate:
          type: boolean
        convergence_lag_rows:
          type: integer
          format: int64
        dropped_rows:
          type: integer
          format: int64
        estimate_skew_p95:
          type: number
        in_flight_reserved:
          type: integer
          format: int64
        note:
          type: string
        tee_lag_rows:
          type: integer
          format: int64
      required:
        - approximate
        - dropped_rows
        - note
        - tee_lag_rows
    GovernancePage:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        limit:
          type: integer
          format: int64
          description: The EFFECTIVE page size the server applied. A requested limit above the 1000 maximum is clamped, and this field is how a client observes that rather than having to infer it.
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        next_offset:
          type:
            - integer
            - "null"
          format: int64
          description: DEPRECATED alongside the `offset` request parameter (RFC 9745/8594 — see that parameter for the dates). The offset of the next page, absent at the end of the feed. It still answers and will keep answering until the sunset date; prefer `next_cursor`, which is stable against rows arriving at the head of this newest-first feed. A response MEMBER cannot carry a conditional deprecation header, which is why the announcement lives on the request parameter and this note.
        offset:
          type: integer
          format: int64
          description: The position this page was actually taken from. When the request supplied a `cursor`, this reports the offset the cursor RESOLVED to (not the `offset` parameter, which a cursor request ignores), so the body never claims a position the server did not use.
        returned:
          type: integer
          format: int64
        total:
          type: integer
          format: int64
          description: The row count within the requested subtree, SATURATED at this live view's 1000-row bound — it is not the true subtree total, and it stops growing at the cap. Use governanceRefusalsExport for an uncapped count.
      required:
        - has_more
        - limit
        - offset
        - returned
        - total
    GovernancePreviewAttach:
      type: object
      properties:
        level:
          type: string
        node:
          type: string
    GovernancePreviewExactLimit:
      type: object
      description: Wire shape of GovernancePreviewExactLimit (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        estimate:
          type: string
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        observed:
          type:
            - number
            - "null"
        period:
          type: string
        resets_at:
          type: string
        rows:
          type: integer
          format: int64
        unit:
          type: string
        unpriced_excluded:
          type: integer
          format: int64
        would_refuse:
          type: boolean
      required:
        - class
        - estimate
        - fraction
        - limit_id
        - limit_type
        - node
        - rows
        - would_refuse
    GovernancePreviewLimitSpec:
      type: object
      properties:
        algorithm:
          type: string
        amount:
          type: integer
          format: int64
        amount_usd:
          type:
            - number
            - "null"
        attach:
          $ref: '#/components/schemas/GovernancePreviewAttach'
        burst:
          type: integer
          format: int64
        id:
          type: string
        match:
          $ref: '#/components/schemas/GovernancePreviewMatch'
        period:
          type: string
        type:
          type: string
      required:
        - attach
        - id
        - type
    GovernancePreviewMatch:
      type: object
      properties:
        endpoint:
          type: array
          items:
            type: string
        model:
          type: array
          items:
            type: string
        provider:
          type: array
          items:
            type: string
    GovernancePreviewRateLimit:
      type: object
      description: Wire shape of GovernancePreviewRateLimit (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        amount:
          type: integer
          format: int64
        approx_peak:
          type:
            - integer
            - "null"
          format: int64
        can_estimate:
          type: boolean
        estimate:
          type: string
        fraction:
          type:
            - number
            - "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        rate_windows_approximated:
          type: boolean
        reason:
          type: string
        unit:
          type: string
        window:
          type: string
        would_refuse:
          type:
            - boolean
            - "null"
      required:
        - amount
        - can_estimate
        - estimate
        - limit_id
        - limit_type
        - node
        - rate_windows_approximated
    GovernancePreviewRefusal:
      type: object
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        observed:
          type:
            - number
            - "null"
      required:
        - class
        - fraction
        - limit_id
        - limit_type
        - node
    GovernancePreviewRequest:
      type: object
      properties:
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewLimitSpec'
        tenants:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewTenant'
      required:
        - limits
    GovernancePreviewResponse:
      type: object
      description: Wire shape of GovernancePreviewResponse (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        approximations:
          type: array
          items:
            type: string
        exact:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewExactLimit'
        kind:
          type: string
        primary_instrument:
          type: string
        rate_class:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewRateLimit'
        rows_replayed:
          type: integer
          format: int64
        torn:
          type: integer
          format: int64
        truncated:
          type: boolean
        window:
          $ref: '#/components/schemas/Window'
        would_refuse_now:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewRefusal'
        would_refuse_now_note:
          type: string
      required:
        - approximations
        - exact
        - kind
        - primary_instrument
        - rate_class
        - rows_replayed
        - torn
        - truncated
        - window
        - would_refuse_now
        - would_refuse_now_note
    GovernancePreviewTenant:
      type: object
      properties:
        org:
          type: string
        teams:
          type: array
          items:
            type: string
        users:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
      required:
        - org
    GovernanceReconcileResponse:
      type: object
      properties:
        adopted:
          type: boolean
        alert:
          type: string
        drift_count:
          type: integer
          format: int64
        kind:
          type: string
        rollup_source:
          type: string
        rows_folded:
          type: integer
          format: int64
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileDriftScope'
        torn:
          type: integer
          format: int64
      required:
        - adopted
        - alert
        - drift_count
        - kind
        - rollup_source
        - rows_folded
        - scopes
        - torn
    GovernanceRefusal:
      type: object
      properties:
        approximate:
          type: boolean
        http_status:
          type: integer
          format: int64
        kid:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        posture:
          type: string
        req_id:
          type: string
        ts:
          type: string
        type:
          type: string
      required:
        - approximate
        - http_status
        - req_id
        - ts
        - type
    GovernanceRefusalExportResponse:
      type: object
      properties:
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        tokentriage_export:
          $ref: '#/components/schemas/ExportMeta'
      required:
        - refusals
        - tokentriage_export
    GovernanceRefusalRef:
      type: object
      properties:
        feed:
          type: string
        limit_id:
          type: string
        node:
          type: string
        req_id:
          type: string
    GovernanceRefusalsResponse:
      type: object
      properties:
        page:
          anyOf:
            - $ref: '#/components/schemas/GovernancePage'
            - type: "null"
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
      required:
        - refusals
    GovernanceReportExcluded:
      type: object
      properties:
        unattributed:
          type: integer
          format: int64
        unpriced:
          type: integer
          format: int64
      required:
        - unattributed
        - unpriced
    GovernanceRevokeResponse:
      type: object
      properties:
        kid:
          type: string
        state:
          type: string
      required:
        - kid
        - state
    GovernanceSelfServeBinding:
      type: object
      properties:
        role:
          type: string
        root:
          type: string
      required:
        - role
        - root
    GovernanceSelfServeResponse:
      type: object
      properties:
        bindings:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceSelfServeBinding'
        can_write:
          type: boolean
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
        node:
          type: string
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
        note:
          type: string
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        status:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
        user:
          type: string
      required:
        - bindings
        - can_write
        - keys
        - node
        - nodes
        - note
        - refusals
        - status
        - user
    GovernanceShadowLimit:
      type: object
      properties:
        limit_id:
          type: string
        rows_observed:
          type: integer
          format: int64
        would_refuse_count:
          type: integer
          format: int64
      required:
        - limit_id
        - rows_observed
        - would_refuse_count
    GovernanceShadowResponse:
      type: object
      properties:
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceShadowLimit'
        note:
          type: string
        shadow:
          type: boolean
      required:
        - limits
        - note
        - shadow
    GovernanceStatusResponse:
      type: object
      properties:
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
      required:
        - nodes
    GovernanceSuspension:
      type: object
      properties:
        bound:
          $ref: '#/components/schemas/DistributedBound'
        cap:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        cap_amount:
          type:
            - integer
            - "null"
          format: int64
        class:
          type: string
        fail_posture:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        reason:
          type: string
      required:
        - bound
        - class
        - fail_posture
        - limit_id
        - limit_type
        - node
        - reason
    GovernanceTenantTokenMintRequest:
      type: object
      properties:
        expires_in:
          type: string
          description: 'A GO DURATION STRING, not an ISO 8601 duration and not a number of seconds: a decimal number with a unit suffix, e.g. `168h`, `30m`, `1h30m`. Valid units are ns, us, ms, s, m, h — there is no day or week unit, so a week is `168h`. ABSENT means the token never expires; a PRESENT value must be positive, so `0s` is refused 400 rather than silently minting an eternal credential.'
        node:
          type: string
      required:
        - node
    GovernanceTenantTokenMintResponse:
      type: object
      properties:
        audit_id:
          type: string
        record:
          $ref: '#/components/schemas/ShareToken'
        token:
          type: string
      required:
        - audit_id
        - record
        - token
    GovernanceTenantViewResponse:
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
        node:
          type: string
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
        read_only:
          type: boolean
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        scope_node:
          type: string
          description: The subtree root the presented token is FROZEN to — the boundary of everything in this response. `node` is the effective (possibly drilled-down) subtree, always this node or a descendant of it.
        status:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
      required:
        - keys
        - node
        - nodes
        - read_only
        - refusals
        - scope_node
        - status
    GovernanceTreeNode:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
        label:
          type: string
        parent:
          type: string
      required:
        - id
        - kind
    GovernanceUsageReport:
      type: object
      properties:
        excluded:
          $ref: '#/components/schemas/GovernanceReportExcluded'
        period:
          type: string
        rows:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceUsageRow'
        tokentriage_export:
          $ref: '#/components/schemas/ExportMeta'
      required:
        - excluded
        - period
        - rows
        - tokentriage_export
    GovernanceUsageRow:
      type: object
      properties:
        bucket:
          type: string
        estimated_rows:
          type: integer
          format: int64
        input_tokens:
          type: integer
          format: int64
        node:
          type: string
        output_tokens:
          type: integer
          format: int64
        requests:
          type: integer
          format: int64
        spend:
          $ref: '#/components/schemas/Money'
        total_tokens:
          type: integer
          format: int64
        unpriced_rows:
          type: integer
          format: int64
      required:
        - bucket
        - estimated_rows
        - input_tokens
        - node
        - output_tokens
        - requests
        - spend
        - total_tokens
        - unpriced_rows
    GuardResult:
      type: object
      description: Wire shape of GuardResult (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        label:
          type: string
        name:
          type: string
        params:
          type: object
          additionalProperties:
            type: string
        passed:
          type: boolean
        reason:
          type: string
        repriced:
          type: boolean
      required:
        - name
        - passed
    HTTP:
      type: object
      properties:
        dialect:
          type: string
        endpoint:
          type: string
        path:
          type: string
        provider:
          type: string
        status:
          type: integer
          format: int64
        stream:
          type: boolean
      required:
        - dialect
        - path
        - status
        - stream
    HeaderDisclosure:
      type: object
      description: One wire header this API sets or reads, with what it means. The list exists because the generated document's own description is frozen and predates several shipped headers.
      properties:
        description:
          type: string
          description: What the header means and when it appears. Human-oriented prose.
        direction:
          type: string
          description: '`request` if the client sends it, `response` if the server returns it.'
          enum:
            - request
            - response
        name:
          type: string
          description: The header field name as documented. Field names are case-insensitive (RFC 9110 §5.1) and Go canonicalises them on the wire, so match it case-insensitively.
      required:
        - description
        - direction
        - name
    IdempotencyPolicy:
      type: object
      description: The replay contract for unsafe writes that advertise `x-idempotent` (§2.5 / B.1 rule 7). Its BOUNDS are published rather than implied, because a retention window a client cannot rely on at the far end is worse undocumented than documented.
      properties:
        enforcement:
          type: string
          enum:
            - in_process_per_node
        header:
          type: string
          description: The request header that opts into exactly-once semantics. Operations that do not advertise `x-idempotent` IGNORE it, so a client cannot believe it has a guarantee it was never given.
        max_key_length:
          type: integer
          format: int64
          description: The longest accepted key, in characters. A longer one is a 400 naming the header rather than an unbounded map key.
        max_request_bytes:
          type: integer
          format: int64
          description: 'The largest keyed request body. The payload identity is a hash of the WHOLE body, so a body the server cannot hold cannot be identified: it is refused 413 naming this bound rather than hashed in part.'
        notes:
          type: array
          description: The behavioural caveats, including the two that bound the guarantee. Human-oriented prose.
          items:
            type: string
        operations:
          type: array
          description: The operationIds that honour the key, derived from the registry. An EMPTY list means no operation in this contract revision does — which is a statement, not a silence.
          items:
            type: string
        replay_header:
          type: string
          description: Set to `true` on a response served from the store rather than freshly executed. Without it a replay would be indistinguishable from a re-execution.
        retention_hours:
          type: integer
          format: int64
          description: The MAXIMUM replay window, never a guarantee — see `enforcement`. After it, the key is forgotten and reusing it executes a NEW request.
        scope:
          type: string
          description: '`principal`: a key is namespaced to its caller, so one caller can neither read another''s stored response nor learn that a key is in use.'
          enum:
            - principal
        store_max_bytes:
          type: integer
          format: int64
          description: The store's retained-response-bytes ceiling, and the bound that actually caps memory.
        store_max_records:
          type: integer
          format: int64
          description: The store's record ceiling. Whichever ceiling binds first evicts — see `enforcement`.
      required:
        - enforcement
        - header
        - max_key_length
        - max_request_bytes
        - notes
        - operations
        - replay_header
        - retention_hours
        - scope
        - store_max_bytes
        - store_max_records
    IndexedThrough:
      type: object
      properties:
        file:
          type: string
        offset:
          type: integer
          format: int64
        ts:
          type: string
      required:
        - file
        - offset
        - ts
    InstancesResponse:
      type: object
      description: The federation roster. When `federated` is false this is the local instance ALONE and `note` says so — never a fabricated fleet.
      properties:
        federated:
          type: boolean
          description: Whether any REMOTE peer is configured. False means this deployment answers from one instance.
        note:
          type: string
          description: 'An honest disclosure about the roster''s state: that federation is off, that named peers are degraded and EXCLUDED from federated totals, or that this instance could not answer its own leg.'
        peers:
          type: array
          items:
            $ref: '#/components/schemas/PeerHealth'
      required:
        - federated
        - peers
    Judge:
      type: object
      properties:
        model:
          type: string
        sample_rate:
          type: number
        spend_usd:
          type: number
          description: 'What the shadow judge''s duplicated requests cost, summed over the graded rows only, in US dollars. It is a BARE number rather than the Money envelope every other dollar on this API carries, and the difference is real: the sum adds each graded row''s recorded duplicate cost without consulting that row''s `duplicate_unpriced` flag, so a duplicate whose cost could not be resolved contributes 0 and this figure UNDER-reports rather than disclosing the exclusion. Treat it as a lower bound on judge spend, not as a settled cost. (The fix is in the producing engine, not in this contract — recorded with its owner in docs/ap_wave1_report.md.)'
      required:
        - model
        - sample_rate
        - spend_usd
    Latency:
      type: object
      properties:
        overhead_us:
          type: integer
          format: int64
        total_ms:
          type: integer
          format: int64
        ttft_ms:
          type: integer
          format: int64
      required:
        - overhead_us
        - total_ms
        - ttft_ms
    LoginRequest:
      type: object
      properties:
        password:
          type: string
        token:
          type: string
        user:
          type: string
    LogoutResponse:
      type: object
      properties:
        ok:
          type: boolean
      required:
        - ok
    MCPEntry:
      type: object
      properties:
        calls:
          type: integer
          format: int64
        estimated:
          type: boolean
        input_tokens_attributed:
          type: integer
          format: int64
        result_bytes:
          type: integer
          format: int64
        server:
          type: string
        tool:
          type: string
      required:
        - calls
        - estimated
        - input_tokens_attributed
        - result_bytes
        - server
        - tool
    ManagementAPICaps:
      type: object
      description: What this management contract IS and where to read it, plus the three cross-cutting policies (§2.5) whose numbers a client needs BEFORE it writes a retry loop. Every value here is read from the constant the server enforces, so the advertised policy and the enforced policy are the same object rather than two copies that can drift.
      properties:
        docs_public:
          type: boolean
          description: Whether THIS deployment opened the reference and the served specification to unauthenticated callers (`dashboard.docs_public`). Advertised because it is an operator decision a client could otherwise only discover by getting a 401.
        docs_url:
          type: string
          description: 'The self-hosted API reference. Deep links are stable: `#<operationId>` addresses an operation and `#<operationId>-<paramName>` one of its parameters — the anchors a deprecation `Link` header points at.'
        epoch:
          type: integer
          format: int64
          description: 'The wire-compatibility promise, and the leading number of `version`. The `/v1` path segment is a permanent NAMESPACE and is not this number (B.2): within an epoch every change is additive.'
        events:
          $ref: '#/components/schemas/EventLogCaps'
        headers:
          type: array
          description: The wire headers this API sets or reads. It exists because the generated document's own `info.description` is frozen and predates four of them, so this is where they are machine-readable.
          items:
            $ref: '#/components/schemas/HeaderDisclosure'
        idempotency:
          $ref: '#/components/schemas/IdempotencyPolicy'
        pagination:
          $ref: '#/components/schemas/PaginationPolicy'
        privileged_scopes:
          type: array
          description: 'The subset of `scopes` that the `*:read` / `*:write` wildcards NEVER expand to (§2.3): a token must name each one explicitly, so a broadly-scoped credential can never mint, apply config, administer users or bulk-export captured payloads by accident.'
          items:
            type: string
        problem_type_base:
          type: string
          description: 'The prefix every `type` member in an error body carries: append a slug to dereference one on this host. It is version-FREE on purpose — a type URI is a stable identifier that appears in clients'' logs and alert rules, so it must not move when the contract epoch does.'
        problems_url:
          type: string
          description: The CLOSED problem-type registry as JSON — every error `type` this API can emit, with its status and its permitted extension members.
        rate_limits:
          $ref: '#/components/schemas/RateLimitPolicy'
        scopes:
          type: array
          description: Every scope the registered operations require, derived from the registry the server mounts from — so this list cannot describe a surface the server does not serve. Each entry is `<resource>:<verb>`.
          items:
            type: string
        spec_url:
          type: string
          description: 'Where the OpenAPI document this server obeys is served, on this origin. Never a CDN: the whole surface is air-gapped by design.'
        version:
          type: string
          description: '`<epoch>.<revision>` — the same string as the served document''s `info.version`. The revision increments on every additive regeneration, so a client can tell WHICH revision it is talking to without diffing.'
        webhooks:
          $ref: '#/components/schemas/WebhookCaps'
      required:
        - docs_public
        - docs_url
        - epoch
        - events
        - headers
        - idempotency
        - pagination
        - privileged_scopes
        - problem_type_base
        - problems_url
        - rate_limits
        - scopes
        - spec_url
        - version
        - webhooks
    MeasurementPass:
      type: object
      description: Wire shape of MeasurementPass (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        at:
          type: string
        considered:
          type: integer
          format: int64
        due:
          type: integer
          format: int64
        measured:
          type: integer
          format: int64
        reason:
          type: string
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/MeasurementRefusal'
        wired:
          type: boolean
      required:
        - considered
        - due
        - measured
        - refusals
        - wired
    MeasurementRefusal:
      type: object
      properties:
        id:
          type: string
        permanent:
          type: boolean
        reason:
          type: string
      required:
        - id
        - permanent
        - reason
    MetacostResponse:
      type: object
      description: What measurement itself cost. A source appears only when there is a real dollar to report under it — never a synthetic zero, and never a line for a component the ledger records no dollar for.
      properties:
        provenance:
          $ref: '#/components/schemas/Provenance'
        sources:
          type: array
          items:
            $ref: '#/components/schemas/MetacostSource'
      required:
        - provenance
        - sources
    MetacostSource:
      type: object
      description: One measurement-spend line. `spend_usd` is typed Money; `cap_usd` and `burn_pct` appear only where a ceiling is configured for the same period the spend is reported over.
      properties:
        burn_pct:
          type:
            - number
            - "null"
          description: '`spend_usd` as a percentage of `cap_usd`. Absent whenever `cap_usd` is, because a percentage of nothing is not a number this API will invent.'
        cap_usd:
          type:
            - number
            - "null"
          description: 'The CONFIGURED ceiling for this source, in dollars — an operator input echoed back, not a measured cost. Absent when none is configured: a `0` ceiling would read as unlimited headroom, which is a claim nobody made.'
        name:
          type: string
          description: '`shadow` (duplicate traffic graded against the chosen tier) or `response_cache` (the cache''s own metered embedding and verification calls).'
        period:
          type: string
          description: The calendar period the spend is reported over. A ceiling expressed over a DIFFERENT period is not rescaled and not quoted — quoting a daily cap against a month's burn would show a 3000% burn on a deployment that is exactly on budget.
        spend_usd:
          $ref: '#/components/schemas/Money'
      required:
        - name
        - spend_usd
    MetricSet:
      type: object
      description: The resolved metric values for one group or one series point. A metric that was not requested is ABSENT, not zero — the two are different answers and this shape keeps them distinguishable.
      properties:
        cache_savings_licensed:
          anyOf:
            - $ref: '#/components/schemas/Assessed'
            - type: "null"
        cache_savings_shadow:
          anyOf:
            - $ref: '#/components/schemas/Assessed'
            - type: "null"
        cost_per_1k_tokens:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        cost_per_request:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        cost_usd:
          description: Typed Money. `usd` is `null` for a group whose rows the pricing could not price; the excluded rows are counted in `provenance.unpriced_rows`. A `0` here means a genuinely free group, and nothing else.
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        metacost_usd:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        p95_cost:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        p99_ttft_ms:
          type:
            - number
            - "null"
          description: Milliseconds to first token at the 99th percentile — a latency, not a dollar.
        requests:
          type:
            - integer
            - "null"
          format: int64
        savings:
          description: Typed Savings — Money plus the `basis` that computed it and the `verdict_ref` that licenses it. The server REFUSES to marshal a savings value missing either, so an unlicensed savings figure cannot reach a client.
          anyOf:
            - $ref: '#/components/schemas/Savings'
            - type: "null"
        savings_assessed:
          anyOf:
            - $ref: '#/components/schemas/Assessed'
            - type: "null"
        tokens_by_type:
          type: object
          additionalProperties:
            type: integer
            format: int64
        unpriced_rows:
          type:
            - integer
            - "null"
          format: int64
    ModeSetDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/ModeSetDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - mode.set
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    ModeSetDeliveryData:
      type: object
      properties:
        mode_set:
          $ref: '#/components/schemas/ModeSetEvent'
      required:
        - mode_set
    ModeSetEvent:
      type: object
      properties:
        actor:
          type: string
        badges:
          type: array
          items:
            type: string
        mode:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
      required:
        - actor
        - badges
        - mode
        - origin
    ModelRewrite:
      type: object
      properties:
        from:
          type: string
        to:
          type: string
      required:
        - from
        - to
    Money:
      type: object
      description: 'A typed dollar (§2.6). HAND-PINNED: Money.MarshalJSON nulls usd on an unpriced value — usd is null when unpriced, NEVER 0 — which reflection over the *float64 field cannot express. estimated/unpriced carry the provenance, so a raw float or a $0-for-unpriced is unrepresentable.'
      properties:
        estimated:
          type: boolean
        unpriced:
          type: boolean
          description: true ⇒ this value is excluded from dollar totals and usd is null
        usd:
          type:
            - number
            - "null"
          description: null when unpriced (never 0)
      required:
        - estimated
        - unpriced
        - usd
    OracleResult:
      type: object
      properties:
        label:
          type: string
        score:
          type: integer
          format: int64
      required:
        - label
        - score
    Outcome:
      type: object
      properties:
        client_disconnect:
          type: boolean
        error:
          type: string
        finish_reason:
          type: string
        oracle:
          anyOf:
            - $ref: '#/components/schemas/OracleResult'
            - type: "null"
        shadow_grade:
          anyOf:
            - $ref: '#/components/schemas/ShadowGrade'
            - type: "null"
        signals:
          type: object
          additionalProperties:
            type: boolean
      required:
        - client_disconnect
        - error
        - finish_reason
        - signals
    OverrunBound:
      type: object
      properties:
        approximate:
          type: boolean
          description: 'ALWAYS true. A budget total is a bound, not a settlement: rows can be in flight through the tee, and rows can have been dropped. `tee_lag_rows` and `dropped_rows` quantify what this total does not yet include.'
        dropped_rows:
          type: integer
          format: int64
        note:
          type: string
        tee_lag_rows:
          type: integer
          format: int64
      required:
        - approximate
        - dropped_rows
        - note
        - tee_lag_rows
    PageRequest:
      type: object
      properties:
        cursor:
          type: string
          description: OPAQUE page position, echoed back VERBATIM from a previous response's `next_cursor`. Absent means the first page. Never construct one. On the operations that embed THIS type it is base64url over the server's own keyset position record — stated for this type rather than for the API, because two operations (`analyticsRows`, `shareRows`) declare their own cursor and encode a scan-window offset instead; echoing the value back is correct everywhere, parsing one is supported nowhere. The encoding is an interop promise rather than confidentiality, and a cursor carries no secret, no credential and no identity, only a position you could already see in the page it came from.
        limit:
          type: integer
          format: int64
          description: Page size. Absent or 0 takes this operation's documented default; above its maximum the request is REFUSED with a 400 naming the cap, never silently clamped — a clamped page is a lie about what the server heard. The pair in force is advertised at `/capabilities.management_api.pagination`, which each operation may override.
    PageResponse:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
      required:
        - has_more
    PaginationPolicy:
      type: object
      description: The cursor contract every paginated list obeys (§2.5 / B.1 rule 4). One shape, one codec, one end-of-feed signal, so two list operations can never disagree about what the last page looks like.
      properties:
        cursor_encoding:
          type: string
          description: '`opaque_base64url`. Opacity is an INTEROP promise, not confidentiality: echo a `next_cursor` back verbatim and never construct one, but note that base64 is an encoding, so a cursor carries no secret, no credential and no identity — only a position you could already see in the page it came from.'
          enum:
            - opaque_base64url
        default_limit:
          type: integer
          format: int64
          description: The page size an absent or zero `limit` resolves to. An operation that documents its own overrides it.
        max_cursor_bytes:
          type: integer
          format: int64
          description: The longest cursor accepted. A cursor is server-minted and small; an oversized one is a malformed request.
        max_limit:
          type: integer
          format: int64
        notes:
          type: array
          description: The consequences a client's loop condition depends on. Human-oriented prose.
          items:
            type: string
        style:
          type: string
          description: '`cursor`: request `cursor` + `limit`, response `next_cursor` + `has_more`. The end of a feed is signalled by `next_cursor` being ABSENT — never present-and-empty.'
          enum:
            - cursor
      required:
        - cursor_encoding
        - default_limit
        - max_cursor_bytes
        - max_limit
        - notes
        - style
    PeerHealth:
      type: object
      description: One instance's health card. It carries only REFERENCES across the federation boundary — a URL and an environment-variable NAME — never a credential value.
      properties:
        error:
          type: string
          description: A human-readable reason for a non-`ok` status. Never a credential value and never a secret.
        last_seen:
          type: string
        local:
          type: boolean
        name:
          type: string
        pricing_snapshots:
          type: array
          items:
            type: string
        reachable:
          type: boolean
        status:
          type: string
          description: '`ok` (its rows are in the merge), `unreachable` (connection refused / DNS / non-2xx), `timeout` (the per-peer deadline elapsed), `auth_failed` (401/403 — the `token_env` reference did not authorize), or `local_error` (a fault in THIS instance''s own leg). The first four describe a NETWORK HOP and can never apply to the local instance, which is queried in-process; `local_error` exists so a local fault is never dressed up as an unreachable peer.'
        token_env:
          type: string
          description: The NAME of the environment variable this server reads the peer's bearer token from at call time — NEVER the token itself. The value is read at the moment of the call, bound to an outbound header, and never stored on this shape, logged, or returned; there is no field on this type through which it could travel.
        token_present:
          type: boolean
          description: Whether the environment variable named by `token_env` is set ON THIS SERVER. A presence flag computed at call time — it reports ON the credential without carrying it.
        url:
          type: string
      required:
        - local
        - name
        - pricing_snapshots
        - reachable
        - status
        - token_env
        - token_present
        - url
    PostgresReachability:
      type: object
      properties:
        err:
          type: string
        pending_propagation:
          type: integer
          format: int64
        replica_ready:
          type: boolean
        revocation_bound:
          type: string
        since:
          type: string
        staleness_bound:
          type: string
        up:
          type: boolean
      required:
        - replica_ready
        - revocation_bound
        - up
    PricingDiffRow:
      type: object
      properties:
        in_table:
          type: boolean
        key:
          type: string
        override_in:
          type: number
        override_out:
          type: number
        status:
          type: string
        table_in:
          type:
            - number
            - "null"
      required:
        - in_table
        - key
        - override_in
        - override_out
        - status
    PricingMatchRequest:
      type: object
      properties:
        at:
          type: string
        model:
          type: string
        provider:
          type: string
      required:
        - model
    PricingMatchResponse:
      type: object
      properties:
        at:
          type: string
        key:
          type: string
        overlay:
          type: boolean
        priced:
          type: boolean
        rates:
          anyOf:
            - $ref: '#/components/schemas/PricingRates'
            - type: "null"
        stamp:
          type: string
          description: The `pricing_snapshot` provenance string that WOULD be written onto a ledger row priced by this resolution — the same value that appears as `cost.pricing_snapshot` on a real row, so a match result and a logged row can be compared directly. Empty on a miss, because nothing would be stamped.
        steps:
          type: array
          description: The resolution trace, in order, always present and never null. On a deployment with no overrides configured the first step says so in those words rather than reporting a fabricated override branch that was never consulted.
          items:
            type: string
        winner:
          type: string
          description: 'Which source priced the pair: `override` (an effective-dated override entry), `table` (the base catalogue), or `miss` — nothing priced it, `priced` is false, `rates` is absent, and a real request for this pair would be recorded UNPRICED. Rendered verbatim; never re-worded by a client.'
      required:
        - at
        - key
        - overlay
        - priced
        - stamp
        - steps
        - winner
    PricingModel:
      type: object
      properties:
        cache_r:
          type: number
        cache_w:
          type: number
        in:
          type: number
        max_in:
          type: integer
          format: int64
        max_out:
          type: integer
          format: int64
        mode:
          type: string
        out:
          type: number
        per_char:
          type: number
        per_image:
          type: number
        per_second:
          type: number
        reason:
          type: number
        tiers:
          type: array
          items:
            $ref: '#/components/schemas/PricingTier'
    PricingOverride:
      type: object
      properties:
        cache_r:
          type: number
        cache_w:
          type: number
        created_at:
          type: string
        created_by:
          type: string
        effective_from:
          type: string
        in:
          type: number
        key:
          type: string
        note:
          type: string
        out:
          type: number
        status:
          type: string
      required:
        - created_at
        - created_by
        - effective_from
        - in
        - key
        - out
        - status
    PricingOverrideRequest:
      type: object
      properties:
        action:
          type: string
        cache_r:
          type: number
        cache_w:
          type: number
        effective_from:
          type: string
        in:
          type: number
        key:
          type: string
        note:
          type: string
        out:
          type: number
      required:
        - key
    PricingOverrideResponse:
      type: object
      properties:
        count:
          type: integer
          format: int64
        entry:
          $ref: '#/components/schemas/PricingOverride'
        note:
          type: string
        pending_restart:
          type: boolean
          description: 'Always true, and it is the field that matters most on this response: the artifact was written and audited, but the override overlay is built at BOOT, so live pricing does not change until the next restart. A 200 here is not a claim that traffic is being priced differently.'
        version:
          type: integer
          format: int64
      required:
        - count
        - entry
        - note
        - pending_restart
        - version
    PricingOverridesResponse:
      type: object
      properties:
        configured:
          type: boolean
        note:
          type: string
        overlay_active:
          type: boolean
        overrides:
          type: array
          description: Every entry in the artifact, ACTIVE and RETIRED. The artifact is retire-not-delete so that rows priced under an entry stay explicable after it is withdrawn; filter on `status == "active"` for current pricing rather than expecting the server to have forgotten.
          items:
            $ref: '#/components/schemas/PricingOverride'
        path:
          type: string
        version:
          type: integer
          format: int64
      required:
        - configured
        - overlay_active
        - overrides
        - version
    PricingProvenance:
      type: object
      properties:
        fetched:
          type: string
        generator:
          type: string
        source:
          type: string
        upstream_sha256:
          type: string
      required:
        - fetched
        - generator
        - source
        - upstream_sha256
    PricingRates:
      type: object
      properties:
        cache_r:
          type: number
        cache_w:
          type: number
        in:
          type: number
        out:
          type: number
      required:
        - cache_r
        - cache_w
        - in
        - out
    PricingSnapshotResponse:
      type: object
      properties:
        diff:
          type:
            - array
            - "null"
          description: |-
            Every override entry, active and retired, rendered against the base table it layers over. It is an OVERRIDE-vs-TABLE diff, not a diff between two catalogue snapshots — a second snapshot is not resident at runtime.

            THREE STATES, and they are distinguishable on the wire. ABSENT: you did not pass `view=diff`. `[]`: you asked and there is nothing to diff — always accompanied by a `note` saying WHICH kind of nothing, because "no overrides are configured" and "an overlay is live but its artifact is empty" are different situations for an operator who has just configured one. A non-empty array: the rows. An empty diff can therefore never render identically to a response that never asked for one.
          items:
            $ref: '#/components/schemas/PricingDiffRow'
        model_count:
          type: integer
          format: int64
        models:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/PricingModel'
        note:
          type: string
        overlay_active:
          type: boolean
          description: Whether the override overlay is LIVE. The overlay is built at boot from `pricing.overrides_file`, so an artifact written since the last restart is listed and diffed by this API but is not yet what the engine prices against. Check this before reading `diff` as a description of live pricing.
        provenance:
          $ref: '#/components/schemas/PricingProvenance'
        snapshot:
          type: string
      required:
        - model_count
        - overlay_active
        - provenance
        - snapshot
    PricingTier:
      type: object
      properties:
        above:
          type: integer
          format: int64
        in:
          type: number
        out:
          type: number
      required:
        - above
    PricingUnpricedResponse:
      type: object
      description: The distinct pricing keys this deployment could not price, with the row and token counts behind each. Every `cost_usd` here is the UNPRICED Money value — `usd` null, never 0 — because every row counted is a pricing MISS by construction; `cohort` states which rows were counted, including the heuristically-identified pre-flag ones.
      properties:
        cohort:
          type: string
        provenance:
          $ref: '#/components/schemas/Provenance'
        rows:
          type: array
          items:
            $ref: '#/components/schemas/PricingUnpricedRow'
        total_rows:
          type: integer
          format: int64
      required:
        - cohort
        - provenance
        - rows
        - total_rows
    PricingUnpricedRow:
      type: object
      properties:
        cost_usd:
          $ref: '#/components/schemas/Money'
          description: 'ALWAYS the UNPRICED Money value (`usd` null). It is not a cost that happens to be zero and it is not a total waiting to be filled in: these rows are the ones nothing could price, so the only honest dollar for them is the absence of one. Rows in the pre-flag heuristic cohort would otherwise aggregate to a confident `{usd: 0, estimated: true}`, rendering a pricing miss as a nearly-free real cost on the one surface built to surface misses.'
        pricing_key:
          type: string
          description: The price-table lookup key the ledger recorded for these rows (`openai/gpt-4o`). It is a public catalogue coordinate, and it is the string to give `POST /pricing/match` or to create an override for. It is EMPTY for rows where no key resolved at all, which is a different failure from a key that resolved and matched nothing.
        rows:
          type: integer
          format: int64
        tokens:
          type: object
          description: Total tokens per token TYPE (input/output/cache_read/cache_write/reasoning) across these rows — the scale signal. Together with `rows` it is how you tell an unpriced model that served three requests from one that served three million.
          additionalProperties:
            type: integer
            format: int64
      required:
        - cost_usd
        - pricing_key
        - rows
    Problem:
      type: object
      description: 'The ONE error shape this API returns. Every non-2xx response on every operation is `application/problem+json` (RFC 9457) with this body — there is no second error envelope, no per-resource variant, and no bare string. **Switch on `code`, never on `detail`.** `code` is a stable identifier drawn from a CLOSED registry served at `/tt/api/v1/problems`; `detail` is occurrence-specific prose written for a human and may be reworded at any time without a contract change. `status` mirrors the HTTP status, so a client that has only the body still knows what happened. Extension members are closed and per-type: which ones an occurrence may carry is recorded in that type''s registry entry and documented on its `/tt/api/problems/<slug>` page. RFC 9457 requires clients to IGNORE members they do not recognise, which is what makes adding one an additive change. A problem discloses the real reason with the same honesty as a success shape — but never a secret and never the value of a credential path parameter.'
      properties:
        code:
          type: string
          description: 'The stable, machine-readable identifier for this problem type — the same slug as the tail of `type`, repeated here so a client can branch on one flat string and an operator can grep logs for it. **This is the member to switch on.** The vocabulary is CLOSED: every value is registered, enumerated at `/tt/api/v1/problems`, and never reused after retirement. Each operation''s response catalog lists the domain codes it can return; the framework codes (`bad_request`, `unauthenticated`, `forbidden`, `not_found`, `method_not_allowed`, `payload_too_large`, `rate_limited`, `internal`) are implicit on every operation and documented once at the document level.'
        current_generation:
          type: integer
          format: int64
          description: 'On `generation_conflict` (body-carried CAS) and `precondition_failed` (a failed `If-Match`): the config generation that actually won. Re-read the config at this generation, rebase your change, and retry — the write was refused, not partially applied.'
        detail:
          type: string
          description: Occurrence-specific explanation, written for a human and naming the actual failing input where one exists. **Never parse this.** It is the one member with no stability promise; its wording may change in any revision. Everything a program needs is in `code`, `status`, or a typed extension member.
        enable_hint:
          type: string
          description: 'On `feature_absent`: how to wire this feature up in THIS deployment. A 404 `feature_absent` is a permanent not-configured state, deliberately not a retried 503 — retrying will never cure it, and configuration will. This member says what to configure.'
        errors:
          type: array
          description: 'On `validation_failed`: one entry per rejected field, located by RFC 6901 JSON pointer into the request body you sent. All failures are reported together rather than one per round trip.'
          items:
            $ref: '#/components/schemas/FieldError'
        legacy_kind:
          type: string
          description: TRANSITIONAL, and expected to disappear. During the epoch-1→2 error migration this preserved the pre-migration ad-hoc error kind so a client mid-migration could still recognise a failure it already handled. The ratchet that tracked it reached ZERO at MS-B, so nothing emits it today. Do not write new code against it; branch on `code`.
        oldest_seq:
          type: integer
          format: int64
          description: 'On `cursor_expired`: the oldest sequence still live in the bounded feed. Your cursor pointed BELOW it, so the rows between are gone. You are told this rather than being silently restarted at the tail — reconcile the gap from `POST /audit`, the underived truth, then resume from a fresh cursor. A silent restart would hide a hole you would otherwise discover much later, or never.'
        reasons:
          type: array
          description: 'On `promote_gate_refused`: the gate''s own reasons, verbatim and unsummarised. The refusal is quoted rather than paraphrased so the operator reads what the gate actually decided.'
          items:
            type: string
        refusal:
          description: 'On a governance-mediated refusal: a REFERENCE to the refusal row that caused this — the request and firing limit plus the feed path to look it up. A reference, never the row itself, and never a secret; the refusal feed is the authoritative record and is where the full context lives.'
          anyOf:
            - $ref: '#/components/schemas/GovernanceRefusalRef'
            - type: "null"
        request_id:
          type: string
          description: The identifier for THIS request, identical to the `TT-Request-Id` response header every response carries. Server-minted and unguessable; quote it in a bug report and it locates the audit row and the log line for the same call. Present on every problem, including ones produced before any handler ran.
        required_role:
          type: string
          description: 'On `forbidden`: the session-ladder floor (`viewer`|`operator`|`admin`) this operation requires and the calling SESSION principal does not hold. Absent for token principals, whose authorization is by scope — see `required_scope`.'
        required_scope:
          type: string
          description: 'On `forbidden`: the exact scope the calling TOKEN lacks, also returned in the `TT-Required-Scope` response header. A 403 always names what was missing rather than leaving the caller to guess — mint a token carrying this scope, or ask an operator to. Note the `*:read` / `*:write` wildcards never expand to a privileged scope; those must be named on the token explicitly.'
        restart_required:
          type: array
          description: 'On `restart_required`: the exact configuration paths that cannot be applied to a running process. The change was refused rather than half-applied, and this names precisely what would need a restart, so the decision belongs to the operator instead of to the server.'
          items:
            type: string
        retry_after_seconds:
          type: integer
          format: int64
          description: 'On `rate_limited` and `budget_exceeded`: how long to wait before retrying, in seconds, mirroring the `Retry-After` header. This is the ONLY signal on this API that authorizes an automatic retry — a client must never retry a 4xx that does not carry it.'
        status:
          type: integer
          format: int64
          description: The HTTP status code, mirrored into the body (RFC 9457 §3.1.2) so a client holding only the payload — a log line, a queued failure, a webhook delivery record — still knows what happened.
        successor:
          type: string
          description: 'On `gone`: the operation that replaced this sunset one. A retired operation answers 410 naming its successor rather than a bare 404, so a client discovers the migration instead of concluding the path never existed.'
        title:
          type: string
          description: A short, human-readable summary, FIXED for a given `type` — it never varies between occurrences of the same problem. Safe to show in a UI; useless for branching (use `code`).
        type:
          type: string
          description: A relative URI, `/tt/api/problems/<slug>`, identifying WHAT went wrong — stable across epochs and dereferenceable on this host, so an air-gapped deployment can read the explanation without egress. The slug tail is the stable identifier; resolve it against the served document's origin, and treat an unrecognised `type` as a server you do not understand rather than as a transient condition to retry.
      required:
        - code
        - request_id
        - status
        - title
        - type
    ProblemTypeInfo:
      type: object
      description: 'One registered problem type: what an error body''s `type` points at. The set is closed, so a client may switch on it exhaustively and treat an unrecognised type as "a server I do not understand" rather than as a transient condition.'
      properties:
        description:
          type: string
          description: What the type means and how to remediate it — the same text the rendered page shows.
        extensions:
          type: array
          description: The CLOSED set of Problem extension members an occurrence of this type may carry. RFC 9457 clients must ignore unknown members, so this list can grow additively within an epoch.
          items:
            type: string
        slug:
          type: string
          description: The stable identifier, and the same string every occurrence carries as `code`. Renaming one is a breaking change; a slug is never reused after retirement.
        status:
          type: integer
          format: int64
          description: The HTTP status occurrences of this type carry. One meaning per status is deliberate — a permanent not-wired state is a 404, not a retried 503; a failed `If-Match` is 412 while body-carried CAS is 409.
        title:
          type: string
          description: The fixed human title for this type. Occurrence-specific prose is in the body's `detail`, never here.
        type:
          type: string
          description: The exact relative URI an error body of this type carries, and the join key between an error a client just received and this documentation. Dereference it on this host for a rendered page.
      required:
        - description
        - extensions
        - slug
        - status
        - title
        - type
    ProblemTypesResponse:
      type: object
      description: 'The CLOSED problem-type registry (§2.4). Every non-2xx this API emits carries a `type` drawn from this list; dereferencing that URI on this host renders the same row as a page. The registry is closed by construction: a type is added by registration in the server''s source, never at runtime, so a client may treat an unknown `type` as a server it does not understand rather than as a transient condition.'
      properties:
        count:
          type: integer
          format: int64
          description: 'The registry size. Redundant with the list''s length BY DESIGN and cheap: this is a closed registry, so a consumer that pinned a count notices a change without diffing.'
        problem_types:
          type: array
          description: Every registered type, sorted by slug.
          items:
            $ref: '#/components/schemas/ProblemTypeInfo'
      required:
        - count
        - problem_types
    Proof:
      type: object
      description: Wire shape of Proof (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        bound:
          type:
            - number
            - "null"
        evidence_refs:
          type: array
          items:
            type: string
        guards:
          type: array
          items:
            $ref: '#/components/schemas/GuardResult'
        labeled_rows:
          type: integer
          format: int64
        method:
          type: string
        pass_rate:
          type:
            - number
            - "null"
        quality_safety:
          type: string
        required_rows:
          type: integer
          format: int64
        verdict:
          type: string
      required:
        - labeled_rows
        - method
        - quality_safety
        - verdict
    Provenance:
      type: object
      properties:
        demo_rows:
          type: integer
          format: int64
        estimated_rows:
          type: integer
          format: int64
        indexed_through:
          $ref: '#/components/schemas/IndexedThrough'
        path:
          type: string
        pricing_snapshots:
          type: array
          items:
            type: string
        rows:
          type: integer
          format: int64
        source:
          type: string
        truncated:
          type: boolean
          description: |-
            TRUE when this response is PARTIAL — it does not describe the whole matched set. The causes are per-operation and each operation documents its own: on the analytics reads it means the query DEADLINE was hit mid-scan (the row cap is a page-size bound there, refused with a 400 rather than silently applied); on `shareRows` it additionally means the requested `limit` was clamped to the deployment row cap, or that the concurrency semaphore refused the read and the row array is empty by budget rather than by absence.

            What it NEVER means: that rows were dropped without being counted. The sibling counters (`rows`, `unpriced_rows`, `estimated_rows`, `demo_rows`) describe what was scanned, and a partial answer is disclosed here rather than rendered as a complete one.
        unpriced_rows:
          type: integer
          format: int64
        window:
          $ref: '#/components/schemas/Window'
      required:
        - demo_rows
        - estimated_rows
        - indexed_through
        - pricing_snapshots
        - rows
        - source
        - truncated
        - unpriced_rows
        - window
    ProviderAuthRefs:
      type: object
      properties:
        access_key_id_env:
          anyOf:
            - $ref: '#/components/schemas/ProviderCredRef'
            - type: "null"
        credentials_file_env:
          anyOf:
            - $ref: '#/components/schemas/ProviderCredRef'
            - type: "null"
        secret_access_key_env:
          anyOf:
            - $ref: '#/components/schemas/ProviderCredRef'
            - type: "null"
        session_token_env:
          anyOf:
            - $ref: '#/components/schemas/ProviderCredRef'
            - type: "null"
    ProviderCard:
      type: object
      properties:
        api_key_env:
          type: string
          description: The NAME of the environment variable this upstream's credential is read from — never its value. The server reads only the boolean `os.LookupEnv` returns for it; the string return is not bound anywhere on this path, so there is no value here to leak into a response, a log line or an audit row. Empty when no key is configured, which is the `inbound` key status.
        auth_refs:
          anyOf:
            - $ref: '#/components/schemas/ProviderAuthRefs'
            - type: "null"
        auth_scheme:
          type: string
          description: 'Inferred from the CONFIG BLOCK''S SHAPE, and ABSENT when the shape does not determine it: a credentials-file reference means `gcp_oauth`, AWS key parts mean `aws_sigv4`. The bearer-vs-header distinction for a single-key upstream is not inferable from config alone, so no scheme is reported rather than a plausible one being guessed; `key_status` already covers that credential''s presence.'
        base_url:
          type: string
        configured:
          type: boolean
        cooldown:
          $ref: '#/components/schemas/ProviderCooldown'
        dialect:
          type: string
        health:
          type: string
          description: Always `unknown`. Live health and cooldown counters live in the proxy's request path and are not reachable from the management plane, so this server cannot observe an upstream's health — and reporting a green it cannot see would be the most consequential lie available on this surface. `cooldown` beside it is the CONFIGURED policy for the same reason, not a live state.
        id:
          type: string
        key_status:
          type: string
          description: '`configured` — a key env var is named and set. `missing` — it is named and NOT set, which is the actionable state and the reason this field exists. `inbound` — no key is configured for this upstream and the caller''s own Authorization header is forwarded as-is.'
        prompt_cache:
          type: string
          description: 'The PROVIDER''S OWN prompt-cache posture, from the built-in catalog descriptor this upstream binds to: `explicit` (caller-placed breakpoints), `implicit` (automatic prefix caching), `none`, or `unknown` for an operator-defined inline descriptor the built-in catalog cannot speak for. This is NOT TokenTriage''s own response cache, which is a separate surface.'
      required:
        - api_key_env
        - base_url
        - configured
        - cooldown
        - dialect
        - health
        - id
        - key_status
        - prompt_cache
    ProviderCooldown:
      type: object
      properties:
        enabled:
          type: boolean
        failure_threshold:
          type: number
        min_requests:
          type: integer
          format: int64
        seconds:
          type: integer
          format: int64
      required:
        - enabled
        - failure_threshold
        - min_requests
        - seconds
    ProviderCredRef:
      type: object
      properties:
        env:
          type: string
          description: 'An environment variable NAME. This type has exactly two fields and neither can hold a credential value — that is the structural guarantee, not a convention: there is nowhere for a secret to be assigned.'
        present:
          type: boolean
      required:
        - env
        - present
    ProviderProbeResponse:
      type: object
      properties:
        api_key_env:
          type: string
        id:
          type: string
        key_present:
          type: boolean
        note:
          type: string
        probed:
          type: boolean
          description: False in this build. A live probe — an actual request to the upstream using the referenced credential — is not implemented; this operation reports what such a probe WOULD use and audits the operator's attempt. No upstream request is made and no key value is read.
      required:
        - api_key_env
        - id
        - key_present
        - note
        - probed
    ProvidersResponse:
      type: object
      properties:
        note:
          type: string
        providers:
          type: array
          items:
            $ref: '#/components/schemas/ProviderCard'
        source:
          type: string
      required:
        - providers
    QualityHeld:
      type: object
      properties:
        evidence_ref:
          type: string
        reason:
          type: string
        verdict:
          type: string
          enum:
            - BEAT
            - NOT_BEAT
            - ON_FRONTIER
            - CANNOT_MEASURE
      required:
        - verdict
    QualitySource:
      type: object
      properties:
        computed_at:
          type: string
        delta:
          type:
            - number
            - "null"
          description: 'The signed quality delta, null when the source refused to measure. Always present for ON_FRONTIER, where the sign is the whole point: it says how far below the frontier the served labels place routing.'
        judge:
          anyOf:
            - $ref: '#/components/schemas/Judge'
            - type: "null"
        labeled_rows:
          type: integer
          format: int64
        min_labeled_rows:
          type: integer
          format: int64
        note:
          type: string
        policy_fingerprint_match:
          type: boolean
          description: False when the routing policy CHANGED since this verdict was computed — the verdict then describes a policy that is no longer running, which is why the promote gate refuses on it even when the verdict itself is BEAT.
        reason:
          type: string
        verdict:
          type: string
          description: 'One of four CLOSED states, rendered verbatim — never re-worded by a client. BEAT: a judged counterfactual win. NOT_BEAT: a counterfactual with sufficient labels whose delta did not clear the epsilon. ON_FRONTIER: served-model labels, which can only place routing ON or below the frontier and therefore cannot demonstrate skill; the signed delta is shown with a note saying so. CANNOT_MEASURE: the REFUSAL — too few labels — carrying `reason` instead of a fabricated number.'
          enum:
            - BEAT
            - NOT_BEAT
            - ON_FRONTIER
            - CANNOT_MEASURE
        window:
          $ref: '#/components/schemas/Window'
      required:
        - labeled_rows
        - min_labeled_rows
        - policy_fingerprint_match
        - verdict
        - window
    QueryGroup:
      type: object
      properties:
        key:
          type: object
          description: The group's value for each requested `group_by` dimension, in request order.
          additionalProperties:
            type: string
        metrics:
          $ref: '#/components/schemas/MetricSet'
      required:
        - key
        - metrics
    QueryRequest:
      type: object
      description: An aggregation query. `metrics` is required; everything else narrows or reshapes the result. Semantics are golden-equal to the CLI's report command — one engine, one answer.
      properties:
        baseline:
          type: string
        bucket:
          type: string
          description: A duration (e.g. `1h`, `1d`) that turns the result into `series` instead of `groups`. Absent ⇒ `groups`.
        filter:
          description: The closed filter AST (Appendix B.3). The same grammar the `q=` text form on GET lists compiles to; there is no third query language, and no function calls, subqueries or field-to-field comparisons — by design, forever.
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        group_by:
          type: array
          description: At most TWO dimensions natively (e.g. `["request.model","dim.tenant"]`). More is a 400 rather than a silently-dropped dimension.
          items:
            type: string
        metrics:
          type: array
          description: 'REQUIRED. From the pinned vocabulary: `cost_usd`, `requests`, `tokens_by_type`, `savings`, `p95_cost`, `p99_ttft`. A metric outside it is a 400 naming the failing input.'
          items:
            type: string
        order:
          type: string
          description: Which metric orders the top-N, and in which direction (`-cost_usd` for most-expensive-first).
        since:
          type: string
          description: RFC 3339 UTC lower bound, inclusive. Absent ⇒ the whole ledger.
        top:
          type: integer
          format: int64
          description: 'Keep only the top N groups by the ordering metric. It is a RESULT bound, not a page: there is no cursor into a top-N, and the groups it drops are dropped, not paged.'
        tz:
          type: string
        until:
          type: string
          description: RFC 3339 UTC upper bound, exclusive. Absent ⇒ now.
      required:
        - metrics
    QueryResponse:
      type: object
      description: Exactly one of `groups` (a group-by result) or `series` (a bucketed result) is populated, decided by whether the request carried a `bucket`. `provenance` is always present. `federation` is present ONLY when the answer was merged across peer instances; when it is, EVERY group key carries an additional `instance` dimension, and `federation.excludes` must be read before any total is trusted.
      properties:
        coverage:
          anyOf:
            - $ref: '#/components/schemas/FleetCoverage'
            - type: "null"
        federation:
          description: 'Present IFF this result was merged across federated peer instances. Its ABSENCE is meaningful and is the single-instance signal — it is never emitted empty. When present, every group key carries an additional `instance` dimension, and `federation.excludes` / `federation.exclusion_note` are the disclosure that a peer''s rows are MISSING from the totals. Read them BEFORE `provenance.truncated`, which reports only THIS instance''s budget state and cannot see a dead peer: a merged result that dropped an unreachable peer still reports `truncated: false`.'
          anyOf:
            - $ref: '#/components/schemas/Federation'
            - type: "null"
        groups:
          type: array
          items:
            $ref: '#/components/schemas/QueryGroup'
        provenance:
          $ref: '#/components/schemas/Provenance'
        series:
          type: array
          items:
            $ref: '#/components/schemas/SeriesPoint'
      required:
        - provenance
    RateLimitPolicy:
      type: object
      description: 'The per-principal rate budget IN FORCE ON THIS DEPLOYMENT (§2.5 / B.1 rule 8). Read it before writing a retry loop: the two budgets are separate, the window is fixed rather than sliding, and the units of the two headers that report them differ deliberately.'
      properties:
        algorithm:
          type: string
          description: '`fixed_window`: a counter per window, not a sliding window and not a token bucket. The consequence a client must size against is that up to about TWICE the budget can be admitted across a single window boundary.'
          enum:
            - fixed_window
        enforcement:
          type: string
          description: '`per_node`: the budgets are in-process. Two instances behind a load balancer have two independent budgets, so the effective ceiling for a client that fans out is this number times the node count.'
          enum:
            - per_node
        notes:
          type: array
          description: 'The caveats a number alone cannot carry. Human-oriented prose: read it once, do not parse it.'
          items:
            type: string
        read_per_min:
          type: integer
          format: int64
          description: Requests per window on the READ budget. An operation marked `x-read-only` draws it REGARDLESS of its HTTP method — several heavy reads are served over POST because they carry a filter body, and the budget is about what the operation does.
        reset_unit:
          type: string
          description: 'The unit of the `X-RateLimit-Reset` header: `epoch_seconds`, an ABSOLUTE unix timestamp. Subtract the current time to get a wait.'
          enum:
            - epoch_seconds
            - delta_seconds
        retry_after_unit:
          type: string
          description: 'The unit of the `Retry-After` header: `delta_seconds`, a RELATIVE wait. It answers the same question as `reset_unit` in a different unit — never compare the two values directly.'
          enum:
            - epoch_seconds
            - delta_seconds
        scope:
          type: string
          description: '`principal`: a verified session or token is metered on its own identity wherever it calls from; an unauthenticated caller is metered on its source host, so callers behind one NAT egress share a budget.'
          enum:
            - principal
        window_seconds:
          type: integer
          format: int64
          description: The length of the fixed window both budgets are expressed over.
        write_per_min:
          type: integer
          format: int64
          description: Requests per window on the WRITE budget, so a write storm cannot starve the reads an operator needs during an incident.
      required:
        - algorithm
        - enforcement
        - notes
        - read_per_min
        - reset_unit
        - retry_after_unit
        - scope
        - window_seconds
        - write_per_min
    RealizedRecord:
      type: object
      description: Wire shape of RealizedRecord (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        excluded_rows:
          type: integer
          format: int64
        feedback_ref:
          type: string
        generation:
          type:
            - integer
            - "null"
          format: int64
        low_coverage:
          type: boolean
        method:
          type: string
        prior_tier_note:
          type: string
        projected_ref:
          type: boolean
        quality_held:
          $ref: '#/components/schemas/QualityHeld'
        realized:
          $ref: '#/components/schemas/Assessed'
        rows_at_prior_tier:
          type:
            - integer
            - "null"
          format: int64
        rows_measured:
          type: integer
          format: int64
        superseded_by_config_change:
          type: boolean
        volume_note:
          $ref: '#/components/schemas/VolumeNote'
        window:
          $ref: '#/components/schemas/Window'
      required:
        - excluded_rows
        - method
        - projected_ref
        - quality_held
        - realized
        - rows_measured
        - volume_note
        - window
    Reasoning:
      type: object
      properties:
        effort:
          type: string
      required:
        - effort
    Recommendation:
      type: object
      description: Wire shape of Recommendation (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        applied:
          anyOf:
            - $ref: '#/components/schemas/AppliedInfo'
            - type: "null"
        blocked_reason:
          type: string
        detected_at:
          type: string
        dismissed_reason:
          type: string
        id:
          type: string
        opportunity:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain.'
        projected:
          anyOf:
            - $ref: '#/components/schemas/Assessed'
            - type: "null"
        proof:
          $ref: '#/components/schemas/Proof'
        realized:
          anyOf:
            - $ref: '#/components/schemas/RealizedRecord'
            - type: "null"
        recipe:
          type: string
        scope:
          type: string
        status:
          type: string
        type:
          type: string
        v:
          type: integer
          format: int64
        window:
          $ref: '#/components/schemas/Window'
      required:
        - detected_at
        - id
        - proof
        - scope
        - status
        - type
        - v
        - window
    RecommendationApplyRequest:
      type: object
      description: The apply body. Sending `{}` (or no body at all) requests a real apply at the generation the server reads, which is the reading the operator floor and the CSRF gate make safe to take literally.
      properties:
        expected_generation:
          type: integer
          format: int64
          description: |-
            An OPTIONAL compare-and-swap against the running config's generation. Zero (or absent) means the apply proceeds at whatever generation it reads.

            A client that showed the operator a preview SHOULD send the generation that preview reported. Without it, a config that moved underneath the operator is applied over anyway, and the diff that gets committed is not the diff anybody looked at. With it, that case is refused as a generation conflict carrying the current generation, so the client can re-preview.
        preview:
          type: boolean
          description: 'True stops after the diff: the candidate is compiled, validated and diffed, and then nothing is written and nothing is reloaded. The response is the same shape a committed apply returns with `applied: false`, so a UI can render the preview and the result through one code path. A preview is still recorded in the audit trail — "who looked at what this would do" stays answerable — but it is recorded as a preview rather than as a change.'
        ttl:
          type: string
          description: 'Staleness tolerance for a cache-opportunity artifact, as a duration string. The detector deliberately leaves this hole rather than picking a number on the operator''s behalf: how stale an answer may be is a product decision, not a property of the traffic that was observed. Artifacts that do not compile a TTL into their candidate ignore it.'
      required:
        - expected_generation
        - preview
        - ttl
    RecommendationApplyResponse:
      type: object
      description: Returned by a preview and by a committed apply. `applied` separates the two and is false for a preview.
      properties:
        applied:
          type: boolean
          description: |-
            True when the config was written and reloaded; false for a preview.

            ★ THOSE ARE THE ONLY TWO CASES, and the sentence is worth stating precisely because an earlier draft of it was wrong. A REFUSED apply does not return this type at all — it returns `application/problem+json` with a registered code — so there is no `applied: false` to read on a refusal. A client should branch on the HTTP status first and only then on this field.
        audit_id:
          type: string
          description: The audit row this apply wrote, present on a committed apply. It links the config change, the artifact's recorded version and the trail entry, so a later reader can get from any one of the three to the other two.
        changes:
          type: array
          description: The semantic diff of the compiled candidate against the running config — the shape `POST /config/diff` renders, produced by the same differ, so a recommendation's diff and a hand-built one are comparable.
          items:
            $ref: '#/components/schemas/ConfigChange'
        generation:
          type: integer
          format: int64
          description: |-
            On a PREVIEW, the generation the candidate was compiled against. On a COMMITTED apply, the NEW generation the write produced. Those are different numbers and the `applied` flag is what tells them apart.

            A refusal carries no `generation` here because it carries no response of this type; the generation-conflict problem carries the running generation as its own `current_generation` member instead.
        note:
          type: string
          description: Disclosures that are not refusals — chiefly the evidence state the operator acted on. A candidate can be applicable and still rest on evidence worth reading before acting; that is what lands here rather than in a refusal.
        preview:
          type: boolean
        rec_id:
          type: string
        restart_required:
          type: array
          description: |-
            Config paths in this candidate that a running process does not pick up. Present even when empty (rather than null) so a client can affirmatively render "this is a hot change".

            A NON-EMPTY value on a committed apply is not something to expect: a candidate touching a restart-required path is REFUSED before anything is written, so this field carries the paths on the refusal rather than describing a change that half-landed.
          items:
            type: string
        summary:
          type: string
        used_params:
          type: object
          description: The parameters the compiler actually consumed, echoed back. Present when the candidate needed any — a TTL, a threshold — so the operator can see which of the values they supplied reached the config, rather than assuming a silently-ignored parameter took effect.
          additionalProperties:
            type: string
      required:
        - applied
        - changes
        - generation
        - preview
        - rec_id
        - restart_required
        - summary
    RecommendationDismissRequest:
      type: object
      description: The dismiss body. `reason` is required.
      properties:
        reason:
          type: string
          description: |-
            Why this recommendation is being dismissed. REQUIRED, and a blank or whitespace-only value is refused with 400.

            It is required because it is retained on the artifact and is what a later reader — or the same operator three months on — has to go on when the same opportunity is detected again. A dismissal with no reason is indistinguishable from one nobody remembers making, and the detector will surface the opportunity again.
      required:
        - reason
    RecommendationDismissResponse:
      type: object
      description: The recorded dismissal, read back from the store after the transition.
      properties:
        audit_id:
          type: string
          description: The audit row this dismissal wrote, carrying the reason.
        dismissed:
          type: boolean
        reason:
          type: string
        rec_id:
          type: string
        status:
          type: string
          description: The artifact's lifecycle state AFTER the transition, read back from the store rather than assumed from the request. It is served so a client can reconcile its own view without a follow-up read.
      required:
        - dismissed
        - reason
        - rec_id
        - status
    RecommendationEvidenceResponse:
      type: object
      properties:
        evidence_ref:
          type: string
        id:
          type: string
        proof:
          $ref: '#/components/schemas/Proof'
        provenance:
          $ref: '#/components/schemas/Provenance'
        sample_req_ids:
          type: array
          items:
            type: string
      required:
        - id
        - proof
        - provenance
    RecommendationsResponse:
      type: object
      description: Wire shape of RecommendationsResponse (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        evaluated:
          type: array
          items:
            $ref: '#/components/schemas/EvaluatedType'
        measurement:
          anyOf:
            - $ref: '#/components/schemas/MeasurementPass'
            - type: "null"
        provenance:
          $ref: '#/components/schemas/Provenance'
        reason:
          type: string
        recommendations:
          type: array
          items:
            $ref: '#/components/schemas/Recommendation'
        scanned_at:
          type: string
      required:
        - provenance
        - recommendations
    ReconcileAliasMatch:
      type: object
      properties:
        from:
          type: string
        lines:
          type: integer
          format: int64
        note:
          type: string
        suspect:
          type: boolean
        to:
          type: string
        usd:
          type: number
      required:
        - from
        - lines
        - note
        - to
        - usd
    ReconcileArtifact:
      type: object
      properties:
        actor:
          type: string
        file_name:
          type: string
        file_sha256:
          type: string
        format:
          type: string
        id:
          type: string
        imported_at:
          type: string
        report:
          $ref: '#/components/schemas/ReconcileReport'
        v:
          type: integer
          format: int64
      required:
        - actor
        - file_sha256
        - format
        - id
        - imported_at
        - report
        - v
    ReconcileDayRange:
      type: object
      properties:
        first:
          type: string
        last:
          type: string
      required:
        - first
        - last
    ReconcileDriftScope:
      type: object
      properties:
        canonical:
          $ref: '#/components/schemas/Money'
        drift:
          $ref: '#/components/schemas/Money'
        drifted:
          type: boolean
        limit_id:
          type: string
        node:
          type: string
        orphan:
          type: boolean
        rollup:
          $ref: '#/components/schemas/Money'
        scope:
          type: string
      required:
        - canonical
        - drift
        - drifted
        - rollup
        - scope
    ReconcileExplanation:
      type: object
      properties:
        kind:
          type: string
        note:
          type: string
        rows:
          type: integer
          format: int64
        usd:
          $ref: '#/components/schemas/Money'
      required:
        - kind
        - note
        - usd
    ReconcileExplanationTotal:
      type: object
      properties:
        kind:
          type: string
        note:
          type: string
        rows:
          type: integer
          format: int64
        slices:
          type: integer
          format: int64
        usd:
          $ref: '#/components/schemas/Money'
      required:
        - kind
        - note
        - usd
    ReconcileReport:
      type: object
      properties:
        aliases:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileAliasMatch'
        coverage:
          anyOf:
            - $ref: '#/components/schemas/FleetCoverage'
            - type: "null"
        decomposition:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileExplanationTotal'
        delta:
          $ref: '#/components/schemas/Money'
        estimated_rows:
          type: integer
          format: int64
        explained:
          $ref: '#/components/schemas/Money'
        invoice_rows:
          type: integer
          format: int64
        invoice_total:
          $ref: '#/components/schemas/Money'
        ledger_days:
          anyOf:
            - $ref: '#/components/schemas/ReconcileDayRange'
            - type: "null"
        ledger_rows:
          type: integer
          format: int64
        ledger_total:
          $ref: '#/components/schemas/Money'
        ledger_window:
          $ref: '#/components/schemas/Window'
        rounding_epsilon_usd:
          type: number
        slices:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileSlice'
        unexplained:
          $ref: '#/components/schemas/Money'
        unpriced_rows:
          type: integer
          format: int64
      required:
        - decomposition
        - delta
        - estimated_rows
        - explained
        - invoice_rows
        - invoice_total
        - ledger_rows
        - ledger_total
        - ledger_window
        - rounding_epsilon_usd
        - slices
        - unexplained
        - unpriced_rows
    ReconcileReportsResponse:
      type: object
      description: The stored reconciliations plus the parsers this deployment accepts, so an import picker is built from this one read rather than a hard-coded list that can drift from the deployment.
      properties:
        formats:
          type: array
          items:
            type: string
        reports:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileArtifact'
      required:
        - formats
        - reports
    ReconcileSlice:
      type: object
      properties:
        day:
          type: string
        delta:
          $ref: '#/components/schemas/Money'
        estimated_rows:
          type: integer
          format: int64
        explanations:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileExplanation'
        invoice_usd:
          $ref: '#/components/schemas/Money'
        ledger_rows:
          type: integer
          format: int64
        ledger_usd:
          $ref: '#/components/schemas/Money'
        model:
          type: string
        unexplained:
          $ref: '#/components/schemas/Money'
        unpriced_rows:
          type: integer
          format: int64
      required:
        - day
        - delta
        - estimated_rows
        - explanations
        - invoice_usd
        - ledger_rows
        - ledger_usd
        - model
        - unexplained
        - unpriced_rows
    Record:
      type: object
      properties:
        content:
          anyOf:
            - $ref: '#/components/schemas/Content'
            - type: "null"
        cost:
          $ref: '#/components/schemas/Cost'
        decision:
          $ref: '#/components/schemas/Decision'
        demo:
          type: boolean
        dims:
          anyOf:
            - $ref: '#/components/schemas/Dims'
            - type: "null"
        disclosures:
          anyOf:
            - $ref: '#/components/schemas/Disclosures'
            - type: "null"
        feedback:
          type: array
          items:
            $ref: '#/components/schemas/Feedback'
        http:
          $ref: '#/components/schemas/HTTP'
        latency:
          $ref: '#/components/schemas/Latency'
        mcp:
          type: array
          items:
            $ref: '#/components/schemas/MCPEntry'
        outcome:
          $ref: '#/components/schemas/Outcome'
        replica:
          type: string
        req_id:
          type: string
        request:
          $ref: '#/components/schemas/Request'
        signals:
          type: object
          additionalProperties: {}
        trace:
          $ref: '#/components/schemas/Trace'
        ts:
          type: string
        units:
          anyOf:
            - $ref: '#/components/schemas/Units'
            - type: "null"
        usage:
          $ref: '#/components/schemas/Usage'
        v:
          type: integer
          format: int64
      required:
        - cost
        - decision
        - http
        - latency
        - mcp
        - outcome
        - req_id
        - request
        - signals
        - trace
        - ts
        - usage
        - v
    RecordResponse:
      type: object
      description: One decision record, its companions, its content-capture references, and the cost arithmetic that defends its dollar line by line.
      properties:
        content_refs:
          type: array
          items:
            $ref: '#/components/schemas/ContentRef'
        cost:
          $ref: '#/components/schemas/CostArithmetic'
        provenance:
          $ref: '#/components/schemas/Provenance'
        record:
          $ref: '#/components/schemas/Record'
      required:
        - cost
        - provenance
        - record
    RefusalEnforcement:
      type: object
      properties:
        approximate:
          type: boolean
        bound:
          anyOf:
            - $ref: '#/components/schemas/Bound'
            - type: "null"
        note:
          type: string
        posture:
          type: string
      required:
        - approximate
        - posture
    RefusedBy:
      type: object
      properties:
        limit:
          type:
            - number
            - "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        observed:
          type:
            - number
            - "null"
        period:
          type: string
        resets_at:
          type: string
        retry_after_s:
          type: integer
          format: int64
        unit:
          type: string
      required:
        - node
    RefusedByLimitBlock:
      type: object
      properties:
        enforcement:
          anyOf:
            - $ref: '#/components/schemas/RefusalEnforcement'
            - type: "null"
        refused_by:
          anyOf:
            - $ref: '#/components/schemas/RefusedBy'
            - type: "null"
        type:
          type: string
      required:
        - type
    ReplayByRule:
      type: object
      properties:
        changed:
          type: integer
          format: int64
        rows:
          type: integer
          format: int64
        rule:
          type: string
        would_cost_delta:
          $ref: '#/components/schemas/Money'
          description: 'The per-rule delta as a typed Money, and the AUTHORITATIVE one. `usd` is null whenever NO row priced across the whole simulation — which PROVES no individual rule priced either, and is the case a bare per-rule number renders as an unqualified "$0.00" for every rule at once. Where the simulation did price some rows, this reports priced: internal/replay aggregates only rows/changed/cost per rule, so a rule whose OWN rows were all unpriced is not distinguishable here and `provenance.unpriced_rows` is the disclosure that bounds it.'
        would_cost_delta_usd:
          type: number
          description: 'The per-rule share of the simulated delta, in US dollars. RETAINED beside the typed `would_cost_delta` for the shipped dashboard, and it disagrees with it in the same case the response-level pair does: an all-unpriced simulation reports 0 here and UNPRICED there. Prefer `would_cost_delta`. It remains a bare number because the replay engine does not expose a per-RULE unpriced count, so the "no row priced" state cannot be determined at this granularity; the response-level `would_cost_delta` and `provenance.unpriced_rows` carry that disclosure for the simulation as a whole.'
      required:
        - changed
        - rows
        - rule
        - would_cost_delta
        - would_cost_delta_usd
    ReplayRequest:
      type: object
      properties:
        policy_yaml:
          type: string
        since:
          type: string
        until:
          type: string
      required:
        - policy_yaml
        - since
    ReplayResponse:
      type: object
      description: Wire shape of ReplayResponse (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        approximations:
          type: array
          items:
            type: string
        by_rule:
          type: array
          items:
            $ref: '#/components/schemas/ReplayByRule'
        changed:
          type: integer
          format: int64
        changed_examples:
          type: array
          items:
            type: string
        kind:
          type: string
        provenance:
          $ref: '#/components/schemas/Provenance'
        rows:
          type: integer
          format: int64
        rows_with_unavailable_inputs:
          type: integer
          format: int64
          description: Rows whose draft-referenced runtime inputs (`ctx.*`, `candidate.latency.*`, `candidate.reward_*`) could not be reconstructed from the log. They are counted in `rows` but excluded from `changed` and from the cost delta, because a re-decision against absent state is not a verdict. When a strict MAJORITY of rows are in this state the whole replay is refused 422 instead of reported.
        unavailable_inputs:
          type: array
          items:
            type: string
        would_cost_delta:
          $ref: '#/components/schemas/Money'
          description: 'The simulated dollar delta as a typed Money, and the AUTHORITATIVE one: `usd` is null when NO replayed row priced, so an all-unpriced simulation reads as UNPRICED rather than as a confident "$0.00, no change". Pinned rows (whose tier the pin chose, not the draft), degraded rows (re-decided against runtime state the log could not reconstruct) and unpriced rows never contribute, and each is disclosed by count — `provenance.unpriced_rows` and `rows_with_unavailable_inputs`.'
        would_cost_delta_usd:
          type: number
          description: 'The same delta as a bare number, RETAINED because the shipped dashboard parses this field. **Prefer `would_cost_delta`.** These two DISAGREE when no replayed row priced: the typed value is UNPRICED and this one is 0, because a float has no way to say "nothing could be priced". They can never disagree about the value of a PRICED delta — both come from one input through one marshaler — but a client that reads only this field cannot tell an all-unpriced simulation from a genuinely unchanged one.'
      required:
        - approximations
        - by_rule
        - changed
        - changed_examples
        - kind
        - provenance
        - rows
        - rows_with_unavailable_inputs
        - unavailable_inputs
        - would_cost_delta
        - would_cost_delta_usd
    ReplicaCoverage:
      type: object
      properties:
        as_of:
          anyOf:
            - $ref: '#/components/schemas/IndexedThrough'
            - type: "null"
        error:
          type: string
        replica:
          type: string
        rows:
          type:
            - integer
            - "null"
          format: int64
        status:
          type: string
      required:
        - replica
        - status
    ReplicaGovernanceHealth:
      type: object
      properties:
        error:
          type: string
        health:
          anyOf:
            - $ref: '#/components/schemas/GovernanceHealthResponse'
            - type: "null"
        replica:
          type: string
        status:
          type: string
      required:
        - replica
        - status
    Request:
      type: object
      properties:
        has_tools:
          type: boolean
        max_tokens:
          type: integer
          format: int64
        messages:
          type: integer
          format: int64
        model:
          type: string
        prompt_bytes:
          type: integer
          format: int64
        prompt_sha256:
          type: string
        reasoning:
          anyOf:
            - $ref: '#/components/schemas/Reasoning'
            - type: "null"
        system_bytes:
          type: integer
          format: int64
        tool_count:
          type: integer
          format: int64
        tool_result_count:
          type: integer
          format: int64
      required:
        - has_tools
        - max_tokens
        - messages
        - model
        - prompt_bytes
        - prompt_sha256
        - system_bytes
        - tool_count
        - tool_result_count
    ResponseCacheBlock:
      type: object
      properties:
        age_ms:
          type: integer
          format: int64
        bypass_reason:
          type: string
        embed_cost:
          anyOf:
            - $ref: '#/components/schemas/StageCost'
            - type: "null"
        embed_us:
          type: integer
          format: int64
        entry_sha7:
          type: string
        kind:
          type: string
        lookup_us:
          type: integer
          format: int64
        outcome:
          type: string
        recall_config:
          type: string
        recall_exact:
          type:
            - boolean
            - "null"
        rule:
          type: string
        saved:
          anyOf:
            - $ref: '#/components/schemas/StageCost'
            - type: "null"
        saved_basis:
          type: string
        scope_sha7:
          type: string
        serving_tier:
          type: string
        similarity:
          type: number
        source_req_id:
          type: string
        source_usage:
          anyOf:
            - $ref: '#/components/schemas/Usage'
            - type: "null"
        stored:
          type: boolean
        stored_model:
          type: string
        stored_provider:
          type: string
        strategy:
          type: string
        threshold:
          type: number
        verified:
          type:
            - boolean
            - "null"
        verified_active:
          type: boolean
        verified_bound:
          type: number
        verified_calibrated:
          type: boolean
        verified_samples:
          type: integer
          format: int64
      required:
        - outcome
        - stored
    RowsRequest:
      type: object
      description: 'A page request over the canonical decision log. `cursor` is opaque: echo back a `next_cursor` verbatim; never construct one.'
      properties:
        columns:
          type: array
          description: Reserved for a projection; the server returns whole rows today, so this narrows nothing yet.
          items:
            type: string
        cursor:
          type: string
          description: 'OPAQUE page position, echoed verbatim from a previous response''s `next_cursor`. Absent ⇒ the first page. It is a position in THIS ordering: change `sort` and restart the sequence, because a boundary computed under one order means nothing under another.'
        filter:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        limit:
          type: integer
          format: int64
          description: |-
            Page size. Absent or 0 ⇒ 100, the house default advertised at `/capabilities.management_api.pagination.default_limit`. Above this deployment's MAXIMUM it is REFUSED with a 400 naming the maximum — never silently clamped, because a clamped limit is a lie about what the server heard.

            The maximum is DEPLOYMENT-SPECIFIC: `min(dashboard.query.row_cap, 1000)`, i.e. the house ceiling on a default install and the operator's own query row cap where they have tightened it. It is lower than the advertised `max_limit` exactly when an operator has tightened the query budget — read the 400's detail, which always names the value in force for THIS server. That is what `/capabilities…pagination.notes` means by "per-operation caps override these".
        sort:
          type: string
          description: '`field` or `-field` over `ts`, `cost.usd`, `latency.total_ms`, `latency.ttft_ms`, `usage.total_tokens`; `req_id` breaks ties so the order is total. Default `-ts`. An unknown field is a 400.'
    RowsResponse:
      type: object
      description: A page of VERBATIM ledger records with their companions joined. The rows are the system of record's own bytes — this layer strips nothing, so each row's `cost.unpriced` flag survives to the client.
      properties:
        has_more:
          type: boolean
          description: Derived from `next_cursor`, never stated independently, so the two can never disagree. `false` on the last page is an explicit statement where an absent field would be a silence a client has to guess at.
        next_cursor:
          type: string
          description: 'The opaque position to pass as the next request''s `cursor`. ABSENT at the end of the feed — absence IS the end-of-feed signal. Present whenever more rows remain, INCLUDING when the scan stopped at this deployment''s row cap: the next page resumes past it rather than reporting a feed that ended early.'
        provenance:
          $ref: '#/components/schemas/Provenance'
        rows:
          type: array
          items:
            $ref: '#/components/schemas/Record'
      required:
        - has_more
        - provenance
        - rows
    SavedView:
      type: object
      properties:
        created_at:
          type: string
        created_by:
          type: string
        id:
          type: string
        name:
          type: string
        pinned:
          type: boolean
        query:
          $ref: '#/components/schemas/ViewQuery'
        surface:
          type: string
        v:
          type: integer
          format: int64
      required:
        - created_at
        - created_by
        - id
        - name
        - pinned
        - query
        - surface
        - v
    Savings:
      type: object
      description: 'A savings figure (§2.6). HAND-PINNED: the server REFUSES to marshal a Savings without both basis and verdict_ref (guardrail 1), so an unlicensed "saved X%" number cannot exist on the wire; usd is null when unpriced, never 0.'
      properties:
        basis:
          type: string
          description: the measured basis, e.g. "would_usd[top]-would_usd[chosen], fixed usage"
        estimated:
          type: boolean
        excluded_rows:
          type: integer
          format: int64
          description: rows with a real counterfactual EXCLUDED because they do not license a claim — counted, never silently dropped; omitted when 0
        unpriced:
          type: boolean
        usd:
          type:
            - number
            - "null"
          description: null when unpriced (never 0)
        verdict_ref:
          type: string
          description: the evidence verdict that licenses this figure
      required:
        - basis
        - estimated
        - unpriced
        - usd
        - verdict_ref
    Schedule:
      type: object
      properties:
        cadence:
          type: string
        channel_id:
          type: string
        created_at:
          type: string
        created_by:
          type: string
        format:
          type: string
        id:
          type: string
        last_run:
          type: string
        next_run:
          type: string
        run_history:
          type: array
          items:
            $ref: '#/components/schemas/ScheduleRun'
        v:
          type: integer
          format: int64
        view_id:
          type: string
      required:
        - cadence
        - channel_id
        - created_at
        - created_by
        - id
        - v
        - view_id
    ScheduleChannel:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
      required:
        - id
        - type
    ScheduleRun:
      type: object
      properties:
        at:
          type: string
        channel_id:
          type: string
        error:
          type: string
        missed:
          type: boolean
        ok:
          type: boolean
        scheduled_for:
          type: string
        stamp:
          anyOf:
            - $ref: '#/components/schemas/ScheduleRunStamp'
            - type: "null"
      required:
        - at
        - channel_id
        - ok
        - scheduled_for
    ScheduleRunStamp:
      type: object
      properties:
        bytes:
          type: integer
          format: int64
        demo:
          type: boolean
        estimated_rows:
          type: integer
          format: int64
        format:
          type: string
        generated_at:
          type: string
        instance:
          type: string
        pricing_snapshots:
          type: array
          items:
            type: string
        rows:
          type: integer
          format: int64
        unpriced_excluded:
          type: integer
          format: int64
      required:
        - bytes
        - demo
        - estimated_rows
        - format
        - generated_at
        - instance
        - pricing_snapshots
        - rows
        - unpriced_excluded
    SchedulesPage:
      type: object
      properties:
        channels:
          type: array
          items:
            $ref: '#/components/schemas/ScheduleChannel'
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        schedules:
          type: array
          items:
            $ref: '#/components/schemas/Schedule'
      required:
        - has_more
        - schedules
    SchedulesResponse:
      type: object
      properties:
        channels:
          type: array
          items:
            $ref: '#/components/schemas/ScheduleChannel'
        schedules:
          type: array
          items:
            $ref: '#/components/schemas/Schedule'
      required:
        - schedules
    ScoredCand:
      type: object
      description: Wire shape of ScoredCand (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        score:
          type: number
        terms:
          type: object
          additionalProperties:
            type: number
        tier:
          type: string
      required:
        - score
        - tier
    SeriesPoint:
      type: object
      properties:
        key:
          type: object
          additionalProperties:
            type: string
        metrics:
          $ref: '#/components/schemas/MetricSet'
        ts:
          type: string
          description: The bucket's START instant, RFC 3339 UTC. Buckets are left-closed and right-open.
      required:
        - metrics
        - ts
    ServingKeyRef:
      type: object
      properties:
        kid:
          type: string
        node:
          type: string
        rotated_from:
          type: string
    SessionResponse:
      type: object
      description: The resolved principal. `csrf` is present ONLY for a session principal and is RE-ISSUED on every call — it is not a minted-once secret, and it is never emitted for a `tta_` management token or a `tts_` share bearer, which have no session to bind it to.
      properties:
        csrf:
          type: string
          description: The session-bound CSRF token to echo in the `x-tt-csrf` header on every mutating request. ABSENT for a token or share principal — those callers authenticate per request and have no session for a CSRF value to protect.
        role:
          type: string
          description: 'The principal''s role on the session ladder: `viewer`, `operator` or `admin`. For a token principal this is the COARSE half of its authority; `scopes` on the token record is the fine half.'
        user:
          type: string
        via:
          type: string
          description: 'How this request authenticated: `session` (a cookie), `bearer` (a `tta_` management token or a legacy `ttk_` token), or `off` (the loopback no-auth posture). It is what tells a client whether `csrf` will be present — only `session` carries one. The three values are internal/authn''s own vocabulary (Principal.Via); they are NOT the credential-family prefixes, which is why `bearer` covers both token kinds.'
      required:
        - role
        - user
        - via
    ShadowGrade:
      type: object
      properties:
        chosen_ok:
          type:
            - boolean
            - "null"
        duplicate_cost_usd:
          type: number
        duplicate_tier:
          type: string
        duplicate_unpriced:
          type: boolean
        judge_model:
          type: string
        shadow_ok:
          type:
            - boolean
            - "null"
      required:
        - duplicate_cost_usd
        - duplicate_tier
        - judge_model
    ShareMintRequest:
      type: object
      properties:
        expires_in:
          type: string
        query:
          anyOf:
            - $ref: '#/components/schemas/ViewQuery'
            - type: "null"
        view_id:
          type: string
    ShareMintResponse:
      type: object
      properties:
        audit_id:
          type: string
          description: 'The id of the audit row this mint wrote. It is a CORRELATION handle, not a credential: it identifies the event, and holding it grants nothing.'
        record:
          $ref: '#/components/schemas/ShareToken'
        token:
          type: string
          description: The SHOWN-ONCE `tts_` secret. Returned exactly once, by this operation only, and never persisted, logged, written to an audit row, or re-fetchable. Losing it means minting a new token, not recovering this one.
      required:
        - audit_id
        - record
        - token
    ShareReadResponse:
      type: object
      properties:
        name:
          type: string
        pricing_snapshot:
          type: string
        query:
          $ref: '#/components/schemas/ViewQuery'
        read_only:
          type: boolean
        surface:
          type: string
      required:
        - name
        - query
        - read_only
        - surface
    ShareRowsRequest:
      type: object
      description: 'Paging only. The six scope-bearing members are documented because PRESENTING one is a 403: they exist so a widening attempt is refused as a refusal rather than as a parse error about an unknown field.'
      properties:
        columns:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a column projection here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
        cursor:
          type: string
          description: 'Page position, echoed back VERBATIM from a previous response''s `next_cursor`. Absent means the first page. It is opaque in the sense that matters — do not construct one — but be aware it is NOT the base64url keyset cursor `/capabilities.management_api.pagination.cursor_encoding` advertises for the house list feeds: this operation replays the shipped `POST /rows` engine, whose position is a decimal offset into the scan window. The difference is invisible to a client that echoes the value back and visible to one that parses it, which is why it is written down.'
        filter:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a row filter here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
        group_by:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a grouping here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
        limit:
          type: integer
          format: int64
          description: 'Page size. Absent, 0, or above this deployment''s query row cap is CLAMPED to the cap, with `provenance.truncated: true` on the response disclosing that it happened. That is the sanctioned alternate form of the house limit policy — the other list operations REFUSE an over-cap limit with a 400 — and it is allowed here only because the response carries the disclosure. A clamp without one would be a silent lie about what the server heard.'
        metrics:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a metric set here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
        since:
          type:
            - string
            - "null"
          description: 'REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a time floor here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
        sort:
          type:
            - string
            - "null"
          description: 'REJECT-LIST MEMBER — do not send it. A share token''s scope is FROZEN at mint, so presenting a sort order here is a scope-widening attempt and the operation answers 403 `forbidden`; the value is never read. It is declared only so that refusal is a refusal: without the member, strict decoding would answer 400 `bad_request: unknown field` — a parse complaint where the server in fact understood exactly what was asked and declined it. A legal body omits all six and carries only `cursor` and `limit`.'
    ShareScope:
      type: object
      description: 'The FROZEN scope a share token replays: exactly one of a saved-view reference, an inline query artifact, or a governance node subtree. It is immutable after mint — a bearer can never widen it.'
      properties:
        gov_node:
          type: string
        query:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. Present on a query-scoped token: the serialized query artifact frozen at mint, a JSON OBJECT. It is carried verbatim and is NOT re-validated at read time, which is why it is published as an opaque value rather than a reflected shape — the token replays these bytes, so pinning a schema over them would document a contract the read path does not enforce. Mutually exclusive with `view_id` and `gov_node`.'
        view_id:
          type: string
    ShareToken:
      type: object
      description: 'A share token''s REDACTED record: everything needed to manage the token and nothing that could reconstruct or confirm its secret. The store holds only the SHA-256 of the plaintext, and this shape has no field able to carry that digest.'
      properties:
        created_at:
          type: string
        created_by:
          type: string
        expired:
          type: boolean
          description: Derived against the server clock at read time, not stored. A token can be dead by revocation OR by expiry; both are rendered so an operator is never told a live-looking token is usable.
        expires_at:
          type:
            - string
            - "null"
        id:
          type: string
        revoked:
          type: boolean
        revoked_at:
          type:
            - string
            - "null"
        scope:
          $ref: '#/components/schemas/ShareScope'
      required:
        - created_at
        - expired
        - id
        - revoked
        - scope
    ShareTokensResponse:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        tokens:
          type: array
          items:
            $ref: '#/components/schemas/ShareToken'
      required:
        - has_more
        - tokens
    SharedStateDisclosure:
      type: object
      properties:
        note:
          type: string
        shared_state_enabled:
          type: boolean
        tiers:
          type: array
          items:
            type: string
      required:
        - note
        - shared_state_enabled
        - tiers
    Silence:
      type: object
      description: One operator silence. `active` is DERIVED against the server clock at read time rather than stored, and expired silences remain listed — the question "why did nobody get paged?" is usually asked after the silence has lapsed, and removing it from the read would erase the answer.
      properties:
        active:
          type: boolean
        actor:
          type: string
          description: Who installed the silence — the same actor recorded in the `alert.silence` audit row this write produces.
        created_at:
          type: string
        expires_at:
          type: string
          description: MANDATORY, and the reason this write can be refused. A silence with no bound is how an alerting system dies quietly; the bound's LENGTH is the operator's choice, its existence is not.
        note:
          type: string
        rule_id:
          type: string
      required:
        - active
        - created_at
        - expires_at
        - rule_id
    SilenceRequest:
      type: object
      description: 'A silence to install. `expires_at` is required: absent, malformed and already-past values all take the same 422 `silence_requires_expiry` refusal rather than being defaulted to anything.'
      properties:
        expires_at:
          type: string
        note:
          type: string
        rule_id:
          type: string
      required:
        - expires_at
        - rule_id
    SilencesResponse:
      type: object
      properties:
        silences:
          type: array
          items:
            $ref: '#/components/schemas/Silence'
      required:
        - silences
    SplitRuleDesc:
      type: object
      properties:
        driver:
          type: string
        id:
          type: string
        match:
          type: string
        split:
          type: string
        unapplicable:
          type: string
      required:
        - id
        - match
        - split
    StageCost:
      type: object
      properties:
        estimated:
          type: boolean
        unpriced:
          type: boolean
        usd:
          type: number
      required:
        - estimated
        - unpriced
        - usd
    Staleness:
      type: object
      properties:
        indexed_through:
          $ref: '#/components/schemas/IndexedThrough'
        stream_cap:
          type: integer
          format: int64
        stream_clients:
          type: integer
          format: int64
      required:
        - indexed_through
        - stream_cap
        - stream_clients
    SweepPoint:
      type: object
      properties:
        cost:
          $ref: '#/components/schemas/Money'
          description: 'The repriced total at this threshold. UNPRICED (usd null) unless EVERY row at this threshold priced: a partial sum would drop the unpriced rows silently and can INVERT the cost curve, which is the one thing a threshold sweep exists to show. `unpriced_rows` is how many were excluded.'
        frontier_pct:
          type: number
        quality:
          type:
            - number
            - "null"
          description: Null below the oracle-labeled row floor — refused rather than fabricated. `CalibrationSweep.quality_refusal` states how many labels were found and how many are needed.
        reclassified:
          type: integer
          format: int64
        threshold:
          type: number
        unpriced_rows:
          type: integer
          format: int64
      required:
        - cost
        - frontier_pct
        - reclassified
        - threshold
        - unpriced_rows
    ThresholdDiffEntry:
      type: object
      properties:
        direction:
          type: string
        kind:
          type: string
        new_value:
          type: number
        old_value:
          type:
            - number
            - "null"
        rule_id:
          type: string
        signal:
          type: string
      required:
        - direction
        - new_value
        - rule_id
        - signal
    Token:
      type: object
      properties:
        created_at:
          type: string
        expires_at:
          type: string
          description: RFC 3339 UTC; absent means non-expiring. After a rotation this is the END OF THE PREDECESSOR'S GRACE WINDOW, brought FORWARD — rotation never extends a life — and `rotated_to` names the successor that caused it.
        id:
          type: string
        kind:
          type: string
          description: One of `management` (a `tta_` token minted through this API, carrying explicit scopes) or `legacy_bearer` (a pre-existing `ttk_` token). A `legacy_bearer` row reports an EMPTY `scopes` list because it stores none — its authority is its role's FULL expansion, so an empty list there means "everything this role can do", not "nothing". Read this field before concluding a row is harmless.
        label:
          type: string
        last_used_day:
          type: string
          description: The UTC DAY ("2006-01-02") this token last authenticated — deliberately NOT a timestamp, which is why the field is named for its grammar (`/governance/keys` exposes an RFC 3339 HOURLY `last_used`; these are different shapes). Day granularity answers "is this credential still in use?" without making the token store a per-request telemetry channel or a write amplifier on the read path.
        node:
          type: string
          description: An optional tenancy-subtree binding. A bound token is confined with a "/"-boundary containment check, so a token bound to `org:acme` reaches `org:acme/team:ml` and NOT `org:acme-evil`; it is refused on any operation that takes no node parameter, because a binding that cannot be applied must not be silently ignored.
        revoked_at:
          type: string
        role:
          type: string
          description: One of `viewer`, `operator`, `admin` — the session-ladder floor this credential clears, independent of `scopes`.
        rotated_from:
          type: string
        rotated_to:
          type: string
        scopes:
          type: array
          description: 'The ENFORCED grant. Each entry is `<resource>:<verb>`; `*:read` and `*:write` expand over COMMON verbs only, so a privileged scope (`tokens:mint`, `tokens:write`, `keys:mint`, `config:apply`, `users:admin`, `share:admin`, `content:read`) is never granted implicitly and must be named. Two of those seven carry a COMMON verb and are privileged anyway, for the same reason stated differently: `content:read` is bulk egress of verbatim prompts and completions, and `tokens:write` irreversibly destroys a credential. A wildcard is a statement about a VERB CLASS, and neither of those acts belongs to one.'
          items:
            type: string
        state:
          type: string
          description: 'One of `active`, `revoked`, `expired`. Revoked beats expired: a token revoked before its expiry reads as `revoked`, because an operator acted.'
        stub:
          type: string
          description: 'A DISPLAY fragment: the first four base64url characters of the secret BODY, with NO prefix — the same shape virtual keys already expose (internal/vkey/mintresult.go). Not credential material: four characters of a 256-bit body leave 232 bits unknown. It exists so an operator can recognise a token in a listing without any read ever re-revealing one; `kind` is what names the credential family.'
      required:
        - created_at
        - id
        - kind
        - role
        - scopes
        - state
    TokenEvent:
      type: object
      properties:
        actor:
          type: string
        id:
          type: string
        origin:
          type: string
        role:
          type: string
        summary:
          type: string
      required:
        - actor
        - origin
    TokenMintRequest:
      type: object
      properties:
        expires_in_days:
          type: integer
          format: int64
          description: 'Bounds the token''s life in days; 0 or absent means non-expiring. Rotation inherits the REMAINING life, never a fresh full term. A negative value, or one above 3650 (about ten years), is refused 422 rather than silently ignored: both used to fall through to "non-expiring", which answered 200 to a caller who had explicitly asked for a bounded credential.'
        label:
          type: string
        node:
          type: string
          description: OPTIONAL tenancy-subtree binding, e.g. `org:acme` or `org:acme/team:ml`. A bound token is confined by a "/"-boundary containment check — `org:acme` contains `org:acme/team:ml` and NOT `org:acme-evil` — and is refused on any operation that takes no node parameter, because a binding that cannot be applied must never be silently ignored.
        role:
          type: string
          description: REQUIRED. One of `viewer`, `operator`, `admin` — the session-ladder floor this credential clears. It is the COARSE half of the token's authority and `scopes` is the fine half; a grant naming scopes the role does not imply is refused 422 naming the role that would. viewer clears the read-only surface; operator adds the COMMON writes and `config:apply`; admin adds the credential and user administration plane — and, because a role expands over verb CLASSES, admin is also the lowest role that grants any PRIVILEGED scope, including the two whose verb looks common (`content:read`, `tokens:write`).
        scopes:
          type: array
          description: 'REQUIRED, non-empty. Each entry is `<resource>:<verb>`, and the vocabulary is exactly the set this document names as `x-required-scope` on its operations. `*:read` and `*:write` expand over COMMON verbs ONLY, so the privileged scopes `keys:mint`, `tokens:mint`, `tokens:write`, `config:apply`, `users:admin`, `share:admin` and `content:read` must each be named explicitly. If you want the resulting token to be able to REVOKE another token, `["*:write"]` is not enough — name `tokens:write`, and mint it under `role: admin`, which is the lowest role whose expansion grants a privileged scope of that shape. Two rules bound the grant: it may not exceed what `role` implies, and it may not exceed what the CALLING credential itself holds; a grant that breaches either is refused 422 naming the offending scope and the role that would have worked. Example: {"role":"viewer","scopes":["governance:read"]}.'
          items:
            type: string
      required:
        - role
        - scopes
    TokenMintResponse:
      type: object
      description: 'The SHOWN-ONCE mint/rotate reveal: `secret` is returned exactly once and is never persisted, logged, written to an audit row, or re-fetchable (the store holds only its SHA-256). The ONE token schema that carries a secret; every read surface in this resource is secret-free.'
      properties:
        secret:
          type: string
        token:
          $ref: '#/components/schemas/Token'
        warning:
          type: string
      required:
        - secret
        - token
        - warning
    TokenMintedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/TokenMintedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - token.minted
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    TokenMintedDeliveryData:
      type: object
      properties:
        token_minted:
          $ref: '#/components/schemas/TokenEvent'
      required:
        - token_minted
    TokenResponse:
      type: object
      properties:
        token:
          $ref: '#/components/schemas/Token'
      required:
        - token
    TokenRevokedDelivery:
      type: object
      properties:
        created_at:
          type: string
          description: The EMITTING AUDIT ROW's timestamp, verbatim (RFC 3339 UTC) — not the tap's clock, so `GET /events` and `POST /audit` cannot disagree about when the thing happened.
        data:
          $ref: '#/components/schemas/TokenRevokedDeliveryData'
        id:
          type: string
          description: 'Stable, unique for this instance: `ev_<epoch>_<seq>`. The epoch names the ring incarnation, which is what lets a cursor minted before a restart be RECOGNISED as stale instead of silently satisfied with an empty page.'
        request:
          $ref: '#/components/schemas/EventRequest'
        seq:
          type: integer
          format: int64
          description: 'Monotonic per instance and never reused within an epoch. It is the pagination key AND the expiry coordinate: compare your own position against `oldest_seq` to know how close you are to falling off the ring. Stripe''s webhook documentation explicitly refuses to offer an ordering guarantee; this is the reconciliation upgrade that refusal costs a consumer.'
        type:
          type: string
          enum:
            - token.revoked
      required:
        - created_at
        - data
        - id
        - request
        - seq
        - type
    TokenRevokedDeliveryData:
      type: object
      properties:
        token_revoked:
          $ref: '#/components/schemas/TokenEvent'
      required:
        - token_revoked
    TokenRotateRequest:
      type: object
      properties:
        grace_seconds:
          type: integer
          format: int64
          description: How long the PREDECESSOR stays usable after rotation, so a fleet can roll without a gap; 0 or absent revokes it immediately. It can only bring an expiry FORWARD — rotating a token that expires in 30 seconds with a one-hour grace still leaves it expiring in 30 seconds — so it is a hygiene control, never a way to extend a credential someone deliberately time-boxed.
    TokensResponse:
      type: object
      properties:
        tokens:
          type: array
          items:
            $ref: '#/components/schemas/Token'
      required:
        - tokens
    Trace:
      type: object
      properties:
        agent:
          type: string
        parent_id:
          type: string
        source:
          type: string
        step_class:
          type: string
        step_class_source:
          type: string
        step_id:
          type: string
        trace_id:
          type: string
        w3c:
          type: string
      required:
        - agent
        - parent_id
        - source
        - step_class
        - step_class_source
        - step_id
        - trace_id
        - w3c
    TraceEdge:
      type: object
      properties:
        from:
          type: string
        to:
          type: string
      required:
        - from
        - to
    TraceNode:
      type: object
      properties:
        agent:
          type: string
        model:
          type: string
        own_cost:
          $ref: '#/components/schemas/Money'
        parent_id:
          type: string
        req_id:
          type: string
        shadow:
          description: The joined counterfactual for this node, present ONLY when the request was sampled, duplicated and judged. A node with no shadow grade carries NO `shadow` — a delta is never inferred or fabricated.
          anyOf:
            - $ref: '#/components/schemas/TraceNodeShadow'
            - type: "null"
        step_class:
          type: string
      required:
        - own_cost
        - req_id
    TraceNodeShadow:
      type: object
      properties:
        delta:
          $ref: '#/components/schemas/Money'
          description: Signed `own_cost - duplicate_cost` (positive ⇒ the counterfactual was cheaper). UNPRICED when the primary node is unpriced, because no honest difference is computable.
        duplicate_cost:
          $ref: '#/components/schemas/Money'
        duplicate_tier:
          type: string
        judge_model:
          type: string
        shadow_ok:
          type:
            - boolean
            - "null"
          description: 'The judge''s verdict on the duplicated response: `true` ⇒ a cheaper `delta` is a VALIDATED saving; `false` ⇒ the alternative was judged worse; `null` ⇒ undecided or failed. In the last two the delta is shown but is NOT a saving, and no client should render it as one.'
      required:
        - delta
        - duplicate_cost
    TraceResponse:
      type: object
      description: 'One agent trace as a cost tree: the nodes, the fan-out edges, and reconciled totals.'
      properties:
        edges:
          type: array
          items:
            $ref: '#/components/schemas/TraceEdge'
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/TraceNode'
        provenance:
          $ref: '#/components/schemas/Provenance'
        totals:
          $ref: '#/components/schemas/TraceTotals'
        trace_id:
          type: string
      required:
        - edges
        - nodes
        - provenance
        - totals
        - trace_id
    TraceTotals:
      type: object
      properties:
        children_cost:
          $ref: '#/components/schemas/Money'
        cost:
          $ref: '#/components/schemas/Money'
        counterfactual_cost:
          description: The shadow tree's total, summing ONLY the nodes that carry a shadow-grade companion. ABSENT when no node in the trace was graded — the whole counterfactual is not-measurable, which is a different statement from zero.
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        measurable_nodes:
          type: integer
          format: int64
        not_measurable_nodes:
          type: integer
          format: int64
          description: How many nodes carry no shadow grade and are therefore excluded from `counterfactual_cost` — the counterfactual twin of `uncosted_rows`.
        own_cost:
          $ref: '#/components/schemas/Money'
        uncosted_rows:
          type: integer
          format: int64
          description: How many nodes in this trace the pricing could not cost. Disclosed as a COUNT beside the totals rather than folded into them as zero — the total is the sum of what could be priced, and this is what it left out.
      required:
        - children_cost
        - cost
        - own_cost
        - uncosted_rows
    TranslationLoss:
      type: object
      properties:
        approximations:
          type: array
          items:
            type: string
        dropped_params:
          type: array
          items:
            type: string
        from:
          type: string
        to:
          type: string
      required:
        - from
        - to
    Units:
      type: object
      properties:
        characters:
          type: integer
          format: int64
        images:
          type: integer
          format: int64
        requests:
          type: integer
          format: int64
        seconds:
          type: number
      required:
        - characters
        - images
        - requests
        - seconds
    UnpricedTrendNote:
      type: object
      properties:
        note:
          type: string
        rows_per_day:
          type: number
        trend:
          type: string
      required:
        - rows_per_day
    Usage:
      type: object
      properties:
        cache_read_tokens:
          type: integer
          format: int64
        cache_write_detail:
          anyOf:
            - $ref: '#/components/schemas/CacheWriteDetail'
            - type: "null"
        cache_write_tokens:
          type: integer
          format: int64
        input_tokens:
          type: integer
          format: int64
        output_tokens:
          type: integer
          format: int64
        reasoning_tokens:
          type: integer
          format: int64
        source:
          type: string
        total_tokens:
          type: integer
          format: int64
      required:
        - cache_read_tokens
        - cache_write_tokens
        - input_tokens
        - output_tokens
        - reasoning_tokens
        - source
        - total_tokens
    User:
      type: object
      description: A user account's SAFE projection. There is deliberately no field capable of holding the argon2id hash or the plaintext, so "a user's stored password" is unrepresentable on any read of this API.
      properties:
        created_at:
          type: string
        name:
          type: string
        role:
          type: string
          description: One of `viewer`, `operator`, `admin`. Changing it revokes the user's live sessions immediately, so a demotion takes effect on the very next request rather than at session expiry.
        updated_at:
          type: string
      required:
        - name
        - role
    UserCreateRequest:
      type: object
      properties:
        name:
          type: string
        password:
          type: string
          description: The initial plaintext password. It is argon2id-hashed before storage and is never returned by any read. Sent over the request body only — this API has no endpoint that echoes it back.
        role:
          type: string
      required:
        - name
        - password
        - role
    UserDeleteResponse:
      type: object
      properties:
        ok:
          type: boolean
        revoked_sessions:
          type: integer
          format: int64
          description: 'How many live sessions the removal killed. Disclosed rather than assumed: removing a compromised account is only complete when its sessions are gone too.'
      required:
        - ok
        - revoked_sessions
    UserUpdateRequest:
      type: object
      properties:
        password:
          type: string
        role:
          type: string
    UsersResponse:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        users:
          type: array
          items:
            $ref: '#/components/schemas/User'
      required:
        - has_more
        - users
    ViewQuery:
      type: object
      properties:
        columns:
          type: array
          items:
            type: string
        filter:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        group_by:
          type: array
          items:
            type: string
        since:
          type: string
        sort:
          type: string
        text:
          type: string
      required:
        - text
    ViewsResponse:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        views:
          type: array
          items:
            $ref: '#/components/schemas/SavedView'
      required:
        - has_more
        - views
    VolumeNote:
      type: object
      properties:
        after_rpd:
          type: number
        before_rpd:
          type: number
      required:
        - after_rpd
        - before_rpd
    WebhookCaps:
      type: object
      description: |-
        The §2.8 delivery advertisement. Every value is read from the running worker and its store rather than restated here, so what is advertised is what is enforced.

        Two of these numbers exist as a constant in this package AND a constant in the delivery worker, because the worker sits below this package and cannot import it. They are pinned equal by a test rather than trusted to agree: an earlier cut had the retry ceiling in two places with no pin, and a dispatcher left at its zero value advertised `max_attempts: 0` — the exact meaningless number that field exists to prevent.
      properties:
        backoff_policy:
          type: string
          description: 'Stated in words because the ladder is a formula, not a number: exponential from a base delay, doubling per attempt, capped, then jittered. The jitter is not decoration — one event fans out to every matching subscription at once, so an endpoint outage fails all of them on the same instant and an unjittered ladder would retry all of them on the same instant too, forever.'
        build_error:
          type: string
          description: |-
            Present when a `webhooks:` section IS configured but one or more subscriptions could not be built — a malformed glob, a glob matching no event type this server can emit, a missing env-ref. It names each failure with its `webhooks[i]` index.

            It exists because `configured: false` was answering two opposite questions with one word: "nothing is configured" and "everything is configured and all of it failed to build". An operator in the second state was being told to add a section they already had.
        configured:
          type: boolean
        degraded:
          type: boolean
          description: true when SOME subscriptions built and others did not. Delivery is running for the ones that built; `build_error` names the rest. A typo in one destination is not an outage in the others.
        durable:
          type: boolean
        max_attempts:
          type: integer
          format: int64
        notes:
          type: array
          items:
            type: string
        payload_retention:
          type: integer
          format: int64
          description: 'How many of the most recent delivery records still hold their payload and can therefore be redelivered. It is smaller than `store_size`: a record stays readable long after its body has been dropped, and `replayable` on the record reports which side of this line it is on.'
        signature:
          type: string
        signature_headers:
          type: array
          items:
            type: string
        store_size:
          type: integer
          format: int64
        subscriptions:
          type: integer
          format: int64
          description: 'How many subscriptions are configured. The IDs are deliberately not listed: `GET /config/running` is where the configured shape is read, and a second place to read it is a second place for it to disagree.'
        tolerance_seconds:
          type: integer
          format: int64
          description: The replay window our own verifier helper enforces and the one we recommend a receiver enforce. It is advertised rather than assumed because a receiver that enforces NO window is accepting replays forever, and a receiver cannot pick a window it was never told about.
      required:
        - backoff_policy
        - configured
        - durable
        - max_attempts
        - notes
        - payload_retention
        - signature
        - signature_headers
        - store_size
        - subscriptions
        - tolerance_seconds
    WebhookDeliveriesResponse:
      type: object
      description: 'One page of the bounded delivery store, newest first, plus the two facts that keep an empty page from lying. `durable: false` distinguishes "nothing failed" from "we restarted"; `configured` distinguishes "nothing has been sent yet" from "delivery is not set up at all".'
      properties:
        configured:
          type: boolean
          description: 'Whether any `webhooks[]` subscription exists on this instance. It exists because an empty list otherwise conflates two opposite situations: delivery working and idle, versus delivery never wired at all.'
        deliveries:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
        durable:
          type: boolean
          description: false ⇒ this store lives only in this process's memory and a restart empties it. The event log (`GET /events`) and the audit trail (`POST /audit`) are the surfaces that survive; this one is a diagnostic buffer and says so rather than letting an empty page be read as good news.
        has_more:
          type: boolean
          description: 'Whether another page follows. DERIVED from next_cursor by the server rather than computed independently, so the two can never contradict each other. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at.'
        next_cursor:
          type: string
          description: The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of the feed — absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member's PRESENCE is correct and testing for a non-empty value is not. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.
        store_size:
          type: integer
          format: int64
      required:
        - configured
        - deliveries
        - durable
        - has_more
        - store_size
    WebhookDelivery:
      type: object
      description: |-
        One message's delivery record: the outcome of sending ONE event to ONE subscription, folded across every attempt made for it.

        It is built by SUBTRACTION. The store behind it holds everything needed to send the message again — the env-refs and the signed payload bytes — which makes this a byte-egress-adjacent read, so the record publishes what diagnoses a failure and nothing that could reconstruct one. See the individual fields for what is absent and why.
      properties:
        attempts:
          type: integer
          format: int64
        error:
          type: string
          description: |-
            The last failure, from a fixed, secret-free vocabulary: a transport category (`timeout`, `canceled`, `dns error`, `tls error`, `connection error`, `transport error`) prefixed by the env-ref it was delivering to; `endpoint returned <status>`; a configuration fault naming the unset or unparseable env-ref; or `shed: …` when the in-flight cap was reached. `canceled` means the process was shutting down mid-attempt, and it is listed because it is reachable and published — an omitted-but-reachable value is the same defect as an undocumented field.

            It is never a raw transport error, because Go's `*url.Error` renders the FULL endpoint URL in its message and that URL is often the credential. A less specific message is the price of not leaking the endpoint.
        event_id:
          type: string
        event_type:
          type: string
          enum:
            - config.applied
            - mode.set
            - governance.key.minted
            - governance.key.rotated
            - governance.key.revoked
            - governance.override.set
            - governance.override.deleted
            - budget.threshold_crossed
            - budget.exceeded
            - token.minted
            - token.revoked
            - calibration.activated
            - cache.purged
        first_attempt_at:
          type: string
        id:
          type: string
          description: The Standard Webhooks `webhook-id` this message was signed with — the SAME value the receiver saw in its header, constant across every attempt and every redelivery. It is ONE id rather than a private record id plus a wire id, because an operator diagnosing a delivery greps their receiver's logs for the header they can see; a separate internal id would force them to correlate by timestamp.
        last_attempt_at:
          type: string
        max_attempts:
          type: integer
          format: int64
          description: |-
            The retry ceiling that was in force for this message. It travels beside `attempts` because "3 attempts" means nothing without knowing whether the ceiling was 3 or 10.

            `attempts` may be BELOW it on a failed delivery, and that is not a truncated ladder: a permanent refusal (any 4xx other than 408, 425 and 429) stops the message immediately, because re-sending a signed body to an endpoint that answered `401` is both useless and a good way to be blocked.
        next_attempt_at:
          type: string
        payload_bytes:
          type: integer
          format: int64
        payload_sha256:
          type: string
          description: 'SHA-256 over the EXACT bytes that were signed and sent. It is here INSTEAD of the payload: `GET /events` already serves that body under `event_id`, and a second copy would be a second egress surface with no second reason to exist. A redelivery carrying the same digest is provably the same message.'
        redelivered:
          type: boolean
        replayable:
          type: boolean
          description: |-
            Whether this record still holds the payload needed to re-send it. Reading it is what lets a caller know `POST /webhooks/deliveries/{id}/redeliver` will be refused 409 BEFORE issuing it, rather than after.

            It goes false while the record REMAINS READABLE, because the store keeps two windows: it retains `store_size` records but only `payload_retention` payloads (both on `GET /capabilities`). A record is small and answers "what has been failing?"; a payload is the whole signed body and only answers "can I push this again?". Splitting them buys a longer diagnostic history at a fraction of the memory, and it is why this field is a real report rather than a constant.
        response_status:
          type: integer
          format: int64
          description: The endpoint's HTTP status on the last attempt that received one. ABSENT means the request never completed — a different fact from "the endpoint answered 0", and the omission is how the difference is stated.
        secret_env:
          type: string
          description: |-
            The NAME of the environment variable holding this subscription's signing secret — never the secret, and never its resolved value. The name is not sensitive: it is already in the config file that `GET /config/running` and `GET /config/export` serve verbatim.

            ★ IT MUST BE UPPERCASE — `^[A-Z_][A-Z0-9_]*$` — and that is stricter than every other `*_env` field in the config on purpose. A Standard Webhooks secret is `whsec_` followed by base64, and about a third of real secrets contain no `+` or `/`, which makes them VALID mixed-case environment-variable names indistinguishable from a reference. Requiring the conventional uppercase spelling is what makes a pasted secret impossible in this field rather than merely unlikely; `url_env` is not held to it, because every real URL contains `://` and is refused on that alone.
        signature_count:
          type: integer
          format: int64
          description: |-
            How many `v1,` elements the `webhook-signature` header carried: 1 normally, 2 or more during a key rotation (the secret's environment variable may hold several whitespace-separated secrets, and every one signs). It answers the operational question a signature's bytes would have answered — "is my rotation live?" — without publishing one.

            **0 means nothing was ever signed**, which is a distinct and useful state rather than a missing value: the attempt failed before signing (the URL env-ref was unset, the secret was unparseable) or was shed at the in-flight cap. Read `error` for which.
        status:
          type: string
          enum:
            - retrying
            - succeeded
            - failed
        subscription_id:
          type: string
        url_env:
          type: string
          description: The NAME of the environment variable holding the destination URL — never the URL. A webhook URL is routinely the credential itself (a Slack incoming hook, a Zapier catch hook and an EventBridge partner URL each carry their entire authorization in the path), so this surface resolves it at send time and never publishes it.
      required:
        - attempts
        - event_id
        - event_type
        - first_attempt_at
        - id
        - last_attempt_at
        - max_attempts
        - payload_bytes
        - payload_sha256
        - redelivered
        - replayable
        - secret_env
        - signature_count
        - status
        - subscription_id
        - url_env
    WebhookRedeliverResponse:
      type: object
      description: 'The acknowledgement of a scheduled re-send. `accepted` makes the asynchrony explicit in the body rather than only in prose: this operation queues a delivery, it does not perform one, because blocking a management request on a third-party endpoint would put an arbitrary remote service on this API''s latency path.'
      properties:
        accepted:
          type: boolean
        audit_id:
          type: string
        delivery:
          $ref: '#/components/schemas/WebhookDelivery'
          description: The record as it stood when the redelivery was ACCEPTED — attempts not yet incremented, status returned to `retrying`. It is a receipt for the scheduling, not a report of the outcome; poll `GET /webhooks/deliveries` for that.
      required:
        - accepted
        - audit_id
        - delivery
    Window:
      type: object
      properties:
        since:
          type: string
        tz:
          type: string
        until:
          type: string
      required:
        - since
        - until
  securitySchemes:
    managementToken:
      type: http
      scheme: bearer
      description: 'A tta_ scoped management token (§2.3): SHA-256-at-rest, shown exactly once at mint, rotatable with lineage, revocable immediately. Each operation''s security entry lists its required scope (also x-required-scope); the *:read / *:write wildcards expand over common scopes only — a privileged scope (config:apply, content:read, keys:mint, share:admin, tokens:mint, tokens:write, users:admin) must be named on the token. An insufficient scope is a 403 problem carrying required_scope and the TT-Required-Scope header.'
    session:
      type: apiKey
      in: cookie
      name: tt_session
      description: A dashboard session. Session principals are gated by the operation's x-role-floor (viewer|operator|admin); unsafe methods additionally require the session CSRF header. CSRF values are never issued to tta_/tts_ principals.
