openapi: 3.1.0
info:
  title: OpenCloud Control Plane API
  version: 1.0.0-alpha.1
  description: Canonical contract used by OpenCloud clients. App runtime APIs are documented separately.
servers:
  - url: https://api.opencloud.ai
paths:
  /v1/onboarding/agent:
    post:
      operationId: startAgentOnboarding
      summary: Start passwordless agent onboarding
      description: Creates a provisional user and first app for a new email, or requests verification for
        an existing identity.
      tags:
        - Onboarding
      security: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
                projectName:
                  type: string
                  minLength: 1
                  maxLength: 120
                visibility:
                  default: private
                  type: string
                  enum:
                    - public
                    - private
              required:
                - email
                - projectName
                - visibility
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  onboardingId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  state:
                    type: string
                    enum:
                      - awaiting_email_verification
                      - provisional_ready
                      - ready
                  existingUser:
                    type: boolean
                  verification:
                    type: object
                    properties:
                      required:
                        type: boolean
                        const: true
                      status:
                        type: string
                        enum:
                          - pending
                          - verified
                      expiresAt:
                        type: string
                      emailSent:
                        type: boolean
                    required:
                      - required
                      - status
                      - expiresAt
                      - emailSent
                    additionalProperties: {}
                  app:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          name:
                            type: string
                          slug:
                            type: string
                          appUrl:
                            type: string
                            format: uri
                          authUrl:
                            type: string
                            format: uri
                          apiUrl:
                            type: string
                            format: uri
                          visibility:
                            type: string
                            enum:
                              - public
                              - private
                          state:
                            type: string
                            enum:
                              - draft
                              - provisioning
                              - deploying
                              - active
                              - failed
                              - rolling_back
                              - archived
                              - deleting
                              - deleted
                          ownerUserId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          desiredDeploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          activeDeploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          createdAt:
                            type: string
                          updatedAt:
                            type: string
                        required:
                          - id
                          - name
                          - slug
                          - appUrl
                          - authUrl
                          - apiUrl
                          - visibility
                          - state
                          - ownerUserId
                          - desiredDeploymentId
                          - activeDeploymentId
                          - createdAt
                          - updatedAt
                        additionalProperties: {}
                      - type: "null"
                  operation:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          appId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          deploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          type:
                            type: string
                          state:
                            type: string
                            enum:
                              - queued
                              - running
                              - succeeded
                              - failed
                              - cancelled
                          actorType:
                            type: string
                          actorId:
                            type: string
                          idempotencyKey:
                            type: string
                          error:
                            anyOf:
                              - type: object
                                propertyNames:
                                  type: string
                                additionalProperties: {}
                              - type: "null"
                          createdAt:
                            type: string
                          updatedAt:
                            type: string
                          steps:
                            type: array
                            items:
                              type: object
                              properties:
                                id:
                                  type: string
                                  format: uuid
                                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                                name:
                                  type: string
                                state:
                                  type: string
                                  enum:
                                    - queued
                                    - running
                                    - succeeded
                                    - failed
                                    - cancelled
                                attempt:
                                  type: integer
                                  minimum: -9007199254740991
                                  maximum: 9007199254740991
                                startedAt:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                                finishedAt:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                                output:
                                  anyOf:
                                    - type: object
                                      propertyNames:
                                        type: string
                                      additionalProperties: {}
                                    - type: "null"
                                error:
                                  anyOf:
                                    - type: object
                                      propertyNames:
                                        type: string
                                      additionalProperties: {}
                                    - type: "null"
                              required:
                                - id
                                - name
                                - state
                                - attempt
                                - startedAt
                                - finishedAt
                                - output
                                - error
                              additionalProperties: {}
                        required:
                          - id
                          - appId
                          - deploymentId
                          - type
                          - state
                          - actorType
                          - actorId
                          - idempotencyKey
                          - error
                          - createdAt
                          - updatedAt
                        additionalProperties: {}
                      - type: "null"
                  credential:
                    anyOf:
                      - type: object
                        properties:
                          token:
                            type: string
                          expiresAt:
                            type: string
                        required:
                          - token
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  completionToken:
                    type: string
                required:
                  - onboardingId
                  - state
                  - existingUser
                  - verification
                  - app
                  - operation
                  - credential
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes: []
      x-opencloud-idempotency: required
  /v1/onboarding/agent/{onboardingId}/complete:
    post:
      operationId: completeAgentOnboarding
      summary: Complete agent onboarding
      description: Completes an onboarding after email verification and returns the authorized app context.
      tags:
        - Onboarding
      security: []
      parameters:
        - name: onboardingId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                completionToken:
                  type: string
                  minLength: 32
                  maxLength: 512
              required:
                - completionToken
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  onboardingId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  state:
                    type: string
                    enum:
                      - awaiting_email_verification
                      - provisional_ready
                      - ready
                  existingUser:
                    type: boolean
                  verification:
                    type: object
                    properties:
                      required:
                        type: boolean
                        const: true
                      status:
                        type: string
                        enum:
                          - pending
                          - verified
                      expiresAt:
                        type: string
                      emailSent:
                        type: boolean
                    required:
                      - required
                      - status
                      - expiresAt
                      - emailSent
                    additionalProperties: {}
                  app:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          name:
                            type: string
                          slug:
                            type: string
                          appUrl:
                            type: string
                            format: uri
                          authUrl:
                            type: string
                            format: uri
                          apiUrl:
                            type: string
                            format: uri
                          visibility:
                            type: string
                            enum:
                              - public
                              - private
                          state:
                            type: string
                            enum:
                              - draft
                              - provisioning
                              - deploying
                              - active
                              - failed
                              - rolling_back
                              - archived
                              - deleting
                              - deleted
                          ownerUserId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          desiredDeploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          activeDeploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          createdAt:
                            type: string
                          updatedAt:
                            type: string
                        required:
                          - id
                          - name
                          - slug
                          - appUrl
                          - authUrl
                          - apiUrl
                          - visibility
                          - state
                          - ownerUserId
                          - desiredDeploymentId
                          - activeDeploymentId
                          - createdAt
                          - updatedAt
                        additionalProperties: {}
                      - type: "null"
                  operation:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          appId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          deploymentId:
                            anyOf:
                              - type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              - type: "null"
                          type:
                            type: string
                          state:
                            type: string
                            enum:
                              - queued
                              - running
                              - succeeded
                              - failed
                              - cancelled
                          actorType:
                            type: string
                          actorId:
                            type: string
                          idempotencyKey:
                            type: string
                          error:
                            anyOf:
                              - type: object
                                propertyNames:
                                  type: string
                                additionalProperties: {}
                              - type: "null"
                          createdAt:
                            type: string
                          updatedAt:
                            type: string
                          steps:
                            type: array
                            items:
                              type: object
                              properties:
                                id:
                                  type: string
                                  format: uuid
                                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                                name:
                                  type: string
                                state:
                                  type: string
                                  enum:
                                    - queued
                                    - running
                                    - succeeded
                                    - failed
                                    - cancelled
                                attempt:
                                  type: integer
                                  minimum: -9007199254740991
                                  maximum: 9007199254740991
                                startedAt:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                                finishedAt:
                                  anyOf:
                                    - type: string
                                    - type: "null"
                                output:
                                  anyOf:
                                    - type: object
                                      propertyNames:
                                        type: string
                                      additionalProperties: {}
                                    - type: "null"
                                error:
                                  anyOf:
                                    - type: object
                                      propertyNames:
                                        type: string
                                      additionalProperties: {}
                                    - type: "null"
                              required:
                                - id
                                - name
                                - state
                                - attempt
                                - startedAt
                                - finishedAt
                                - output
                                - error
                              additionalProperties: {}
                        required:
                          - id
                          - appId
                          - deploymentId
                          - type
                          - state
                          - actorType
                          - actorId
                          - idempotencyKey
                          - error
                          - createdAt
                          - updatedAt
                        additionalProperties: {}
                      - type: "null"
                  credential:
                    anyOf:
                      - type: object
                        properties:
                          token:
                            type: string
                          expiresAt:
                            type: string
                        required:
                          - token
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  completionToken:
                    type: string
                required:
                  - onboardingId
                  - state
                  - existingUser
                  - verification
                  - app
                  - operation
                  - credential
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes: []
      x-opencloud-idempotency: none
  /v1/apps:
    post:
      operationId: createApp
      summary: Create an app
      description: Creates an app with a server-generated unique address. Operators also provide the
        owning user identifier.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                visibility:
                  default: private
                  type: string
                  enum:
                    - public
                    - private
                slug:
                  type: string
                  minLength: 3
                  maxLength: 63
                  pattern: ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$
                ownerUserId:
                  type: string
                  format: uuid
                  pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
              required:
                - name
                - visibility
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  app:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      name:
                        type: string
                      slug:
                        type: string
                      appUrl:
                        type: string
                        format: uri
                      authUrl:
                        type: string
                        format: uri
                      apiUrl:
                        type: string
                        format: uri
                      visibility:
                        type: string
                        enum:
                          - public
                          - private
                      state:
                        type: string
                        enum:
                          - draft
                          - provisioning
                          - deploying
                          - active
                          - failed
                          - rolling_back
                          - archived
                          - deleting
                          - deleted
                      ownerUserId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      desiredDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      activeDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                    required:
                      - id
                      - name
                      - slug
                      - appUrl
                      - authUrl
                      - apiUrl
                      - visibility
                      - state
                      - ownerUserId
                      - desiredDeploymentId
                      - activeDeploymentId
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                  operation:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      type:
                        type: string
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - succeeded
                          - failed
                          - cancelled
                      actorType:
                        type: string
                      actorId:
                        type: string
                      idempotencyKey:
                        type: string
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      steps:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            name:
                              type: string
                            state:
                              type: string
                              enum:
                                - queued
                                - running
                                - succeeded
                                - failed
                                - cancelled
                            attempt:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            startedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            finishedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            output:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                            error:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                          required:
                            - id
                            - name
                            - state
                            - attempt
                            - startedAt
                            - finishedAt
                            - output
                            - error
                          additionalProperties: {}
                    required:
                      - id
                      - appId
                      - deploymentId
                      - type
                      - state
                      - actorType
                      - actorId
                      - idempotencyKey
                      - error
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                required:
                  - app
                  - operation
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:create
      x-opencloud-idempotency: required
    get:
      operationId: listApps
      summary: List accessible apps
      description: Lists apps visible to the authenticated actor.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    name:
                      type: string
                    slug:
                      type: string
                    appUrl:
                      type: string
                      format: uri
                    authUrl:
                      type: string
                      format: uri
                    apiUrl:
                      type: string
                      format: uri
                    visibility:
                      type: string
                      enum:
                        - public
                        - private
                    state:
                      type: string
                      enum:
                        - draft
                        - provisioning
                        - deploying
                        - active
                        - failed
                        - rolling_back
                        - archived
                        - deleting
                        - deleted
                    ownerUserId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    desiredDeploymentId:
                      anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: "null"
                    activeDeploymentId:
                      anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: "null"
                    createdAt:
                      type: string
                    updatedAt:
                      type: string
                  required:
                    - id
                    - name
                    - slug
                    - appUrl
                    - authUrl
                    - apiUrl
                    - visibility
                    - state
                    - ownerUserId
                    - desiredDeploymentId
                    - activeDeploymentId
                    - createdAt
                    - updatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}:
    get:
      operationId: getApp
      summary: Get an app
      description: Returns the app, canonical URLs, and deployment state.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  name:
                    type: string
                  slug:
                    type: string
                  appUrl:
                    type: string
                    format: uri
                  authUrl:
                    type: string
                    format: uri
                  apiUrl:
                    type: string
                    format: uri
                  visibility:
                    type: string
                    enum:
                      - public
                      - private
                  state:
                    type: string
                    enum:
                      - draft
                      - provisioning
                      - deploying
                      - active
                      - failed
                      - rolling_back
                      - archived
                      - deleting
                      - deleted
                  ownerUserId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  desiredDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  activeDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                required:
                  - id
                  - name
                  - slug
                  - appUrl
                  - authUrl
                  - apiUrl
                  - visibility
                  - state
                  - ownerUserId
                  - desiredDeploymentId
                  - activeDeploymentId
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
    patch:
      operationId: configureApp
      summary: Configure an app
      description: Changes an app title or visibility. Generated addresses remain stable.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                visibility:
                  type: string
                  enum:
                    - public
                    - private
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  app:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      name:
                        type: string
                      slug:
                        type: string
                      appUrl:
                        type: string
                        format: uri
                      authUrl:
                        type: string
                        format: uri
                      apiUrl:
                        type: string
                        format: uri
                      visibility:
                        type: string
                        enum:
                          - public
                          - private
                      state:
                        type: string
                        enum:
                          - draft
                          - provisioning
                          - deploying
                          - active
                          - failed
                          - rolling_back
                          - archived
                          - deleting
                          - deleted
                      ownerUserId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      desiredDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      activeDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                    required:
                      - id
                      - name
                      - slug
                      - appUrl
                      - authUrl
                      - apiUrl
                      - visibility
                      - state
                      - ownerUserId
                      - desiredDeploymentId
                      - activeDeploymentId
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                  operation:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      type:
                        type: string
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - succeeded
                          - failed
                          - cancelled
                      actorType:
                        type: string
                      actorId:
                        type: string
                      idempotencyKey:
                        type: string
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      steps:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            name:
                              type: string
                            state:
                              type: string
                              enum:
                                - queued
                                - running
                                - succeeded
                                - failed
                                - cancelled
                            attempt:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            startedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            finishedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            output:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                            error:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                          required:
                            - id
                            - name
                            - state
                            - attempt
                            - startedAt
                            - finishedAt
                            - output
                            - error
                          additionalProperties: {}
                    required:
                      - id
                      - appId
                      - deploymentId
                      - type
                      - state
                      - actorType
                      - actorId
                      - idempotencyKey
                      - error
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                required:
                  - app
                  - operation
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: required
  /v1/apps/{appId}/drafts:
    post:
      operationId: createDraft
      summary: Create a source draft
      description: Creates a server-side source draft, cloning the active release by default.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                cloneActive:
                  default: true
                  type: boolean
              required:
                - cloneActive
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  name:
                    type: string
                  status:
                    type: string
                    enum:
                      - open
                      - validated
                      - deploying
                      - deployed
                      - discarded
                  revision:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  deployedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - id
                  - appId
                  - baseDeploymentId
                  - name
                  - status
                  - revision
                  - createdAt
                  - updatedAt
                  - deployedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
    get:
      operationId: listDrafts
      summary: List source drafts
      description: Lists source drafts for an app.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    appId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    baseDeploymentId:
                      anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: "null"
                    name:
                      type: string
                    status:
                      type: string
                      enum:
                        - open
                        - validated
                        - deploying
                        - deployed
                        - discarded
                    revision:
                      type: integer
                      exclusiveMinimum: 0
                      maximum: 9007199254740991
                    createdAt:
                      type: string
                    updatedAt:
                      type: string
                    deployedAt:
                      anyOf:
                        - type: string
                        - type: "null"
                  required:
                    - id
                    - appId
                    - baseDeploymentId
                    - name
                    - status
                    - revision
                    - createdAt
                    - updatedAt
                    - deployedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}:
    get:
      operationId: getDraft
      summary: Get a source draft
      description: Returns source draft status and revision.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  name:
                    type: string
                  status:
                    type: string
                    enum:
                      - open
                      - validated
                      - deploying
                      - deployed
                      - discarded
                  revision:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  deployedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - id
                  - appId
                  - baseDeploymentId
                  - name
                  - status
                  - revision
                  - createdAt
                  - updatedAt
                  - deployedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
    delete:
      operationId: discardDraft
      summary: Discard a draft
      description: Marks an undeployed source draft as discarded.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  name:
                    type: string
                  status:
                    type: string
                    enum:
                      - open
                      - validated
                      - deploying
                      - deployed
                      - discarded
                  revision:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  deployedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - id
                  - appId
                  - baseDeploymentId
                  - name
                  - status
                  - revision
                  - createdAt
                  - updatedAt
                  - deployedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}/files:
    get:
      operationId: listDraftFiles
      summary: List draft files
      description: Lists draft file metadata without returning file contents.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    path:
                      type: string
                    sha256:
                      type: string
                      pattern: ^[a-f0-9]{64}$
                    baseSha256:
                      anyOf:
                        - type: string
                          pattern: ^[a-f0-9]{64}$
                        - type: "null"
                    sizeBytes:
                      type: integer
                      minimum: 0
                      maximum: 9007199254740991
                    deleted:
                      type: boolean
                    updatedAt:
                      type: string
                    content:
                      anyOf:
                        - type: string
                        - type: "null"
                    contentBase64:
                      type: string
                  required:
                    - path
                    - sha256
                    - baseSha256
                    - sizeBytes
                    - deleted
                    - updatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
    patch:
      operationId: applyDraftChanges
      summary: Apply draft file changes
      description: Applies text/base64 changes with revision and per-file hash guards.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                expectedRevision:
                  type: integer
                  exclusiveMinimum: 0
                  maximum: 9007199254740991
                changes:
                  minItems: 1
                  maxItems: 200
                  type: array
                  items:
                    type: object
                    properties:
                      path:
                        type: string
                        minLength: 1
                        maxLength: 512
                      baseSha256:
                        anyOf:
                          - type: string
                            pattern: ^[a-f0-9]{64}$
                          - type: "null"
                      content:
                        type: string
                      contentBase64:
                        type: string
                      delete:
                        type: boolean
                    required:
                      - path
                    additionalProperties: false
              required:
                - expectedRevision
                - changes
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  draft:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      baseDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      name:
                        type: string
                      status:
                        type: string
                        enum:
                          - open
                          - validated
                          - deploying
                          - deployed
                          - discarded
                      revision:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      deployedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                    required:
                      - id
                      - appId
                      - baseDeploymentId
                      - name
                      - status
                      - revision
                      - createdAt
                      - updatedAt
                      - deployedAt
                    additionalProperties: {}
                  files:
                    type: array
                    items:
                      type: object
                      properties:
                        path:
                          type: string
                        sha256:
                          type: string
                          pattern: ^[a-f0-9]{64}$
                        baseSha256:
                          anyOf:
                            - type: string
                              pattern: ^[a-f0-9]{64}$
                            - type: "null"
                        sizeBytes:
                          type: integer
                          minimum: 0
                          maximum: 9007199254740991
                        deleted:
                          type: boolean
                        updatedAt:
                          type: string
                        content:
                          anyOf:
                            - type: string
                            - type: "null"
                        contentBase64:
                          type: string
                      required:
                        - path
                        - sha256
                        - baseSha256
                        - sizeBytes
                        - deleted
                        - updatedAt
                      additionalProperties: {}
                required:
                  - draft
                  - files
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}/files/read:
    post:
      operationId: readDraftFiles
      summary: Read draft files
      description: Reads selected draft file contents and hashes.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                paths:
                  minItems: 1
                  maxItems: 100
                  type: array
                  items:
                    type: string
                    minLength: 1
                    maxLength: 512
              required:
                - paths
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    path:
                      type: string
                    sha256:
                      type: string
                      pattern: ^[a-f0-9]{64}$
                    baseSha256:
                      anyOf:
                        - type: string
                          pattern: ^[a-f0-9]{64}$
                        - type: "null"
                    sizeBytes:
                      type: integer
                      minimum: 0
                      maximum: 9007199254740991
                    deleted:
                      type: boolean
                    updatedAt:
                      type: string
                    content:
                      anyOf:
                        - type: string
                        - type: "null"
                    contentBase64:
                      type: string
                  required:
                    - path
                    - sha256
                    - baseSha256
                    - sizeBytes
                    - deleted
                    - updatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}/diff:
    get:
      operationId: diffDraft
      summary: Diff a draft
      description: Summarizes added, changed, and deleted source files.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  added:
                    type: array
                    items:
                      type: string
                  modified:
                    type: array
                    items:
                      type: string
                  deleted:
                    type: array
                    items:
                      type: string
                  unchanged:
                    type: array
                    items:
                      type: string
                required:
                  - added
                  - modified
                  - deleted
                  - unchanged
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}/validate:
    post:
      operationId: validateDraft
      summary: Validate a draft
      description: Runs the canonical bundler and records an authoritative digest.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                version:
                  type: string
                  minLength: 1
                  maxLength: 120
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  draftId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  revision:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                  passed:
                    type: boolean
                  artifactSha256:
                    anyOf:
                      - type: string
                        pattern: ^[a-f0-9]{64}$
                      - type: "null"
                  manifest:
                    anyOf:
                      - {}
                      - type: "null"
                  files:
                    type: array
                    items:
                      type: string
                  diagnostics:
                    type: array
                    items:
                      type: object
                      properties:
                        level:
                          type: string
                          enum:
                            - error
                            - warning
                        code:
                          type: string
                        message:
                          type: string
                      required:
                        - level
                        - message
                      additionalProperties: false
                  createdAt:
                    type: string
                required:
                  - id
                  - draftId
                  - revision
                  - passed
                  - artifactSha256
                  - manifest
                  - files
                  - diagnostics
                  - createdAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/drafts/{draftId}/deploy:
    post:
      operationId: deployDraft
      summary: Deploy a validated draft
      description: Deploys exactly the validated draft revision and returns a durable operation.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  draft:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      baseDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      name:
                        type: string
                      status:
                        type: string
                        enum:
                          - open
                          - validated
                          - deploying
                          - deployed
                          - discarded
                      revision:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      deployedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                    required:
                      - id
                      - appId
                      - baseDeploymentId
                      - name
                      - status
                      - revision
                      - createdAt
                      - updatedAt
                      - deployedAt
                    additionalProperties: {}
                  deployment:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      version:
                        type: string
                      artifactSha256:
                        type: string
                        pattern: ^[a-f0-9]{64}$
                      javascriptSdkVersion:
                        type: string
                      manifest: {}
                      state:
                        type: string
                        enum:
                          - queued
                          - validating
                          - deploying
                          - active
                          - superseded
                          - failed
                          - rolled_back
                          - deleted
                      rollbackOfDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      activatedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                    required:
                      - id
                      - appId
                      - version
                      - artifactSha256
                      - javascriptSdkVersion
                      - manifest
                      - state
                      - rollbackOfDeploymentId
                      - error
                      - createdAt
                      - activatedAt
                    additionalProperties: {}
                  operation:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      type:
                        type: string
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - succeeded
                          - failed
                          - cancelled
                      actorType:
                        type: string
                      actorId:
                        type: string
                      idempotencyKey:
                        type: string
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      steps:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            name:
                              type: string
                            state:
                              type: string
                              enum:
                                - queued
                                - running
                                - succeeded
                                - failed
                                - cancelled
                            attempt:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            startedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            finishedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            output:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                            error:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                          required:
                            - id
                            - name
                            - state
                            - attempt
                            - startedAt
                            - finishedAt
                            - output
                            - error
                          additionalProperties: {}
                    required:
                      - id
                      - appId
                      - deploymentId
                      - type
                      - state
                      - actorType
                      - actorId
                      - idempotencyKey
                      - error
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                required:
                  - draft
                  - deployment
                  - operation
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: required
  /v1/apps/{appId}/drafts/{draftId}/dev-sessions:
    post:
      operationId: startDevSession
      summary: Start a development session
      description: Creates or resumes an isolated preview for a validated draft and optionally applies its
        exact revision.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: draftId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                apply:
                  default: true
                  type: boolean
              required:
                - apply
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  draftId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  status:
                    type: string
                    enum:
                      - active
                      - verifying
                      - verified
                      - stale
                      - stopped
                      - expired
                  previewUrl:
                    type: string
                    format: uri
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  activeRevision:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          draftRevision:
                            type: integer
                            exclusiveMinimum: 0
                            maximum: 9007199254740991
                          artifactSha256:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                          migrationDigest:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                        required:
                          - id
                          - draftRevision
                          - artifactSha256
                          - migrationDigest
                        additionalProperties: false
                      - type: "null"
                  verification:
                    anyOf:
                      - type: object
                        properties:
                          receiptId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          revisionId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          expiresAt:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - receiptId
                          - revisionId
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  capabilities:
                    type: object
                    properties:
                      frontend:
                        type: boolean
                        const: true
                      database:
                        type: boolean
                        const: true
                      functions:
                        type: boolean
                        const: true
                      productionSecrets:
                        type: boolean
                        const: false
                      cron:
                        type: boolean
                        const: false
                      storageSandbox:
                        type: boolean
                        const: false
                      syntheticAuth:
                        type: boolean
                        const: false
                    required:
                      - frontend
                      - database
                      - functions
                      - productionSecrets
                      - cron
                      - storageSandbox
                      - syntheticAuth
                    additionalProperties: false
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  lastActivityAt:
                    type: string
                  expiresAt:
                    type: string
                required:
                  - id
                  - appId
                  - draftId
                  - status
                  - previewUrl
                  - baseDeploymentId
                  - activeRevision
                  - verification
                  - capabilities
                  - createdAt
                  - updatedAt
                  - lastActivityAt
                  - expiresAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}:
    get:
      operationId: getDevSession
      summary: Get a development session
      description: Returns preview state, capabilities, and verification status.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  draftId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  status:
                    type: string
                    enum:
                      - active
                      - verifying
                      - verified
                      - stale
                      - stopped
                      - expired
                  previewUrl:
                    type: string
                    format: uri
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  activeRevision:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          draftRevision:
                            type: integer
                            exclusiveMinimum: 0
                            maximum: 9007199254740991
                          artifactSha256:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                          migrationDigest:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                        required:
                          - id
                          - draftRevision
                          - artifactSha256
                          - migrationDigest
                        additionalProperties: false
                      - type: "null"
                  verification:
                    anyOf:
                      - type: object
                        properties:
                          receiptId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          revisionId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          expiresAt:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - receiptId
                          - revisionId
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  capabilities:
                    type: object
                    properties:
                      frontend:
                        type: boolean
                        const: true
                      database:
                        type: boolean
                        const: true
                      functions:
                        type: boolean
                        const: true
                      productionSecrets:
                        type: boolean
                        const: false
                      cron:
                        type: boolean
                        const: false
                      storageSandbox:
                        type: boolean
                        const: false
                      syntheticAuth:
                        type: boolean
                        const: false
                    required:
                      - frontend
                      - database
                      - functions
                      - productionSecrets
                      - cron
                      - storageSandbox
                      - syntheticAuth
                    additionalProperties: false
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  lastActivityAt:
                    type: string
                  expiresAt:
                    type: string
                required:
                  - id
                  - appId
                  - draftId
                  - status
                  - previewUrl
                  - baseDeploymentId
                  - activeRevision
                  - verification
                  - capabilities
                  - createdAt
                  - updatedAt
                  - lastActivityAt
                  - expiresAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
    delete:
      operationId: stopDevSession
      summary: Stop a development session
      description: Removes its preview artifacts, Function links, and dev schema.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  draftId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  status:
                    type: string
                    enum:
                      - active
                      - verifying
                      - verified
                      - stale
                      - stopped
                      - expired
                  previewUrl:
                    type: string
                    format: uri
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  activeRevision:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          draftRevision:
                            type: integer
                            exclusiveMinimum: 0
                            maximum: 9007199254740991
                          artifactSha256:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                          migrationDigest:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                        required:
                          - id
                          - draftRevision
                          - artifactSha256
                          - migrationDigest
                        additionalProperties: false
                      - type: "null"
                  verification:
                    anyOf:
                      - type: object
                        properties:
                          receiptId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          revisionId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          expiresAt:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - receiptId
                          - revisionId
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  capabilities:
                    type: object
                    properties:
                      frontend:
                        type: boolean
                        const: true
                      database:
                        type: boolean
                        const: true
                      functions:
                        type: boolean
                        const: true
                      productionSecrets:
                        type: boolean
                        const: false
                      cron:
                        type: boolean
                        const: false
                      storageSandbox:
                        type: boolean
                        const: false
                      syntheticAuth:
                        type: boolean
                        const: false
                    required:
                      - frontend
                      - database
                      - functions
                      - productionSecrets
                      - cron
                      - storageSandbox
                      - syntheticAuth
                    additionalProperties: false
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  lastActivityAt:
                    type: string
                  expiresAt:
                    type: string
                required:
                  - id
                  - appId
                  - draftId
                  - status
                  - previewUrl
                  - baseDeploymentId
                  - activeRevision
                  - verification
                  - capabilities
                  - createdAt
                  - updatedAt
                  - lastActivityAt
                  - expiresAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/apply:
    post:
      operationId: applyDevRevision
      summary: Apply a draft revision to development
      description: Materializes the exact validated draft and replays migrations into its isolated dev
        schema when needed.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  draftId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  status:
                    type: string
                    enum:
                      - active
                      - verifying
                      - verified
                      - stale
                      - stopped
                      - expired
                  previewUrl:
                    type: string
                    format: uri
                  baseDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  activeRevision:
                    anyOf:
                      - type: object
                        properties:
                          id:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          draftRevision:
                            type: integer
                            exclusiveMinimum: 0
                            maximum: 9007199254740991
                          artifactSha256:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                          migrationDigest:
                            type: string
                            pattern: ^[a-f0-9]{64}$
                        required:
                          - id
                          - draftRevision
                          - artifactSha256
                          - migrationDigest
                        additionalProperties: false
                      - type: "null"
                  verification:
                    anyOf:
                      - type: object
                        properties:
                          receiptId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          revisionId:
                            type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          expiresAt:
                            anyOf:
                              - type: string
                              - type: "null"
                        required:
                          - receiptId
                          - revisionId
                          - expiresAt
                        additionalProperties: false
                      - type: "null"
                  capabilities:
                    type: object
                    properties:
                      frontend:
                        type: boolean
                        const: true
                      database:
                        type: boolean
                        const: true
                      functions:
                        type: boolean
                        const: true
                      productionSecrets:
                        type: boolean
                        const: false
                      cron:
                        type: boolean
                        const: false
                      storageSandbox:
                        type: boolean
                        const: false
                      syntheticAuth:
                        type: boolean
                        const: false
                    required:
                      - frontend
                      - database
                      - functions
                      - productionSecrets
                      - cron
                      - storageSandbox
                      - syntheticAuth
                    additionalProperties: false
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  lastActivityAt:
                    type: string
                  expiresAt:
                    type: string
                required:
                  - id
                  - appId
                  - draftId
                  - status
                  - previewUrl
                  - baseDeploymentId
                  - activeRevision
                  - verification
                  - capabilities
                  - createdAt
                  - updatedAt
                  - lastActivityAt
                  - expiresAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/request:
    post:
      operationId: requestDevApp
      summary: Request a development preview path
      description: Performs a bounded GET or HEAD against the capability preview and returns the response
        for agent inspection.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                path:
                  default: /
                  type: string
                  minLength: 1
                  maxLength: 2048
                method:
                  default: GET
                  type: string
                  enum:
                    - GET
                    - HEAD
              required:
                - path
                - method
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  contentType:
                    anyOf:
                      - type: string
                      - type: "null"
                  requestId:
                    anyOf:
                      - type: string
                      - type: "null"
                  body:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - status
                  - contentType
                  - requestId
                  - body
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/data:
    post:
      operationId: mutateDevData
      summary: Write development fixture data
      description: Performs one bounded REST write against only the isolated development schema.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                path:
                  type: string
                  minLength: 1
                  maxLength: 2048
                method:
                  default: POST
                  type: string
                  enum:
                    - POST
                    - PUT
                    - PATCH
                    - DELETE
                body: {}
              required:
                - path
                - method
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  contentType:
                    anyOf:
                      - type: string
                      - type: "null"
                  requestId:
                    anyOf:
                      - type: string
                      - type: "null"
                  body:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - status
                  - contentType
                  - requestId
                  - body
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/functions/{functionName}/invoke:
    post:
      operationId: invokeDevFunction
      summary: Explicitly invoke a development Function
      description: Boots one isolated dev Function invocation with no production secrets, cron, or
        implicit browser execution.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: functionName
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z][a-z0-9-]{0,62}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                body: {}
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  requestId:
                    anyOf:
                      - type: string
                      - type: "null"
                  body: {}
                required:
                  - status
                  - requestId
                  - body
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/verify:
    post:
      operationId: verifyDevSession
      summary: Verify a development revision
      description: Runs Chromium, console, HTTP, and optional primary-flow checks and issues a receipt
        bound to the exact revision.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  session:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      draftId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      status:
                        type: string
                        enum:
                          - active
                          - verifying
                          - verified
                          - stale
                          - stopped
                          - expired
                      previewUrl:
                        type: string
                        format: uri
                      baseDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      activeRevision:
                        anyOf:
                          - type: object
                            properties:
                              id:
                                type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              draftRevision:
                                type: integer
                                exclusiveMinimum: 0
                                maximum: 9007199254740991
                              artifactSha256:
                                type: string
                                pattern: ^[a-f0-9]{64}$
                              migrationDigest:
                                type: string
                                pattern: ^[a-f0-9]{64}$
                            required:
                              - id
                              - draftRevision
                              - artifactSha256
                              - migrationDigest
                            additionalProperties: false
                          - type: "null"
                      verification:
                        anyOf:
                          - type: object
                            properties:
                              receiptId:
                                type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              revisionId:
                                type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              expiresAt:
                                anyOf:
                                  - type: string
                                  - type: "null"
                            required:
                              - receiptId
                              - revisionId
                              - expiresAt
                            additionalProperties: false
                          - type: "null"
                      capabilities:
                        type: object
                        properties:
                          frontend:
                            type: boolean
                            const: true
                          database:
                            type: boolean
                            const: true
                          functions:
                            type: boolean
                            const: true
                          productionSecrets:
                            type: boolean
                            const: false
                          cron:
                            type: boolean
                            const: false
                          storageSandbox:
                            type: boolean
                            const: false
                          syntheticAuth:
                            type: boolean
                            const: false
                        required:
                          - frontend
                          - database
                          - functions
                          - productionSecrets
                          - cron
                          - storageSandbox
                          - syntheticAuth
                        additionalProperties: false
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      lastActivityAt:
                        type: string
                      expiresAt:
                        type: string
                    required:
                      - id
                      - appId
                      - draftId
                      - status
                      - previewUrl
                      - baseDeploymentId
                      - activeRevision
                      - verification
                      - capabilities
                      - createdAt
                      - updatedAt
                      - lastActivityAt
                      - expiresAt
                    additionalProperties: {}
                  receipt:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      revisionId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      artifactSha256:
                        type: string
                        pattern: ^[a-f0-9]{64}$
                      expiresAt:
                        type: string
                      summary:
                        type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                    required:
                      - id
                      - revisionId
                      - artifactSha256
                      - expiresAt
                      - summary
                    additionalProperties: {}
                required:
                  - session
                  - receipt
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/requests:
    get:
      operationId: listDevInvocations
      summary: List development Function invocations
      description: Returns bounded, redacted Function outcomes correlated by request ID.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: limit
          in: query
          required: true
          schema:
            default: 100
            type: integer
            minimum: 1
            maximum: 200
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    requestId:
                      type: string
                    correlationId:
                      type: string
                    functionName:
                      type: string
                    caller:
                      type: string
                    status:
                      anyOf:
                        - type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        - type: "null"
                    durationMs:
                      type: integer
                      minimum: 0
                      maximum: 9007199254740991
                    error:
                      anyOf:
                        - type: object
                          propertyNames:
                            type: string
                          additionalProperties: {}
                        - type: "null"
                    createdAt:
                      type: string
                  required:
                    - id
                    - requestId
                    - correlationId
                    - functionName
                    - caller
                    - status
                    - durationMs
                    - error
                    - createdAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-receipts:
    get:
      operationId: listDevReceipts
      summary: List development verification receipts
      description: Returns durable exact-revision verification evidence, including after a dev session is
        stopped.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: limit
          in: query
          required: true
          schema:
            default: 50
            type: integer
            minimum: 1
            maximum: 200
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    sessionId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    revisionId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    artifactSha256:
                      type: string
                      pattern: ^[a-f0-9]{64}$
                    engineVersion:
                      type: string
                    summary:
                      type: object
                      propertyNames:
                        type: string
                      additionalProperties: {}
                    createdAt:
                      type: string
                    expiresAt:
                      type: string
                  required:
                    - id
                    - sessionId
                    - revisionId
                    - artifactSha256
                    - engineVersion
                    - summary
                    - createdAt
                    - expiresAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/dev-sessions/{sessionId}/promote:
    post:
      operationId: promoteDevRevision
      summary: Promote a verified development revision
      description: Deploys only the exact draft revision covered by the current, unexpired verification receipt.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  draft:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      baseDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      name:
                        type: string
                      status:
                        type: string
                        enum:
                          - open
                          - validated
                          - deploying
                          - deployed
                          - discarded
                      revision:
                        type: integer
                        exclusiveMinimum: 0
                        maximum: 9007199254740991
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      deployedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                    required:
                      - id
                      - appId
                      - baseDeploymentId
                      - name
                      - status
                      - revision
                      - createdAt
                      - updatedAt
                      - deployedAt
                    additionalProperties: {}
                  deployment:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      version:
                        type: string
                      artifactSha256:
                        type: string
                        pattern: ^[a-f0-9]{64}$
                      javascriptSdkVersion:
                        type: string
                      manifest: {}
                      state:
                        type: string
                        enum:
                          - queued
                          - validating
                          - deploying
                          - active
                          - superseded
                          - failed
                          - rolled_back
                          - deleted
                      rollbackOfDeploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      activatedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                    required:
                      - id
                      - appId
                      - version
                      - artifactSha256
                      - javascriptSdkVersion
                      - manifest
                      - state
                      - rollbackOfDeploymentId
                      - error
                      - createdAt
                      - activatedAt
                    additionalProperties: {}
                  operation:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      type:
                        type: string
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - succeeded
                          - failed
                          - cancelled
                      actorType:
                        type: string
                      actorId:
                        type: string
                      idempotencyKey:
                        type: string
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      steps:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            name:
                              type: string
                            state:
                              type: string
                              enum:
                                - queued
                                - running
                                - succeeded
                                - failed
                                - cancelled
                            attempt:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            startedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            finishedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            output:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                            error:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                          required:
                            - id
                            - name
                            - state
                            - attempt
                            - startedAt
                            - finishedAt
                            - output
                            - error
                          additionalProperties: {}
                    required:
                      - id
                      - appId
                      - deploymentId
                      - type
                      - state
                      - actorType
                      - actorId
                      - idempotencyKey
                      - error
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                required:
                  - draft
                  - deployment
                  - operation
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: required
  /v1/apps/{appId}/verifications:
    post:
      operationId: verifyApp
      summary: Verify an app release
      description: Starts one durable verification run covering control state, HTTPS health, SDK pinning,
        and Chromium.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  verification:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      operationId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - passed
                          - failed
                      phases:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            passed:
                              type: boolean
                            detail:
                              type: string
                            durationMs:
                              type: number
                              minimum: 0
                          required:
                            - name
                            - passed
                            - detail
                            - durationMs
                          additionalProperties: false
                      diagnostics:
                        type: array
                        items:
                          type: object
                          properties:
                            level:
                              type: string
                              enum:
                                - error
                                - warning
                            code:
                              type: string
                            message:
                              type: string
                          required:
                            - level
                            - message
                          additionalProperties: false
                      startedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                      finishedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                      createdAt:
                        type: string
                    required:
                      - id
                      - appId
                      - deploymentId
                      - operationId
                      - state
                      - phases
                      - diagnostics
                      - startedAt
                      - finishedAt
                      - createdAt
                    additionalProperties: {}
                  operation:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      appId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      deploymentId:
                        anyOf:
                          - type: string
                            format: uuid
                            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                          - type: "null"
                      type:
                        type: string
                      state:
                        type: string
                        enum:
                          - queued
                          - running
                          - succeeded
                          - failed
                          - cancelled
                      actorType:
                        type: string
                      actorId:
                        type: string
                      idempotencyKey:
                        type: string
                      error:
                        anyOf:
                          - type: object
                            propertyNames:
                              type: string
                            additionalProperties: {}
                          - type: "null"
                      createdAt:
                        type: string
                      updatedAt:
                        type: string
                      steps:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                              format: uuid
                              pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                            name:
                              type: string
                            state:
                              type: string
                              enum:
                                - queued
                                - running
                                - succeeded
                                - failed
                                - cancelled
                            attempt:
                              type: integer
                              minimum: -9007199254740991
                              maximum: 9007199254740991
                            startedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            finishedAt:
                              anyOf:
                                - type: string
                                - type: "null"
                            output:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                            error:
                              anyOf:
                                - type: object
                                  propertyNames:
                                    type: string
                                  additionalProperties: {}
                                - type: "null"
                          required:
                            - id
                            - name
                            - state
                            - attempt
                            - startedAt
                            - finishedAt
                            - output
                            - error
                          additionalProperties: {}
                    required:
                      - id
                      - appId
                      - deploymentId
                      - type
                      - state
                      - actorType
                      - actorId
                      - idempotencyKey
                      - error
                      - createdAt
                      - updatedAt
                    additionalProperties: {}
                required:
                  - verification
                  - operation
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: required
  /v1/apps/{appId}/verifications/{verificationId}:
    get:
      operationId: getVerification
      summary: Get a verification run
      description: Returns verification phases and diagnostics.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: verificationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  operationId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - passed
                      - failed
                  phases:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        passed:
                          type: boolean
                        detail:
                          type: string
                        durationMs:
                          type: number
                          minimum: 0
                      required:
                        - name
                        - passed
                        - detail
                        - durationMs
                      additionalProperties: false
                  diagnostics:
                    type: array
                    items:
                      type: object
                      properties:
                        level:
                          type: string
                          enum:
                            - error
                            - warning
                        code:
                          type: string
                        message:
                          type: string
                      required:
                        - level
                        - message
                      additionalProperties: false
                  startedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                  finishedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                  createdAt:
                    type: string
                required:
                  - id
                  - appId
                  - deploymentId
                  - operationId
                  - state
                  - phases
                  - diagnostics
                  - startedAt
                  - finishedAt
                  - createdAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/deployments:
    get:
      operationId: listDeployments
      summary: List deployments
      description: Lists immutable app deployments.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    appId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    version:
                      type: string
                    artifactSha256:
                      type: string
                      pattern: ^[a-f0-9]{64}$
                    javascriptSdkVersion:
                      type: string
                    manifest: {}
                    state:
                      type: string
                      enum:
                        - queued
                        - validating
                        - deploying
                        - active
                        - superseded
                        - failed
                        - rolled_back
                        - deleted
                    rollbackOfDeploymentId:
                      anyOf:
                        - type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        - type: "null"
                    error:
                      anyOf:
                        - type: object
                          propertyNames:
                            type: string
                          additionalProperties: {}
                        - type: "null"
                    createdAt:
                      type: string
                    activatedAt:
                      anyOf:
                        - type: string
                        - type: "null"
                  required:
                    - id
                    - appId
                    - version
                    - artifactSha256
                    - javascriptSdkVersion
                    - manifest
                    - state
                    - rollbackOfDeploymentId
                    - error
                    - createdAt
                    - activatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/deployments/{deploymentId}:
    get:
      operationId: getDeployment
      summary: Get a deployment
      description: Returns an immutable deployment and manifest.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: deploymentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  version:
                    type: string
                  artifactSha256:
                    type: string
                    pattern: ^[a-f0-9]{64}$
                  javascriptSdkVersion:
                    type: string
                  manifest: {}
                  state:
                    type: string
                    enum:
                      - queued
                      - validating
                      - deploying
                      - active
                      - superseded
                      - failed
                      - rolled_back
                      - deleted
                  rollbackOfDeploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  activatedAt:
                    anyOf:
                      - type: string
                      - type: "null"
                required:
                  - id
                  - appId
                  - version
                  - artifactSha256
                  - javascriptSdkVersion
                  - manifest
                  - state
                  - rollbackOfDeploymentId
                  - error
                  - createdAt
                  - activatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/deployments/{deploymentId}/rollback:
    post:
      operationId: rollbackDeployment
      summary: Roll back an app
      description: Restores a previously active deployment.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: deploymentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  type:
                    type: string
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - succeeded
                      - failed
                      - cancelled
                  actorType:
                    type: string
                  actorId:
                    type: string
                  idempotencyKey:
                    type: string
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  steps:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        name:
                          type: string
                        state:
                          type: string
                          enum:
                            - queued
                            - running
                            - succeeded
                            - failed
                            - cancelled
                        attempt:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        startedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        finishedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        output:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                        error:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                      required:
                        - id
                        - name
                        - state
                        - attempt
                        - startedAt
                        - finishedAt
                        - output
                        - error
                      additionalProperties: {}
                required:
                  - id
                  - appId
                  - deploymentId
                  - type
                  - state
                  - actorType
                  - actorId
                  - idempotencyKey
                  - error
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:rollback
      x-opencloud-idempotency: required
  /v1/operations/{operationId}:
    get:
      operationId: getOperation
      summary: Get a durable operation
      description: Returns operation state, steps, and sanitized errors.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: operationId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  type:
                    type: string
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - succeeded
                      - failed
                      - cancelled
                  actorType:
                    type: string
                  actorId:
                    type: string
                  idempotencyKey:
                    type: string
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  steps:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        name:
                          type: string
                        state:
                          type: string
                          enum:
                            - queued
                            - running
                            - succeeded
                            - failed
                            - cancelled
                        attempt:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        startedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        finishedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        output:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                        error:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                      required:
                        - id
                        - name
                        - state
                        - attempt
                        - startedAt
                        - finishedAt
                        - output
                        - error
                      additionalProperties: {}
                required:
                  - id
                  - appId
                  - deploymentId
                  - type
                  - state
                  - actorType
                  - actorId
                  - idempotencyKey
                  - error
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
  /v1/apps/{appId}/secrets:
    get:
      operationId: listSecrets
      summary: List secret metadata
      description: Lists secret names and timestamps; values are never returned.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      pattern: ^[A-Z][A-Z0-9_]{0,127}$
                    createdAt:
                      type: string
                    updatedAt:
                      type: string
                  required:
                    - name
                    - createdAt
                    - updatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
  /v1/apps/{appId}/secrets/{name}/generate:
    post:
      operationId: generateSecret
      summary: Generate a secret
      description: Generates and stores a random secret entirely inside OpenCloud. The value is never returned.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: name
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Z][A-Z0-9_]{0,127}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                bytes:
                  default: 32
                  type: integer
                  minimum: 16
                  maximum: 256
                encoding:
                  default: base64url
                  type: string
                  enum:
                    - base64url
                    - hex
              required:
                - bytes
                - encoding
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    pattern: ^[A-Z][A-Z0-9_]{0,127}$
                  stored:
                    type: boolean
                    const: true
                  generatedBytes:
                    type: integer
                    exclusiveMinimum: 0
                    maximum: 9007199254740991
                required:
                  - name
                  - stored
                  - generatedBytes
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
  /v1/apps/{appId}/secrets/{name}/entry-link:
    post:
      operationId: createSecretEntryLink
      summary: Create a secure secret-entry link
      description: Creates a short-lived, one-time browser link where the user can enter a secret outside
        the agent conversation.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: name
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Z][A-Z0-9_]{0,127}$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    pattern: ^[A-Z][A-Z0-9_]{0,127}$
                  url:
                    type: string
                    format: uri
                  expiresAt:
                    type: string
                required:
                  - name
                  - url
                  - expiresAt
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
  /v1/apps/{appId}/secrets/{name}:
    delete:
      operationId: deleteSecret
      summary: Delete a secret
      description: Deletes one app secret by name.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: name
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Z][A-Z0-9_]{0,127}$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    pattern: ^[A-Z][A-Z0-9_]{0,127}$
                  deleted:
                    type: boolean
                    const: true
                required:
                  - name
                  - deleted
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
  /v1/apps/{appId}/backups:
    get:
      operationId: listBackups
      summary: List backups
      description: Lists immutable app backups.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:read
      x-opencloud-idempotency: none
    post:
      operationId: createBackup
      summary: Create a backup
      description: Starts an immutable manual backup.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  type:
                    type: string
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - succeeded
                      - failed
                      - cancelled
                  actorType:
                    type: string
                  actorId:
                    type: string
                  idempotencyKey:
                    type: string
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  steps:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        name:
                          type: string
                        state:
                          type: string
                          enum:
                            - queued
                            - running
                            - succeeded
                            - failed
                            - cancelled
                        attempt:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        startedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        finishedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        output:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                        error:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                      required:
                        - id
                        - name
                        - state
                        - attempt
                        - startedAt
                        - finishedAt
                        - output
                        - error
                      additionalProperties: {}
                required:
                  - id
                  - appId
                  - deploymentId
                  - type
                  - state
                  - actorType
                  - actorId
                  - idempotencyKey
                  - error
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: required
  /v1/apps/{appId}/backups/schedule:
    put:
      operationId: configureBackupSchedule
      summary: Configure backup schedule
      description: Sets no, daily, or weekly app backups.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                schedule:
                  type: string
                  enum:
                    - none
                    - daily
                    - weekly
              required:
                - schedule
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  type:
                    type: string
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - succeeded
                      - failed
                      - cancelled
                  actorType:
                    type: string
                  actorId:
                    type: string
                  idempotencyKey:
                    type: string
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  steps:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        name:
                          type: string
                        state:
                          type: string
                          enum:
                            - queued
                            - running
                            - succeeded
                            - failed
                            - cancelled
                        attempt:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        startedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        finishedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        output:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                        error:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                      required:
                        - id
                        - name
                        - state
                        - attempt
                        - startedAt
                        - finishedAt
                        - output
                        - error
                      additionalProperties: {}
                required:
                  - id
                  - appId
                  - deploymentId
                  - type
                  - state
                  - actorType
                  - actorId
                  - idempotencyKey
                  - error
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: required
  /v1/apps/{appId}/backups/{backupId}/restore:
    post:
      operationId: restoreBackup
      summary: Restore a backup
      description: Restores an app database through the guarded restore path.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: backupId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 200
          description: Caller-stable key. Reusing it with different input returns a conflict.
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  appId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  deploymentId:
                    anyOf:
                      - type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      - type: "null"
                  type:
                    type: string
                  state:
                    type: string
                    enum:
                      - queued
                      - running
                      - succeeded
                      - failed
                      - cancelled
                  actorType:
                    type: string
                  actorId:
                    type: string
                  idempotencyKey:
                    type: string
                  error:
                    anyOf:
                      - type: object
                        propertyNames:
                          type: string
                        additionalProperties: {}
                      - type: "null"
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  steps:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                        name:
                          type: string
                        state:
                          type: string
                          enum:
                            - queued
                            - running
                            - succeeded
                            - failed
                            - cancelled
                        attempt:
                          type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        startedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        finishedAt:
                          anyOf:
                            - type: string
                            - type: "null"
                        output:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                        error:
                          anyOf:
                            - type: object
                              propertyNames:
                                type: string
                              additionalProperties: {}
                            - type: "null"
                      required:
                        - id
                        - name
                        - state
                        - attempt
                        - startedAt
                        - finishedAt
                        - output
                        - error
                      additionalProperties: {}
                required:
                  - id
                  - appId
                  - deploymentId
                  - type
                  - state
                  - actorType
                  - actorId
                  - idempotencyKey
                  - error
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - owner
      x-opencloud-idempotency: required
  /v1/apps/{appId}/cron/invocations:
    get:
      operationId: listCronInvocations
      summary: List cron invocations
      description: Lists scheduled function invocation history.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: name
          in: query
          required: false
          schema:
            type: string
        - name: state
          in: query
          required: false
          schema:
            type: string
            enum:
              - running
              - succeeded
              - failed
        - name: after
          in: query
          required: false
          schema:
            type: string
            format: date-time
            pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
        - name: limit
          in: query
          required: true
          schema:
            default: 50
            type: integer
            minimum: 1
            maximum: 200
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    appId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    deploymentId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    cronName:
                      type: string
                    functionName:
                      type: string
                    state:
                      type: string
                      enum:
                        - running
                        - succeeded
                        - failed
                    scheduledAt:
                      type: string
                    startedAt:
                      type: string
                    finishedAt:
                      anyOf:
                        - type: string
                        - type: "null"
                    responseStatus:
                      anyOf:
                        - type: integer
                          minimum: -9007199254740991
                          maximum: 9007199254740991
                        - type: "null"
                    error:
                      anyOf:
                        - type: object
                          propertyNames:
                            type: string
                          additionalProperties: {}
                        - type: "null"
                  required:
                    - id
                    - appId
                    - deploymentId
                    - cronName
                    - functionName
                    - state
                    - scheduledAt
                    - startedAt
                    - finishedAt
                    - responseStatus
                    - error
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/cron/{name}/invoke:
    post:
      operationId: invokeCron
      summary: Invoke a cron job
      description: Manually enqueues one enabled cron function.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: name
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 63
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted:
                    type: boolean
                    const: true
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  deploymentId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  cronName:
                    type: string
                  functionName:
                    type: string
                  jobId:
                    type: string
                required:
                  - accepted
                  - appId
                  - deploymentId
                  - cronName
                  - functionName
                  - jobId
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:deploy
      x-opencloud-idempotency: none
  /v1/apps/{appId}/agent-feed:
    get:
      operationId: getAgentFeed
      summary: Get the app Agent Feed
      description: Returns the stable, bounded app health, signal, alert, and recent-event contract for agents.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: since
          in: query
          required: false
          schema:
            type: string
            format: date-time
            pattern: ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  contractVersion:
                    type: string
                    const: "1"
                  app:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                        pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                      name:
                        type: string
                      slug:
                        type: string
                      state:
                        type: string
                        enum:
                          - draft
                          - provisioning
                          - deploying
                          - active
                          - failed
                          - rolling_back
                          - archived
                          - deleting
                          - deleted
                      activeDeployment:
                        anyOf:
                          - type: object
                            properties:
                              id:
                                type: string
                                format: uuid
                                pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                              version:
                                type: string
                              state:
                                type: string
                                enum:
                                  - queued
                                  - validating
                                  - deploying
                                  - active
                                  - superseded
                                  - failed
                                  - rolled_back
                                  - deleted
                            required:
                              - id
                              - version
                              - state
                            additionalProperties: false
                          - type: "null"
                    required:
                      - id
                      - name
                      - slug
                      - state
                      - activeDeployment
                    additionalProperties: {}
                  observedAt:
                    type: string
                  telemetry:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - available
                          - unavailable
                      latestIngestedAt:
                        anyOf:
                          - type: string
                          - type: "null"
                      ingestionLagSeconds:
                        anyOf:
                          - type: number
                          - type: "null"
                      stale:
                        type: boolean
                    required:
                      - status
                      - latestIngestedAt
                      - ingestionLagSeconds
                      - stale
                    additionalProperties: {}
                  signals:
                    type: array
                    items: {}
                  alerts:
                    type: array
                    items: {}
                  events:
                    type: array
                    items: {}
                  eventsTruncated:
                    type: boolean
                  nextSince:
                    type: string
                required:
                  - contractVersion
                  - app
                  - observedAt
                  - telemetry
                  - signals
                  - alerts
                  - events
                  - eventsTruncated
                  - nextSince
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/alert-rules:
    get:
      operationId: listAlertRules
      summary: List custom metric alert rules
      description: Lists the app's bounded fixed-threshold alert rules.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    name:
                      type: string
                      minLength: 1
                      maxLength: 120
                    metric:
                      type: string
                      minLength: 1
                      maxLength: 63
                      pattern: ^[a-z][a-z0-9_]*$
                    aggregation:
                      type: string
                      enum:
                        - sum
                        - rate
                        - latest
                        - min
                        - max
                        - avg
                    operator:
                      type: string
                      enum:
                        - gt
                        - gte
                        - lt
                        - lte
                        - eq
                    threshold:
                      type: number
                      minimum: -1000000000000000
                      maximum: 1000000000000000
                    window:
                      type: string
                      enum:
                        - 5m
                        - 15m
                        - 1h
                        - 24h
                    minimumSamples:
                      default: 1
                      type: integer
                      minimum: 1
                      maximum: 100000
                    severity:
                      default: warning
                      type: string
                      enum:
                        - info
                        - warning
                        - critical
                    enabled:
                      default: true
                      type: boolean
                    id:
                      type: string
                      minLength: 1
                      maxLength: 63
                      pattern: ^[a-z][a-z0-9-]*$
                    appId:
                      type: string
                      format: uuid
                      pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                    createdAt:
                      type: string
                    updatedAt:
                      type: string
                  required:
                    - name
                    - metric
                    - aggregation
                    - operator
                    - threshold
                    - window
                    - minimumSamples
                    - severity
                    - enabled
                    - id
                    - appId
                    - createdAt
                    - updatedAt
                  additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/alert-rules/{ruleId}:
    put:
      operationId: putAlertRule
      summary: Create or replace a custom metric alert rule
      description: Creates or replaces one fixed-window threshold rule for a metric declared by the active
        deployment.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 63
            pattern: ^[a-z][a-z0-9-]*$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                metric:
                  type: string
                  minLength: 1
                  maxLength: 63
                  pattern: ^[a-z][a-z0-9_]*$
                aggregation:
                  type: string
                  enum:
                    - sum
                    - rate
                    - latest
                    - min
                    - max
                    - avg
                operator:
                  type: string
                  enum:
                    - gt
                    - gte
                    - lt
                    - lte
                    - eq
                threshold:
                  type: number
                  minimum: -1000000000000000
                  maximum: 1000000000000000
                window:
                  type: string
                  enum:
                    - 5m
                    - 15m
                    - 1h
                    - 24h
                minimumSamples:
                  default: 1
                  type: integer
                  minimum: 1
                  maximum: 100000
                severity:
                  default: warning
                  type: string
                  enum:
                    - info
                    - warning
                    - critical
                enabled:
                  default: true
                  type: boolean
              required:
                - name
                - metric
                - aggregation
                - operator
                - threshold
                - window
                - minimumSamples
                - severity
                - enabled
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                    minLength: 1
                    maxLength: 120
                  metric:
                    type: string
                    minLength: 1
                    maxLength: 63
                    pattern: ^[a-z][a-z0-9_]*$
                  aggregation:
                    type: string
                    enum:
                      - sum
                      - rate
                      - latest
                      - min
                      - max
                      - avg
                  operator:
                    type: string
                    enum:
                      - gt
                      - gte
                      - lt
                      - lte
                      - eq
                  threshold:
                    type: number
                    minimum: -1000000000000000
                    maximum: 1000000000000000
                  window:
                    type: string
                    enum:
                      - 5m
                      - 15m
                      - 1h
                      - 24h
                  minimumSamples:
                    default: 1
                    type: integer
                    minimum: 1
                    maximum: 100000
                  severity:
                    default: warning
                    type: string
                    enum:
                      - info
                      - warning
                      - critical
                  enabled:
                    default: true
                    type: boolean
                  id:
                    type: string
                    minLength: 1
                    maxLength: 63
                    pattern: ^[a-z][a-z0-9-]*$
                  appId:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                required:
                  - name
                  - metric
                  - aggregation
                  - operator
                  - threshold
                  - window
                  - minimumSamples
                  - severity
                  - enabled
                  - id
                  - appId
                  - createdAt
                  - updatedAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
    delete:
      operationId: deleteAlertRule
      summary: Delete a custom metric alert rule
      description: Deletes one app-scoped alert rule.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
        - name: ruleId
          in: path
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 63
            pattern: ^[a-z][a-z0-9-]*$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                    const: true
                required:
                  - deleted
                additionalProperties: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:configure
      x-opencloud-idempotency: none
  /v1/apps/{appId}/logs/query:
    post:
      operationId: queryLogs
      summary: Query app logs
      description: Runs an app-scoped, bounded log query.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              propertyNames:
                type: string
              additionalProperties: {}
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/metrics/query:
    post:
      operationId: queryMetrics
      summary: Query app metrics
      description: Runs an app-scoped, bounded metrics query.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              propertyNames:
                type: string
              additionalProperties: {}
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/usage:
    get:
      operationId: getUsage
      summary: Get app usage
      description: Returns app-scoped usage rollups.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - app:observe
      x-opencloud-idempotency: none
  /v1/apps/{appId}/credentials:
    post:
      operationId: createCredential
      summary: Create an app credential
      description: Creates a short-lived app-scoped credential for CLI compatibility.
      tags:
        - Control plane
      security:
        - bearerAuth: []
        - browserSession: []
      parameters:
        - name: appId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 120
                expiresInHours:
                  default: 24
                  type: integer
                  minimum: 1
                  maximum: 168
                scopes:
                  minItems: 1
                  type: array
                  items:
                    type: string
                    enum:
                      - app:read
                      - app:deploy
                      - app:configure
                      - app:observe
                      - app:rollback
                      - app:restart
              required:
                - name
                - expiresInHours
                - scopes
              additionalProperties: false
      responses:
        "200":
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
                  name:
                    type: string
                  token:
                    type: string
                  prefix:
                    type: string
                  scopes:
                    type: array
                    items:
                      type: string
                  createdAt:
                    type: string
                  expiresAt:
                    type: string
                required:
                  - id
                  - name
                  - token
                  - prefix
                  - scopes
                  - createdAt
                  - expiresAt
                additionalProperties: {}
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          $ref: "#/components/responses/Conflict"
      x-opencloud-scopes:
        - owner
      x-opencloud-idempotency: none
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: OpenCloud app or provisional account credential
    browserSession:
      type: apiKey
      in: cookie
      name: oc_session
  schemas:
    Error:
      type: object
      required:
        - statusCode
        - code
        - message
      properties:
        statusCode:
          type: integer
        code:
          type: string
        message:
          type: string
        details: {}
        requestId:
          type: string
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: Actor is not authorized for this app or operation
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Conflict:
      description: Conflict or stale revision
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
