openapi: 3.0.3
info:
  title: Athena RS
  version: 2.5.0
  description: |
    Athena gateway API and driver.
    Every operation returns at least one 2xx (success) and one 5xx (server error) response; 3xx (redirect) is optional where applicable.
servers:
- url: https://athena-db.com
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/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/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: database_audit_log is not available in the target database
  /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: database_audit_log is not available in the target database
    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: database_audit_log is not available in the target database
  /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: database_audit_log is not available in the target database
  /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: database_audit_log is not available in the target database
  /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: database_audit_log is not available in the target database
  /query/sql:
    post:
      summary: Execute SQL using selected driver
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                query:
                  type: string
                driver:
                  type: string
                  enum:
                  - athena
                  - postgresql
                  - supabase
                db_name:
                  type: string
              required:
              - query
              - driver
              - db_name
      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/fetch:
    post:
      summary: Fetch data with conditions
      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
              required:
              - conditions
      responses:
        '200':
          description: OK
        '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
              required:
              - conditions
      responses:
        '200':
          description: OK
        '400':
          description: Bad request
        '500':
          description: Internal error
  /data:
    get:
      summary: Fetch data via GET
      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: 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
      parameters:
      - in: header
        name: X-User-Id
        required: true
        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-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
      responses:
        '200':
          description: Inserted
        '400':
          description: Bad request
        '409':
          description: Conflict
        '500':
          description: Internal error
  /gateway/delete:
    delete:
      summary: Delete a row by id
      parameters:
      - in: header
        name: X-User-Id
        required: true
        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
      responses:
        '200':
          description: Deleted
        '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)
      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
      responses:
        '200':
          description: OK
        '400':
          description: Bad request
        '500':
          description: Internal error
  /gateway/query:
    post:
      summary: Run a raw SQL query against the selected PostgreSQL pool
      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
      responses:
        '200':
          description: OK
        '400':
          description: Bad request
        '500':
          description: Internal error
  /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 optional prebuilt reference and overrides. Uses X-Athena-Client for routing (same as gateway).
      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: Prebuilt pipeline name from config/pipelines.yaml
                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
                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
                sink:
                  type: object
                  properties:
                    table_name:
                      type: string
      responses:
        '200':
          description: Pipeline run result
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    description: Inserted rows
                  rows_fetched:
                    type: integer
                  rows_inserted:
                    type: integer
                  errors:
                    type: array
        '400':
          description: Bad request (e.g. missing X-Athena-Client or unknown pipeline)
        '500':
          description: Internal error
  /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
      parameters:
      - $ref: '#/components/parameters/AdminApiKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApiKeyRequest'
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateApiKeyResponse'
        '409':
          description: Duplicate public_id
        '500':
          $ref: '#/components/responses/DefaultServerError5xx'
  /admin/api-keys/{id}:
    patch:
      summary: Update an existing API key
      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
      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/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
      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
      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
      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-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
      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
      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
      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
      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
      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/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/{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
  /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
  /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'
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
  schemas:
    ApiKeyRecord:
      type: object
      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
      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
      required:
      - name
    UpdateApiKeyRequest:
      type: object
      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
      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
      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
      properties:
        clients:
          type: array
          items:
            type: string
    AthenaClientRecord:
      type: object
      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
      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
      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
      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'
    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
