openapi: 3.0.3
info:
  title: Athena RS
  version: 3.12.1
  description: |
    Athena gateway API and driver.

    This specification covers the core gateway CRUD/query surface, management/admin provisioning and schema operations,
    backup/storage utilities, pipeline orchestration, and registry/schema discovery routes.

    Authentication and routing model:
    - Most gateway routes resolve a target client from `X-Athena-Client`.
    - Management routes require client-bound API keys with matching rights.
    - Admin routes require the static admin key (`ATHENA_KEY_12`).

    Response model:
    - Most routes return a standard JSON envelope (`status`, `message`, `data`/`error`).
    - RPC routes (`/gateway/rpc`, `/rpc/{function_name}`) return `{ "data": [...], "count": ... }` on success.

    Every operation returns at least one 2xx (success) and one 5xx (server error) response; 3xx (redirect) is optional where applicable.
servers:
- url: https://athena-cluster.com
tags:
- name: Health
  description: Liveness, readiness, and cluster probe endpoints.
- name: Gateway
  description: Core CRUD/query/RPC operations routed by `X-Athena-Client`.
- name: Pipelines
  description: Config-driven source-transform-sink execution and simulation endpoints.
- name: Management
  description: Client-bound schema and provisioning operations guarded by management rights.
- name: Admin
  description: Static-admin-key control plane for API keys, client catalog, backups, and observability metadata.
- name: Schema
  description: Table/column/migration discovery endpoints.
- name: Storage
  description: S3-compatible object and bucket utility endpoints.
- name: Registry
  description: Router and API registry discovery endpoints.
