---
openapi: 3.0.3
info:
  title: APIX Registry API
  description: |
    The open autonomous agent service discovery registry.

    ## Agent Workflow

    1. `GET /` — Read the HATEOAS root to discover all entry points and this OpenAPI spec URL.
    2. `GET /services?capability=<term>` — Search for PRODUCTION services by capability keyword    (e.g. nlp, translation, speech-to-text, image-classification).
    3. Follow `openApiSpecUrl` or `mcpSpecUrl` in the returned service record to learn how to call it.

    ## Registering a Service

    POST /services with a BSM payload and an `X-Api-Key` header. The endpoint URL is the unique key — re-posting the same endpoint updates the existing record (UPSERT).

    ## Verification Levels (O-levels)

    Services start at UNVERIFIED. The registry progressively verifies registrant identity (O1 DNS), legal entity (O2 GLEIF/LEI), and technical hygiene (O3). Higher O-levels indicate greater trustworthiness. Filter replacement candidates by minimum O-level via `GET /services/{id}/replacements?minOLevel=LEGAL_ENTITY_VERIFIED`.
  contact:
    url: https://api-index.org
  version: "0.1"
security:
- ApiKey: []
paths:
  /:
    get:
      tags:
      - Index Resource
      parameters:
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IndexResponse"
            text/html:
              schema:
                $ref: "#/components/schemas/TemplateInstance"
  /admin/sandboxes:
    get:
      tags:
      - Admin Resource
      summary: List all sandboxes (admin)
      description: Requires X-Admin-Key.
      parameters:
      - name: page
        in: query
        schema:
          format: int32
          default: 0
          type: integer
      - name: size
        in: query
        schema:
          format: int32
          default: 50
          type: integer
      - name: tier
        in: query
        schema:
          type: string
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminSandboxListResponse"
  /admin/sandboxes/{uuid}:
    get:
      tags:
      - Admin Resource
      summary: Full dashboard view of any sandbox (admin)
      description: Requires X-Admin-Key.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxDashboardResponse"
    delete:
      tags:
      - Admin Resource
      summary: Delete a sandbox and all its services (admin)
      description: Requires X-Admin-Key. DEMO-tier sandboxes are protected.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
  /admin/sandboxes/{uuid}/tier:
    patch:
      tags:
      - Admin Resource
      summary: Promote sandbox tier (admin)
      description: Requires X-Admin-Key.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        "200":
          description: OK
  /admin/stats:
    get:
      tags:
      - Admin Resource
      summary: Global registry statistics (admin)
      description: Requires X-Admin-Key.
      parameters:
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AdminStatsResponse"
  /devices:
    get:
      tags:
      - Device Resource
      parameters:
      - name: capability
        in: query
        schema:
          type: string
      - name: deviceClass
        in: query
        schema:
          type: string
      - name: protocol
        in: query
        schema:
          type: string
      responses:
        "200":
          description: OK
  /devices/{id}:
    get:
      tags:
      - Device Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceResponse"
  /devices/{id}/replacements:
    get:
      tags:
      - Device Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: deviceClass
        in: query
        schema:
          type: string
      - name: minOLevel
        in: query
        schema:
          type: string
      - name: protocol
        in: query
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReplacementsResponse"
  /mail-signing-keys:
    get:
      tags:
      - Mail Signing Key Resource
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MailSigningKeyResponse"
  /organizations:
    post:
      tags:
      - Organization Resource
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgRegistrationRequest"
      responses:
        "200":
          description: OK
  /organizations/self-service:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: X-Internal-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgSelfServiceRequest"
      responses:
        "200":
          description: OK
  /organizations/{id}:
    get:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /organizations/{id}/audit-log:
    get:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      - name: X-Org-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/OrgAuditEventResponse"
  /organizations/{id}/confirm-key-rotation-dns:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Org-Rotation-Secret
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgKeyRotateResponse"
  /organizations/{id}/earned-level:
    patch:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /organizations/{id}/event-feed:
    get:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: since
        in: query
        schema:
          type: string
      - name: X-Api-Key
        in: header
        schema:
          type: string
      - name: X-Org-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgEventFeedResponse"
  /organizations/{id}/fraud-lock:
    delete:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /organizations/{id}/notify-key-rotation-fraud:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgFraudReportRequest"
      responses:
        "200":
          description: OK
  /organizations/{id}/request-tan:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgTanRequestBody"
      responses:
        "200":
          description: OK
  /organizations/{id}/request-upgrade:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Org-Api-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgUpgradeRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /organizations/{id}/rotate-key:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Org-Rotation-Secret
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
  /organizations/{id}/rotate-key-dns:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Org-Rotation-Secret
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
  /organizations/{id}/rotate-key-with-tan:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgTanRotateRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgKeyRotateResponse"
  /organizations/{id}/temp-grant:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrgTempGrantRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
    delete:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /organizations/{id}/verify:
    post:
      tags:
      - Organization Resource
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Org-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrgResponse"
  /sandbox/admin/{uuid}/tier:
    patch:
      tags:
      - Sandbox Resource
      summary: Promote sandbox tier (admin only)
      description: "Changes a sandbox tier, updating rate limits, caps, and expiry.\
        \ Requires X-Admin-Key matching the global registry admin key."
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Admin-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: string
      responses:
        "200":
          description: OK
  /sandbox/feedback-schema:
    get:
      tags:
      - Sandbox Resource
      summary: Agent experience feedback schema
      description: Returns the rated dimensions with question text and scale labels.
        Agents read this before submitting feedback. No authentication required.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackSchemaResponse"
  /sandbox/register:
    post:
      tags:
      - Sandbox Resource
      summary: Create a sandbox namespace
      description: "Creates an isolated test namespace. Returns the sandbox UUID and\
        \ an API key shown exactly once — store both immediately. The UUID is the\
        \ permanent resource identifier used in all subsequent requests. The name\
        \ is a human-readable label only; it does not need to be unique. Free tier:\
        \ 30 days lifetime, 60 req/min."
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SandboxRegistrationRequest"
      responses:
        "200":
          description: OK
  /sandbox/{uuid}:
    get:
      tags:
      - Sandbox Resource
      summary: Sandbox root — HATEOAS navigation
      description: Returns sandbox metadata and navigation links. No authentication
        required. The UUID is the permanent resource identifier.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxIndexResponse"
  /sandbox/{uuid}/api-key:
    patch:
      tags:
      - Sandbox Resource
      summary: Rotate the sandbox API key
      description: Invalidates the current API key and issues a new one. All agents
        must be updated with the new key. The new key is shown exactly once — store
        it immediately. Requires X-Maintenance-Key returned at registration.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Maintenance-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
      security:
      - SandboxMaintenanceKey: []
  /sandbox/{uuid}/dashboard:
    get:
      tags:
      - Sandbox Resource
      summary: Full sandbox dashboard — for the portal
      description: "Returns the complete sandbox view: tier caps, usage counters,\
        \ recent agent visits with geo-resolved coordinates. Intended for the portal\
        \ UI; no authentication required."
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxDashboardResponse"
  /sandbox/{uuid}/extend:
    patch:
      tags:
      - Sandbox Resource
      summary: Extend sandbox expiry
      description: Resets the expiry to now + tier lifetime (e.g. 30 days for FREE).
        Can be called on an already-expired sandbox to reactivate it. Requires X-Maintenance-Key
        returned at registration.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Maintenance-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
      security:
      - SandboxMaintenanceKey: []
  /sandbox/{uuid}/feedback:
    get:
      tags:
      - Sandbox Resource
      summary: Aggregated feedback results
      description: Returns average scores per dimension across all submissions for
        this sandbox. Requires X-Api-Key matching this sandbox's key.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FeedbackAggregateResponse"
      security:
      - SandboxApiKey: []
    post:
      tags:
      - Sandbox Resource
      summary: Submit agent experience feedback
      description: Records dimension scores (0–10) for this sandbox. No authentication
        required. Unknown dimension keys are ignored. At least one valid dimension
        key is required.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FeedbackSubmissionRequest"
      responses:
        "200":
          description: OK
  /sandbox/{uuid}/services:
    get:
      tags:
      - Sandbox Resource
      summary: List or search services in the sandbox
      description: "Returns services registered in this sandbox. Optionally filter\
        \ by ?capability=, ?stage=, and ?property=key:value. Multiple ?property= parameters\
        \ are ANDed together. No authentication required."
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: capability
        in: query
        schema:
          type: string
      - name: property
        in: query
        schema:
          type: array
          items:
            type: string
      - name: stage
        in: query
        schema:
          type: string
      - name: X-Forwarded-For
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/ServiceResponse"
    post:
      tags:
      - Sandbox Resource
      summary: Register a service in the sandbox
      description: Registers a test service. No KYC or O-level enforcement. Requires
        X-Api-Key matching this sandbox's key.
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Api-Key
        in: header
        schema:
          type: string
      - name: X-Forwarded-For
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BsmPayload"
      responses:
        "200":
          description: OK
      security:
      - SandboxApiKey: []
  /sandbox/{uuid}/telemetry:
    get:
      tags:
      - Sandbox Resource
      summary: Sandbox usage statistics
      description: "Returns cumulative request counts by event type, last activity\
        \ timestamp, and tier metadata. Requires X-Api-Key matching this sandbox's\
        \ key."
      parameters:
      - name: uuid
        in: path
        required: true
        schema:
          type: string
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SandboxTelemetryResponse"
      security:
      - SandboxApiKey: []
  /services:
    get:
      tags:
      - Service Resource
      summary: Search services by capability and optional extension properties
      description: "Returns services matching the given capability keyword. Omitting\
        \ ?stage= defaults to PRODUCTION only — this is the standard agent query.\
        \ When a valid sandbox API key is provided via X-Api-Key, results are scoped\
        \ to that sandbox and stage defaults to DEVELOPMENT. Use ?stage=DEVELOPMENT\
        \ or ?stage=BETA to discover pre-production services. Capability values are\
        \ lowercase kebab-case strings defined by the registrant (e.g. nlp, translation,\
        \ speech-to-text). The search matches exact capability tokens, not substrings.\
        \ Use ?property=key:value to filter by custom extension properties stored\
        \ in the service's 'extensions' object. Multiple ?property= parameters are\
        \ ANDed together. Example: ?capability=translation&property=region:eu&property=dataResidency:DE"
      parameters:
      - name: capability
        in: query
        description: "Capability keyword to search for (e.g. nlp, translation, speech-to-text).\
          \ Required."
        schema:
          type: string
        example: nlp
      - name: property
        in: query
        description: Extension property filter in key:value format. Matches against
          the service's extensions object. Repeatable for AND logic.
        schema:
          type: array
          items:
            type: string
        example: region:eu
      - name: stage
        in: query
        description: "Lifecycle stage filter. Defaults to PRODUCTION if omitted. Valid\
          \ values: DEVELOPMENT, BETA, PRODUCTION, DEPRECATED."
        schema:
          type: string
        example: PRODUCTION
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
    post:
      tags:
      - Service Resource
      summary: Register or update a service
      description: Registers a new service or updates an existing one (UPSERT keyed
        on endpoint URL). Requires the X-Api-Key header. Returns the UUID of the created
        or updated service. The service starts at stage=DEVELOPMENT and oLevel=UNVERIFIED
        unless specified in the payload.
      parameters:
      - name: X-Api-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BsmPayload"
      responses:
        "200":
          description: OK
      security:
      - ApiKey: []
  /services/browse:
    get:
      tags:
      - Service Resource
      summary: Browse production services
      description: "Returns a paginated list of PRODUCTION services in the public\
        \ registry. Optionally filter by capability keyword. No authentication required.\
        \ Intended for human-facing portal UIs and developer tooling. Maximum page\
        \ size is 100. Default representation is JSON: requests without an Accept\
        \ header — or with only Accept: */* — receive JSON. Requests asking explicitly\
        \ for a representation we do not serve (application/xml etc.) receive 406.\
        \ Use Accept: text/html for the browser-friendly rendering."
      parameters:
      - name: capability
        in: query
        description: "Optional capability filter (e.g. nlp, translation). Omit to\
          \ list all production services."
        schema:
          type: string
        example: translation
      - name: page
        in: query
        description: Zero-based page index.
        schema:
          format: int32
          default: 0
          type: integer
        example: 0
      - name: size
        in: query
        description: Items per page. Capped at 100.
        schema:
          format: int32
          default: 20
          type: integer
        example: 20
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceBrowsePage"
            text/html:
              schema:
                $ref: "#/components/schemas/TemplateInstance"
  /services/{id}:
    get:
      tags:
      - Service Resource
      summary: Get service by ID
      description: "Returns the full service record including BSM payload, verification\
        \ level, liveness status, and lifecycle metadata. Default representation is\
        \ JSON: requests without an Accept header — or with only Accept: */* — receive\
        \ JSON. Requests asking explicitly for a representation we do not serve (application/xml\
        \ etc.) receive 406. Use Accept: text/html for the browser-friendly rendering."
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceResponse"
            text/html:
              schema:
                $ref: "#/components/schemas/TemplateInstance"
    patch:
      tags:
      - Service Resource
      summary: Update service metadata
      description: "Partially updates a service record. Only fields present in the\
        \ request body are changed. Stage transitions (e.g. DEVELOPMENT → PRODUCTION,\
        \ PRODUCTION → DEPRECATED) are validated: transitioning to DEPRECATED requires\
        \ sunsetAt to be set; DECOMMISSIONED requires sunsetAt to have passed."
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: X-Api-Key
        in: header
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ServicePatchRequest"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceResponse"
      security:
      - ApiKey: []
  /services/{id}/history:
    get:
      tags:
      - Service Resource
      summary: Get version history
      description: "Returns the audit trail of all changes to a service record — registrations,\
        \ BSM updates, stage transitions, O-level assignments, and sunset declarations\
        \ — in reverse chronological order."
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/VersionHistoryEntry"
  /services/{id}/olevel:
    patch:
      tags:
      - Service Resource
      summary: Set verification level
      description: Assigns an O-level to a service after the registry operator has
        completed verification. O-levels progress from UNVERIFIED → IDENTITY_VERIFIED
        → LEGAL_ENTITY_VERIFIED → HYGIENE_VERIFIED → OPERATIONALLY_VERIFIED → AUDITED.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: level
        in: query
        description: Target O-level value.
        schema:
          type: string
        example: IDENTITY_VERIFIED
      - name: X-Api-Key
        in: header
        schema:
          type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ServiceResponse"
      security:
      - ApiKey: []
  /services/{id}/replacements:
    get:
      tags:
      - Service Resource
      summary: Find replacement services
      description: "Returns services that declare themselves as replacements for the\
        \ given service ID. Use this when a service is DEPRECATED or DECOMMISSIONED\
        \ to find the recommended migration target. Optionally filter by minimum O-level,\
        \ IoT readiness, device class, or protocol."
      parameters:
      - name: id
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/UUID"
      - name: deviceClass
        in: query
        description: "Filter by IoT device class (e.g. sensor, actuator, gateway)."
        schema:
          type: string
      - name: minOLevel
        in: query
        description: Minimum O-level of replacement candidates.
        schema:
          type: string
        example: IDENTITY_VERIFIED
      - name: protocol
        in: query
        description: "Filter by IoT protocol (e.g. MQTT, AMQP, HTTP)."
        schema:
          type: string
      responses:
        "200":
          description: OK
