# Endpoint-coverage drift manifest for the otari SDKs.
#
# CANONICAL COPY. This file is the single source of truth. The codegen workflow
# (.github/workflows/otari-sdk-codegen.yml) pushes it into all four SDK repos
# alongside the generated core, so the copies there are generated artifacts.
# Edit this file; never edit the copies, they are overwritten on every regen.
#
# tests/unit/test_sdk_endpoint_coverage.py checks it against
# docs/public/openapi.json from the same commit: every "METHOD path" the spec
# exposes (excluding /api/v1/health* meta routes) must appear under [covered] or
# [excluded], and every entry here must still exist in the spec. So adding a
# gateway endpoint fails the gateway's own build until it is classified here.
# That is the point: drift fails in the repo that causes it, at the moment it
# is caused, rather than surfacing later in four downstream repos.
#
#   [covered]   a public SDK wrapper surfaces this endpoint
#   [excluded]  deliberately not surfaced; give a reason
#
# Lines are "METHOD /path" with an optional "# reason" trailer; blank lines and
# comment lines are ignored.

[covered]
# Inference
POST /api/v1/chat/completions
POST /api/v1/responses
POST /api/v1/messages
POST /api/v1/messages/count_tokens
POST /api/v1/embeddings
POST /api/v1/moderations
POST /api/v1/rerank
GET /api/v1/models
# Batches
POST /api/v1/batches
GET /api/v1/batches
GET /api/v1/batches/{batch_id}
POST /api/v1/batches/{batch_id}/cancel
GET /api/v1/batches/{batch_id}/results
# Control plane: keys
POST /api/v1/keys
GET /api/v1/keys
GET /api/v1/keys/{key_id}
PATCH /api/v1/keys/{key_id}
DELETE /api/v1/keys/{key_id}
# Control plane: users
POST /api/v1/users
GET /api/v1/users
GET /api/v1/users/{user_id}
PATCH /api/v1/users/{user_id}
DELETE /api/v1/users/{user_id}
GET /api/v1/users/{user_id}/usage
# Control plane: budgets
POST /api/v1/budgets
GET /api/v1/budgets
GET /api/v1/budgets/{budget_id}
PATCH /api/v1/budgets/{budget_id}
DELETE /api/v1/budgets/{budget_id}
# Control plane: pricing
POST /api/v1/pricing
GET /api/v1/pricing
GET /api/v1/pricing/{model_key}
GET /api/v1/pricing/{model_key}/history
DELETE /api/v1/pricing/{model_key}
# Control plane: usage
GET /api/v1/usage
# Images
POST /api/v1/images/generations
# Audio
POST /api/v1/audio/speech
POST /api/v1/audio/transcriptions

[excluded]
GET /api/v1/models/{model_id}                       # redundant, list_models covers discovery
# Caller-orchestrated MCP is public, but no SDK shell wraps it yet. Typed
# mcp.list_tools and mcp.execute methods are the next step for the SDKs, and
# execution needs one deliberate property no generated wrapper gives it for
# free: exactly one HTTP attempt, never a retry.
GET /api/v1/mcp/servers/{mcp_server_id}/tools       # not yet wrapped
POST /api/v1/mcp/execute                            # not yet wrapped
# Files API: added to the gateway spec, not yet wrapped by any SDK shell.
POST /api/v1/files                       # not yet wrapped
GET /api/v1/files                        # not yet wrapped
GET /api/v1/files/{file_id}              # not yet wrapped
GET /api/v1/files/{file_id}/content      # not yet wrapped
DELETE /api/v1/files/{file_id}           # not yet wrapped

