openapi: 3.0.3
info:
  title: FlowForge API
  version: 0.4.0
  description: |-
    Workflow orchestration for DAG-shaped jobs.

    Every failure is returned as `{"error": {"code", "message", "details"}}`.
paths:
  /api/v1/auth/token/:
    post:
      operationId: auth_token_create
      tags:
      - auth
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AuthTokenRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/AuthTokenRequest'
          application/json:
            schema:
              $ref: '#/components/schemas/AuthTokenRequest'
        required: true
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthToken'
          description: ''
  /api/v1/auth/jwt/:
    post:
      operationId: auth_jwt_create
      description: |-
        Takes a set of user credentials and returns an access and refresh JSON web
        token pair to prove the authentication of those credentials.
      tags:
      - auth
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenObtainPairRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenObtainPairRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/TokenObtainPairRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenObtainPair'
          description: ''
  /api/v1/auth/jwt/refresh/:
    post:
      operationId: auth_jwt_refresh_create
      description: |-
        Takes a refresh type JSON web token and returns an access type JSON web
        token if the refresh token is valid.
      tags:
      - auth
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRefreshRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRefreshRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/TokenRefreshRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenRefresh'
          description: ''
  /api/v1/auth/me/:
    get:
      operationId: auth_me_retrieve
      description: Return the caller's own account.
      tags:
      - auth
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
          description: ''
  /api/v1/workflows/:
    get:
      operationId: workflows_list
      description: List published definitions, or publish a new one.
      parameters:
      - name: cursor
        required: false
        in: query
        description: The pagination cursor value.
        schema:
          type: string
      - in: query
        name: name
        schema:
          type: string
      - in: query
        name: name__startswith
        schema:
          type: string
      - name: ordering
        required: false
        in: query
        description: Which field to use when ordering the results.
        schema:
          type: string
      - name: page_size
        required: false
        in: query
        description: Number of results to return per page.
        schema:
          type: integer
      - in: query
        name: version
        schema:
          type: integer
      tags:
      - workflows
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedWorkflowSummaryList'
          description: ''
    post:
      operationId: workflows_create
      description: |-
        Publish a definition.

        The body is validated by the JSON Schema in `core.domain.schema`, not by
        a serializer: that schema is also what the parser and the persistence
        round trip use, and a second validator here would drift from it.
      tags:
      - workflows
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowDefinitionDocRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/WorkflowDefinitionDocRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/WorkflowDefinitionDocRequest'
        required: true
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
          description: ''
        '200':
          description: Identical definition already published.
        '400':
          description: Malformed payload, or an invalid graph.
  /api/v1/workflows/{name}/{version}/:
    get:
      operationId: workflows_retrieve
      description: |-
        One published definition, by name and version.

        `(name, version)` is the domain's identity for a definition, so the URL uses
        it rather than the row's UUID. A client that knows which definition it wants
        already knows both values; making it look up an id first would be a second
        round trip for nothing.
      parameters:
      - in: path
        name: name
        schema:
          type: string
        required: true
      - in: path
        name: version
        schema:
          type: integer
        required: true
      tags:
      - workflows
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
          description: ''
  /api/v1/runs/:
    get:
      operationId: runs_list
      description: List the caller's runs, or trigger a new one.
      parameters:
      - in: query
        name: created_after
        schema:
          type: string
          format: date-time
      - in: query
        name: created_before
        schema:
          type: string
          format: date-time
      - name: cursor
        required: false
        in: query
        description: The pagination cursor value.
        schema:
          type: string
      - name: ordering
        required: false
        in: query
        description: Which field to use when ordering the results.
        schema:
          type: string
      - name: page_size
        required: false
        in: query
        description: Number of results to return per page.
        schema:
          type: integer
      - in: query
        name: state
        schema:
          type: string
      - in: query
        name: workflow
        schema:
          type: string
      tags:
      - runs
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaginatedWorkflowRunSummaryList'
          description: ''
    post:
      operationId: runs_create
      description: |-
        Trigger a run of a published definition.

        The idempotency key is optional, and its absence is not an error — it
        just means the caller accepts that a retry creates a second run.
      parameters:
      - in: header
        name: Idempotency-Key
        schema:
          type: string
        description: Replaying the same key returns the original run instead of creating
          a second one.
      tags:
      - runs
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TriggerRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TriggerRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/TriggerRequest'
        required: true
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '202':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
          description: ''
        '400':
          description: The body is not a valid trigger.
        '404':
          description: No such published definition.
        '422':
          description: Idempotency-Key reused with a different body.
  /api/v1/runs/{id}/:
    get:
      operationId: runs_retrieve
      description: |-
        One run and the state of each of its tasks.

        A run belonging to another tenant is a 404, not a 403: the queryset is
        scoped to the caller's organization, so the row is invisible by construction.
        That is the whole point of resolving visibility in the queryset rather than
        in an object-level permission — a caller cannot learn that another tenant's
        run exists by probing for its id.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - runs
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRun'
          description: ''
  /api/v1/runs/{id}/events/:
    get:
      operationId: runs_events_list
      description: |-
        The append-only event log for one run, in the order it happened.

        A run belonging to somebody else is a 404, not a 403 — the same visibility
        rule as :class:`RunDetailView`, so a caller cannot learn that another user's
        run exists by probing for its events. The list is returned as a plain array,
        oldest first: the default cursor pagination orders newest-first, which is the
        wrong direction for a chronological log, and the per-run event count is
        bounded by the number of tasks and their attempts.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      - name: ordering
        required: false
        in: query
        description: Which field to use when ordering the results.
        schema:
          type: string
      tags:
      - runs
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TaskRunEvent'
          description: ''
  /api/v1/task-runs/{id}/requeue/:
    post:
      operationId: task_runs_requeue_create
      description: Requeue the task run and return its refreshed state.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - task runs
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskRun'
          description: ''
        '409':
          description: The task run is not FAILED.
        '401':
          description: Missing or invalid credentials.
        '404':
          description: No such task run, or not visible to the caller.
  /api/v1/api-keys/:
    get:
      operationId: api_keys_list
      description: List the active organization's keys (metadata only).
      tags:
      - api keys
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ApiKey'
          description: ''
    post:
      operationId: api_keys_create
      description: Issue a key and return its plaintext value once.
      tags:
      - api keys
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyCreateRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ApiKeyCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ApiKeyCreateRequest'
        required: true
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCreated'
          description: ''
  /api/v1/api-keys/{id}/rotate/:
    post:
      operationId: api_keys_rotate_create
      description: Rotate the key named by ``pk`` within the caller's organization.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - api keys
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCreated'
          description: ''
  /api/v1/api-keys/{id}/revoke/:
    post:
      operationId: api_keys_revoke_create
      description: Revoke the key named by ``pk`` within the caller's organization.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - api keys
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
          description: ''
  /api/v1/members/:
    get:
      operationId: members_list
      description: List the active organization's members.
      tags:
      - members
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Membership'
          description: ''
    post:
      operationId: members_create
      description: Add an existing user to the organization with a role.
      tags:
      - members
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MembershipCreateRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MembershipCreateRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/MembershipCreateRequest'
        required: true
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Membership'
          description: ''
  /api/v1/members/{id}/:
    patch:
      operationId: members_partial_update
      description: Change the member's role.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - members
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchedMembershipRoleRequest'
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/PatchedMembershipRoleRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/PatchedMembershipRoleRequest'
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Membership'
          description: ''
    delete:
      operationId: members_destroy
      description: Remove the member from the organization.
      parameters:
      - in: path
        name: id
        schema:
          type: string
          format: uuid
        required: true
      tags:
      - members
      security:
      - ApiKeyAuth: []
      - jwtAuth: []
      - tokenAuth: []
      - cookieAuth: []
      responses:
        '204':
          description: The member was removed.
  /api/v1/metrics/:
    get:
      operationId: metrics_retrieve
      description: Render the registry in the Prometheus text format.
      tags:
      - observability
      security:
      - {}
      responses:
        '200':
          description: Prometheus text exposition format.
