openapi: 3.1.0
info:
  title: openmine-payments API
  version: 0.0.0
  description: Stablecoin payment processor
  contact:
    name: Open Mine
    url: https://openmine.io
servers:
  - url: https://pay.openmine.io/api/v1
    description: Production
security:
  - bearerAuth: []
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'API key issued via the admin UI. Bearer prefix required: `Authorization: Bearer npay_...`'
  schemas:
    Invoice:
      type:
        - object
      properties:
        id:
          type:
            - string
          minLength: 1
        status:
          type:
            - string
          enum:
            - pending
            - confirming
            - confirmed
            - expired
            - refunded
            - manually_paid
        amount_usd:
          type:
            - string
        client_external_id:
          type:
            - string
          minLength: 1
        purpose:
          type:
            - string
          minLength: 1
        addresses:
          type:
            - array
          items:
            type:
              - object
            properties:
              chain:
                type:
                  - string
                enum:
                  - eth
                  - bsc
                  - tron
                  - polygon
                  - avax
              address:
                type:
                  - string
                minLength: 1
              derivation_path:
                type:
                  - string
                minLength: 1
              assets:
                type:
                  - array
                items:
                  type:
                    - object
                  properties:
                    asset:
                      type:
                        - string
                      enum:
                        - USDT
                        - USDC
                    token_contract_address:
                      type:
                        - string
                      minLength: 1
                    token_decimals:
                      type:
                        - integer
                      minimum: 0
                  required:
                    - asset
                    - token_contract_address
                    - token_decimals
                minItems: 1
            required:
              - chain
              - address
              - derivation_path
              - assets
        transactions:
          type:
            - array
          items:
            type:
              - object
            properties:
              chain:
                type:
                  - string
                enum:
                  - eth
                  - bsc
                  - tron
                  - polygon
                  - avax
              asset:
                type:
                  - string
                enum:
                  - USDT
                  - USDC
              address:
                type:
                  - string
                minLength: 1
              tx_hash:
                type:
                  - string
                minLength: 1
              log_index:
                type:
                  - integer
              from_address:
                type:
                  - string
                minLength: 1
              amount_raw:
                type:
                  - string
              amount_normalized:
                type:
                  - string
              block_number:
                type:
                  - integer
                minimum: 0
              block_hash:
                type:
                  - string
                minLength: 1
              confirmations:
                type:
                  - integer
                minimum: 0
              status:
                type:
                  - string
                enum:
                  - seen
                  - confirmed
                  - reorged
              asset_mismatch:
                type:
                  - boolean
              selected_asset:
                type:
                  - string
                  - 'null'
                enum:
                  - USDT
                  - USDC
              first_seen_at:
                type:
                  - string
              last_seen_at:
                type:
                  - string
            required:
              - chain
              - asset
              - address
              - tx_hash
              - log_index
              - from_address
              - amount_raw
              - amount_normalized
              - block_number
              - block_hash
              - confirmations
              - status
              - asset_mismatch
              - selected_asset
              - first_seen_at
              - last_seen_at
        expires_at:
          type:
            - string
        created_at:
          type:
            - string
        confirmed_at:
          type:
            - string
            - 'null'
        webhook_url:
          type:
            - string
            - 'null'
        success_url:
          type:
            - string
            - 'null'
        cancel_url:
          type:
            - string
            - 'null'
        test:
          type:
            - boolean
        amount_received:
          type:
            - string
        overpaid:
          type:
            - boolean
        underpaid:
          type:
            - boolean
        checkout_url:
          type:
            - string
      required:
        - id
        - status
        - amount_usd
        - client_external_id
        - purpose
        - addresses
        - transactions
        - expires_at
        - created_at
        - confirmed_at
        - webhook_url
        - success_url
        - cancel_url
        - test
        - amount_received
        - overpaid
        - underpaid
        - checkout_url
    CreateInvoiceRequest:
      type:
        - object
      properties:
        amount_usd:
          type:
            - string
          pattern: ^\d+(\.\d{1,2})?$
        client_external_id:
          type:
            - string
          minLength: 1
          maxLength: 200
        purpose:
          type:
            - string
          minLength: 1
          maxLength: 200
        chains:
          type:
            - array
          items:
            type:
              - string
            enum:
              - eth
              - bsc
              - tron
              - polygon
              - avax
          minItems: 1
        assets:
          type:
            - array
          items:
            type:
              - string
            enum:
              - USDT
              - USDC
          minItems: 1
        chain:
          type:
            - string
          enum:
            - eth
            - bsc
            - tron
            - polygon
            - avax
        asset:
          type:
            - string
          enum:
            - USDT
            - USDC
        expires_in_seconds:
          default: 1800
          type:
            - integer
          minimum: 60
          maximum: 3600
        webhook_url:
          type:
            - string
          format: uri
        success_url:
          type:
            - string
          format: uri
        cancel_url:
          type:
            - string
          format: uri
        test:
          type:
            - boolean
      required:
        - amount_usd
        - client_external_id
        - purpose
    CreateInvoiceResponse:
      type:
        - object
      properties:
        id:
          type:
            - string
          minLength: 1
        status:
          type:
            - string
          enum:
            - pending
            - confirming
            - confirmed
            - expired
            - refunded
            - manually_paid
        amount_usd:
          type:
            - string
        client_external_id:
          type:
            - string
          minLength: 1
        purpose:
          type:
            - string
          minLength: 1
        addresses:
          type:
            - array
          items:
            type:
              - object
            properties:
              chain:
                type:
                  - string
                enum:
                  - eth
                  - bsc
                  - tron
                  - polygon
                  - avax
              address:
                type:
                  - string
                minLength: 1
              derivation_path:
                type:
                  - string
                minLength: 1
              assets:
                type:
                  - array
                items:
                  type:
                    - object
                  properties:
                    asset:
                      type:
                        - string
                      enum:
                        - USDT
                        - USDC
                    token_contract_address:
                      type:
                        - string
                      minLength: 1
                    token_decimals:
                      type:
                        - integer
                      minimum: 0
                  required:
                    - asset
                    - token_contract_address
                    - token_decimals
                minItems: 1
            required:
              - chain
              - address
              - derivation_path
              - assets
        transactions:
          type:
            - array
          items:
            type:
              - object
            properties:
              chain:
                type:
                  - string
                enum:
                  - eth
                  - bsc
                  - tron
                  - polygon
                  - avax
              asset:
                type:
                  - string
                enum:
                  - USDT
                  - USDC
              address:
                type:
                  - string
                minLength: 1
              tx_hash:
                type:
                  - string
                minLength: 1
              log_index:
                type:
                  - integer
              from_address:
                type:
                  - string
                minLength: 1
              amount_raw:
                type:
                  - string
              amount_normalized:
                type:
                  - string
              block_number:
                type:
                  - integer
                minimum: 0
              block_hash:
                type:
                  - string
                minLength: 1
              confirmations:
                type:
                  - integer
                minimum: 0
              status:
                type:
                  - string
                enum:
                  - seen
                  - confirmed
                  - reorged
              asset_mismatch:
                type:
                  - boolean
              selected_asset:
                type:
                  - string
                  - 'null'
                enum:
                  - USDT
                  - USDC
              first_seen_at:
                type:
                  - string
              last_seen_at:
                type:
                  - string
            required:
              - chain
              - asset
              - address
              - tx_hash
              - log_index
              - from_address
              - amount_raw
              - amount_normalized
              - block_number
              - block_hash
              - confirmations
              - status
              - asset_mismatch
              - selected_asset
              - first_seen_at
              - last_seen_at
        expires_at:
          type:
            - string
        created_at:
          type:
            - string
        confirmed_at:
          type:
            - string
            - 'null'
        webhook_url:
          type:
            - string
            - 'null'
        success_url:
          type:
            - string
            - 'null'
        cancel_url:
          type:
            - string
            - 'null'
        test:
          type:
            - boolean
        amount_received:
          type:
            - string
        overpaid:
          type:
            - boolean
        underpaid:
          type:
            - boolean
        checkout_url:
          type:
            - string
        networks:
          type:
            - array
          items:
            type:
              - object
            properties:
              chain:
                type:
                  - string
                enum:
                  - eth
                  - bsc
                  - tron
                  - polygon
                  - avax
              label:
                type:
                  - string
                minLength: 1
              standard:
                type:
                  - string
                minLength: 1
              assets:
                type:
                  - array
                items:
                  type:
                    - string
                  enum:
                    - USDT
                    - USDC
                minItems: 1
            required:
              - chain
              - label
              - standard
              - assets
        payment_method:
          type:
            - object
          properties:
            chain:
              type:
                - string
              enum:
                - eth
                - bsc
                - tron
                - polygon
                - avax
            asset:
              type:
                - string
              enum:
                - USDT
                - USDC
            address:
              type:
                - string
              minLength: 1
            amount:
              type:
                - string
            token_contract_address:
              type:
                - string
              minLength: 1
            token_decimals:
              type:
                - integer
              minimum: 0
            derivation_path:
              type:
                - string
              minLength: 1
          required:
            - chain
            - asset
            - address
            - amount
            - token_contract_address
            - token_decimals
            - derivation_path
      required:
        - id
        - status
        - amount_usd
        - client_external_id
        - purpose
        - addresses
        - transactions
        - expires_at
        - created_at
        - confirmed_at
        - webhook_url
        - success_url
        - cancel_url
        - test
        - amount_received
        - overpaid
        - underpaid
        - checkout_url
        - networks
    Network:
      type:
        - object
      properties:
        chain:
          type:
            - string
          enum:
            - eth
            - bsc
            - tron
            - polygon
            - avax
        label:
          type:
            - string
          minLength: 1
        standard:
          type:
            - string
          minLength: 1
        assets:
          type:
            - array
          items:
            type:
              - string
            enum:
              - USDT
              - USDC
          minItems: 1
      required:
        - chain
        - label
        - standard
        - assets
    SelectPaymentMethodRequest:
      type:
        - object
      properties:
        chain:
          type:
            - string
          enum:
            - eth
            - bsc
            - tron
            - polygon
            - avax
        asset:
          type:
            - string
          enum:
            - USDT
            - USDC
      required:
        - chain
        - asset
    PaymentMethodResponse:
      type:
        - object
      properties:
        chain:
          type:
            - string
          enum:
            - eth
            - bsc
            - tron
            - polygon
            - avax
        asset:
          type:
            - string
          enum:
            - USDT
            - USDC
        address:
          type:
            - string
          minLength: 1
        amount:
          type:
            - string
        token_contract_address:
          type:
            - string
          minLength: 1
        token_decimals:
          type:
            - integer
          minimum: 0
        derivation_path:
          type:
            - string
          minLength: 1
      required:
        - chain
        - asset
        - address
        - amount
        - token_contract_address
        - token_decimals
        - derivation_path
    TestAdvanceRequest:
      type:
        - object
      properties:
        amount:
          default: exact
          type:
            - string
          enum:
            - exact
            - under
            - over
        advance_to:
          default: confirmed
          type:
            - string
          enum:
            - confirming
            - confirmed
    ErrorResponse:
      type:
        - object
      properties:
        error:
          type:
            - object
          properties:
            code:
              type:
                - string
              enum:
                - INVALID_REQUEST
                - UNAUTHENTICATED
                - FORBIDDEN
                - NOT_FOUND
                - CONFLICT
                - TOO_MANY_REQUESTS
                - INTERNAL
            message:
              type:
                - string
            details: {}
          required:
            - code
            - message
      required:
        - error
    HealthResponse:
      type:
        - object
      properties:
        ok:
          type:
            - boolean
        version:
          type:
            - string
        chains:
          type:
            - object
          properties:
            eth:
              type:
                - object
              properties:
                enabled:
                  type:
                    - boolean
                last_tip_at:
                  type:
                    - string
                    - 'null'
                last_tick_at:
                  type:
                    - string
                    - 'null'
                last_block_number:
                  type:
                    - integer
                    - 'null'
                  minimum: 0
                listener_healthy:
                  type:
                    - boolean
                    - 'null'
              required:
                - enabled
                - last_tip_at
                - last_tick_at
                - last_block_number
                - listener_healthy
            bsc:
              type:
                - object
              properties:
                enabled:
                  type:
                    - boolean
                last_tip_at:
                  type:
                    - string
                    - 'null'
                last_tick_at:
                  type:
                    - string
                    - 'null'
                last_block_number:
                  type:
                    - integer
                    - 'null'
                  minimum: 0
                listener_healthy:
                  type:
                    - boolean
                    - 'null'
              required:
                - enabled
                - last_tip_at
                - last_tick_at
                - last_block_number
                - listener_healthy
            tron:
              type:
                - object
              properties:
                enabled:
                  type:
                    - boolean
                last_tip_at:
                  type:
                    - string
                    - 'null'
                last_tick_at:
                  type:
                    - string
                    - 'null'
                last_block_number:
                  type:
                    - integer
                    - 'null'
                  minimum: 0
                listener_healthy:
                  type:
                    - boolean
                    - 'null'
              required:
                - enabled
                - last_tip_at
                - last_tick_at
                - last_block_number
                - listener_healthy
            polygon:
              type:
                - object
              properties:
                enabled:
                  type:
                    - boolean
                last_tip_at:
                  type:
                    - string
                    - 'null'
                last_tick_at:
                  type:
                    - string
                    - 'null'
                last_block_number:
                  type:
                    - integer
                    - 'null'
                  minimum: 0
                listener_healthy:
                  type:
                    - boolean
                    - 'null'
              required:
                - enabled
                - last_tip_at
                - last_tick_at
                - last_block_number
                - listener_healthy
            avax:
              type:
                - object
              properties:
                enabled:
                  type:
                    - boolean
                last_tip_at:
                  type:
                    - string
                    - 'null'
                last_tick_at:
                  type:
                    - string
                    - 'null'
                last_block_number:
                  type:
                    - integer
                    - 'null'
                  minimum: 0
                listener_healthy:
                  type:
                    - boolean
                    - 'null'
              required:
                - enabled
                - last_tip_at
                - last_tick_at
                - last_block_number
                - listener_healthy
          required:
            - eth
            - bsc
            - tron
            - polygon
            - avax
      required:
        - ok
        - version
        - chains