# The Playground, which is the dashboard's own chat page and is excluded on a
# stronger ground than "not yet wrapped": its completion endpoint is authorized
# by a dashboard session cookie, so an SDK holding an API key cannot call it at
# all, and the surrounding reads store what that page remembers for the signed-in
# person. An SDK caller wanting a completion has POST /api/v1/chat/completions,
# which this one delegates to.
POST /api/v1/playground/chat/completions                        # session-authorized, not an SDK surface
GET /api/v1/playground/tools                                    # session-authorized, not an SDK surface
GET /api/v1/playground/consent                                  # session-authorized, not an SDK surface
PUT /api/v1/playground/consent                                  # session-authorized, not an SDK surface
GET /api/v1/playground/conversations                            # session-authorized, not an SDK surface
POST /api/v1/playground/conversations                           # session-authorized, not an SDK surface
GET /api/v1/playground/conversations/{conversation_id}/messages  # session-authorized, not an SDK surface
DELETE /api/v1/playground/conversations/{conversation_id}       # session-authorized, not an SDK surface
GET /api/v1/playground/comparisons                              # session-authorized, not an SDK surface
POST /api/v1/playground/comparisons                             # session-authorized, not an SDK surface
DELETE /api/v1/playground/comparisons/{comparison_id}           # session-authorized, not an SDK surface
GET /api/v1/playground/favorite-models                          # session-authorized, not an SDK surface
PUT /api/v1/playground/favorite-models                          # session-authorized, not an SDK surface

# Dashboard / admin management surface: present in the gateway spec, not yet
# wrapped by any SDK shell. Deferred here so the drift gate stays green; move to
# [covered] when a shell surfaces them. Kept identical across all four SDKs.
# Model aliases
GET /api/v1/aliases                                 # not yet wrapped
POST /api/v1/aliases                                # not yet wrapped
DELETE /api/v1/aliases/{name}                       # not yet wrapped
# Provider credentials (runtime provider management)
GET /api/v1/provider-credentials                    # not yet wrapped
POST /api/v1/provider-credentials                   # not yet wrapped
POST /api/v1/provider-credentials/test              # not yet wrapped
PATCH /api/v1/provider-credentials/{instance}       # not yet wrapped
DELETE /api/v1/provider-credentials/{instance}      # not yet wrapped
POST /api/v1/provider-credentials/{instance}/test   # not yet wrapped
# Providers catalog
GET /api/v1/providers                               # not yet wrapped
GET /api/v1/providers/catalog                       # not yet wrapped
# Runtime settings
GET /api/v1/settings                                # not yet wrapped
PATCH /api/v1/settings                              # not yet wrapped
GET /api/v1/settings/mail                           # not yet wrapped
POST /api/v1/settings/mail/test                     # not yet wrapped
GET /api/v1/settings/maintenance-mode               # not yet wrapped
PATCH /api/v1/settings/maintenance-mode             # not yet wrapped
# Model discovery / metadata
GET /api/v1/models/discoverable                     # not yet wrapped
GET /api/v1/models/metadata                         # not yet wrapped
# Other control-plane extensions of already-covered resources
GET /api/v1/usage/count                             # not yet wrapped
POST /api/v1/keys/{key_id}/rotate                   # not yet wrapped
GET /api/v1/budgets/{budget_id}/reset-logs          # not yet wrapped