paths:
  /:
    get:
      summary: API root and route listing
      description: Returns health status and a list of all available routes with methods and summaries (PostgREST-style).
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: athena is online
                  version:
                    type: string
                    description: API version (cargo_toml_version)
                  athena_api:
                    type: string
                    example: online
                  athena_deadpool:
                    type: string
                    example: online
                  athena_scylladb:
                    type: string
                    enum:
                    - online
                    - offline
                  cargo_toml_version:
                    type: string
                  routes:
                    type: array
                    description: All available routes with path, methods, and summary
                    items:
                      type: object
                      properties:
                        path:
                          type: string
                          example: /gateway/data
                        methods:
                          type: array
                          items:
                            type: string
                          example:
                          - POST
                        summary:
                          type: string
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /ping:
    get:
      summary: Health check
      description: Returns `pong` so orchestrators and load balancers can verify the process is alive.
      responses:
        '200':
          description: Pong response
          content:
            text/plain:
              schema:
                type: string
                example: pong
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /health:
    get:
      summary: Structured health check with local provisioning diagnostics
      description: Returns Athena process health plus a proactive local Docker provisioning diagnostic for the current process user.
      responses:
        '200':
          description: Health response
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ok
                  message:
                    type: string
                    example: athena is online
                  version:
                    type: string
                  athena_api:
                    type: string
                  athena_deadpool:
                    type: string
                  athena_scylladb:
                    type: string
                  gateway_auth_store:
                    type: string
                  gateway_benchmark_client:
                    type: string
                  cargo_toml_version:
                    type: string
                  provisioning:
                    type: object
                    properties:
                      local_docker:
                        type: object
                        properties:
                          available:
                            type: boolean
                          status:
                            type: string
                            example: permission_denied
                          detail:
                            type: string
                          hint:
                            type: string
                            nullable: true
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /health/cluster:
    get:
      summary: Cluster mirror health and version checks
      description: Checks Athena mirror domains, returns reachability, latency, throughput, and reported version metadata.
      responses:
        '200':
          description: Cluster health details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClusterHealthResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/capabilities:
    get:
      summary: List management API capabilities for a client
      description: Requires `X-Athena-Client` and a client-bound API key with `management.read`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
        description: optional API key mirror of the `apikey` header
      responses:
        '200':
          description: Management capability details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ManagementCapabilitiesEnvelope'
        '400':
          description: Missing or unknown X-Athena-Client
        '401':
          description: Missing or invalid API key
        '403':
          description: API key is not client-bound or lacks management.read
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/provision/providers/neon:
    post:
      summary: Provision/register a Neon Postgres database via management API
      description: Requires `X-Athena-Client` and a client-bound API key with `management.provision.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Neon database provisioned and optionally registered
        '400':
          description: Invalid provider payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.provision.write or key is not client-bound
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/provision/providers/railway:
    post:
      summary: Provision/register a Railway Postgres database via management API
      description: Requires `X-Athena-Client` and a client-bound API key with `management.provision.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Railway database provisioned and optionally registered
        '400':
          description: Invalid provider payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.provision.write or key is not client-bound
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/provision/providers/render:
    post:
      summary: Provision/register a Render Postgres database via management API
      description: Requires `X-Athena-Client` and a client-bound API key with `management.provision.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Render database provisioned and optionally registered
        '400':
          description: Invalid provider payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.provision.write or key is not client-bound
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/provision/local/databases:
    get:
      summary: List local cluster PostgreSQL databases for the caller client
      description: Requires `X-Athena-Client` and a client-bound API key with `management.provision.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Database inventory loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalClusterDatabasesResponse'
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.provision.write or key is not client-bound
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a local cluster PostgreSQL database for the caller client
      description: Requires `X-Athena-Client` and a client-bound API key with `management.provision.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LocalClusterCreateDatabaseRequest'
      responses:
        '201':
          description: Local database created
        '400':
          description: Invalid payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.provision.write or key is not client-bound
        '409':
          description: Database already exists
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /management/tables:
    post:
      summary: Create a managed table
      description: Requires `X-Athena-Client` and a client-bound API key with `management.tables.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTableRequest'
      responses:
        '201':
          description: Table created
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Invalid payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.tables.write or key is not client-bound
        '409':
          description: Table already exists
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/tables/{table_name}:
    patch:
      summary: Apply safe additive ALTER TABLE operations
      description: Requires `X-Athena-Client` and a client-bound API key with `management.tables.write`.
      parameters:
      - in: path
        name: table_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EditTableRequest'
      responses:
        '200':
          description: Table updated
        '400':
          description: Invalid payload or unsupported alteration
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.tables.write or key is not client-bound
        '404':
          description: Table not found
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
    delete:
      summary: Drop a managed table
      description: Requires `X-Athena-Client` and a client-bound API key with `management.tables.drop`.
      parameters:
      - in: path
        name: table_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DropTableRequest'
      responses:
        '200':
          description: Table dropped
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.tables.drop or key is not client-bound
        '404':
          description: Table not found
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/tables/{table_name}/columns/{column_name}:
    delete:
      summary: Drop a managed table column
      description: Requires `X-Athena-Client` and a client-bound API key with `management.columns.drop`.
      parameters:
      - in: path
        name: table_name
        required: true
        schema:
          type: string
      - in: path
        name: column_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DropColumnRequest'
      responses:
        '200':
          description: Column dropped
        '400':
          description: Invalid payload or reserved column
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.columns.drop or key is not client-bound
        '404':
          description: Column not found
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/indexes:
    post:
      summary: Create an index
      description: Requires `X-Athena-Client` and a client-bound API key with `management.indexes.write`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateIndexRequest'
      responses:
        '201':
          description: Index created
        '400':
          description: Invalid payload
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.indexes.write or key is not client-bound
        '404':
          description: Table not found
        '409':
          description: Index already exists
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/indexes/{index_name}:
    delete:
      summary: Drop an index
      description: Requires `X-Athena-Client` and a client-bound API key with `management.indexes.drop`.
      parameters:
      - in: path
        name: index_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DropIndexRequest'
      responses:
        '200':
          description: Index dropped
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.indexes.drop or key is not client-bound
        '404':
          description: Index not found
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/extensions/install:
    post:
      summary: Install a Postgres extension
      description: |
        Installs an allowlisted Postgres extension on the selected `X-Athena-Client` database.
        Requires a client-bound API key with `management.extensions.write`. Audit rows require `public.database_audit_log` on the gateway logging Postgres database.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - extension_name
              properties:
                extension_name:
                  type: string
                  description: Extension to install (allowlisted).
                  example: pgcrypto
                if_not_exists:
                  type: boolean
                  default: false
      responses:
        '200':
          description: Extension installed
        '400':
          description: Invalid payload or extension is not allowlisted
        '401':
          description: Missing or invalid API key
        '403':
          description: Missing management.extensions.write or key is not client-bound
        '503':
          description: public.database_audit_log is missing on the gateway logging database, or the logging store is unavailable
  /management/functions:
    get:
      summary: List Postgres functions for the target client
      description: |
        Queries `pg_proc` for ordinary functions (`prokind = 'f'`). Requires management client context and `management.functions.read`.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
        description: Management API key (also accepted via x-api-key, Authorization Bearer, etc.)
      - in: query
        name: schema
        required: false
        schema:
          type: string
        description: Filter by schema name
      - in: query
        name: name_like
        required: false
        schema:
          type: string
        description: ILIKE pattern for function name (server wraps with `%`)
      - in: query
        name: include_system
        required: false
        schema:
          type: boolean
          default: false
        description: When false, excludes `pg_catalog` and `information_schema`
      - in: query
        name: include_definition
        required: false
        schema:
          type: boolean
          default: false
        description: When true, includes `pg_get_functiondef` in the response
      responses:
        '200':
          description: Functions listed (standard success envelope with `data.functions`)
        '400':
          description: Invalid query parameters
        '401':
          description: Unauthorized
        '403':
          description: Missing management.functions.read
        '500':
          description: Query failed
    put:
      summary: Create or replace a Postgres function from DDL
      description: |
        Runs validated `CREATE OR REPLACE FUNCTION` DDL inside a transaction with audit tables. Requires `management.functions.write`
        Audit rows require `public.database_audit_log` and `public.function_ddl_audit_log` on the gateway logging Postgres database.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpsertFunctionRequest'
      responses:
        '200':
          description: Function upserted
        '400':
          description: Invalid DDL
        '401':
          description: Unauthorized
        '403':
          description: Missing management.functions.write
        '503':
          description: Audit tables missing on gateway logging database or logging store unavailable
    delete:
      summary: Drop a Postgres function by signature
      description: |
        Issues `DROP FUNCTION schema.function(arg_types...) RESTRICT`. Requires `management.functions.drop` and the same audit tables as upsert.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DropFunctionRequest'
      responses:
        '200':
          description: Function dropped
        '400':
          description: Invalid request
        '401':
          description: Unauthorized
        '403':
          description: Missing management.functions.drop
        '404':
          description: Function not found
        '503':
          description: Audit tables missing on gateway logging database or logging store unavailable
  /query/sql:
    post:
      summary: Execute SQL using selected driver
      description: |
        For `driver: postgresql`, connection resolution matches gateway handlers: send `X-Athena-Client` for a registered client pool, or `X-JDBC-URL` with a `jdbc:postgresql://...` URL for a direct connection (subject to gateway JDBC allowlists when configured).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GatewaySqlExecutionRequest'
      responses:
        '200':
          description: Query executed
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Invalid driver
        '500':
          description: Driver execution failed
        '503':
          description: Athena/Scylla unavailable
  /gateway/sql:
    post:
      summary: Execute SQL via driver (gateway alias)
      description: |
        Same request body and behavior as `POST /query/sql`. Provided for SDKs that target a `/gateway/*` prefix.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GatewaySqlExecutionRequest'
      responses:
        '200':
          description: Query executed
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Invalid driver
        '500':
          description: Driver execution failed
        '503':
          description: Athena/Scylla unavailable
  /query/count:
    post:
      summary: Cached row count (COUNT query or table reference)
      description: |
        Returns a single row count using the same cache layers as gateway fetch (in-process + Redis when configured).
        Provide either validated `query` (single SELECT COUNT ... with no GROUP BY / UNION / comments) or `table_name` with optional `table_schema` (default `public`).
        `Cache-Control: no-cache` bypasses the cache. Responses include `X-Athena-Cached`, `X-Athena-Cache-Outcome`, and `X-Athena-Cache-Source` when applicable.
      parameters:
      - in: header
        name: X-Athena-Client
        description: Required when driver is postgresql
        schema:
          type: string
      - in: header
        name: Cache-Control
        description: Set to `no-cache` to bypass cached count
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                driver:
                  type: string
                  enum:
                  - athena
                  - postgresql
                  - supabase
                db_name:
                  type: string
                query:
                  type: string
                  description: Validated COUNT-only SQL (mutually exclusive with table_name)
                table_name:
                  type: string
                  description: Table name when not using raw query (mutually exclusive with query)
                table_schema:
                  type: string
                  description: Schema name; defaults to public when using table_name
              required:
              - driver
              - db_name
            example:
              driver: postgresql
              db_name: athena_logging
              table_name: http_request_log
              table_schema: public
      responses:
        '200':
          description: Row count computed or served from cache
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      count:
                        type: integer
                        format: int64
                      db_name:
                        type: string
                      duration_ms:
                        type: integer
                        format: int64
                      cache_key:
                        type: string
                      cache_lookup_outcome:
                        type: string
              example:
                status: success
                message: Row count computed
                data:
                  count: 2451
                  db_name: athena_logging
                  duration_ms: 12
                  cache_lookup_outcome: hit_local
        '400':
          description: Invalid driver, ambiguous body, or invalid count SQL
        '500':
          description: Driver execution failed
        '503':
          description: Athena/Scylla unavailable
  /gateway/fetch:
    post:
      summary: Fetch data with conditions
      description: |
        Fetches rows from a table or view using structured conditions, optional pagination, sort instructions,
        and optional post-processing (`group_by`, `time_granularity`, aggregation options).

        Notes:
        - Route selection is based on `X-Athena-Client` (or custom Supabase headers for `custom_supabase`).
        - `X-Strip-Nulls` can be used to remove null-valued fields from response payloads.
        - Cache behavior may include `X-Athena-Cached`, `X-Athena-Cache-Outcome`, and `X-Athena-Cache-Source` headers.
      tags:
      - Gateway
      parameters:
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        schema:
          type: string
      - in: header
        name: X-Strip-Nulls
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        description: optional API key mirror of the `apikey` header
        schema:
          type: string
      - in: header
        name: x-supabase-url
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                view_name:
                  type: string
                table_name:
                  type: string
                columns:
                  oneOf:
                  - type: array
                    items:
                      type: string
                  - type: string
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      eq_column:
                        type: string
                      eq_value:
                        type: string
                limit:
                  type: integer
                current_page:
                  type: integer
                page_size:
                  type: integer
                offset:
                  type: integer
                total_pages:
                  type: integer
                strip_nulls:
                  type: boolean
                group_by:
                  type: string
                  description: Column name to group results by
                time_granularity:
                  type: string
                  enum:
                  - day
                  - hour
                  - minute
                  description: Time granularity for grouping timestamp data
                aggregation_column:
                  type: string
                  description: Column to aggregate (required when using aggregation_strategy)
                aggregation_strategy:
                  type: string
                  enum:
                  - cumulative_sum
                  description: Aggregation strategy to apply (requires aggregation_column)
                aggregation_dedup:
                  type: boolean
                  description: Whether to deduplicate during aggregation
                sortBy:
                  type: object
                  description: Optional sort (camelCase). Use sort_by for snake_case.
                  properties:
                    field:
                      type: string
                      description: Column name to sort by
                    direction:
                      type: string
                      enum:
                      - asc
                      - desc
                      - ascending
                      - descending
                      default: asc
                sort_by:
                  type: object
                  description: Optional sort (snake_case). Same shape as sortBy.
                  properties:
                    field:
                      type: string
                    direction:
                      type: string
                      enum:
                      - asc
                      - desc
                      - ascending
                      - descending
              required:
              - conditions
            example:
              table_name: http_request_log
              columns:
              - id
              - path
              - status_code
              - created_at
              conditions:
              - eq_column: client_name
                eq_value: athena_logging
              limit: 25
              current_page: 1
              sortBy:
                field: created_at
                direction: desc
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
              example:
                status: success
                message: Fetched 25 rows
                data:
                  rows:
                  - id: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
                    path: /gateway/fetch
                    status_code: 200
                    created_at: 2026-04-14T10:15:00Z
                  row_count: 25
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/data:
    post:
      summary: Legacy alias for `/gateway/fetch`
      description: Mirrors `/gateway/fetch` for clients still calling `/gateway/data` with the same payload/response envelope.
      parameters:
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        schema:
          type: string
      - in: header
        name: X-Strip-Nulls
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        description: optional API key mirror of the `apikey` header
        schema:
          type: string
      - in: header
        name: x-supabase-url
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                view_name:
                  type: string
                table_name:
                  type: string
                columns:
                  oneOf:
                  - type: array
                    items:
                      type: string
                  - type: string
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      eq_column:
                        type: string
                      eq_value:
                        type: string
                limit:
                  type: integer
                current_page:
                  type: integer
                page_size:
                  type: integer
                offset:
                  type: integer
                total_pages:
                  type: integer
                strip_nulls:
                  type: boolean
                group_by:
                  type: string
                  description: Column name to group results by
                time_granularity:
                  type: string
                  enum:
                  - day
                  - hour
                  - minute
                  description: Time granularity for grouping timestamp data
                aggregation_column:
                  type: string
                  description: Column to aggregate (required when using aggregation_strategy)
                aggregation_strategy:
                  type: string
                  enum:
                  - cumulative_sum
                  description: Aggregation strategy to apply (requires aggregation_column)
                aggregation_dedup:
                  type: boolean
                  description: Whether to deduplicate during aggregation
                sortBy:
                  type: object
                  description: Optional sort (camelCase). Use sort_by for snake_case.
                  properties:
                    field:
                      type: string
                    direction:
                      type: string
                      enum:
                      - asc
                      - desc
                      - ascending
                      - descending
                sort_by:
                  type: object
                  description: Optional sort (snake_case). Same shape as sortBy.
                  properties:
                    field:
                      type: string
                    direction:
                      type: string
                      enum:
                      - asc
                      - desc
                      - ascending
                      - descending
              required:
              - conditions
      responses:
        '200':
          description: OK
        '400':
          description: Bad request
        '500':
          description: Internal error
  /data:
    get:
      summary: Fetch data via GET
      description: |
        Compatibility GET wrapper for fetch semantics. Converts query string filters and paging inputs into
        an internal fetch-style request.

        Use `sort_by`/`sort_direction` for simple ordering and the aggregation query parameters for grouped output.
      tags:
      - Gateway
      parameters:
      - in: query
        name: view
        required: true
        schema:
          type: string
      - in: query
        name: eq_column
        required: true
        schema:
          type: string
      - in: query
        name: eq_value
        required: true
        schema:
          type: string
      - in: query
        name: columns
        schema:
          type: string
      - in: query
        name: limit
        schema:
          type: integer
      - in: query
        name: current_page
        schema:
          type: integer
      - in: query
        name: page_size
        schema:
          type: integer
      - in: query
        name: offset
        schema:
          type: integer
      - in: query
        name: total_pages
        schema:
          type: integer
      - in: query
        name: strip_nulls
        schema:
          type: boolean
      - in: query
        name: sort_by
        schema:
          type: string
        description: Column name to sort by (forwarded as sortBy.field in the internal POST)
      - in: query
        name: sort_direction
        schema:
          type: string
          enum:
          - asc
          - desc
          - ascending
          - descending
        description: Sort direction (default asc)
      - in: query
        name: group_by
        schema:
          type: string
        description: Column name to group results by
      - in: query
        name: time_granularity
        schema:
          type: string
          enum:
          - day
          - hour
          - minute
        description: Time granularity for grouping timestamp data
      - in: query
        name: aggregation_column
        schema:
          type: string
        description: Column to aggregate (required when using aggregation_strategy)
      - in: query
        name: aggregation_strategy
        schema:
          type: string
          enum:
          - cumulative_sum
        description: Aggregation strategy to apply (requires aggregation_column)
      - in: query
        name: aggregation_dedup
        schema:
          type: boolean
        description: Whether to deduplicate during aggregation
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        schema:
          type: string
      - in: header
        name: X-Strip-Nulls
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        description: optional API key mirror of the `apikey` header
        schema:
          type: string
      - in: header
        name: x-supabase-url
        description: Optional custom Supabase URL when X-Athena-Client is custom_supabase
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        description: Optional custom Supabase key when X-Athena-Client is custom_supabase
        schema:
          type: string
      responses:
        '200':
          description: OK
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/insert:
    put:
      summary: Insert a row into a table
      description: |
        Inserts one row (or server-normalized payload) into the target table selected by `X-Athena-Client`.

        The optional `X-Athena-Insert-Window` header can enable request-scoped batching for Postgres-backed inserts.
        Conflict responses (409) are typically raised for unique/constraint violations.
      tags:
      - Gateway
      parameters:
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Company-Id
        required: true
        schema:
          type: string
      - in: header
        name: X-Organization-Id
        required: true
        schema:
          type: string
      - in: header
        name: X-Publish-Event
        schema:
          type: string
      - in: header
        name: X-Athena-Insert-Window
        description: |
          Optional Postgres insert batching window in milliseconds (1–60000).
          When non-zero, enables or overrides `gateway.insert_execution_window_ms` for this request.
          The handler blocks until the window elapses and the row is flushed; under load, connection
          hold time can increase by up to this value. Ignored when `X-Athena-Deadpool-Enable` requests
          deadpool, when `update_body` is present, or for non-Postgres clients.
        schema:
          type: integer
          minimum: 1
          maximum: 60000
      - in: header
        name: X-Athena-Client
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                table_name:
                  type: string
                insert_body:
                  type: object
                update_body:
                  type: object
              required:
              - table_name
              - insert_body
            example:
              table_name: app_events
              insert_body:
                event_type: user_login
                user_id: u_123
                source: dashboard
      responses:
        '200':
          description: Inserted
          content:
            application/json:
              schema:
                type: object
              example:
                status: success
                message: Row inserted
                data:
                  table_name: app_events
        '400':
          description: Bad request
        '409':
          description: Conflict
        '500':
          description: Internal error
  /gateway/delete:
    delete:
      summary: Delete a row by id
      description: |
        Deletes rows from the target table, usually by primary/resource identifier fields in the request body.
        Route authorization and client routing follow the same model as other gateway mutations.
      tags:
      - Gateway
      parameters:
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Company-Id
        required: true
        schema:
          type: string
      - in: header
        name: X-Organization-Id
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-supabase-url
        description: Optional custom Supabase URL when X-Athena-Client is custom_supabase
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        description: Optional custom Supabase key when X-Athena-Client is custom_supabase
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                table_name:
                  type: string
                resource_id:
                  type: string
              required:
              - table_name
              - resource_id
            example:
              table_name: app_events
              resource_id: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
              example:
                status: success
                message: Row deleted
                data:
                  table_name: app_events
                  resource_id: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/update:
    post:
      summary: Fetch or update data using a SQLx client (same payload as /gateway/fetch)
      description: |
        Applies updates to rows matching supplied conditions. Payload shape mirrors fetch-style condition blocks
        so callers can reuse filtering logic between read and write flows.

        Supports optional post-processing fields for response shaping where applicable.
      tags:
      - Gateway
      parameters:
      - in: header
        name: X-User-Id
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        description: selects which PostgreSQL pool to use
        schema:
          type: string
      - in: header
        name: X-Strip-Nulls
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        description: optional API key mirror of the `apikey` header
        schema:
          type: string
      - in: header
        name: x-supabase-url
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        description: Required when X-Athena-Client is custom_supabase
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                view_name:
                  type: string
                table_name:
                  type: string
                columns:
                  oneOf:
                  - type: array
                    items:
                      type: string
                  - type: string
                conditions:
                  type: array
                  items:
                    type: object
                    properties:
                      eq_column:
                        type: string
                      eq_value:
                        type: string
                limit:
                  type: integer
                current_page:
                  type: integer
                page_size:
                  type: integer
                offset:
                  type: integer
                total_pages:
                  type: integer
                strip_nulls:
                  type: boolean
                group_by:
                  type: string
                  description: Column name to group results by
                time_granularity:
                  type: string
                  enum:
                  - day
                  - hour
                  - minute
                  description: Time granularity for grouping timestamp data
                aggregation_column:
                  type: string
                  description: Column to aggregate (required when using aggregation_strategy)
                aggregation_strategy:
                  type: string
                  enum:
                  - cumulative_sum
                  description: Aggregation strategy to apply (requires aggregation_column)
                aggregation_dedup:
                  type: boolean
                  description: Whether to deduplicate during aggregation
              required:
              - conditions
            example:
              table_name: app_events
              conditions:
              - eq_column: id
                eq_value: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
              update_body:
                source: api
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
              example:
                status: success
                message: Rows updated
                data:
                  affected_rows: 1
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/query:
    post:
      summary: Run a raw SQL query against the selected PostgreSQL pool
      description: |
        Executes raw SQL against the client selected by `X-Athena-Client`.

        This route is intended for advanced workflows where structured gateway payloads are insufficient.
        Use carefully with parameterized SQL on the caller side when constructing dynamic queries.
      tags:
      - Gateway
      parameters:
      - in: header
        name: X-Athena-Client
        description: selects which PostgreSQL pool the SQL executes against
        required: true
        schema:
          type: string
      - in: header
        name: x-api-key
        description: optional API key mirror of the `apikey` header in the body
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                query:
                  description: raw SQL to execute
                  type: string
              required:
              - query
            example:
              query: SELECT id, path, status_code FROM http_request_log ORDER BY created_at DESC LIMIT 10;
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
              example:
                status: success
                message: Query executed
                data:
                  rows:
                  - id: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
                    path: /gateway/fetch
                    status_code: 200
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/rpc:
    post:
      summary: Execute a Postgres function as RPC
      description: |
        Invokes `schema.function(arg => ...)` on the pool selected by `X-Athena-Client`. Requires API key with right `gateway.rpc.execute`.
        Success responses return **`{"data": [...], "count": ...}`** (not the usual `status`/`message` envelope). Rows are JSON objects keyed by selected columns.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      - in: header
        name: x-supabase-url
        schema:
          type: string
          format: uri
      - in: header
        name: x-supabase-key
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GatewayRpcRequest'
            example:
              function: get_recent_events
              schema: public
              args:
                p_limit: 10
              select: id,event_type,created_at
              order:
                column: created_at
                ascending: false
      responses:
        '200':
          description: RPC result rows
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  count:
                    type: integer
                    format: int64
                    description: Present when exact count was requested
              example:
                data:
                - id: 5d845b27-8d06-40a0-9e76-08de9d9f3c1f
                  event_type: user_login
                  created_at: 2026-04-14T10:15:00Z
                count: 1
        '400':
          description: Invalid RPC request or binding error
        '401':
          description: Unauthorized
        '403':
          description: Missing gateway.rpc.execute
        '500':
          description: Database or internal error
  /rpc/{function_name}:
    get:
      summary: RPC compatibility (GET, query-string args)
      description: |
        Same execution as `POST /gateway/rpc` with `function` taken from the path. Query keys: `schema`, `select`, `count`, `limit`, `offset`, `order` (`column` or `column.desc`).
        Other keys are either RPC arguments or filters written as `column=op.value` (e.g. `status=eq.active`). Arrays use `{a,b}` in the value portion.
      parameters:
      - name: function_name
        in: path
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      responses:
        '200':
          description: RPC result rows (`data` + optional `count`)
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  count:
                    type: integer
                    format: int64
        '400':
          description: Invalid query string
        '401':
          description: Unauthorized
        '500':
          description: Database or internal error
    post:
      summary: RPC compatibility (POST JSON body)
      description: |
        Body is either a plain JSON object of named arguments for `function_name`, or a full `GatewayRpcRequest`-shaped wrapper (`function`, `schema`, `args`, `filters`, etc.).
      parameters:
      - name: function_name
        in: path
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
              - $ref: '#/components/schemas/GatewayRpcRequest'
              - type: object
                additionalProperties: true
                description: Bare argument object when not using wrapper fields
      responses:
        '200':
          description: RPC result rows
          content:
            application/json:
              schema:
                type: object
                required:
                - data
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  count:
                    type: integer
                    format: int64
        '400':
          description: Invalid body
        '401':
          description: Unauthorized
        '500':
          description: Database or internal error
  /gateway/deferred/{request_id}:
    get:
      summary: Fetch deferred request status
      description: |
        Returns the current state of a deferred gateway request (`queued`, `running`, `completed`, `failed`, etc.)
        together with available execution metadata.
      tags:
      - Gateway
      parameters:
      - in: path
        name: request_id
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deferred request status loaded
          content:
            application/json:
              schema:
                type: object
        '404':
          description: Deferred request not found
        '503':
          description: Deferred queue/status storage unavailable
  /gateway/deferred/{request_id}/requeue:
    post:
      summary: Requeue a deferred request
      description: |
        Re-enqueues an existing deferred request for execution. Useful for retries after transient failures
        or for manually reprocessing a previously completed/failed deferred item.
      tags:
      - Gateway
      parameters:
      - in: path
        name: request_id
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deferred request requeued
          content:
            application/json:
              schema:
                type: object
        '404':
          description: Deferred request not found
        '503':
          description: Deferred queue unavailable
  /rest/v1/{table}:
    get:
      summary: Query a table using PostgREST-style filters
      description: |
        Mirrors the Supabase PostgREST query syntax. Supply filter expressions (`column.eq=value`, `column.lt=value`, `or=(cond1,cond2)`, etc.), `select`, `limit`, `offset`, `order`, and the optional `Range` header (`items=0-24`). The handler also honors `Prefer: return=minimal`.
      parameters:
      - in: path
        name: table
        required: true
        schema:
          type: string
        description: Target table name (sanitized before execution).
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
        description: Selects which Postgres pool executes the query.
      - in: header
        name: apikey
        required: true
        schema:
          type: string
        description: Gateway API key (can also be passed via Authorization or x-api-key).
      - in: header
        name: x-api-key
        schema:
          type: string
        description: Mirror of `apikey` useful for proxies.
      - in: query
        name: select
        schema:
          type: string
        description: PostgREST `select` clause (comma separated columns).
      - in: query
        name: limit
        schema:
          type: integer
        description: Maximum number of rows to return (default 100).
      - in: query
        name: offset
        schema:
          type: integer
        description: Row offset (default 0).
      - in: query
        name: order
        schema:
          type: string
        description: Column ordering using `.asc`/`.desc` (e.g. `id.desc`).
      - in: query
        name: or
        schema:
          type: string
        description: PostgREST `or` expression to build grouped filters (`(a.eq.1,b.eq.2)`).
      - in: header
        name: Range
        schema:
          type: string
        description: Optional `items=start-end` range header that overrides offset/limit.
      responses:
        '200':
          description: Rows fetched (Content-Range header reports `items start-end/total`).
          headers:
            Content-Range:
              description: items start-end/*
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        '400':
          description: Invalid filters or missing headers
        '500':
          description: Internal error
    post:
      summary: Insert or upsert rows via the REST gateway
      description: |
        Accepts a single object or array payload. Set `Prefer: return=minimal` for HTTP 204 responses and `Prefer: resolution=merge-duplicates` for upserts.
      parameters:
      - in: path
        name: table
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      - in: header
        name: Prefer
        schema:
          type: string
        description: Use `return=minimal` for 204 or `resolution=merge-duplicates` for UPSERT.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
              - type: object
              - type: array
                items:
                  type: object
      responses:
        '200':
          description: Rows inserted or upserted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        '204':
          description: 'Insert succeeded with Prefer: return=minimal'
        '400':
          description: Invalid payload or headers
        '500':
          description: Internal error
    patch:
      summary: Update rows filtered via PostgREST expressions
      description: |
        Filters are required (`column.eq=value`, `column.lte=value`, `or=(...)`). The request body must be an object describing the columns to patch. `Prefer: return=minimal` controls whether the response returns data.
      parameters:
      - in: path
        name: table
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      - in: header
        name: Prefer
        schema:
          type: string
        description: Set `return=minimal` for HTTP 204.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Rows updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        '204':
          description: 'Update succeeded with Prefer: return=minimal'
        '400':
          description: Missing filters or invalid payload
        '500':
          description: Internal error
    delete:
      summary: Delete rows using PostgREST-style filters
      description: |
        Filters are mandatory. Use the same query syntax as GET and include `Prefer: return=minimal` for HTTP 204 responses.
      parameters:
      - in: path
        name: table
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: apikey
        required: true
        schema:
          type: string
      - in: header
        name: x-api-key
        schema:
          type: string
      - in: header
        name: Prefer
        schema:
          type: string
        description: Set `return=minimal` for HTTP 204 responses.
      responses:
        '200':
          description: Rows deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
        '204':
          description: 'Delete succeeded with Prefer: return=minimal'
        '400':
          description: Missing filters
        '500':
          description: Internal error
  /pipelines:
    post:
      summary: Run a config-driven pipeline (source → transform → sink)
      description: |
        Executes a pipeline with either an inline source/transform/sink definition or an optional prebuilt template reference plus overrides. `source.client` and `sink.client` override the Postgres/Supabase pool per step; when omitted, `X-Athena-Client` is used. Optional column aliasing and `dry_run` are supported.

        When gateway API keys are enforced, the key is validated against `X-Athena-Client`; step client overrides still route fetch/insert to other registered clients if specified.

        Successful runs emit three rows to `pipeline_step_log` on the configured logging database (`gateway.logging_client`), correlatable via `pipeline_run_id` in the response.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                pipeline:
                  type: string
                  description: Existing template name from boot YAML or database-backed runtime templates.
                source:
                  type: object
                  properties:
                    table_name:
                      type: string
                    view_name:
                      type: string
                    columns:
                      type: array
                      items:
                        type: string
                    conditions:
                      type: array
                      items:
                        type: object
                        properties:
                          eq_column:
                            type: string
                          eq_value:
                            type: string
                    limit:
                      type: integer
                    client:
                      type: string
                      description: Optional source step client override (falls back to X-Athena-Client).
                transform:
                  type: object
                  properties:
                    group_by:
                      type: string
                    time_granularity:
                      type: string
                      enum:
                      - day
                      - hour
                      - minute
                    aggregation_column:
                      type: string
                    aggregation_strategy:
                      type: string
                      enum:
                      - cumulative_sum
                    aggregation_dedup:
                      type: boolean
                    column_aliases:
                      type: object
                      additionalProperties:
                        type: string
                      description: Optional map of source column names to sink aliases.
                sink:
                  type: object
                  properties:
                    table_name:
                      type: string
                    client:
                      type: string
                      description: Optional sink step client override (falls back to X-Athena-Client).
                dry_run:
                  type: boolean
                  description: When true, fetch+transform runs but sink inserts are skipped.
            example:
              source:
                table_name: http_request_log
                columns:
                - client_name
                - created_at
                - status_code
                limit: 100
              transform:
                group_by: client_name
                aggregation_column: status_code
                aggregation_strategy: cumulative_sum
              sink:
                table_name: gateway_rollups
              dry_run: true
      responses:
        '200':
          description: Pipeline run result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    description: Inserted rows (or preview rows for dry runs)
                  rows_fetched:
                    type: integer
                  rows_inserted:
                    type: integer
                  would_insert:
                    type: integer
                    nullable: true
                    description: Preview row count for dry runs. This is a candidate-row estimate, not sink validation.
                  dry_run:
                    type: boolean
                  source_client:
                    type: string
                  sink_client:
                    type: string
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        index:
                          type: integer
                        error:
                          type: string
                  pipeline_run_id:
                    type: string
                    description: UUID correlating `pipeline_step_log` rows for this invocation.
              example:
                data: []
                rows_fetched: 100
                rows_inserted: 0
                would_insert: 12
                dry_run: true
                source_client: athena_logging
                sink_client: athena_logging
                errors: []
                pipeline_run_id: 0a9b5f6f-8a6c-4ba8-b41a-4a12ec2f5cb7
        '400':
          description: Bad request (e.g. missing X-Athena-Client or unknown pipeline)
        '500':
          description: Internal error
  /pipelines/simulate:
    post:
      summary: Simulate a pipeline without writing
      description: Executes fetch+transform only and returns a dry-run preview payload. Dry runs stay side-effect free and do not validate sink insertability; `would_insert` is a preview count, not a guaranteed successful insert count.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Same schema as POST /pipelines.
      responses:
        '200':
          description: Dry-run pipeline result
        '400':
          description: Bad request
        '500':
          description: Internal error
  /pipelines/templates:
    get:
      summary: List available pipeline templates
      description: Returns named pipeline templates merged from boot YAML and database-backed runtime templates.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Template list
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        source_table:
                          type: string
                        sink_table:
                          type: string
                        has_transform:
                          type: boolean
                        source:
                          type: string
                          enum:
                          - yaml
                          - database
                          description: Indicates whether the template came from boot YAML or the database-backed runtime registry.
                        mutable:
                          type: boolean
                          description: Database-backed templates are mutable; boot YAML templates are read-only.
                        source_client:
                          type: string
                          nullable: true
                          description: Optional `source.client` from YAML; omit uses request header client.
                        sink_client:
                          type: string
                          nullable: true
                          description: Optional `sink.client` from YAML; omit uses request header client.
                        definition:
                          type: object
                          properties:
                            source:
                              type: object
                              properties:
                                table_name:
                                  type: string
                                view_name:
                                  type: string
                                  nullable: true
                                columns:
                                  type: array
                                  items:
                                    type: string
                                conditions:
                                  type: array
                                  items:
                                    type: object
                                    properties:
                                      eq_column:
                                        type: string
                                      eq_value:
                                        type: string
                                limit:
                                  type: integer
                                client:
                                  type: string
                                  nullable: true
                            transform:
                              type: object
                              nullable: true
                              properties:
                                group_by:
                                  type: string
                                time_granularity:
                                  type: string
                                  enum:
                                  - day
                                  - hour
                                  - minute
                                aggregation_column:
                                  type: string
                                aggregation_strategy:
                                  type: string
                                aggregation_dedup:
                                  type: boolean
                                column_aliases:
                                  type: object
                                  additionalProperties:
                                    type: string
                            sink:
                              type: object
                              properties:
                                table_name:
                                  type: string
                                client:
                                  type: string
                                  nullable: true
        '500':
          description: Internal error
  /admin/pipeline-templates:
    get:
      summary: List database-backed pipeline templates
      parameters:
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Pipeline template list
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/pipeline-templates/{template_name}:
    get:
      summary: Fetch one database-backed pipeline template
      parameters:
      - in: path
        name: template_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Pipeline template record
        '404':
          description: Template not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    put:
      summary: Create or update a database-backed pipeline template
      parameters:
      - in: path
        name: template_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - definition
              properties:
                definition:
                  type: object
                  description: Full pipeline definition to persist (source/transform/sink only; no dry_run).
                metadata:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Saved pipeline template
        '400':
          description: Invalid pipeline definition
        '409':
          description: Template name conflicts with a boot YAML pipeline
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete a database-backed pipeline template
      parameters:
      - in: path
        name: template_name
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deleted pipeline template
        '404':
          description: Template not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-keys:
    get:
      summary: List all API keys
      description: Requires the static admin key stored in `ATHENA_KEY_12`.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Paginated API key records
          content:
            application/json:
              schema:
                type: object
                properties:
                  api_keys:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKeyRecord'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a new API key
      description: |
        Creates a new API key record and returns a one-time plaintext credential (`ath_{public}.{secret}`).
        Persist the returned secret securely; it is not retrievable later from the API.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApiKeyRequest'
            example:
              name: gateway-readonly
              description: Read-only key for dashboard fetch operations
              client_name: athena_logging
              rights:
              - gateway.fetch.execute
              - gateway.query.execute
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateApiKeyResponse'
              example:
                api_key: ath_abcd1234efgh5678.secretfragment
                record:
                  id: f5c4dfb5-3318-4577-a0c3-ea03fc06f942
                  public_id: abcd1234efgh5678
                  name: gateway-readonly
                  description: Read-only key for dashboard fetch operations
                  client_name: athena_logging
                  is_active: true
                  rights:
                  - gateway.fetch.execute
                  - gateway.query.execute
        '409':
          description: Duplicate public_id
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-keys/{id}:
    patch:
      summary: Update an existing API key
      description: Updates API key metadata, optional client binding, activation state, expiration, and grants.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateApiKeyRequest'
      responses:
        '200':
          description: API key updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  record:
                    $ref: '#/components/schemas/ApiKeyRecord'
        '404':
          description: API key not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete an API key
      description: Removes (or archives) an API key so it can no longer authorize requests.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      responses:
        '200':
          description: API key removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
        '404':
          description: API key not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/webhooks:
    get:
      summary: List gateway webhooks
      description: Definitions for outbound HTTP hooks fired after successful gateway operations (requires static admin key).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: limit
        in: query
        schema:
          type: integer
          default: 50
      - name: offset
        in: query
        schema:
          type: integer
          default: 0
      - name: client_name
        in: query
        description: When set, only webhooks targeting this Athena client (`client_name` / `X-Athena-Client`) are returned.
        schema:
          type: string
      responses:
        '200':
          description: Webhooks and total count
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create or upsert a gateway webhook (idempotent natural key)
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Webhook saved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/webhooks/events:
    get:
      summary: List webhook event types
      description: |
        Returns every supported `route_key` for `gateway_webhook` definitions, with titles and descriptions
        (gateway, PostgREST, and schema/management DDL triggers). Requires the static admin key.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Event catalog in `data.events`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/webhooks/{id}:
    get:
      summary: Get a gateway webhook by id
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/WebhookIdParam'
      responses:
        '200':
          description: Webhook record
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '404':
          description: Webhook not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    patch:
      summary: Update a gateway webhook
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/WebhookIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated webhook
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '404':
          description: Webhook not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete a gateway webhook
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/WebhookIdParam'
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '404':
          description: Webhook not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/webhooks/{id}/deliveries:
    get:
      summary: List webhook delivery attempts
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/WebhookIdParam'
      - name: limit
        in: query
        schema:
          type: integer
      - name: offset
        in: query
        schema:
          type: integer
      responses:
        '200':
          description: Delivery log rows
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '404':
          description: Webhook not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/webhooks/{id}/test:
    post:
      summary: Execute a test webhook delivery
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/WebhookIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Test completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiSuccessEnvelope'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-key-rights:
    get:
      summary: List API key rights
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Available rights
          content:
            application/json:
              schema:
                type: object
                properties:
                  rights:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKeyRightRecord'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a new API key right
      description: Defines a reusable right/capability string that can be granted to one or more API keys.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyRightRequest'
      responses:
        '201':
          description: API key right created
          content:
            application/json:
              schema:
                type: object
                properties:
                  right:
                    $ref: '#/components/schemas/ApiKeyRightRecord'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-key-rights/{id}:
    patch:
      summary: Update an API key right
      description: Renames or updates metadata for an existing API key right definition.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyRightRequest'
      responses:
        '200':
          description: API key right updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  right:
                    $ref: '#/components/schemas/ApiKeyRightRecord'
        '404':
          description: Right not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete an API key right
      description: Deletes an API key right definition and removes associated grants.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      responses:
        '200':
          description: API key right removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
        '404':
          description: Right not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-keys/{id}/ip-policy:
    get:
      summary: Return per-key IP policy (whitelist, blacklist, virgin state)
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      responses:
        '200':
          description: IP policy snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/ApiKeyIpPolicyResponse'
        '404':
          description: API key not found
  /admin/api-keys/{id}/ip-whitelist:
    post:
      summary: Add per-key IP whitelist entries
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IpEntriesRequest'
      responses:
        '200':
          description: Entries inserted
    delete:
      summary: Remove per-key IP whitelist entries
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IpEntriesRequest'
      responses:
        '200':
          description: Entries removed
  /admin/api-keys/{id}/ip-blacklist:
    post:
      summary: Add per-key IP blacklist entries
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IpEntriesRequest'
      responses:
        '200':
          description: Entries inserted
    delete:
      summary: Remove per-key IP blacklist entries
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IpEntriesRequest'
      responses:
        '200':
          description: Entries removed
  /admin/api-keys/{id}/ip-seen:
    get:
      summary: List observed IPs for a given API key
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      - name: limit
        in: query
        schema:
          type: integer
          default: 100
      - name: offset
        in: query
        schema:
          type: integer
          default: 0
      responses:
        '200':
          description: Observed IP rows ordered by last_seen_at DESC
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      records:
                        type: array
                        items:
                          $ref: '#/components/schemas/SeenIpRecord'
  /admin/api-keys/{id}/virgin/promote:
    post:
      summary: Copy seen IPs into the whitelist and mark the virgin key as resolved
      description: Honors `max_whitelist_ips` when picking seen IPs; inserts the earliest-seen entries first.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      responses:
        '200':
          description: Key locked in
  /admin/api-keys/{id}/virgin/reset:
    post:
      summary: Reset virgin counters (optionally truncate seen IPs)
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - $ref: '#/components/parameters/ApiKeyIdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VirginResetRequest'
      responses:
        '200':
          description: Virgin state reset
  /admin/ip-global-whitelist:
    get:
      summary: List global IP whitelist rules (precedence over per-key rules)
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Global whitelist entries
    post:
      summary: Upsert a global IP whitelist rule
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GlobalIpRuleRequest'
      responses:
        '200':
          description: Rule upserted
    delete:
      summary: Delete a global IP whitelist rule
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: addr
        in: query
        required: true
        schema:
          type: string
      - name: client_name
        in: query
        schema:
          type: string
      responses:
        '200':
          description: Rule deleted (or not found)
  /admin/ip-global-blacklist:
    get:
      summary: List global IP blacklist rules (precedence over all other rules)
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Global blacklist entries
    post:
      summary: Upsert a global IP blacklist rule
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GlobalIpRuleRequest'
      responses:
        '200':
          description: Rule upserted
    delete:
      summary: Delete a global IP blacklist rule
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: addr
        in: query
        required: true
        schema:
          type: string
      - name: client_name
        in: query
        schema:
          type: string
      responses:
        '200':
          description: Rule deleted (or not found)
  /admin/api-key-config:
    get:
      summary: Get global API key enforcement configuration
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Global+per-client config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyConfigResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    put:
      summary: Toggle global API key enforcement
      description: Enables or disables global API key enforcement for routes that honor centralized key policy.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyConfigRequest'
      responses:
        '200':
          description: Updated global config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyConfigResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-key-clients:
    get:
      summary: List per-client API key enforcement overrides
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Client configs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyClientListResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-key-clients/{client_name}:
    put:
      summary: Update or insert a client's enforcement override
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyClientConfigRequest'
      responses:
        '200':
          description: Client config upserted
          content:
            application/json:
              schema:
                type: object
                properties:
                  client:
                    $ref: '#/components/schemas/ApiKeyClientConfig'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Remove a client's enforcement override
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Client override deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  client_name:
                    type: string
        '404':
          description: Client config not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients:
    get:
      summary: List Athena clients from the database-backed catalog
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Athena clients and their runtime state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminClientsResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a database-backed Athena client
      description: Registers a new client in the catalog so it can be resolved by `X-Athena-Client` routing.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveAthenaClientRequest'
      responses:
        '201':
          description: Athena client created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminClientResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}:
    patch:
      summary: Update an Athena client
      description: Updates client metadata and connection source fields for an existing catalog entry.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaveAthenaClientRequest'
      responses:
        '200':
          description: Athena client updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminClientResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Soft-delete an Athena client
      description: Soft-deletes the client in catalog storage and removes it from active runtime routing.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Athena client deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminClientResponse'
        '404':
          description: Athena client not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/freeze:
    put:
      summary: Freeze or unfreeze an Athena client
      description: Toggles temporary routing disablement without deleting the underlying catalog entry.
      tags:
      - Admin
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FreezeAthenaClientRequest'
      responses:
        '200':
          description: Athena client freeze state updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminClientResponse'
        '404':
          description: Athena client not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/statistics:
    get:
      summary: List Athena client statistics
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Aggregated request and operation statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientStatisticsListResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/statistics/refresh:
    post:
      summary: Rebuild Athena client statistics from gateway logs
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Statistics refreshed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientStatisticsListResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/statistics:
    get:
      summary: Inspect Athena client statistics and touched tables
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Per-client statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientStatisticsDetailResponse'
        '404':
          description: Client statistics not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/statistics/drilldown:
    get:
      summary: List gateway operation log rows for a client table operation
      description: |
        Returns rows from `gateway_operation_log` filtered by `client`, `table_name`, and `operation`, with optional
        `status` (`all`, `errors`, or `normal`) and pagination. Requires the static admin key (`ATHENA_KEY_12`) and a
        connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      - name: table_name
        in: query
        required: true
        schema:
          type: string
        description: Table name (matches `gateway_operation_log.table_name`).
      - name: operation
        in: query
        required: true
        schema:
          type: string
        description: Operation name (matches `gateway_operation_log.operation`).
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - all
          - errors
          - normal
          default: all
        description: |
          `errors` includes rows where `error` is true or `status_code` is at least 400; `normal` excludes those.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Drilldown rows and pagination echo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientStatisticsDrilldownData'
        '400':
          description: Invalid query parameters
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/pressure:
    get:
      summary: List latest Athena client pressure snapshots
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Latest pressure snapshot per client plus the latest worker run
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      snapshots:
                        type: array
                        items:
                          type: object
                      last_run:
                        type: object
                        nullable: true
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/pressure/backfill:
    post:
      summary: Queue a client pressure backfill request
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                hours_back:
                  type: integer
                  minimum: 1
                  maximum: 720
                  default: 24
      responses:
        '202':
          description: Backfill request accepted for background processing
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      request:
                        type: object
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/pressure:
    get:
      summary: Inspect Athena client pressure detail
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Latest client pressure snapshot, per-table pressure, and recent history
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      snapshot:
                        type: object
                      tables:
                        type: array
                        items:
                          type: object
                      history:
                        type: array
                        items:
                          type: object
                      last_run:
                        type: object
                        nullable: true
        '404':
          description: Client pressure not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/query-optimizations:
    get:
      summary: List query optimization recommendations for a client
      description: |
        Returns stored recommendations from the logging database with optional status filter and pagination.
        Requires the static admin key (`ATHENA_KEY_12`) and a connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - all
          - open
          - applied
          - dismissed
        description: Filter by recommendation status; omit or `all` for every status.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Recommendations and pagination echo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryOptimizationRecommendationsListData'
        '400':
          description: Invalid status filter
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/query-optimizations/refresh:
    post:
      summary: Refresh query optimization recommendations for a client
      description: |
        Recomputes recommendations for the client and returns the new run plus rows.
        Requires the static admin key (`ATHENA_KEY_12`) and a connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Refresh run and recommendations
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryOptimizationRefreshData'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/query-optimizations/runs:
    get:
      summary: List query optimization runs for a client
      description: |
        Returns historical refresh runs with pagination.
        Requires the static admin key (`ATHENA_KEY_12`) and a connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Runs and pagination echo
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryOptimizationRunsListData'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/clients/{client_name}/query-optimizations/{recommendation_id}/apply:
    post:
      summary: Apply a query optimization recommendation
      description: |
        Applies the recommendation identified by `recommendation_id` for `client_name`.
        Optional JSON body may set `actor` for audit metadata.
        Requires the static admin key (`ATHENA_KEY_12`) and a connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      - name: recommendation_id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApplyQueryOptimizationRequest'
      responses:
        '200':
          description: Apply result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QueryOptimizationApplyData'
        '404':
          description: Recommendation not found for this client and id
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/vacuum-health:
    get:
      summary: List latest vacuum health snapshot per Postgres client
      description: |
        Returns the most recent `vacuum_health_snapshots` row per `client_name` stored in the logging database
        (populated by the background vacuum health collector). Requires the static admin key (`ATHENA_KEY_12`).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Latest vacuum health summaries
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VacuumHealthListResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/vacuum-health/{client_name}:
    get:
      summary: Latest vacuum health snapshot and per-table stats for one client
      description: |
        Returns the latest snapshot for `client_name` plus all `vacuum_health_table_stats` rows for that snapshot.
        Requires the static admin key (`ATHENA_KEY_12`).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Snapshot and per-table statistics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VacuumHealthDetailResponse'
        '404':
          description: No vacuum health snapshots for this client
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/admission-events:
    get:
      summary: List admission limiter events with optional decision and client filters
      description: |
        Returns rows from `gateway_admission_events` in descending `created_at` order.
        Requires the static admin key (`ATHENA_KEY_12`) and a connected `athena_logging` pool.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: decision
        in: query
        required: false
        schema:
          type: string
        description: Optional decision filter (for example `deferred` or `rejected`).
      - name: client
        in: query
        required: false
        schema:
          type: string
        description: Optional client name filter (matches `gateway_admission_events.client_name`).
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Admission event rows and applied filters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdmissionEventsListData'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups:
    post:
      summary: Create a database backup and upload to S3
      description: |
        Uses `pg_dump` against the provided `client_name`, uploads the archive to the configured S3 bucket, and stores an optional `label` in the object metadata.
        Requires the static admin key (`ATHENA_KEY_12`) and S3 configuration (`ATHENA_BACKUP_S3_BUCKET`, `ATHENA_BACKUP_S3_PREFIX`, and credentials).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateBackupRequest'
      responses:
        '200':
          description: Backup created and uploaded
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                  client_name:
                    type: string
                  label:
                    type: string
                    nullable: true
        '400':
          description: Missing or unknown client_name
        '401':
          description: Invalid admin API key
        '503':
          description: S3 not configured
    get:
      summary: List available backups in S3
      description: Returns S3 backup objects filtered by optional `client_name` prefix, newest first.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: query
        required: false
        schema:
          type: string
        description: When set, only list backups for this client.
      responses:
        '200':
          description: Backup listing
          content:
            application/json:
              schema:
                type: object
                properties:
                  backups:
                    type: array
                    items:
                      $ref: '#/components/schemas/BackupObject'
        '401':
          description: Invalid admin API key
        '503':
          description: S3 not configured
  /admin/backups/config:
    get:
      summary: Return backup storage configuration metadata
      description: Returns non-secret S3 backup configuration (bucket, region, prefix, endpoint) for operators.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Backup storage configuration
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Invalid admin API key
        '503':
          description: S3 not configured
  /admin/backups/jobs:
    get:
      summary: List backup and restore jobs
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: query
        required: false
        schema:
          type: string
      - name: status
        in: query
        required: false
        schema:
          type: string
      - name: job_type
        in: query
        required: false
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 50
      responses:
        '200':
          description: Backup job listing
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Invalid admin API key
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups/jobs/{id}:
    get:
      summary: Get a backup or restore job with logs
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      responses:
        '200':
          description: Backup job and recent logs
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Invalid admin API key
        '404':
          description: Job not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete a completed, failed, or cancelled backup job
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      responses:
        '200':
          description: Backup job deleted
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Job is still running or pending
        '401':
          description: Invalid admin API key
        '404':
          description: Job not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups/jobs/{id}/cancel:
    post:
      summary: Cancel a running or pending backup or restore job
      description: |
        Marks the job as cancelled in `athena_logging` and signals the worker on this Athena process to stop `pg_dump` or `pg_restore` if still running.
        Requires the static admin key (`ATHENA_KEY_12`).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      responses:
        '200':
          description: Job cancelled or cancellation recorded
        '400':
          description: Job is not running or pending
        '401':
          description: Invalid admin API key
        '404':
          description: Job not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups/schedules:
    get:
      summary: List backup schedules
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Backup schedules listed
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Invalid admin API key
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a backup schedule
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Backup schedule created
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Invalid schedule payload
        '401':
          description: Invalid admin API key
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups/schedules/{id}:
    patch:
      summary: Update a backup schedule
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Backup schedule updated
          content:
            application/json:
              schema:
                type: object
        '400':
          description: Invalid schedule payload
        '401':
          description: Invalid admin API key
        '404':
          description: Schedule not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Delete a backup schedule
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: id
        in: path
        required: true
        schema:
          type: integer
          format: int64
      responses:
        '200':
          description: Backup schedule deleted
          content:
            application/json:
              schema:
                type: object
        '401':
          description: Invalid admin API key
        '404':
          description: Schedule not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/backups/{key}/restore:
    post:
      summary: Restore a database backup from S3
      description: |
        Downloads the backup identified by the S3 object key, runs `pg_restore` into the target `client_name` Postgres pool, and returns the key plus client.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: key
        in: path
        required: true
        schema:
          type: string
        description: S3 object key for the backup (URL-encoded, may contain slashes).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestoreBackupRequest'
      responses:
        '200':
          description: Restore completed
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                  client_name:
                    type: string
        '400':
          description: Missing key or client_name
        '401':
          description: Invalid admin API key
        '503':
          description: S3 not configured
  /admin/backups/{key}/download:
    get:
      summary: Download a backup archive from S3
      description: |
        Streams the raw `application/gzip` backup bytes with a `Content-Disposition: attachment` filename.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: key
        in: path
        required: true
        description: S3 object key for the backup (URL-encoded, may contain slashes).
        schema:
          type: string
      responses:
        '200':
          description: Backup archive
          content:
            application/gzip:
              schema:
                type: string
                format: binary
        '400':
          description: Missing backup key
        '401':
          description: Invalid admin API key
        '404':
          description: Backup not found
        '503':
          description: S3 not configured
  /admin/backups/{key}:
    delete:
      summary: Delete a backup from S3
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: key
        in: path
        required: true
        description: S3 object key for the backup (URL-encoded, may contain slashes).
        schema:
          type: string
      responses:
        '200':
          description: Backup deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
        '400':
          description: Missing backup key
        '401':
          description: Invalid admin API key
        '404':
          description: Backup not found
        '503':
          description: S3 not configured
  /admin/provision:
    post:
      summary: Provision a Postgres database with the Athena schema
      description: |
        Runs the bundled provisioning SQL against either a direct `uri` or a registered `client_name`.
        When `register` is true and `uri` is provided, the client is added to the runtime catalog after provisioning.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProvisionRequest'
      responses:
        '200':
          description: Provisioning completed
          content:
            application/json:
              schema:
                type: object
                properties:
                  statements_executed:
                    type: integer
                  client:
                    type: string
                  registered:
                    type: string
                    nullable: true
                  tables:
                    type: array
                    items:
                      type: string
        '400':
          description: Ambiguous or missing target
        '401':
          description: Invalid admin API key
        '503':
          description: Target client unavailable
  /admin/provision/status:
    get:
      summary: Check whether a client database is provisioned
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: query
        required: true
        schema:
          type: string
        description: Registered client to check.
      responses:
        '200':
          description: Provisioning status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvisionStatusResponse'
        '401':
          description: Invalid admin API key
        '503':
          description: Client unavailable
  /admin/provision/dependencies:
    get:
      summary: Inspect Docker and Postgres dependency status for local provisioning
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Local provisioning dependency status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalProvisionDependencyStatus'
        '401':
          description: Invalid admin API key
  /admin/provision/dependencies/install:
    post:
      summary: Attempt to install Docker and Postgres dependencies for local provisioning
      description: |
        Best-effort Linux-only installer that detects the host package manager, attempts
        to install Docker and PostgreSQL packages, and optionally starts the related services.
        Requires root or passwordless sudo for package installation.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InstallLocalProvisionDependenciesRequest'
      responses:
        '200':
          description: Dependency installation attempt completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalProvisionDependencyInstallResult'
        '400':
          description: Invalid dependency install request
        '401':
          description: Invalid admin API key
        '503':
          description: Host cannot attempt automatic installation
  /admin/provision/instances:
    get:
      summary: List managed local Postgres instances
      description: |
        Returns Docker-managed Athena Postgres instances. When Docker is unavailable,
        the response still includes dependency/runtime status so operators can install
        or repair the local provisioning runtime.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Managed instances and dependency status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListPostgresInstancesResponse'
        '401':
          description: Invalid admin API key
    post:
      summary: Spin up a local Docker-backed Postgres instance
      description: |
        Creates (or reuses) a managed local Postgres container, optionally provisions Athena schema,
        and can register the resulting connection in runtime and/or catalog client registries.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SpinUpInstanceRequest'
      responses:
        '200':
          description: Postgres instance is ready
        '400':
          description: Invalid spin-up payload
        '401':
          description: Invalid admin API key
        '409':
          description: Container conflict (already exists and reuse disabled)
        '503':
          description: Docker unavailable
  /admin/provision/instances/{container_name}:
    get:
      summary: Inspect a local managed Postgres instance
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: container_name
        in: path
        required: true
        schema:
          type: string
      - name: client_name
        in: query
        required: false
        schema:
          type: string
      responses:
        '200':
          description: Instance status loaded
        '401':
          description: Invalid admin API key
        '404':
          description: Instance not found
        '503':
          description: Docker unavailable
    delete:
      summary: Delete a local managed Postgres instance
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: container_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeleteInstanceRequest'
      responses:
        '200':
          description: Instance deleted
        '401':
          description: Invalid admin API key
        '503':
          description: Docker unavailable
  /admin/provision/instances/{container_name}/start:
    post:
      summary: Start a managed Postgres instance
      description: Optionally reconnects the runtime client when `sync_runtime` is true (default).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: container_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InstanceLifecycleRequest'
      responses:
        '200':
          description: Instance started
        '401':
          description: Invalid admin API key
        '404':
          description: Instance not found
        '503':
          description: Docker unavailable
  /admin/provision/instances/{container_name}/stop:
    post:
      summary: Stop a managed Postgres instance
      description: When `sync_runtime` is true (default), marks the linked runtime client unavailable.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: container_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InstanceLifecycleRequest'
      responses:
        '200':
          description: Instance stopped
        '401':
          description: Invalid admin API key
        '404':
          description: Instance not found
        '503':
          description: Docker unavailable
  /admin/provision/instances/{container_name}/bindings:
    post:
      summary: Bind a managed instance host port to a public route mapping
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: container_name
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BindInstancePortRouteRequest'
      responses:
        '200':
          description: Binding saved
        '400':
          description: Invalid payload or missing client
        '401':
          description: Invalid admin API key
        '404':
          description: Instance not found
        '503':
          description: Docker unavailable or port not published
  /admin/provision/providers/neon:
    post:
      summary: Provision/register a Neon Postgres database
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NeonProviderProvisionRequest'
      responses:
        '200':
          description: Neon database provisioned
        '400':
          description: Invalid provider payload
        '401':
          description: Invalid admin API key
        '503':
          description: Provider unavailable
  /admin/provision/providers/railway:
    post:
      summary: Provision/register a Railway Postgres database
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RailwayProviderProvisionRequest'
      responses:
        '200':
          description: Railway database provisioned
        '400':
          description: Invalid provider payload
        '401':
          description: Invalid admin API key
        '503':
          description: Provider unavailable
  /admin/provision/providers/render:
    post:
      summary: Provision/register a Render Postgres database
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenderProviderProvisionRequest'
      responses:
        '200':
          description: Render database provisioned
        '400':
          description: Invalid provider payload
        '401':
          description: Invalid admin API key
        '503':
          description: Provider unavailable
  /admin/provision/local/databases:
    get:
      summary: List local cluster PostgreSQL databases for a registered server client
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      - name: client_name
        in: query
        required: true
        schema:
          type: string
        description: Registered client connected to the target Postgres server.
      responses:
        '200':
          description: Database inventory loaded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocalClusterDatabasesResponse'
        '401':
          description: Invalid admin API key
        '503':
          description: Client unavailable
    post:
      summary: Create a local cluster PostgreSQL database for a registered server client
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/LocalClusterCreateDatabaseRequest'
              - type: object
                required:
                - client_name
      responses:
        '201':
          description: Local database created
        '400':
          description: Invalid payload
        '401':
          description: Invalid admin API key
        '409':
          description: Database already exists
        '503':
          description: Client unavailable
  /api/v2/supabase/ssl_enforcement:
    post:
      summary: Toggle Supabase SSL enforcement for a project
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
                  description: Set to true to enable SSL enforcement, false to disable
                access_token:
                  type: string
                  description: Optional override for SUPABASE_ACCESS_TOKEN
                project_ref:
                  type: string
                  description: Optional override for PROJECT_REF
              required:
              - enabled
      responses:
        '200':
          description: SSL enforcement status returned
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                  data:
                    type: object
                    properties:
                      currentConfig:
                        type: object
                        properties:
                          database:
                            type: boolean
                      appliedSuccessfully:
                        type: boolean
        '400':
          description: Missing credentials or bad payload
        '500':
          description: Supabase API error
  /router/registry:
    get:
      summary: List Athena router registry entries
      responses:
        '200':
          description: An array of router registry objects
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '500':
          description: Failed to load router registry entries
  /metrics:
    get:
      summary: Prometheus metrics endpoint
      description: Exposes Prometheus text metrics including uptime, request counters, management mutation counters, cluster probe gauges, and 24-hour aggregates.
      responses:
        '200':
          description: Metrics payload
          content:
            text/plain:
              schema:
                type: string
                example: |
                  # TYPE athena_uptime_seconds gauge
                  athena_uptime_seconds 123.0
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /registry:
    get:
      summary: List API registry entries
      parameters:
      - in: header
        name: Cache-Control
        schema:
          type: string
        description: Set to `no-cache` to skip the cached response.
      responses:
        '200':
          description: API registry rows from Supabase
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '500':
          description: Failed to list API registry entries
  /registry/{api_registry_id}:
    get:
      summary: Lookup an API registry entry by ID
      parameters:
      - in: path
        name: api_registry_id
        required: true
        schema:
          type: string
        description: Row identifier from the Supabase table
      - in: header
        name: Cache-Control
        schema:
          type: string
        description: Set to `no-cache` to bypass the cached entry.
      responses:
        '200':
          description: Matching registry entry (wrapped in an array)
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '500':
          description: Failed to fetch the registry entry
  /schema/clients:
    get:
      summary: List configured Postgres clients
      description: Protected by the static admin key (`ATHENA_KEY_12`).
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: A JSON object containing a clients array
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientsResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /clients:
    get:
      summary: List Athena/Postgres clients (admin)
      description: Same response as `/schema/clients`, but exposed without the `schema` prefix.
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      responses:
        '200':
          description: Configured clients
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientsResponse'
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /schema/tables:
    get:
      summary: List tables for the selected Postgres client
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
        description: Selects which Postgres pool provides the tables.
      responses:
        '200':
          description: Table schema metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  tables:
                    type: array
                    items:
                      type: object
                      properties:
                        table_schema:
                          type: string
                        table_name:
                          type: string
        '400':
          description: Missing or unknown X-Athena-Client
        '500':
          description: Failed to query information_schema.tables
  /schema/columns:
    get:
      summary: Describe columns for a given table
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      - in: header
        name: X-API-Key
        required: false
        schema:
          type: string
        description: Optional API key (also apikey, X-Athena-Key, Authorization Bearer)
      - in: query
        name: table_name
        required: true
        schema:
          type: string
        description: Postgres table name to describe
      - in: query
        name: table_schema
        required: false
        schema:
          type: string
        description: Optional schema name; when provided, restricts columns to this schema
      responses:
        '200':
          description: Column metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  columns:
                    type: array
                    items:
                      type: object
                      properties:
                        column_name:
                          type: string
                        data_type:
                          type: string
                          nullable: true
                        column_default:
                          type: string
                          nullable: true
                        is_nullable:
                          type: string
                          nullable: true
        '400':
          description: Missing header or invalid table_name
        '500':
          description: Failed to fetch column metadata
  /schema/migrations:
    get:
      summary: List schema migrations (Supabase-style)
      description: Returns migration records from supabase_migrations.schema_migrations. If the table does not exist, returns an empty array with a message instead of an error.
      parameters:
      - in: header
        name: X-Athena-Client
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Migrations list or empty with message when table absent
          content:
            application/json:
              schema:
                type: object
                properties:
                  migrations:
                    type: array
                    items:
                      type: object
                      properties:
                        version:
                          type: string
                          nullable: true
                        name:
                          type: string
                          nullable: true
                  message:
                    type: string
                    description: Present when migrations table does not exist
        '400':
          description: Missing X-Athena-Client
        '500':
          description: Other database error
  /storage/objects:
    post:
      summary: List objects in an S3 bucket (ListObjectsV2)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageListObjectsRequest'
      responses:
        '200':
          description: Object listing
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/objects/head:
    post:
      summary: Load object metadata without downloading the object body
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageGetObjectRequest'
      responses:
        '200':
          description: Object metadata
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/objects/url:
    post:
      summary: Generate a presigned GET URL for an object
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageGetObjectRequest'
      responses:
        '200':
          description: Presigned download URL
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/objects/delete:
    post:
      summary: Delete an object
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageDeleteObjectRequest'
      responses:
        '200':
          description: Object deleted
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/objects/folder:
    post:
      summary: Create a folder placeholder (prefix) in the bucket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageCreateFolderRequest'
      responses:
        '200':
          description: Folder created
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/objects/upload-url:
    post:
      summary: Generate a presigned PUT URL for upload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StoragePresignUploadRequest'
      responses:
        '200':
          description: Presigned upload URL
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/list:
    post:
      summary: List accessible buckets for the supplied S3-compatible credentials
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageS3Connection'
      responses:
        '200':
          description: Bucket listing
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/create:
    post:
      summary: Create a bucket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageS3Credentials'
      responses:
        '200':
          description: Bucket created
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/delete:
    post:
      summary: Delete an empty bucket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageS3Credentials'
      responses:
        '200':
          description: Bucket deleted
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/cors:
    post:
      summary: Read bucket CORS configuration (via s3cmd when available)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageBucketCorsRequest'
      responses:
        '200':
          description: CORS rules or message
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/cors/set:
    post:
      summary: Apply CORS rules to a bucket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageSetBucketCorsRequest'
      responses:
        '200':
          description: CORS updated
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /storage/buckets/cors/delete:
    post:
      summary: Remove CORS configuration from a bucket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageBucketCorsRequest'
      responses:
        '200':
          description: CORS removed
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /typesense/backends/test:
    post:
      summary: Check a Typesense backend connection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TypesenseConnection'
      responses:
        '200':
          description: Typesense backend is reachable
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /typesense/collections/list:
    post:
      summary: List collections available on a Typesense backend
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TypesenseConnection'
      responses:
        '200':
          description: Typesense collections
        '400':
          description: Invalid request
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /typesense/sync-jobs/{job_id}/run:
    post:
      summary: Run a configured Typesense sync job immediately
      parameters:
      - in: path
        name: job_id
        required: true
        schema:
          type: integer
          format: int64
      responses:
        '200':
          description: Typesense sync completed
        '404':
          description: Sync job not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /typesense/search:
    post:
      summary: Search a Typesense collection through an Athena sync binding
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TypesenseSearchRequest'
      responses:
        '200':
          description: Typesense search completed
        '400':
          description: Invalid request
        '404':
          description: Sync job not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /openapi.yaml:
    get:
      summary: Download the embedded OpenAPI YAML
      responses:
        '200':
          description: OpenAPI spec fixture served by the API
          content:
            application/yaml:
              schema:
                type: string
        '500':
          description: Failed to read openapi.yaml
  /openapi-wss.yaml:
    get:
      summary: Download the embedded WebSocket OpenAPI YAML
      responses:
        '200':
          description: WebSocket OpenAPI spec fixture served by the API
          content:
            application/yaml:
              schema:
                type: string
        '500':
          description: Failed to read openapi-wss.yaml
  /wss/info:
    get:
      summary: Read the WebSocket gateway contract
      description: Returns rollout metadata and the current WebSocket gateway message contract.
      responses:
        '200':
          description: WebSocket gateway contract metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: Athena WebSocket gateway scaffold
                  data:
                    type: object
                    properties:
                      transport:
                        type: string
                        example: wss
                      path:
                        type: string
                        example: /wss/gateway
                      state:
                        type: string
                        example: scaffolded
                      notes:
                        type: array
                        items:
                          type: string
                      actions:
                        type: array
                        items:
                          type: string
                        example:
                        - gateway.fetch
                        - gateway.insert
                        - gateway.update
                        - gateway.delete
                        - gateway.query
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /docs:
    get:
      summary: Redirect to the hosted Athena docs
      responses:
        '308':
          description: Permanent redirect to https://xylex.group/docs/athena
          headers:
            Location:
              schema:
                type: string
                format: uri
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /public/{route_key}/{op}:
    post:
      summary: Dispatch a public route alias to gateway operations
      description: Resolves a configured public route key to a provisioned Athena client and forwards to gateway query/fetch/insert/update/delete.
      parameters:
      - in: path
        name: route_key
        required: true
        schema:
          type: string
      - in: path
        name: op
        required: true
        schema:
          type: string
          enum:
          - query
          - fetch
          - insert
          - update
          - delete
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Forwarded successfully
        '400':
          description: Invalid operation or payload
        '403':
          description: Route inactive or operation not allowed
        '404':
          description: Unknown route key
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/public-routes:
    get:
      summary: List public route aliases
      parameters:
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Public routes list
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    post:
      summary: Create a public route alias
      parameters:
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - route_key
              - client_name
              - allowed_ops
              properties:
                route_key:
                  type: string
                client_name:
                  type: string
                allowed_ops:
                  type: array
                  items:
                    type: string
      responses:
        '201':
          description: Created
        '400':
          description: Validation error
        '409':
          description: Duplicate route key
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/public-routes/{route_key}:
    patch:
      summary: Update a public route alias
      parameters:
      - in: path
        name: route_key
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Updated
        '404':
          description: Route not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
    delete:
      summary: Soft-delete a public route alias
      parameters:
      - in: path
        name: route_key
        required: true
        schema:
          type: string
      - in: header
        name: X-Athena-Key
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deleted
        '404':
          description: Route not found
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
components:
  responses:
    DefaultServerError5xx:
      description: Internal server error (or other 5xx). Standard envelope with status, message, and error.
      content:
        application/json:
          schema:
            type: object
            required:
            - status
            - message
            - error
            properties:
              status:
                type: string
                example: error
              message:
                type: string
                description: Human-readable summary
              error:
                type: string
                description: Error detail
  parameters:
    AdminApiKeyHeader:
      name: apikey
      in: header
      description: |
        The static admin key configured as `ATHENA_KEY_12`. Authorization headers (`Bearer <key>`), `X-API-Key`, `X-Athena-Key`, or `?api_key=` are also accepted.
      required: true
      schema:
        type: string
    ApiKeyIdParam:
      name: id
      in: path
      required: true
      description: UUID that identifies an API key or API key right (`api_keys_id`, `api_key_rights_id`).
      schema:
        type: string
        format: uuid
    WebhookIdParam:
      name: id
      in: path
      required: true
      description: Numeric primary key of a `gateway_webhook` row.
      schema:
        type: integer
        format: int64
  schemas:
    ApiSuccessEnvelope:
      type: object
      description: |
        Standard JSON envelope for successful responses (`api_success` in `src/api/response.rs`).
      required:
      - status
      - message
      - data
      properties:
        status:
          type: string
          enum:
          - success
        message:
          type: string
        data:
          type: object
          additionalProperties: true
    ApiKeyRecord:
      type: object
      description: Persisted API key metadata plus resolved rights for listing/detail responses.
      properties:
        id:
          type: string
          format: uuid
        public_id:
          type: string
        name:
          type: string
        description:
          type: string
          nullable: true
        client_name:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        is_active:
          type: boolean
        last_used_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        rights:
          type: array
          items:
            type: string
    CreateApiKeyRequest:
      type: object
      description: Payload used to create a new API key and optional right grants.
      properties:
        name:
          type: string
        description:
          type: string
          nullable: true
        client_name:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        rights:
          type: array
          items:
            type: string
        virgin_mode:
          type: boolean
          description: When true, the key starts with no per-key whitelist/blacklist and learns caller IPs until `virgin_until_n_requests` or `max_whitelist_ips` distinct IPs is reached, whichever comes first.
        virgin_until_n_requests:
          type: integer
          minimum: 0
          description: Lock the key after this many successful authentications; `0` disables the request counter and lets `max_whitelist_ips` drive lock-in.
        max_whitelist_ips:
          type: integer
          minimum: 0
          description: Hard cap on the per-key whitelist; `0` means unlimited.
        ip_whitelist:
          type: array
          items:
            type: string
          description: Initial per-key IP/CIDR whitelist entries. Ignored when `virgin_mode` is true.
        ip_blacklist:
          type: array
          items:
            type: string
          description: Initial per-key IP/CIDR blacklist entries. Ignored when `virgin_mode` is true.
      required:
      - name
    ApiKeyIpPolicyResponse:
      type: object
      description: Full per-key IP policy snapshot including virgin counters and rule lists.
      properties:
        policy:
          type: object
          properties:
            api_key_id:
              type: integer
              format: int64
            virgin_mode:
              type: boolean
            virgin_resolved:
              type: boolean
            virgin_resolved_at:
              type: string
              format: date-time
              nullable: true
            virgin_until_n_requests:
              type: integer
            virgin_request_count:
              type: integer
            max_whitelist_ips:
              type: integer
            whitelist:
              type: array
              items:
                $ref: '#/components/schemas/IpRuleRecord'
            blacklist:
              type: array
              items:
                $ref: '#/components/schemas/IpRuleRecord'
    IpRuleRecord:
      type: object
      properties:
        id:
          type: string
          format: uuid
        addr:
          type: string
          description: CIDR notation (single IPs rendered as `/32` or `/128`).
        label:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
    SeenIpRecord:
      type: object
      properties:
        id:
          type: string
          format: uuid
        addr:
          type: string
        first_seen_at:
          type: string
          format: date-time
        last_seen_at:
          type: string
          format: date-time
        hit_count:
          type: integer
          format: int64
        locked_in:
          type: boolean
    IpEntriesRequest:
      type: object
      description: Bulk add/remove request body for per-key and global IP lists.
      required:
      - addrs
      properties:
        addrs:
          type: array
          items:
            type: string
        label:
          type: string
          nullable: true
    GlobalIpRuleRequest:
      type: object
      required:
      - addr
      properties:
        addr:
          type: string
        client_name:
          type: string
          nullable: true
        label:
          type: string
          nullable: true
    VirginResetRequest:
      type: object
      properties:
        clear_seen:
          type: boolean
          description: When true, truncates the `api_key_ip_seen` rows for the key in addition to resetting counters.
    UpdateApiKeyRequest:
      type: object
      description: Partial update payload for API key metadata, lifecycle state, and right grants.
      properties:
        name:
          type: string
        description:
          type: string
          nullable: true
        client_name:
          type: string
          nullable: true
        expires_at:
          type: string
          format: date-time
          nullable: true
        is_active:
          type: boolean
        rights:
          type: array
          items:
            type: string
    CreateApiKeyResponse:
      type: object
      description: One-time API key creation response that includes the plaintext key and stored record metadata.
      properties:
        api_key:
          type: string
          example: ath_abcdefghijklmnop.secret
        record:
          $ref: '#/components/schemas/ApiKeyRecord'
    ApiKeyRightRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
          nullable: true
      required:
      - name
    ApiKeyRightRecord:
      type: object
      description: Canonical right definition assignable to API keys.
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ApiKeyConfigRequest:
      type: object
      properties:
        enforce_api_keys:
          type: boolean
      required:
      - enforce_api_keys
    ApiKeyGlobalConfig:
      type: object
      properties:
        enforce_api_keys:
          type: boolean
        updated_at:
          type: string
          format: date-time
    ApiKeyClientConfig:
      type: object
      properties:
        client_name:
          type: string
        enforce_api_keys:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ApiKeyConfigResponse:
      type: object
      properties:
        global:
          $ref: '#/components/schemas/ApiKeyGlobalConfig'
        clients:
          type: array
          items:
            $ref: '#/components/schemas/ApiKeyClientConfig'
    ApiKeyClientConfigRequest:
      type: object
      properties:
        enforce_api_keys:
          type: boolean
      required:
      - enforce_api_keys
    ApiKeyClientListResponse:
      type: object
      properties:
        clients:
          type: array
          items:
            $ref: '#/components/schemas/ApiKeyClientConfig'
    ClientsResponse:
      type: object
      description: Simple admin client-name listing response.
      properties:
        clients:
          type: array
          items:
            type: string
    AthenaClientRecord:
      type: object
      description: Database-backed Athena client catalog row used for runtime routing and management.
      properties:
        id:
          type: string
          format: uuid
        client_name:
          type: string
        description:
          type: string
          nullable: true
        pg_uri:
          type: string
          nullable: true
        pg_uri_env_var:
          type: string
          nullable: true
        config_uri_template:
          type: string
          nullable: true
        source:
          type: string
        is_active:
          type: boolean
        is_frozen:
          type: boolean
        last_synced_from_config_at:
          type: string
          format: date-time
          nullable: true
        last_seen_at:
          type: string
          format: date-time
          nullable: true
        metadata:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        deleted_at:
          type: string
          format: date-time
          nullable: true
    RuntimeAthenaClient:
      type: object
      description: In-memory runtime view of a registered client and live pool connectivity state.
      properties:
        client_name:
          type: string
        source:
          type: string
        description:
          type: string
          nullable: true
        pg_uri:
          type: string
          nullable: true
        pg_uri_env_var:
          type: string
          nullable: true
        config_uri_template:
          type: string
          nullable: true
        is_active:
          type: boolean
        is_frozen:
          type: boolean
        pool_connected:
          type: boolean
    SaveAthenaClientRequest:
      type: object
      properties:
        client_name:
          type: string
        description:
          type: string
          nullable: true
        pg_uri:
          type: string
          nullable: true
        pg_uri_env_var:
          type: string
          nullable: true
        is_active:
          type: boolean
    FreezeAthenaClientRequest:
      type: object
      properties:
        is_frozen:
          type: boolean
      required:
      - is_frozen
    AdminClientResponse:
      type: object
      properties:
        client:
          $ref: '#/components/schemas/AthenaClientRecord'
    AdminClientsResponse:
      type: object
      properties:
        clients:
          type: array
          items:
            $ref: '#/components/schemas/AthenaClientRecord'
        runtime:
          type: array
          items:
            $ref: '#/components/schemas/RuntimeAthenaClient'
    ClientStatisticsRecord:
      type: object
      description: Aggregated per-client request and operation counters derived from gateway log tables.
      properties:
        client_name:
          type: string
        total_requests:
          type: integer
        successful_requests:
          type: integer
        failed_requests:
          type: integer
        total_cached_requests:
          type: integer
        total_operations:
          type: integer
        last_request_at:
          type: string
          format: date-time
          nullable: true
        last_operation_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
    ClientTableStatisticsRecord:
      type: object
      description: Per-client, per-table, per-operation counters including error tallies.
      properties:
        client_name:
          type: string
        table_name:
          type: string
        operation:
          type: string
        total_operations:
          type: integer
        error_operations:
          type: integer
        last_operation_at:
          type: string
          format: date-time
          nullable: true
        updated_at:
          type: string
          format: date-time
    ClientStatisticsListResponse:
      type: object
      properties:
        statistics:
          type: array
          items:
            $ref: '#/components/schemas/ClientStatisticsRecord'
    ClientStatisticsDetailResponse:
      type: object
      properties:
        statistics:
          $ref: '#/components/schemas/ClientStatisticsRecord'
        tables:
          type: array
          items:
            $ref: '#/components/schemas/ClientTableStatisticsRecord'
    ClientOperationDrilldownRecord:
      type: object
      properties:
        created_at:
          type: string
          format: date-time
        request_id:
          type: string
        operation:
          type: string
          nullable: true
        table_name:
          type: string
          nullable: true
        client_name:
          type: string
          nullable: true
        method:
          type: string
          nullable: true
        path:
          type: string
          nullable: true
        status_code:
          type: integer
          nullable: true
        duration_ms:
          type: integer
          format: int64
          nullable: true
        operation_time_ms:
          type: number
          nullable: true
        details:
          type: object
          additionalProperties: true
          nullable: true
        error:
          type: boolean
          nullable: true
        message:
          type: string
          nullable: true
        cache_key:
          type: string
          nullable: true
    ClientStatisticsDrilldownData:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/clients/{client_name}/statistics/drilldown
      properties:
        client_name:
          type: string
        table_name:
          type: string
        operation:
          type: string
        status:
          type: string
          description: Echo of the applied filter (`all`, `errors`, or `normal`).
        limit:
          type: integer
        offset:
          type: integer
        rows:
          type: array
          items:
            $ref: '#/components/schemas/ClientOperationDrilldownRecord'
    QueryOptimizationRecommendationsListData:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/clients/{client_name}/query-optimizations
      properties:
        client_name:
          type: string
        status:
          type: string
          description: Echo of the applied filter (`all`, `open`, `applied`, or `dismissed`).
        limit:
          type: integer
        offset:
          type: integer
        recommendations:
          type: array
          items:
            type: object
            additionalProperties: true
    QueryOptimizationRefreshData:
      type: object
      description: Returned in the success envelope `data` field from POST /admin/clients/{client_name}/query-optimizations/refresh
      properties:
        client_name:
          type: string
        run:
          type: object
          additionalProperties: true
        recommendations:
          type: array
          items:
            type: object
            additionalProperties: true
    QueryOptimizationRunsListData:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/clients/{client_name}/query-optimizations/runs
      properties:
        client_name:
          type: string
        limit:
          type: integer
        offset:
          type: integer
        runs:
          type: array
          items:
            type: object
            additionalProperties: true
    ApplyQueryOptimizationRequest:
      type: object
      properties:
        actor:
          type: string
          description: Optional actor label recorded with the apply action.
    QueryOptimizationApplyData:
      type: object
      description: Returned in the success envelope `data` field from POST /admin/clients/{client_name}/query-optimizations/{recommendation_id}/apply
      properties:
        client_name:
          type: string
        result:
          type: object
          additionalProperties: true
    AdmissionEventRecord:
      type: object
      properties:
        id:
          type: integer
          format: int64
        created_at:
          type: string
          format: date-time
        event_id:
          type: string
        method:
          type: string
        route:
          type: string
        client_name:
          type: string
          nullable: true
        request_bytes:
          type: integer
          format: int64
          nullable: true
        defer_requested:
          type: boolean
        is_query_route:
          type: boolean
        decision:
          type: string
        reason:
          type: string
          nullable: true
        deferred_request_id:
          type: string
          nullable: true
        retry_after_seconds:
          type: integer
          nullable: true
        window_seconds:
          type: integer
          nullable: true
        global_limit_per_window:
          type: integer
          format: int64
          nullable: true
        per_client_limit_per_window:
          type: integer
          format: int64
          nullable: true
        meta:
          type: object
          additionalProperties: true
    AdmissionEventsListData:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/admission-events
      properties:
        decision:
          type: string
          nullable: true
        client:
          type: string
          nullable: true
        limit:
          type: integer
        offset:
          type: integer
        events:
          type: array
          items:
            $ref: '#/components/schemas/AdmissionEventRecord'
    VacuumHealthSnapshotRecord:
      type: object
      properties:
        id:
          type: integer
          format: int64
        recorded_at:
          type: string
          format: date-time
        client_name:
          type: string
        host:
          type: string
          nullable: true
        instance_id:
          type: string
          format: uuid
          nullable: true
        total_dead_rows:
          type: integer
          format: int64
        tables_with_bloat:
          type: integer
        xid_freeze_risk:
          type: integer
        tables_needing_vacuum:
          type: integer
        freeze_max_age:
          type: integer
          format: int64
          nullable: true
        collection_error:
          type: string
          nullable: true
    VacuumHealthTableStatRecord:
      type: object
      properties:
        id:
          type: integer
          format: int64
        snapshot_id:
          type: integer
          format: int64
        schemaname:
          type: string
        relname:
          type: string
        n_dead_tup:
          type: integer
          format: int64
        n_live_tup:
          type: integer
          format: int64
        dead_pct:
          type: number
          nullable: true
        last_vacuum:
          type: string
          format: date-time
          nullable: true
        last_autovacuum:
          type: string
          format: date-time
          nullable: true
        xid_age:
          type: integer
          format: int64
          nullable: true
        xid_age_pct_of_freeze_max:
          type: number
          nullable: true
    VacuumHealthListResponse:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/vacuum-health
      properties:
        snapshots:
          type: array
          items:
            $ref: '#/components/schemas/VacuumHealthSnapshotRecord'
    VacuumHealthDetailResponse:
      type: object
      description: Returned in the success envelope `data` field from GET /admin/vacuum-health/{client_name}
      properties:
        snapshot:
          $ref: '#/components/schemas/VacuumHealthSnapshotRecord'
        tables:
          type: array
          items:
            $ref: '#/components/schemas/VacuumHealthTableStatRecord'
    ManagementColumnRequest:
      type: object
      properties:
        name:
          type: string
        data_type:
          type: string
        nullable:
          type: boolean
          default: true
        default_expression:
          type: string
          nullable: true
      required:
      - name
      - data_type
    CreateTableRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
        table_name:
          type: string
        columns:
          type: array
          items:
            $ref: '#/components/schemas/ManagementColumnRequest'
        if_not_exists:
          type: boolean
          description: Accepted for compatibility but ignored; Athena still returns 409 when the table exists.
      required:
      - table_name
    EditTableRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
        operations:
          type: array
          items:
            oneOf:
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - add_column
                column:
                  $ref: '#/components/schemas/ManagementColumnRequest'
              required:
              - type
              - column
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - rename_column
                from:
                  type: string
                to:
                  type: string
              required:
              - type
              - from
              - to
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - set_default
                column_name:
                  type: string
                default_expression:
                  type: string
              required:
              - type
              - column_name
              - default_expression
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - drop_default
                column_name:
                  type: string
              required:
              - type
              - column_name
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - set_not_null
                column_name:
                  type: string
              required:
              - type
              - column_name
            - type: object
              properties:
                type:
                  type: string
                  enum:
                  - drop_not_null
                column_name:
                  type: string
              required:
              - type
              - column_name
      required:
      - operations
    DropTableRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
        cascade:
          type: boolean
          default: false
    DropColumnRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
        cascade:
          type: boolean
          default: false
    CreateIndexRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
        table_name:
          type: string
        index_name:
          type: string
          nullable: true
        columns:
          type: array
          items:
            type: string
        unique:
          type: boolean
          default: false
        method:
          type: string
          default: btree
      required:
      - table_name
      - columns
    DropIndexRequest:
      type: object
      properties:
        schema_name:
          type: string
          default: public
    ManagementCapabilities:
      type: object
      properties:
        backend:
          type: string
        retention_mutation_supported:
          type: boolean
        replication_mutation_supported:
          type: boolean
        supported_operations:
          type: array
          items:
            type: string
        required_rights:
          type: array
          items:
            type: object
            properties:
              operation:
                type: string
              required_right:
                type: string
        allowed_index_methods:
          type: array
          items:
            type: string
        allowed_column_data_types:
          type: array
          items:
            type: string
    ManagementCapabilitiesEnvelope:
      type: object
      properties:
        status:
          type: string
        message:
          type: string
        data:
          $ref: '#/components/schemas/ManagementCapabilities'
    ClusterMirrorHealth:
      type: object
      properties:
        url:
          type: string
          format: uri
        status:
          type: string
          enum:
          - online
          - offline
        latency_ms:
          type: number
          nullable: true
        download_bytes_per_sec:
          type: number
          nullable: true
        cargo_toml_version:
          type: string
          nullable: true
        message:
          type: string
          nullable: true
    ClusterHealthResponse:
      type: object
      properties:
        message:
          type: string
        version:
          type: string
        athena_api:
          type: string
        athena_deadpool:
          type: string
        athena_scylladb:
          type: string
        cargo_toml_version:
          type: string
        mirrors:
          type: array
          items:
            $ref: '#/components/schemas/ClusterMirrorHealth'
    CreateBackupRequest:
      type: object
      properties:
        client_name:
          type: string
          description: Logical client whose Postgres URI will be dumped.
        label:
          type: string
          nullable: true
          description: Optional human-readable label stored in S3 object metadata.
      required:
      - client_name
    RestoreBackupRequest:
      type: object
      properties:
        client_name:
          type: string
          description: Logical client whose Postgres URI is the restore target.
      required:
      - client_name
    BackupObject:
      type: object
      properties:
        id:
          type: string
          description: Backup identifier derived from the S3 object key.
        key:
          type: string
          description: Full S3 object key.
        client_name:
          type: string
        label:
          type: string
          nullable: true
        size_bytes:
          type: integer
          format: int64
        created_at:
          type: string
          description: Timestamp string from S3 metadata.
    ProvisionRequest:
      type: object
      properties:
        uri:
          type: string
          format: uri
          description: Direct Postgres URI to provision (mutually exclusive with client_name).
        client_name:
          type: string
          description: Registered client whose URI will be used (mutually exclusive with uri).
        register:
          type: boolean
          default: false
          description: When true and uri is provided, register the client in the runtime catalog.
        register_name:
          type: string
          nullable: true
          description: Optional client name when registering (defaults to "provisioned").
    ProvisionStatusResponse:
      type: object
      properties:
        client_name:
          type: string
        provisioned:
          type: boolean
        present_tables:
          type: array
          items:
            type: string
        missing_tables:
          type: array
          items:
            type: string
    LocalClusterCreateDatabaseRequest:
      type: object
      properties:
        client_name:
          type: string
          nullable: true
          description: Required for admin API. Ignored in management API (caller `X-Athena-Client` is used).
        database_name:
          type: string
        owner:
          type: string
          nullable: true
        template:
          type: string
          nullable: true
        encoding:
          type: string
          nullable: true
        lc_collate:
          type: string
          nullable: true
        lc_ctype:
          type: string
          nullable: true
        tablespace:
          type: string
          nullable: true
        register_name:
          type: string
          nullable: true
          description: Optional Athena client name when registering. Defaults to database_name.
        description:
          type: string
          nullable: true
        provision_schema:
          type: boolean
          default: true
        register_runtime:
          type: boolean
          default: true
        register_catalog:
          type: boolean
          default: true
      required:
      - database_name
    LocalClusterDatabasesResponse:
      type: object
      properties:
        client_name:
          type: string
        all_databases:
          type: array
          items:
            type: string
        athena_managed_databases:
          type: array
          items:
            type: object
            properties:
              client_name:
                type: string
              database_name:
                type: string
              source:
                type: string
              is_active:
                type: boolean
              is_frozen:
                type: boolean
    LocalProvisionDependencyStatus:
      type: object
      properties:
        os_family:
          type: string
        package_manager:
          type: string
          nullable: true
        running_as_root:
          type: boolean
        sudo_available:
          type: boolean
        sudo_non_interactive_available:
          type: boolean
        systemctl_available:
          type: boolean
        can_attempt_install:
          type: boolean
        docker_binary_available:
          type: boolean
        docker_service_active:
          type: boolean
          nullable: true
        postgres_binary_available:
          type: boolean
        postgres_service_active:
          type: boolean
          nullable: true
        missing:
          type: array
          items:
            type: string
        notes:
          type: array
          items:
            type: string
    LocalProvisionDependencyInstallStep:
      type: object
      properties:
        key:
          type: string
        status:
          type: string
        description:
          type: string
        command:
          type: string
          nullable: true
        detail:
          type: string
          nullable: true
        output:
          type: string
          nullable: true
    InstallLocalProvisionDependenciesRequest:
      type: object
      properties:
        install_docker:
          type: boolean
          default: true
        install_postgres:
          type: boolean
          default: true
        start_services:
          type: boolean
          default: true
        use_sudo:
          type: boolean
          default: true
    LocalProvisionDependencyInstallResult:
      type: object
      properties:
        requested:
          type: object
          additionalProperties: true
        before:
          $ref: '#/components/schemas/LocalProvisionDependencyStatus'
        after:
          $ref: '#/components/schemas/LocalProvisionDependencyStatus'
        overall_status:
          type: string
        steps:
          type: array
          items:
            $ref: '#/components/schemas/LocalProvisionDependencyInstallStep'
    DockerManagedContainer:
      type: object
      properties:
        container_name:
          type: string
        running:
          type: boolean
        status:
          type: string
          nullable: true
        image:
          type: string
          nullable: true
        host_port:
          type: integer
          format: int32
          nullable: true
        labels:
          type: object
          additionalProperties:
            type: string
    ListPostgresInstancesResponse:
      type: object
      properties:
        instances:
          type: array
          items:
            type: object
            allOf:
            - $ref: '#/components/schemas/DockerManagedContainer'
            - type: object
              properties:
                linked_client:
                  type: object
                  nullable: true
                  properties:
                    client_name:
                      type: string
                    runtime_registered:
                      type: boolean
                    runtime_known:
                      type: boolean
                    catalog_registered:
                      type: boolean
                runtime_client:
                  type: object
                  nullable: true
                  additionalProperties: true
                catalog_client:
                  type: object
                  nullable: true
                  additionalProperties: true
        count:
          type: integer
          format: int32
        dependencies:
          $ref: '#/components/schemas/LocalProvisionDependencyStatus'
        instance_runtime_available:
          type: boolean
        instance_runtime_error:
          type: string
          nullable: true
    SpinUpInstanceRequest:
      type: object
      properties:
        client_name:
          type: string
        container_name:
          type: string
          nullable: true
        image:
          type: string
          nullable: true
        host:
          type: string
          nullable: true
        host_port:
          type: integer
          format: int32
          nullable: true
        db_name:
          type: string
          nullable: true
        username:
          type: string
          nullable: true
        password:
          type: string
          nullable: true
        startup_timeout_secs:
          type: integer
          format: int64
          nullable: true
        reuse_existing:
          type: boolean
          default: false
        provision_schema:
          type: boolean
          default: true
        register_runtime:
          type: boolean
          default: true
        register_catalog:
          type: boolean
          default: true
      required:
      - client_name
    DeleteInstanceRequest:
      type: object
      properties:
        client_name:
          type: string
          nullable: true
        remove_runtime_client:
          type: boolean
          default: true
        remove_catalog_client:
          type: boolean
          default: true
    NeonProviderProvisionRequest:
      type: object
      properties:
        client_name:
          type: string
        description:
          type: string
          nullable: true
        connection_uri:
          type: string
          nullable: true
        api_key:
          type: string
          nullable: true
        project_id:
          type: string
          nullable: true
        branch_id:
          type: string
          nullable: true
        database_name:
          type: string
          nullable: true
        role_name:
          type: string
          nullable: true
        endpoint_id:
          type: string
          nullable: true
        api_base_url:
          type: string
          nullable: true
        create_project_if_missing:
          type: boolean
          default: false
        project_name:
          type: string
          nullable: true
        project_create_payload:
          type: object
          nullable: true
        provision_schema:
          type: boolean
          default: true
        register_runtime:
          type: boolean
          default: true
        register_catalog:
          type: boolean
          default: true
      required:
      - client_name
    RailwayProviderProvisionRequest:
      type: object
      properties:
        client_name:
          type: string
        description:
          type: string
          nullable: true
        connection_uri:
          type: string
          nullable: true
        api_key:
          type: string
          nullable: true
        project_id:
          type: string
          nullable: true
        environment_id:
          type: string
          nullable: true
        service_id:
          type: string
          nullable: true
        plugin_id:
          type: string
          nullable: true
        graphql_url:
          type: string
          nullable: true
        create_project_if_missing:
          type: boolean
          default: false
        project_name:
          type: string
          nullable: true
        project_create_input:
          type: object
          nullable: true
        create_service_if_missing:
          type: boolean
          default: false
        service_name:
          type: string
          nullable: true
        service_create_input:
          type: object
          nullable: true
        create_plugin_if_missing:
          type: boolean
          default: false
        plugin_name:
          type: string
          nullable: true
        plugin_create_input:
          type: object
          nullable: true
        provision_schema:
          type: boolean
          default: true
        register_runtime:
          type: boolean
          default: true
        register_catalog:
          type: boolean
          default: true
      required:
      - client_name
    RenderProviderProvisionRequest:
      type: object
      properties:
        client_name:
          type: string
        description:
          type: string
          nullable: true
        connection_uri:
          type: string
          nullable: true
        api_key:
          type: string
          nullable: true
        service_id:
          type: string
          nullable: true
        owner_id:
          type: string
          nullable: true
        api_base_url:
          type: string
          nullable: true
        create_service_if_missing:
          type: boolean
          default: false
        service_name:
          type: string
          nullable: true
        service_create_payload:
          type: object
          nullable: true
        plan:
          type: string
          nullable: true
        region:
          type: string
          nullable: true
        postgres_version:
          type: string
          nullable: true
        disk_size_gb:
          type: integer
          format: int32
          nullable: true
        provision_schema:
          type: boolean
          default: true
        register_runtime:
          type: boolean
          default: true
        register_catalog:
          type: boolean
          default: true
      required:
      - client_name
    GatewaySqlExecutionRequest:
      type: object
      description: Driver-agnostic SQL execution payload used by `/query/sql` and `/gateway/sql`.
      required:
      - query
      - driver
      - db_name
      properties:
        query:
          type: string
        driver:
          type: string
          enum:
          - athena
          - postgresql
          - supabase
        db_name:
          type: string
    GatewayRpcFilterOperator:
      type: string
      enum:
      - eq
      - neq
      - gt
      - gte
      - lt
      - lte
      - in
      - like
      - ilike
      - is
      description: Filter operator for RPC post-processing (JSON may use alias `i_like` for ilike)
    GatewayRpcFilter:
      type: object
      required:
      - column
      - operator
      properties:
        column:
          type: string
        operator:
          $ref: '#/components/schemas/GatewayRpcFilterOperator'
        value:
          description: JSON value compared against the column (default null)
          nullable: true
    GatewayRpcOrder:
      type: object
      required:
      - column
      properties:
        column:
          type: string
        ascending:
          type: boolean
          default: true
    GatewayRpcRequest:
      type: object
      description: Structured RPC invocation payload for Postgres function execution and optional row shaping.
      required:
      - function
      properties:
        function:
          type: string
          description: Function name (or `schema.function` when schema is `public`)
        function_name:
          type: string
          description: Alias of `function` accepted in JSON
        schema:
          type: string
          default: public
        args:
          type: object
          additionalProperties: true
        select:
          type: string
          description: Comma-separated columns for SELECT over RPC result
        filters:
          type: array
          items:
            $ref: '#/components/schemas/GatewayRpcFilter'
        count:
          type: string
          description: Set to `exact` to include an exact row count
        limit:
          type: integer
          format: int64
        offset:
          type: integer
          format: int64
        order:
          $ref: '#/components/schemas/GatewayRpcOrder'
    UpsertFunctionRequest:
      type: object
      required:
      - ddl
      properties:
        ddl:
          type: string
          description: CREATE OR REPLACE FUNCTION ... DDL validated server-side
    DropFunctionRequest:
      type: object
      required:
      - function_name
      - arg_types
      properties:
        schema_name:
          type: string
          default: public
        function_name:
          type: string
        arg_types:
          type: array
          items:
            type: string
          description: Postgres type names for the function signature, e.g. `["text","integer"]`
    InstanceLifecycleRequest:
      type: object
      properties:
        client_name:
          type: string
          nullable: true
          description: Optional client linked to the container for runtime sync
        sync_runtime:
          type: boolean
          default: true
          description: When true, reconnect or mark unavailable after start/stop
    BindInstancePortRouteRequest:
      type: object
      required:
      - route_key
      properties:
        route_key:
          type: string
        client_name:
          type: string
          nullable: true
        public_host:
          type: string
          nullable: true
        public_port:
          type: integer
          format: int32
          nullable: true
        persist_in_catalog:
          type: boolean
          default: true
    StorageS3Connection:
      type: object
      required:
      - endpoint
      - region
      - access_key_id
      - secret_key
      properties:
        endpoint:
          type: string
          description: S3-compatible endpoint URL or host
        region:
          type: string
        access_key_id:
          type: string
        secret_key:
          type: string
    StorageS3Credentials:
      allOf:
      - $ref: '#/components/schemas/StorageS3Connection'
      - type: object
        required:
        - bucket
        properties:
          bucket:
            type: string
    StorageListObjectsRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        properties:
          prefix:
            type: string
            nullable: true
          delimiter:
            type: string
            nullable: true
          continuation_token:
            type: string
            nullable: true
          max_keys:
            type: integer
            format: int32
            default: 200
    StorageGetObjectRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        required:
        - key
        properties:
          key:
            type: string
    StorageDeleteObjectRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        required:
        - key
        properties:
          key:
            type: string
    StorageCreateFolderRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        required:
        - prefix
        properties:
          prefix:
            type: string
    StoragePresignUploadRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        required:
        - key
        properties:
          key:
            type: string
          content_type:
            type: string
            nullable: true
    StorageBucketCorsRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
    StorageSetBucketCorsRequest:
      allOf:
      - $ref: '#/components/schemas/StorageS3Credentials'
      - type: object
        required:
        - rules
        properties:
          rules:
            type: array
            items:
              type: object
              required:
              - allowed_origins
              - allowed_methods
              properties:
                allowed_origins:
                  type: array
                  items:
                    type: string
                allowed_methods:
                  type: array
                  items:
                    type: string
                allowed_headers:
                  type: array
                  items:
                    type: string
                expose_headers:
                  type: array
                  items:
                    type: string
                max_age_seconds:
                  type: integer
                  format: int32
                  nullable: true
    TypesenseConnection:
      type: object
      required:
      - base_url
      - api_key
      properties:
        base_url:
          type: string
          description: Root Typesense node URL, for example https://typesense.example.com:8108
        api_key:
          type: string
        timeout_ms:
          type: integer
          format: int32
          nullable: true
    TypesenseSearchRequest:
      type: object
      required:
      - q
      properties:
        job_id:
          type: integer
          format: int64
          nullable: true
        source_client_name:
          type: string
          nullable: true
        source_schema_name:
          type: string
          nullable: true
        source_table_name:
          type: string
          nullable: true
        q:
          type: string
        query_by:
          type: array
          nullable: true
          items:
            type: string
        filter_by:
          type: string
          nullable: true
        sort_by:
          type: string
          nullable: true
        page:
          type: integer
          format: int32
          nullable: true
        per_page:
          type: integer
          format: int32
          nullable: true