paths:
  /health:
    get:
      tags:
        - service
      summary: Service health
      description: >-
        Public health endpoint. Reports enabled-state and last observed cursor for each supported
        chain.
      security: []
      responses:
        '200':
          description: Service health snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
  /openapi:
    get:
      tags:
        - service
      summary: Serve this OpenAPI document
      description: Serves the committed `docs/api/openapi.yaml`.
      security: []
      responses:
        '200':
          description: OpenAPI YAML
          content:
            application/yaml:
              schema:
                type: string
  /invoices:
    post:
      tags:
        - invoices
      summary: Create an invoice
      description: >-
        Creates an invoice with status `pending` and returns it alongside the offered `networks[]`
        instrument list (chain → coins). Lazy per-chain derivation: NO deposit address is allocated
        up front (`addresses[]` is empty) — render a network→coin selector from `networks[]`, then
        call `POST /invoices/{id}/payment-method` to derive the address for the chosen coin.
        ONE-SHOT shortcut: include scalar `chain` + `asset` in the body to derive that address
        inline in the same call — it is returned as `payment_method`. Each payment-method / address
        entry carries `token_contract_address` + `token_decimals`: the service only detects
        transfers of that EXACT contract. This matters on Polygon and Avalanche, where wallets label
        both native USDC and bridged `USDC.e` simply as "USDC" — integrators must show the payer the
        precise `token_contract_address`, or a deposit in the wrong variant will go undetected and
        the funds will be stuck.
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Opaque client-chosen string. The service caches the response for 24h keyed by (api_key,
            Idempotency-Key). Same key + same body returns the cached response; same key + different
            body returns 409.
          schema:
            type: string
            maxLength: 200
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateInvoiceRequest'
      responses:
        '201':
          description: Invoice created (with offered `networks[]`; `payment_method` for a one-shot create)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateInvoiceResponse'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      tags:
        - invoices
      summary: List invoices
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - pending
              - confirming
              - confirmed
              - expired
              - refunded
              - manually_paid
        - name: client_external_id
          in: query
          schema:
            type: string
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
        - name: cursor
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Paginated list of invoices
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Invoice'
                  next_cursor:
                    type:
                      - string
                      - 'null'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /invoices/{id}:
    get:
      tags:
        - invoices
      summary: Fetch a single invoice
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invoice
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Invoice'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /invoices/{id}/refresh:
    post:
      tags:
        - invoices
      summary: Force a chain re-scan for this invoice (operator/debug)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Invoice after refresh request was enqueued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Invoice'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /invoices/{id}/payment-method:
    post:
      tags:
        - invoices
      summary: Select a payment method (derive the deposit address)
      description: >-
        Lazy per-chain derivation: the buyer (or integrator) picks a coin on a chain, and the
        service derives — or reuses — THE single deposit address for that (invoice, chain),
        returning it with the exact token contract/decimals and the amount to send (the invoice's
        `amount_usd`, stablecoins pegged 1:1). Idempotent per chain: selecting a SECOND coin on the
        SAME chain returns the SAME address (the derivation index is not advanced again). Rejects
        with 400 when the `(chain, asset)` pair is not offered by the invoice, and 409 when the
        invoice is no longer `pending` (expired/confirmed → no new addresses).
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectPaymentMethodRequest'
      responses:
        '200':
          description: The derived (or reused) deposit address for the selected (chain, coin)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentMethodResponse'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /invoices/{id}/test/advance:
    post:
      tags:
        - invoices
      summary: Advance a test invoice through its lifecycle (sandbox)
      description: >-
        Sandbox test-mode control. Drives a TEST invoice (one created with `test: true`) through its
        lifecycle by simulating a deposit: it synthesises a transaction and runs the real state
        machine, so the same `invoice.confirming` / `invoice.confirmed` webhooks fire as for a real
        payment. The request body selects an exact / under / over payment and how far to advance
        (`confirming` or `confirmed`); an empty body simulates an exact, fully-confirmed payment.
        Returns 400 if the invoice is not a test invoice — this endpoint can never act on a real
        invoice.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestAdvanceRequest'
      responses:
        '200':
          description: Invoice after the simulated deposit was applied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Invoice'
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /admin/auth/config:
    get:
      tags:
        - admin
      summary: Public bootstrap/google-availability info
      description: Used by the admin UI to choose between bootstrap and Google sign-in.
      security: []
      responses:
        '200':
          description: Configuration flags
          content:
            application/json:
              schema:
                type: object
                required:
                  - bootstrap_available
                  - google_available
                properties:
                  bootstrap_available:
                    type: boolean
                  google_available:
                    type: boolean
  /admin/auth/bootstrap-login:
    post:
      tags:
        - admin
      summary: Bootstrap admin login (consumes the one-time password)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - password
              properties:
                password:
                  type: string
      responses:
        '200':
          description: Session created
        '400':
          description: Validation or business-rule failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Idempotency key reused with a different request body, or other conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /admin/auth/google/start:
    get:
      tags:
        - admin
      summary: Begin Google OAuth flow
      security: []
      responses:
        '302':
          description: Redirect to Google
        '503':
          description: Google not configured
  /admin/auth/google/callback:
    get:
      tags:
        - admin
      summary: Google OAuth callback
      security: []
      responses:
        '302':
          description: Redirect to admin UI
  /admin/auth/logout:
    post:
      tags:
        - admin
      summary: Destroy the admin session
      responses:
        '200':
          description: Logged out
  /admin/auth/me:
    get:
      tags:
        - admin
      summary: Echo the current admin session
      responses:
        '200':
          description: Current admin
        '401':
          description: Not signed in