# --- Second wave of gateway endpoints (added after the initial manifest) ---
# Dashboard session auth: browser cookie flow for the admin UI, not an SDK surface.
POST /api/v1/auth/session                           # dashboard-only
# Deployment bootstrap: tells the dashboard shell which deployment served it, so
# it exists for a browser deciding what to render. An SDK already knows.
GET /api/v1/bootstrap                               # dashboard-only
DELETE /api/v1/auth/session                         # dashboard-only
# Setting the dashboard sign-in password: the other half of the same browser
# flow. An SDK authenticates with the master key or an API key and never holds
# a password, so there is nothing here for one to wrap.
PUT /api/v1/auth/password                           # dashboard-only
# The name the signed-in identity goes by: the account page's own write, beside
# the credential ones. An SDK authenticates as a key rather than as a person and
# has no account page to render, so there is nothing here for one to wrap.
PATCH /api/v1/auth/profile                          # dashboard-only
# Signup, email verification, and password reset (otari#650): claiming a
# roster identity's dashboard login and recovering it, the same browser-only
# credential flow PUT /api/v1/auth/password is. No SDK caller holds a password.
POST /api/v1/auth/signup                            # dashboard-only
POST /api/v1/auth/verify-email                      # dashboard-only
POST /api/v1/auth/resend-verification               # dashboard-only
POST /api/v1/auth/password/reset                    # dashboard-only
POST /api/v1/auth/password/reset/confirm            # dashboard-only
# Passkeys (otari#652): both ceremonies need a WebAuthn authenticator, which is
# a browser API. An SDK cannot produce an attestation or an assertion, so there
# is nothing here for one to wrap, including the credential list: it exists to
# render the account page's passkey rows.
POST /api/v1/auth/webauthn/register/options         # dashboard-only
POST /api/v1/auth/webauthn/register                 # dashboard-only
POST /api/v1/auth/webauthn/authenticate/options     # dashboard-only
POST /api/v1/auth/webauthn/authenticate             # dashboard-only
GET /api/v1/auth/webauthn/credentials               # dashboard-only
PATCH /api/v1/auth/webauthn/credentials/{credential_id}    # dashboard-only
DELETE /api/v1/auth/webauthn/credentials/{credential_id}   # dashboard-only
# OAuth sign-in (otari#651): the flow is a browser redirect through a third
# party. The authorization URL has to be navigated to, the consent screen
# completed by a person, and the CSRF state kept by the browser that started it
# and checked when the provider sends it back. None of that is something an SDK
# can drive, and the session it ends by minting is an HttpOnly cookie the
# dashboard uses rather than a token an SDK could hold.
GET /api/v1/auth/oauth/{provider}/authorize         # dashboard-only
POST /api/v1/auth/oauth/{provider}/callback         # dashboard-only
# OTLP ingest: OpenTelemetry collector receivers, not an SDK surface.
POST /otlp/v1/logs                                  # otel ingest
POST /otlp/v1/traces                                # otel ingest
POST /otlp/v1/metrics                               # otel ingest
# Usage analytics and maintenance
GET /api/v1/usage/summary                           # not yet wrapped
GET /api/v1/usage/series                            # not yet wrapped
DELETE /api/v1/usage                                # not yet wrapped
POST /api/v1/usage/external-events                  # not yet wrapped
POST /api/v1/usage/set-price                        # not yet wrapped
GET /api/v1/usage/in-flight                         # dashboard-only, per-worker live view
# Agent telemetry read and purge
GET /api/v1/agent-telemetry/summary                 # not yet wrapped
GET /api/v1/agent-telemetry/count                   # not yet wrapped
GET /api/v1/agent-telemetry/series                  # not yet wrapped
DELETE /api/v1/agent-telemetry                      # not yet wrapped
# Routing policies
GET /api/v1/routing/policies                        # not yet wrapped
POST /api/v1/routing/policies                       # not yet wrapped
DELETE /api/v1/routing/policies/{name}              # not yet wrapped
POST /api/v1/routing/policies/explain               # not yet wrapped
POST /api/v1/routing/preferences/rank               # not yet wrapped
GET /api/v1/routing/status                          # not yet wrapped
# Pricing refresh workflow
POST /api/v1/pricing/refresh                        # not yet wrapped
POST /api/v1/pricing/refresh/confirm                # not yet wrapped
POST /api/v1/pricing/refresh/reject                 # not yet wrapped
GET /api/v1/pricing/refresh/pending                 # not yet wrapped
GET /api/v1/pricing/snapshots                       # not yet wrapped
GET /api/v1/pricing/drift                           # not yet wrapped
# Model catalog, grouped by model
GET /api/v1/catalog/models                          # not yet wrapped
GET /api/v1/catalog/models/{model_id}               # not yet wrapped
POST /api/v1/catalog/selectors/refresh              # not yet wrapped
# Providers
GET /api/v1/providers/catalog/{provider_id}         # not yet wrapped
GET /api/v1/providers/health                        # not yet wrapped
POST /api/v1/provider-credentials/reencrypt         # not yet wrapped
# Built-in tool settings and search
GET /api/v1/tools                                   # not yet wrapped
GET /api/v1/tool-settings                           # not yet wrapped
PATCH /api/v1/tool-settings                         # not yet wrapped
POST /api/v1/tool-settings/{service}/test           # not yet wrapped
GET /api/v1/tool-settings/guardrails/profiles       # not yet wrapped
GET /api/v1/tool-settings/guardrails/catalog        # not yet wrapped
POST /api/v1/search                                 # not yet wrapped
POST /api/v1/search/{search_tool_name}              # not yet wrapped
# Search tools (runtime search-tool management)
GET /api/v1/search-tools                            # not yet wrapped
GET /api/v1/search-tools/providers                  # not yet wrapped
POST /api/v1/search-tools                           # not yet wrapped
PATCH /api/v1/search-tools/{name}                   # not yet wrapped
DELETE /api/v1/search-tools/{name}                  # not yet wrapped
POST /api/v1/search-tools/reencrypt                 # not yet wrapped
# Settings
POST /api/v1/settings/master-key/rotate             # not yet wrapped
# Tenancy-scoped budgets: an operator surface with no dashboard page yet either,
# so it is unwrapped rather than dashboard-only. The per-user /api/v1/budgets family
# is [covered]; move these across with it when an SDK grows an admin client.
GET /api/v1/scoped-budgets                                      # not yet wrapped
POST /api/v1/scoped-budgets                                     # not yet wrapped
GET /api/v1/scoped-budgets/{budget_id}                          # not yet wrapped
PATCH /api/v1/scoped-budgets/{budget_id}                        # not yet wrapped
DELETE /api/v1/scoped-budgets/{budget_id}                       # not yet wrapped
# Deployment administration: the accounts on the whole deployment, and the flags
# an operator flips on one. Refused with 404 for anyone who is not an operator,
# and an SDK caller holds a workspace key rather than an operator identity, so
# there is nothing here for a shell to wrap.
GET /api/v1/admin/access                                        # dashboard-only
GET /api/v1/admin/users                                         # dashboard-only
PATCH /api/v1/admin/users/{user_id}                             # dashboard-only
# Tenancy: organizations, workspaces, and their memberships. The dashboard's
# tenancy pages are the consumer; an SDK caller acts inside one workspace with a
# key that already names it, so none of these is an SDK surface yet. Move to
# [covered] if a shell grows an admin client.
POST /api/v1/organizations                                      # dashboard-only
GET /api/v1/organizations/me                                    # dashboard-only
PATCH /api/v1/organizations/me                                  # dashboard-only
GET /api/v1/organizations/me/memberships                        # dashboard-only
POST /api/v1/organizations/me/switch                            # dashboard-only
GET /api/v1/organizations/me/members                            # dashboard-only
POST /api/v1/organizations/me/members                           # dashboard-only
PATCH /api/v1/organizations/me/members/{organization_member_id} # dashboard-only
DELETE /api/v1/organizations/me/members/{organization_member_id} # dashboard-only
GET /api/v1/organizations/me/domains                            # dashboard-only
POST /api/v1/organizations/me/domains                           # dashboard-only
PATCH /api/v1/organizations/me/domains/{organization_domain_id} # dashboard-only
POST /api/v1/organizations/me/domains/{organization_domain_id}/verify # dashboard-only
DELETE /api/v1/organizations/me/domains/{organization_domain_id} # dashboard-only
POST /api/v1/workspaces                                         # dashboard-only
GET /api/v1/workspaces                                          # dashboard-only
GET /api/v1/workspaces/{workspace_id}                           # dashboard-only
PATCH /api/v1/workspaces/{workspace_id}                         # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}                        # dashboard-only
GET /api/v1/workspaces/{workspace_id}/members                   # dashboard-only
POST /api/v1/workspaces/{workspace_id}/members/{user_id}        # dashboard-only
PATCH /api/v1/workspaces/{workspace_id}/members/{user_id}       # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/members/{user_id}      # dashboard-only
GET /api/v1/organizations/me/pricing                            # dashboard-only
POST /api/v1/organizations/me/pricing                           # dashboard-only
PUT /api/v1/organizations/me/pricing/{pricing_id}               # dashboard-only
DELETE /api/v1/organizations/me/pricing/{pricing_id}            # dashboard-only
# The tenant's own view of the usage `/v1/usage` serves deployment-wide
# (otari#837). Excluded for the same reason its deployment-wide siblings above
# are: it is scoped by the caller's dashboard session, and an SDK caller holds an
# API key, which names a workspace rather than a person with memberships.
GET /api/v1/organizations/me/usage                              # dashboard-only
GET /api/v1/organizations/me/usage/count                        # dashboard-only
GET /api/v1/organizations/me/usage/summary                      # dashboard-only
GET /api/v1/organizations/me/usage/series                       # dashboard-only
GET /api/v1/organizations/me/routing-policies                   # dashboard-only
POST /api/v1/organizations/me/routing-policies                  # dashboard-only
DELETE /api/v1/organizations/me/routing-policies/{name}         # dashboard-only
GET /api/v1/organizations/me/aliases                            # dashboard-only
POST /api/v1/organizations/me/aliases                           # dashboard-only
DELETE /api/v1/organizations/me/aliases/{name}                  # dashboard-only
# The member's own keys (otari-ai#1941), excluded for the same reason as the
# usage reads above: scoped by the caller's dashboard session, which an SDK
# caller does not hold.
GET /api/v1/organizations/me/keys                               # dashboard-only
POST /api/v1/organizations/me/keys                              # dashboard-only
PATCH /api/v1/organizations/me/keys/{key_id}                    # dashboard-only
DELETE /api/v1/organizations/me/keys/{key_id}                   # dashboard-only
POST /api/v1/organizations/me/keys/{key_id}/rotate              # dashboard-only
# The organization's own budgets and the ceilings enforcing them
# (otari-ai#1943), excluded for the same reason as the keys above: the
# organization is resolved from the caller's dashboard session rather than named
# in the request, so an SDK caller holding a workspace key has no organization
# for these to act on. The deployment-wide `/v1/budgets` and `/v1/scoped-budgets`
# families are classified separately above.
GET /api/v1/organizations/me/budgets                            # dashboard-only
POST /api/v1/organizations/me/budgets                           # dashboard-only
PATCH /api/v1/organizations/me/budgets/{budget_id}              # dashboard-only
DELETE /api/v1/organizations/me/budgets/{budget_id}             # dashboard-only
GET /api/v1/organizations/me/spend-ceilings                     # dashboard-only
POST /api/v1/organizations/me/spend-ceilings                    # dashboard-only
PATCH /api/v1/organizations/me/spend-ceilings/{ceiling_id}      # dashboard-only
DELETE /api/v1/organizations/me/spend-ceilings/{ceiling_id}     # dashboard-only
GET /api/v1/organizations/me/guardrails                         # operator surface
POST /api/v1/organizations/me/guardrails                        # operator surface
PATCH /api/v1/organizations/me/guardrails/{guardrail_id}        # operator surface
DELETE /api/v1/organizations/me/guardrails/{guardrail_id}       # operator surface
POST /api/v1/organizations/me/member-invitations                # dashboard-only
DELETE /api/v1/organizations/me/member-invitations/{invitation_id} # dashboard-only
# The invitee's side of the same flow, and dashboard-only for the same reason
# the admin's side above is: it acts as a signed-in person deciding about their
# own membership, not as a key-holder calling the gateway.
GET /api/v1/organizations/me/pending-memberships                # dashboard-only
POST /api/v1/organizations/me/pending-memberships/{organization_member_id}/accept # dashboard-only
POST /api/v1/organizations/me/pending-memberships/{organization_member_id}/decline # dashboard-only
# The invitation accept flow is reached by a browser following an emailed
# link, not by an SDK caller acting as a key-holder inside a workspace.
POST /api/v1/invitations/validate                               # accept-flow, not an SDK surface
POST /api/v1/invitations/accept                                 # accept-flow, not an SDK surface
GET /api/v1/workspaces/{workspace_id}/member-budget-policies                 # dashboard-only
POST /api/v1/workspaces/{workspace_id}/member-budget-policies                # dashboard-only
PATCH /api/v1/workspaces/{workspace_id}/member-budget-policies/{default_id}  # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/member-budget-policies/{default_id} # dashboard-only
# The dashboard's first-request setup guide (otari#660). It exists to walk a
# browser from an empty workspace to a working call, and the credential it
# issues is the thing an SDK caller already holds, so there is nothing here for
# a shell to wrap.
GET /api/v1/workspaces/{workspace_id}/activation                # dashboard-only
POST /api/v1/workspaces/{workspace_id}/activation/key           # dashboard-only
POST /api/v1/workspaces/{workspace_id}/activation/dismiss       # dashboard-only
# The workspace's code-execution policy (otari#657) is set by whoever administers
# the workspace, not by the key-holder running code inside it, so it is the same
# dashboard-side surface as the rows above. An SDK caller sees its effect on
# `otari_code_execution` (a 403, a lower iteration ceiling), never the policy row.
GET /api/v1/workspaces/{workspace_id}/code-execution-policy                  # dashboard-only
PUT /api/v1/workspaces/{workspace_id}/code-execution-policy                  # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/code-execution-policy               # dashboard-only
# The workspace's web-search configuration (otari#656) is the same shape of
# surface for the same reason: whoever administers the workspace decides whether
# it may search and how far, not the key-holder searching inside it. An SDK
# caller sees its effect on `otari_web_search` and on `POST /api/v1/search` (a 403, a
# lower result ceiling, a domain filter), never the row.
GET /api/v1/workspaces/{workspace_id}/web-search                            # dashboard-only
PUT /api/v1/workspaces/{workspace_id}/web-search                            # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/web-search                         # dashboard-only
# Organization-scoped provider keys (otari-ai#1748, otari#643): the same
# tenancy-admin surface as the organization/workspace rows just above, and
# excluded for the same reason. An SDK caller acts inside one workspace with a
# key that already carries its resolved credentials; managing which
# organization-scoped BYO key a workspace defaults to is a dashboard concern.
GET /api/v1/organizations/me/provider-keys                                    # dashboard-only
POST /api/v1/organizations/me/provider-keys                                   # dashboard-only
PATCH /api/v1/organizations/me/provider-keys/{key_id}                         # dashboard-only
DELETE /api/v1/organizations/me/provider-keys/{key_id}                        # dashboard-only
POST /api/v1/organizations/me/provider-keys/{key_id}/archive                  # dashboard-only
POST /api/v1/organizations/me/provider-keys/{key_id}/restore                  # dashboard-only
POST /api/v1/organizations/me/provider-keys/{key_id}/default                  # dashboard-only
GET /api/v1/workspaces/{workspace_id}/provider-keys                           # dashboard-only
PATCH /api/v1/workspaces/{workspace_id}/provider-keys/{key_id}                # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/provider-keys/{key_id}               # dashboard-only
GET /api/v1/workspaces/{workspace_id}/provider-keys/{key_id}/models           # dashboard-only
POST /api/v1/workspaces/{workspace_id}/provider-keys/{key_id}/models          # dashboard-only
DELETE /api/v1/workspaces/{workspace_id}/provider-keys/{key_id}/models/{model} # dashboard-only
# Workspace-scoped MCP servers (otari#658): a master-key operator surface for
# registering the endpoints a workspace's requests may reach, alongside the
# tenancy admin rows above. An SDK caller does not manage these; it names them
# by id in `mcp_server_ids` on a completion request, which is already covered.
GET /api/v1/workspaces/{workspace_id}/mcp-servers                             # operator surface
POST /api/v1/workspaces/{workspace_id}/mcp-servers                            # operator surface
PATCH /api/v1/workspaces/{workspace_id}/mcp-servers/{server_id}               # operator surface
DELETE /api/v1/workspaces/{workspace_id}/mcp-servers/{server_id}              # operator surface
# Web-search backend (otari#882): the SearXNG-shaped endpoint one gateway calls
# on another, authenticated by a shared deployment token rather than an API key.
# Its caller is a data-plane gateway, not an application, and no SDK holds that
# token; an application reaches the same search through the otari_web_search
# tool on a completion request.
GET /api/v1/web-search/search                                                 # gateway-to-gateway