components:
  schemas:
    AdminSandboxListResponse:
      type: object
      properties:
        total:
          format: int64
          type: integer
        page:
          format: int32
          type: integer
        size:
          format: int32
          type: integer
        sandboxes:
          type: array
          items:
            $ref: "#/components/schemas/AdminSandboxSummary"
    AdminSandboxSummary:
      type: object
      properties:
        sandboxId:
          type: string
        name:
          type: string
        tier:
          type: string
        ratePerMinute:
          format: int32
          type: integer
        maxServices:
          format: int32
          type: integer
        createdAt:
          $ref: "#/components/schemas/Instant"
        expiresAt:
          $ref: "#/components/schemas/Instant"
        expired:
          type: boolean
        location:
          type: string
        lat:
          format: double
          type: number
        lon:
          format: double
          type: number
        serviceCount:
          format: int64
          type: integer
        totalRequests:
          format: int64
          type: integer
    AdminStatsResponse:
      type: object
      properties:
        totalSandboxes:
          format: int64
          type: integer
        activeSandboxes:
          format: int64
          type: integer
        expiredSandboxes:
          format: int64
          type: integer
        byTier:
          type: object
          additionalProperties:
            format: int64
            type: integer
        totalServices:
          format: int64
          type: integer
        totalRequests:
          format: int64
          type: integer
        registrarPoints:
          type: array
          items:
            $ref: "#/components/schemas/GeoPoint"
    AgentVisit:
      type: object
      properties:
        lat:
          format: double
          type: number
        lon:
          format: double
          type: number
        visitedAt:
          $ref: "#/components/schemas/Instant"
    BsmPayload:
      description: "Bot Service Manifest (BSM) payload — the machine-readable description\
        \ of a service registered in the APIX registry. An AI agent reads this to\
        \ understand what the service does, how to call it, and under what terms."
      required:
      - name
      - description
      - endpoint
      - capabilities
      - registrantEmail
      - registrantName
      - registrantJurisdiction
      - bsmVersion
      type: object
      properties:
        name:
          description: Human-readable service name.
          pattern: \S
          type: string
          example: Acme Translation Service
        description:
          description: "What this service does, in plain language readable by an AI\
            \ agent. Should describe inputs, outputs, and intended use cases."
          pattern: \S
          type: string
          example: "Translates text between 50 languages. Input: source text + target\
            \ language code. Output: translated text with confidence score."
        endpoint:
          description: Base URL of the service endpoint. Must be publicly reachable.
            Agents POST requests here.
          pattern: \S
          type: string
          example: https://api.acme.example/translate
        capabilities:
          description: "Capability identifiers this service fulfils. Use lowercase\
            \ kebab-case strings (e.g. nlp, translation, speech-to-text, image-classification,\
            \ summarisation). Agents search the registry by these values — choose\
            \ terms an agent would naturally use when looking for this type of service."
          minItems: 1
          type: array
          items:
            type: string
          example:
          - translation
          - nlp
        registrantEmail:
          description: Contact email of the registrant. Used for verification notifications
            and O-level progression.
          pattern: \S
          type: string
          example: ops@acme.example
        registrantName:
          description: Full legal name of the registrant (person or organisation).
          pattern: \S
          type: string
          example: Acme GmbH
        registrantJurisdiction:
          description: ISO 3166-1 alpha-2 country code of the registrant's legal jurisdiction.
          pattern: \S
          type: string
          example: DE
        registrantOrgType:
          description: Legal form of the registrant organisation. Defaults to INDIVIDUAL
            if omitted.
          type: string
          allOf:
          - $ref: "#/components/schemas/OrgType"
        registrantLei:
          description: "Legal Entity Identifier (LEI, ISO 17442). 20-character alphanumeric\
            \ code issued by a GLEIF-accredited Local Operating Unit. Required to\
            \ reach O-level LEGAL_ENTITY_VERIFIED (O2) or above."
          type: string
          example: 5493001KJTIIGC8Y1R12
        openApiSpecUrl:
          description: "URL of the OpenAPI 3.x specification for this service. Agents\
            \ follow this link to discover available operations, request/response\
            \ schemas, and authentication requirements."
          type: string
          example: https://api.acme.example/openapi.json
        mcpSpecUrl:
          description: URL of the Model Context Protocol (MCP) manifest. Enables AI
            agents to invoke this service as an MCP tool without writing custom integration
            code.
          type: string
          example: https://api.acme.example/mcp/manifest.json
        policyUrl:
          description: URL of the service's terms of use or acceptable-use policy.
          type: string
          example: https://acme.example/terms
        securityContactUrl:
          description: URL of the security disclosure page (e.g. /.well-known/security.txt).
            Required for O-level HYGIENE_VERIFIED (O3).
          type: string
          example: https://acme.example/.well-known/security.txt
        pricing:
          description: Pricing information. Omit for free or internally-billed services.
          type: object
          allOf:
          - $ref: "#/components/schemas/Pricing"
        bsmVersion:
          description: BSM payload schema version. Must be '0.1' for the current registry.
          pattern: \S
          type: string
          example: "0.1"
        serviceStage:
          description: Lifecycle stage of the service. Defaults to DEVELOPMENT if
            omitted. Only PRODUCTION services are returned by default capability searches
            (?capability=X without an explicit ?stage= parameter).
          type: string
          allOf:
          - $ref: "#/components/schemas/ServiceStage"
        locked:
          description: "IoT migration lock. When true, agents are blocked from automatically\
            \ switching to a replacement service. Use during controlled IoT device\
            \ migration windows."
          type: boolean
        sunsetAt:
          description: "Scheduled decommission timestamp (UTC, ISO 8601). Must be\
            \ set when transitioning to DEPRECATED stage. Agents use this to plan\
            \ migration timelines."
          type: string
          allOf:
          - $ref: "#/components/schemas/Instant"
          example: 2027-01-01T00:00:00Z
        migrationGuideUrl:
          description: URL of a human- or machine-readable migration guide for consumers
            of this service.
          type: string
          example: https://acme.example/migrate-v1-to-v2
        replacesServiceIds:
          description: UUIDs of services this entry supersedes. Consumers of those
            deprecated services are directed here via the /replacements endpoint.
          type: array
          items:
            format: uuid
            pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
            type: string
        extensions:
          description: "Domain-specific extension properties that are not covered\
            \ by the standard BSM fields. Keys are free-form strings; values may be\
            \ strings, numbers, or booleans. Extensions are stored and queryable:\
            \ use ?property=key:value in capability searches to filter by any extension\
            \ field. Example uses: industry vertical ('industry':'healthcare'), geographic\
            \ scope ('region':'eu'), data-residency requirement ('dataResidency':'DE'),\
            \ agent framework ('agentFramework':'langchain')."
          type: object
          additionalProperties: {}
    Candidate:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        name:
          type: string
        endpoint:
          type: string
        oLevel:
          $ref: "#/components/schemas/OLevel"
        serviceStage:
          $ref: "#/components/schemas/ServiceStage"
        iotProfile:
          $ref: "#/components/schemas/IotProfileResponse"
    Charset:
      type: object
      properties:
        name:
          type: string
        aliases:
          type: array
          items:
            type: string
        aliasSet:
          uniqueItems: true
          type: array
          items:
            type: string
        registered:
          type: boolean
    CredentialFormat:
      enum:
      - JWT
      - PSK
      - X509
      - NONE
      type: string
    DimensionScore:
      type: object
      properties:
        key:
          type: string
        question:
          type: string
        average:
          format: double
          type: number
        votes:
          format: int32
          type: integer
    Expression:
      type: object
      properties:
        namespace:
          type: string
        parts:
          type: array
          items:
            $ref: "#/components/schemas/Part"
        literal:
          type: boolean
        literalValue: {}
        origin:
          $ref: "#/components/schemas/Origin"
        generatedId:
          format: int32
          type: integer
    FeedbackAggregateResponse:
      type: object
      properties:
        sandboxId:
          type: string
        name:
          type: string
        totalSubmissions:
          format: int32
          type: integer
        scores:
          type: array
          items:
            $ref: "#/components/schemas/DimensionScore"
        submissionsByProvider:
          type: object
          additionalProperties:
            format: int32
            type: integer
        _links:
          $ref: "#/components/schemas/SandboxLinks"
    FeedbackDimension:
      type: object
      properties:
        key:
          type: string
        question:
          type: string
        minLabel:
          type: string
        maxLabel:
          type: string
    FeedbackSchemaLinks:
      type: object
      properties:
        self:
          $ref: "#/components/schemas/LinkRef1"
    FeedbackSchemaResponse:
      type: object
      properties:
        description:
          type: string
        scale:
          $ref: "#/components/schemas/Scale"
        dimensions:
          type: array
          items:
            $ref: "#/components/schemas/FeedbackDimension"
        _links:
          $ref: "#/components/schemas/FeedbackSchemaLinks"
    FeedbackSubmissionRequest:
      required:
      - scores
      type: object
      properties:
        scores:
          minProperties: 1
          type: object
          additionalProperties:
            format: int32
            type: integer
        agentIdentifier:
          maxLength: 255
          type: string
        comment:
          maxLength: 500
          type: string
        modelIdentifier:
          maxLength: 255
          type: string
        modelProvider:
          maxLength: 100
          type: string
    GeoPoint:
      type: object
      properties:
        lat:
          format: double
          type: number
        lon:
          format: double
          type: number
        name:
          type: string
        tier:
          type: string
    IndexResponse:
      type: object
      properties:
        apixVersion:
          type: string
        name:
          type: string
        description:
          type: string
        stats:
          $ref: "#/components/schemas/RegistryStats"
        _links:
          $ref: "#/components/schemas/RegistryLinks"
    Instant:
      format: date-time
      type: string
      example: 2022-03-10T16:15:50Z
    IotProfileRequest:
      type: object
      properties:
        hubUrl:
          type: string
        protocols:
          type: array
          items:
            $ref: "#/components/schemas/IotProtocol"
        tlsMinVersion:
          type: string
        provisioningEndpoint:
          type: string
        credentialFormat:
          $ref: "#/components/schemas/CredentialFormat"
        deviceIdField:
          type: string
        deviceClasses:
          type: array
          items:
            type: string
        minFirmwareVersion:
          type: string
        firmwareUpdateUrl:
          type: string
        estimatedMigrationMinutes:
          format: int32
          type: integer
    IotProfileResponse:
      type: object
      properties:
        hubUrl:
          type: string
        protocols:
          type: array
          items:
            type: string
        tlsMinVersion:
          type: string
        provisioningEndpoint:
          type: string
        credentialFormat:
          type: string
        deviceIdField:
          type: string
        deviceClasses:
          type: array
          items:
            type: string
        minFirmwareVersion:
          type: string
        firmwareUpdateUrl:
          type: string
        estimatedMigrationMinutes:
          format: int32
          type: integer
    IotProtocol:
      enum:
      - MQTT_3_1_1
      - MQTT_5_0
      - WEBSOCKET
      - COAP
      - HTTP_1_1
      - AMQP
      type: string
    KeyInfo:
      type: object
      properties:
        kid:
          type: string
        kty:
          type: string
        crv:
          type: string
        x:
          type: string
        use:
          type: string
        publicKeySpkiBase64:
          type: string
    LinkRef:
      type: object
      properties:
        href:
          type: string
        templated:
          type: boolean
    LinkRef1:
      type: object
      properties:
        href:
          type: string
        templated:
          type: boolean
    LivenessStatus:
      enum:
      - PENDING
      - LIVE
      - DEGRADED
      - UNREACHABLE
      type: string
    Locale:
      type: object
      properties:
        language:
          type: string
        script:
          type: string
        country:
          type: string
        variant:
          type: string
        extensionKeys:
          uniqueItems: true
          type: array
          items:
            format: byte
            type: string
        unicodeLocaleAttributes:
          uniqueItems: true
          type: array
          items:
            type: string
        unicodeLocaleKeys:
          uniqueItems: true
          type: array
          items:
            type: string
        iSO3Language:
          type: string
        iSO3Country:
          type: string
        displayLanguage:
          type: string
        displayScript:
          type: string
        displayCountry:
          type: string
        displayVariant:
          type: string
        displayName:
          type: string
    MailSigningKeyResponse:
      type: object
      properties:
        issuer:
          type: string
        signingInfo:
          $ref: "#/components/schemas/SigningInfo"
        keys:
          type: array
          items:
            $ref: "#/components/schemas/KeyInfo"
    OLevel:
      description: Organisation verification level assigned by the APIX registry.
        Higher levels indicate greater identity assurance. Agents can filter search
        results by minimum O-level using the minOLevel parameter on the /replacements
        endpoint.
      enum:
      - UNVERIFIED
      - IDENTITY_VERIFIED
      - LEGAL_ENTITY_VERIFIED
      - HYGIENE_VERIFIED
      - OPERATIONALLY_VERIFIED
      - AUDITED
      type: string
    OrgAuditEventResponse:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        eventType:
          $ref: "#/components/schemas/OrgEventType"
        fromLevel:
          $ref: "#/components/schemas/OLevel"
        toLevel:
          $ref: "#/components/schemas/OLevel"
        triggeredBy:
          type: string
        notes:
          type: string
        createdAt:
          $ref: "#/components/schemas/Instant"
        kid:
          type: string
        signature:
          type: string
    OrgEventFeedResponse:
      type: object
      properties:
        orgId:
          $ref: "#/components/schemas/UUID"
        generatedAt:
          $ref: "#/components/schemas/Instant"
        kid:
          type: string
        events:
          type: array
          items:
            $ref: "#/components/schemas/OrgAuditEventResponse"
    OrgEventType:
      enum:
      - REGISTERED
      - LEVEL_EARNED
      - UPGRADE_REQUESTED
      - VERIFICATION_FAILED
      - TEMP_GRANTED
      - TEMP_REVOKED
      - TEMP_EXPIRED
      - LEVEL_REVOKED
      - KEY_ROTATED
      - TAN_ISSUED
      - DNS_ROTATION_INITIATED
      - FRAUD_REPORTED
      - FRAUD_LOCK_CLEARED
      type: string
    OrgFraudReportRequest:
      required:
      - email
      type: object
      properties:
        email:
          pattern: \S
          type: string
        notes:
          type: string
    OrgKeyRotateResponse:
      type: object
      properties:
        apiKey:
          type: string
        rotationSecret:
          type: string
        warning:
          type: string
        signedNotification:
          type: object
          additionalProperties: {}
    OrgRegistrationRequest:
      description: Request body for registering a new organisation. An organisation
        record establishes the legal identity of a registrant and is the starting
        point for O-level verification. Services are registered independently via
        POST /services using the registrant fields in the BSM payload — no prior organisation
        registration is required to submit a service.
      required:
      - registrantName
      - registrantEmail
      - registrantJurisdiction
      - registrantOrgType
      - domain
      - targetOLevel
      type: object
      properties:
        registrantName:
          description: Full legal name of the organisation or individual.
          pattern: \S
          type: string
          example: Acme GmbH
        registrantEmail:
          description: Contact email. Used for O-level verification notifications
            and key rotation challenges.
          pattern: \S
          type: string
          example: ops@acme.example
        registrantJurisdiction:
          description: ISO 3166-1 alpha-2 country code of the legal jurisdiction.
          pattern: \S
          type: string
          example: DE
        registrantOrgType:
          description: "Legal form: INDIVIDUAL, COMMERCIAL, NON_PROFIT, GOVERNMENT,\
            \ ACADEMIC."
          pattern: \S
          type: string
        domain:
          description: Primary internet domain controlled by this organisation (e.g.
            acme.example). The registry places a DNS TXT record challenge on this
            domain for O1 identity verification.
          pattern: \S
          type: string
          example: acme.example
        targetOLevel:
          description: "Desired verification level. The registry initiates verification\
            \ automatically up to this level. Use IDENTITY_VERIFIED (O1) to start;\
            \ upgrade later via PATCH /organizations/{id}/request-upgrade."
          type: string
          allOf:
          - $ref: "#/components/schemas/OLevel"
    OrgResponse:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        registrantName:
          type: string
        registrantEmail:
          type: string
        registrantJurisdiction:
          type: string
        registrantOrgType:
          type: string
        domain:
          type: string
        lei:
          type: string
        targetOLevel:
          $ref: "#/components/schemas/OLevel"
        earnedOLevel:
          $ref: "#/components/schemas/OLevel"
        effectiveOLevel:
          $ref: "#/components/schemas/OLevel"
        verificationStatus:
          $ref: "#/components/schemas/VerificationStatus"
        verificationStep:
          type: string
        verificationError:
          type: string
        dnsVerificationToken:
          type: string
        tempMaxOLevel:
          $ref: "#/components/schemas/OLevel"
        tempMaxExpiresAt:
          $ref: "#/components/schemas/Instant"
        fraudLocked:
          type: boolean
        fraudLockedAt:
          $ref: "#/components/schemas/Instant"
        createdAt:
          $ref: "#/components/schemas/Instant"
        updatedAt:
          $ref: "#/components/schemas/Instant"
    OrgSelfServiceRequest:
      description: Self-service org-registration request used by apix-customer-portal.
        Requires the X-Internal-Key header bound to apix.customer-portal.internal-key.
        No targetOLevel — registration is always UNVERIFIED; the caller has already
        validated the human upstream.
      required:
      - registrantName
      - registrantEmail
      - registrantJurisdiction
      - registrantOrgType
      - domain
      type: object
      properties:
        registrantName:
          description: Full legal name of the organisation.
          pattern: \S
          type: string
          example: Acme GmbH
        registrantEmail:
          description: Contact email — used for later O-level upgrades and key rotation
            challenges.
          pattern: \S
          type: string
          example: ops@acme.example
        registrantJurisdiction:
          description: ISO 3166-1 alpha-2 country code of the legal jurisdiction.
          pattern: \S
          type: string
          example: DE
        registrantOrgType:
          description: "Legal form: INDIVIDUAL, COMMERCIAL, NON_PROFIT, GOVERNMENT,\
            \ ACADEMIC."
          pattern: \S
          type: string
        domain:
          description: Primary internet domain controlled by this organisation.
          pattern: \S
          type: string
          example: acme.example
    OrgTanRequestBody:
      required:
      - email
      type: object
      properties:
        email:
          pattern: \S
          type: string
    OrgTanRotateRequest:
      required:
      - tan
      type: object
      properties:
        tan:
          pattern: \S
          type: string
    OrgTempGrantRequest:
      required:
      - tempMaxOLevel
      - expiresAt
      - grantedBy
      type: object
      properties:
        tempMaxOLevel:
          $ref: "#/components/schemas/OLevel"
        expiresAt:
          $ref: "#/components/schemas/Instant"
        grantedBy:
          pattern: \S
          type: string
        reason:
          type: string
    OrgType:
      description: Legal form of the registrant.
      enum:
      - INDIVIDUAL
      - COMMERCIAL
      - NON_PROFIT
      - GOVERNMENT
      - ACADEMIC
      type: string
    OrgUpgradeRequest:
      required:
      - targetOLevel
      type: object
      properties:
        targetOLevel:
          $ref: "#/components/schemas/OLevel"
    Origin:
      type: object
      properties:
        line:
          format: int32
          type: integer
        lineCharacterStart:
          format: int32
          type: integer
        lineCharacterEnd:
          format: int32
          type: integer
        templateId:
          type: string
        templateGeneratedId:
          type: string
        variant:
          type: object
          allOf:
          - $ref: "#/components/schemas/Variant"
          nullable: true
        synthetic:
          type: boolean
    ParameterDeclaration:
      type: object
      properties:
        typeInfo:
          type: string
        key:
          type: string
        defaultValue:
          $ref: "#/components/schemas/Expression"
        origin:
          $ref: "#/components/schemas/Origin"
    Part:
      type: object
      properties:
        name:
          type: string
        typeInfo:
          type: string
        virtualMethod:
          type: boolean
    Pricing:
      description: Pricing details for metered or subscription services.
      type: object
      properties:
        billingModel:
          description: "Billing model. Known values: PER_CALL, SUBSCRIPTION, FREE."
          type: string
          example: PER_CALL
        pricePerCall:
          description: Price per unit in the stated currency.
          type: number
          example: 0.001
        currency:
          description: ISO 4217 currency code.
          type: string
          example: EUR
        billingUnit:
          description: Billing unit description.
          type: string
          example: per-1k-tokens
    RegistryLinks:
      type: object
      properties:
        self:
          $ref: "#/components/schemas/LinkRef"
        services:
          $ref: "#/components/schemas/LinkRef"
        servicesSearch:
          $ref: "#/components/schemas/LinkRef"
        devices:
          $ref: "#/components/schemas/LinkRef"
        organizations:
          $ref: "#/components/schemas/LinkRef"
        selfServiceEntry:
          $ref: "#/components/schemas/LinkRef"
        openapi:
          $ref: "#/components/schemas/LinkRef"
        registerSandbox:
          $ref: "#/components/schemas/LinkRef"
        feedbackSchema:
          $ref: "#/components/schemas/LinkRef"
        sandbox:
          $ref: "#/components/schemas/LinkRef"
    RegistryStats:
      type: object
      properties:
        registeredServices:
          format: int64
          type: integer
        liveServices:
          format: int64
          type: integer
    RegistryStatus:
      enum:
      - ACTIVE
      - SUSPENDED
      - ARCHIVED
      type: string
    ReplacementsResponse:
      type: object
      properties:
        locked:
          type: boolean
        sunsetAt:
          $ref: "#/components/schemas/Instant"
        candidates:
          type: array
          items:
            $ref: "#/components/schemas/Candidate"
    SandboxDashboardResponse:
      type: object
      properties:
        sandboxId:
          type: string
        name:
          type: string
        tier:
          type: string
        ratePerMinute:
          format: int32
          type: integer
        maxServices:
          format: int32
          type: integer
        maxOrgs:
          format: int32
          type: integer
        createdAt:
          $ref: "#/components/schemas/Instant"
        expiresAt:
          $ref: "#/components/schemas/Instant"
        registrarLocation:
          type: string
        registrarLat:
          format: double
          type: number
        registrarLon:
          format: double
          type: number
        usage:
          type: object
          additionalProperties:
            format: int64
            type: integer
        lastActivityAt:
          $ref: "#/components/schemas/Instant"
        recentVisits:
          type: array
          items:
            $ref: "#/components/schemas/AgentVisit"
    SandboxIndexResponse:
      type: object
      properties:
        sandboxId:
          type: string
        name:
          type: string
        tier:
          type: string
        ratePerMinute:
          format: int32
          type: integer
        expiresAt:
          $ref: "#/components/schemas/Instant"
        _links:
          $ref: "#/components/schemas/SandboxLinks"
    SandboxLinks:
      type: object
      properties:
        self:
          $ref: "#/components/schemas/LinkRef1"
        services:
          $ref: "#/components/schemas/LinkRef1"
        servicesSearch:
          $ref: "#/components/schemas/LinkRef1"
        submitFeedback:
          $ref: "#/components/schemas/LinkRef1"
        feedbackSchema:
          $ref: "#/components/schemas/LinkRef1"
        dashboard:
          $ref: "#/components/schemas/LinkRef1"
    SandboxRegistrationRequest:
      required:
      - name
      type: object
      properties:
        name:
          maxLength: 100
          minLength: 3
          pattern: "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
          type: string
        contactEmail:
          maxLength: 255
          type: string
        location:
          maxLength: 200
          type: string
    SandboxTelemetryResponse:
      type: object
      properties:
        sandboxId:
          type: string
        name:
          type: string
        tier:
          type: string
        ratePerMinute:
          format: int32
          type: integer
        expiresAt:
          $ref: "#/components/schemas/Instant"
        maxServices:
          format: int32
          type: integer
        maxOrgs:
          format: int32
          type: integer
        usage:
          type: object
          additionalProperties:
            format: int64
            type: integer
        lastActivityAt:
          $ref: "#/components/schemas/Instant"
        _links:
          $ref: "#/components/schemas/SandboxLinks"
    Scale:
      type: object
      properties:
        min:
          format: int32
          type: integer
        max:
          format: int32
          type: integer
        minLabel:
          type: string
        maxLabel:
          type: string
    ServiceBrowsePage:
      type: object
      properties:
        total:
          format: int64
          type: integer
        page:
          format: int32
          type: integer
        size:
          format: int32
          type: integer
        items:
          type: array
          items:
            $ref: "#/components/schemas/ServiceSummaryDto"
    ServicePatchRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        endpoint:
          type: string
        capabilities:
          type: array
          items:
            type: string
        registrantEmail:
          type: string
        registrantName:
          type: string
        registrantJurisdiction:
          type: string
        registrantOrgType:
          $ref: "#/components/schemas/OrgType"
        registrantLei:
          type: string
        openApiSpecUrl:
          type: string
        mcpSpecUrl:
          type: string
        policyUrl:
          type: string
        securityContactUrl:
          type: string
        pricing:
          $ref: "#/components/schemas/Pricing"
        bsmVersion:
          type: string
        serviceStage:
          $ref: "#/components/schemas/ServiceStage"
        locked:
          type: boolean
        sunsetAt:
          $ref: "#/components/schemas/Instant"
        migrationGuideUrl:
          type: string
        replacesServiceIds:
          type: array
          items:
            format: uuid
            pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
            type: string
        iotProfile:
          $ref: "#/components/schemas/IotProfileRequest"
    ServiceResponse:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        name:
          type: string
        description:
          type: string
        endpoint:
          type: string
        capabilities:
          type: array
          items:
            type: string
        registrantEmail:
          type: string
        registrantName:
          type: string
        registrantJurisdiction:
          type: string
        registrantOrgType:
          $ref: "#/components/schemas/OrgType"
        registrantLei:
          type: string
        openApiSpecUrl:
          type: string
        mcpSpecUrl:
          type: string
        policyUrl:
          type: string
        securityContactUrl:
          type: string
        pricing:
          $ref: "#/components/schemas/Pricing"
        bsmVersion:
          type: string
        oLevel:
          $ref: "#/components/schemas/OLevel"
        livenessStatus:
          $ref: "#/components/schemas/LivenessStatus"
        serviceStage:
          $ref: "#/components/schemas/ServiceStage"
        registryStatus:
          $ref: "#/components/schemas/RegistryStatus"
        locked:
          type: boolean
        sunsetAt:
          $ref: "#/components/schemas/Instant"
        migrationGuideUrl:
          type: string
        replacesServiceIds:
          type: array
          items:
            format: uuid
            pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
            type: string
        registeredAt:
          $ref: "#/components/schemas/Instant"
        lastUpdatedAt:
          $ref: "#/components/schemas/Instant"
        iotProfile:
          $ref: "#/components/schemas/IotProfileResponse"
        extensions:
          type: object
          additionalProperties: {}
    ServiceStage:
      description: Lifecycle stage of a registered service. Controls visibility in
        default capability searches.
      enum:
      - DEVELOPMENT
      - BETA
      - PRODUCTION
      - DEPRECATED
      - DECOMMISSIONED
      type: string
    ServiceSummaryDto:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        name:
          type: string
        description:
          type: string
        capabilities:
          type: array
          items:
            type: string
        oLevel:
          $ref: "#/components/schemas/OLevel"
        livenessStatus:
          $ref: "#/components/schemas/LivenessStatus"
        serviceStage:
          $ref: "#/components/schemas/ServiceStage"
        registryStatus:
          $ref: "#/components/schemas/RegistryStatus"
        endpoint:
          type: string
        registrantName:
          type: string
        registrantJurisdiction:
          type: string
    SigningInfo:
      type: object
      properties:
        algorithm:
          type: string
        curve:
          type: string
        javaAlgorithmId:
          type: string
        canonicalization:
          type: string
        signatureEncoding:
          type: string
        verificationSteps:
          type: array
          items:
            type: string
        javaKeyReconstruction:
          type: string
    Template:
      type: object
      properties:
        expressions:
          type: array
          items:
            $ref: "#/components/schemas/Expression"
        generatedId:
          type: string
        id:
          type: string
        variant:
          type: object
          allOf:
          - $ref: "#/components/schemas/Variant"
          nullable: true
        parameterDeclarations:
          type: array
          items:
            $ref: "#/components/schemas/ParameterDeclaration"
        fragmentIds:
          uniqueItems: true
          type: array
          items:
            type: string
        fragment:
          type: boolean
        nodes:
          type: array
          items:
            $ref: "#/components/schemas/TemplateNode"
    TemplateInstance:
      type: object
      properties:
        timeout:
          format: int64
          type: integer
        template:
          $ref: "#/components/schemas/Template"
    TemplateNode:
      type: object
      properties:
        expressions:
          type: array
          items:
            $ref: "#/components/schemas/Expression"
        parameterDeclarations:
          type: array
          items:
            $ref: "#/components/schemas/ParameterDeclaration"
        origin:
          $ref: "#/components/schemas/Origin"
        constant:
          type: boolean
        section:
          type: boolean
        text:
          type: boolean
        expression:
          type: boolean
    UUID:
      format: uuid
      pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
      type: string
    Variant:
      type: object
      properties:
        locale:
          $ref: "#/components/schemas/Locale"
        contentType:
          type: string
        encoding:
          $ref: "#/components/schemas/Charset"
        hashCode:
          format: int32
          type: integer
        mediaType:
          type: string
        charset:
          $ref: "#/components/schemas/Charset"
    VerificationStatus:
      enum:
      - PENDING
      - VERIFYING
      - ACHIEVED
      - FAILED
      - MANUAL_REVIEW
      - SUSPENDED
      type: string
    VersionHistoryEntry:
      type: object
      properties:
        id:
          $ref: "#/components/schemas/UUID"
        type:
          type: string
        previousValue:
          type: string
        newValue:
          type: string
        createdAt:
          type: string
  securitySchemes:
    ApiKey:
      type: apiKey
      description: "Registry write key. Required for POST /services, PATCH /services/{id},\
        \ and all other write operations. Read operations (GET) are unauthenticated.\
        \ Contact the registry operator at https://api-index.org to obtain a key."
      name: X-Api-Key
      in: header