components:
  schemas:
    ApiKey:
      type: object
      description: 'An API key as listed: metadata only, never the secret or its hash.'
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          readOnly: true
        prefix:
          type: string
          readOnly: true
        scopes:
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        expires_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        revoked_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        last_used_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
      required:
      - created_at
      - expires_at
      - id
      - last_used_at
      - name
      - prefix
      - revoked_at
      - scopes
    ApiKeyCreateRequest:
      type: object
      description: The body of an API-key creation request.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/ScopesEnum'
          default:
          - read
        expires_at:
          type: string
          format: date-time
          nullable: true
      required:
      - name
    ApiKeyCreated:
      type: object
      description: |-
        The one response that carries the plaintext key.

        ``key`` is read from the serializer context rather than the model: the model
        has no plaintext to read, by design.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          readOnly: true
        prefix:
          type: string
          readOnly: true
        scopes:
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        expires_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        revoked_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        last_used_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        key:
          type: string
          description: The plaintext key, supplied once by the view.
          readOnly: true
      required:
      - created_at
      - expires_at
      - id
      - key
      - last_used_at
      - name
      - prefix
      - revoked_at
      - scopes
    AuthToken:
      type: object
      properties:
        token:
          type: string
          readOnly: true
      required:
      - token
    AuthTokenRequest:
      type: object
      properties:
        username:
          type: string
          writeOnly: true
          minLength: 1
        password:
          type: string
          writeOnly: true
          minLength: 1
      required:
      - password
      - username
    JoinEnum:
      enum:
      - all
      - any
      type: string
      description: |-
        * `all` - all
        * `any` - any
    KindEnum:
      enum:
      - claim
      - finish
      - cancel
      - requeue
      - reclaim
      - promote
      - retry
      - dead
      - skip
      - transition
      type: string
      description: |-
        * `claim` - Claimed by a worker
        * `finish` - Finished by a worker
        * `cancel` - Cancelled by an operator
        * `requeue` - Requeued by an operator
        * `reclaim` - Lease reclaimed by the reaper
        * `promote` - Promoted to ready
        * `retry` - Scheduled for retry
        * `dead` - Exhausted retries
        * `skip` - Branch skipped
        * `transition` - State transition
    Membership:
      type: object
      description: A member of the active organization, with the user's identity.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        user_id:
          type: integer
          readOnly: true
        username:
          type: string
          readOnly: true
        email:
          type: string
          format: email
          readOnly: true
        role:
          allOf:
          - $ref: '#/components/schemas/RoleEnum'
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
      required:
      - created_at
      - email
      - id
      - role
      - user_id
      - username
    MembershipCreateRequest:
      type: object
      description: |-
        The body of an add-member request.

        The target is named by username rather than by id: a caller adding a
        colleague knows the username, not the numeric primary key.
      properties:
        username:
          type: string
          minLength: 1
          maxLength: 150
        role:
          allOf:
          - $ref: '#/components/schemas/RoleEnum'
          default: member
      required:
      - username
    PaginatedWorkflowRunSummaryList:
      type: object
      required:
      - results
      properties:
        next:
          type: string
          nullable: true
          format: uri
          example: http://api.example.org/accounts/?cursor=cD00ODY%3D"
        previous:
          type: string
          nullable: true
          format: uri
          example: http://api.example.org/accounts/?cursor=cj0xJnA9NDg3
        results:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowRunSummary'
    PaginatedWorkflowSummaryList:
      type: object
      required:
      - results
      properties:
        next:
          type: string
          nullable: true
          format: uri
          example: http://api.example.org/accounts/?cursor=cD00ODY%3D"
        previous:
          type: string
          nullable: true
          format: uri
          example: http://api.example.org/accounts/?cursor=cj0xJnA9NDg3
        results:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowSummary'
    PatchedMembershipRoleRequest:
      type: object
      description: The body of a role-change request.
      properties:
        role:
          $ref: '#/components/schemas/RoleEnum'
    RoleEnum:
      enum:
      - owner
      - admin
      - member
      - viewer
      type: string
      description: |-
        * `owner` - Owner
        * `admin` - Admin
        * `member` - Member
        * `viewer` - Viewer
    ScopesEnum:
      enum:
      - read
      - write
      - admin
      type: string
      description: |-
        * `read` - read
        * `write` - write
        * `admin` - admin
    State6d9Enum:
      enum:
      - pending
      - running
      - succeeded
      - failed
      - cancelled
      type: string
      description: |-
        * `pending` - Pending
        * `running` - Running
        * `succeeded` - Succeeded
        * `failed` - Failed
        * `cancelled` - Cancelled
    Task:
      type: object
      description: |-
        One task of a published definition.

        ``config`` is redacted on the way out: a task may legitimately carry a
        password or a token to execute, but a client reading the definition must not
        receive it. The worker still gets the real config in its dispatch payload;
        redaction applies to what humans and clients read, not to execution.
      properties:
        key:
          type: string
          readOnly: true
        task_type:
          type: string
          readOnly: true
        depends_on:
          readOnly: true
        config:
          readOnly: true
      required:
      - config
      - depends_on
      - key
      - task_type
    TaskDocRequest:
      type: object
      description: |-
        Documentation-only mirror of one task in `core.domain.schema`.

        Never validated. See the module docstring.
      properties:
        key:
          type: string
          minLength: 1
        task_type:
          type: string
          minLength: 1
        depends_on:
          type: array
          items:
            type: string
            minLength: 1
        config:
          type: object
          additionalProperties: {}
        when:
          type: string
          nullable: true
          minLength: 1
        join:
          $ref: '#/components/schemas/JoinEnum'
        compensate_for:
          type: string
          nullable: true
          minLength: 1
      required:
      - key
      - task_type
    TaskRun:
      type: object
      description: One task's progress within a run.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        task_key:
          type: string
          readOnly: true
        state:
          allOf:
          - $ref: '#/components/schemas/TaskRunStateEnum'
          readOnly: true
        attempt:
          type: integer
          readOnly: true
        fencing_token:
          type: integer
          readOnly: true
        lease_owner:
          type: string
          readOnly: true
          nullable: true
        lease_expires_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        started_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        finished_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
      required:
      - attempt
      - created_at
      - fencing_token
      - finished_at
      - id
      - lease_expires_at
      - lease_owner
      - started_at
      - state
      - task_key
    TaskRunEvent:
      type: object
      description: One entry in a task run's append-only event log.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        task_run:
          type: string
          format: uuid
          readOnly: true
        task_key:
          type: string
          readOnly: true
        kind:
          allOf:
          - $ref: '#/components/schemas/KindEnum'
          readOnly: true
        from_state:
          type: string
          readOnly: true
          nullable: true
        to_state:
          type: string
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
      required:
      - created_at
      - from_state
      - id
      - kind
      - task_key
      - task_run
      - to_state
    TaskRunStateEnum:
      enum:
      - pending
      - ready
      - running
      - succeeded
      - skipped
      - failed
      - retrying
      - dead
      - cancelled
      type: string
      description: |-
        * `pending` - Pending
        * `ready` - Ready
        * `running` - Running
        * `succeeded` - Succeeded
        * `skipped` - Skipped
        * `failed` - Failed
        * `retrying` - Retrying
        * `dead` - Dead
        * `cancelled` - Cancelled
    TokenObtainPair:
      type: object
      properties:
        access:
          type: string
          readOnly: true
        refresh:
          type: string
          readOnly: true
      required:
      - access
      - refresh
    TokenObtainPairRequest:
      type: object
      properties:
        username:
          type: string
          writeOnly: true
          minLength: 1
        password:
          type: string
          writeOnly: true
          minLength: 1
      required:
      - password
      - username
    TokenRefresh:
      type: object
      properties:
        access:
          type: string
          readOnly: true
      required:
      - access
    TokenRefreshRequest:
      type: object
      properties:
        refresh:
          type: string
          writeOnly: true
          minLength: 1
      required:
      - refresh
    TriggerRequest:
      type: object
      description: 'The body of a run trigger: which published definition to instantiate.'
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
        version:
          type: integer
          minimum: 1
      required:
      - name
      - version
    User:
      type: object
      description: The authenticated caller, as `/auth/me/` reports them.
      properties:
        id:
          type: integer
          readOnly: true
        username:
          type: string
          readOnly: true
        email:
          type: string
          format: email
          readOnly: true
        is_staff:
          type: boolean
          readOnly: true
        is_active:
          type: boolean
          readOnly: true
      required:
      - email
      - id
      - is_active
      - is_staff
      - username
    Workflow:
      type: object
      description: A published definition, with its tasks.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          readOnly: true
        version:
          type: integer
          readOnly: true
        description:
          type: string
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/Task'
          readOnly: true
      required:
      - created_at
      - description
      - id
      - name
      - tasks
      - version
    WorkflowDefinitionDocRequest:
      type: object
      description: |-
        Documentation-only mirror of `WORKFLOW_DEFINITION_SCHEMA`.

        Exists so the OpenAPI document can name the fields instead of describing the
        body as a free-form object. `test_doc_serializer_matches_the_schema` asserts
        it stays in step with the real schema, which is the validator.
      properties:
        name:
          type: string
          minLength: 1
        version:
          type: integer
        description:
          type: string
          minLength: 1
        tasks:
          type: array
          items:
            $ref: '#/components/schemas/TaskDocRequest'
      required:
      - name
      - tasks
      - version
    WorkflowRun:
      type: object
      description: A run, with the state of each of its tasks.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        workflow_name:
          type: string
          readOnly: true
        workflow_version:
          type: integer
          readOnly: true
        state:
          allOf:
          - $ref: '#/components/schemas/State6d9Enum'
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        started_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        finished_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        task_runs:
          type: array
          items:
            $ref: '#/components/schemas/TaskRun'
          readOnly: true
      required:
      - created_at
      - finished_at
      - id
      - started_at
      - state
      - task_runs
      - workflow_name
      - workflow_version
    WorkflowRunSummary:
      type: object
      description: A run in a list, without its task runs.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        workflow_name:
          type: string
          readOnly: true
        workflow_version:
          type: integer
          readOnly: true
        state:
          allOf:
          - $ref: '#/components/schemas/State6d9Enum'
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        started_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
        finished_at:
          type: string
          format: date-time
          readOnly: true
          nullable: true
      required:
      - created_at
      - finished_at
      - id
      - started_at
      - state
      - workflow_name
      - workflow_version
    WorkflowSummary:
      type: object
      description: |-
        A definition in a list, without its tasks.

        The task list is what makes a definition large; a cursor-paginated list of
        20 definitions should not carry every task of every one of them.
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
        name:
          type: string
          readOnly: true
        version:
          type: integer
          readOnly: true
        description:
          type: string
          readOnly: true
        created_at:
          type: string
          format: date-time
          readOnly: true
        task_count:
          type: integer
          readOnly: true
      required:
      - created_at
      - description
      - id
      - name
      - task_count
      - version
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
    cookieAuth:
      type: apiKey
      in: cookie
      name: sessionid
    jwtAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
    tokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: Token-based authentication with required prefix "Token"
