openapi: 3.0.3
info:
  title: Orchestrations API
  version: 1.0.0
  description: >
    Declarative workflow execution layer for SOAT. Define multi-step pipelines as
    directed graphs, execute them as runs, and inspect state and artifacts.
  contact:
    name: SOAT API Support

servers:
  - url: '{baseUrl}'
    description: Base URL of your SOAT deployment (e.g. https://your-soat.com or http://localhost:5047)
    variables:
      baseUrl:
        description: The base URL of your SOAT deployment
        default: http://localhost:5047

tags:
  - name: Orchestrations
    description: Manage orchestrations and their runs

security:
  - bearerAuth: []

paths:
  /api/v1/orchestrations:
    post:
      tags:
        - Orchestrations
      summary: Create an orchestration
      description: Creates a new orchestration workflow definition in the project.
      operationId: createOrchestration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrchestrationRequest'
      responses:
        '201':
          description: Orchestration created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Orchestration'
        '400':
          description: Validation error
        '401':
          description: Unauthorized
        '403':
          description: Forbidden

    get:
      tags:
        - Orchestrations
      summary: List orchestrations
      description: Returns orchestrations accessible to the caller.
      operationId: listOrchestrations
      parameters:
        - in: query
          name: project_id
          schema:
            type: string
          description: Filter by project public ID
      responses:
        '200':
          description: List of orchestrations
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Orchestration'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden

  /api/v1/orchestrations/validate:
    post:
      tags:
        - Orchestrations
      summary: Validate an orchestration graph
      description: >
        Statically validates an orchestration graph without persisting
        anything. Checks that every node has its required field, node ids are
        unique, edges reference existing nodes, the graph is acyclic (unless it
        contains a loop node), and every `input_mapping` `{"var": "..."}`
        reference resolves to a state key written by an upstream node or seeded
        by `input_schema`. Returns blocking `errors` and non-blocking
        `warnings` (e.g. a state key only written on a conditional branch). The
        same `errors` checks are enforced on create and update, which fail with
        `400` when any error is present.
      operationId: validateOrchestration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateOrchestrationRequest'
      responses:
        '200':
          description: Validation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '401':
          description: Unauthorized

  /api/v1/orchestrations/{orchestration_id}:
    get:
      tags:
        - Orchestrations
      summary: Get an orchestration
      description: Returns the orchestration with nodes and edges.
      operationId: getOrchestration
      parameters:
        - $ref: '#/components/parameters/orchestration_id'
      responses:
        '200':
          description: Orchestration details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Orchestration'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found

    patch:
      tags:
        - Orchestrations
      summary: Update an orchestration
      description: Partially updates an orchestration definition.
      operationId: updateOrchestration
      parameters:
        - $ref: '#/components/parameters/orchestration_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOrchestrationRequest'
      responses:
        '200':
          description: Updated orchestration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Orchestration'
        '400':
          description: Validation error
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found

    delete:
      tags:
        - Orchestrations
      summary: Delete an orchestration
      description: Deletes an orchestration definition and all its runs.
      operationId: deleteOrchestration
      parameters:
        - $ref: '#/components/parameters/orchestration_id'
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found

  /api/v1/orchestration-runs:
    post:
      tags:
        - Orchestrations
      summary: Start an orchestration run
      description: >-
        Creates a new run for the orchestration named by orchestration_id. By
        default the run executes durably in the background: the response returns
        immediately with status "running" and progress is observed via
        get-orchestration-run or run lifecycle webhook events
        (orchestration_runs.started/awaiting_input/succeeded/failed). Delay and
        poll waits park the run as "sleeping" and are woken by a background
        scheduler, surviving restarts. Pass wait=true to block until the run
        reaches a terminal or awaiting_input state (the legacy synchronous
        behaviour).
      operationId: startOrchestrationRun
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartRunRequest'
      responses:
        '201':
          description: Run created and executed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestrationRun'
        '400':
          description: Validation error
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Orchestration not found

    get:
      tags:
        - Orchestrations
      summary: List orchestration runs
      description: Returns orchestration runs the caller can access, optionally filtered by orchestration.
      operationId: listOrchestrationRuns
      parameters:
        - name: orchestration_id
          in: query
          required: false
          description: Filter by orchestration public ID (orch_...)
          schema:
            type: string
      responses:
        '200':
          description: List of runs
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OrchestrationRun'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden

  /api/v1/orchestration-runs/{run_id}/cancel:
    post:
      tags:
        - Orchestrations
      summary: Cancel an orchestration run
      description: Cancels a run that has not yet reached a terminal state.
      operationId: cancelOrchestrationRun
      parameters:
        - $ref: '#/components/parameters/run_id'
      responses:
        '200':
          description: Cancelled run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestrationRun'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found
        '409':
          description: Run is already in a terminal state

  /api/v1/orchestration-runs/{run_id}/human-input:
    post:
      tags:
        - Orchestrations
      summary: Submit human input
      description: Provides human input to a run that is awaiting_input at a human node.
      operationId: submitHumanInput
      parameters:
        - $ref: '#/components/parameters/run_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HumanInputRequest'
      responses:
        '200':
          description: Run after processing human input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestrationRun'
        '400':
          description: Invalid input
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found
        '409':
          description: Run is not awaiting input

  /api/v1/orchestration-runs/{run_id}/resume:
    post:
      tags:
        - Orchestrations
      summary: Resume an orchestration run
      description: Resumes an awaiting_input orchestration run from its last checkpoint.
      operationId: resumeOrchestrationRun
      parameters:
        - $ref: '#/components/parameters/run_id'
      responses:
        '200':
          description: Resumed run
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestrationRun'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found
        '409':
          description: Run is not awaiting input

  /api/v1/orchestration-runs/{run_id}:
    get:
      tags:
        - Orchestrations
      summary: Get an orchestration run
      description: Returns the status, state, and artifacts of a specific run.
      operationId: getOrchestrationRun
      parameters:
        - $ref: '#/components/parameters/run_id'
      responses:
        '200':
          description: Run details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestrationRun'
        '401':
          description: Unauthorized
        '403':
          description: Forbidden
        '404':
          description: Not found

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  parameters:
    orchestration_id:
      in: path
      name: orchestration_id
      required: true
      schema:
        type: string
      description: Public ID of the orchestration (orch_...)
    run_id:
      in: path
      name: run_id
      required: true
      schema:
        type: string
      description: Public ID of the run (run_...)

  schemas:
    OrchestrationNode:
      type: object
      description: A single execution unit in the orchestration graph.
      required:
        - id
        - type
      properties:
        id:
          type: string
          description: Unique node identifier within this orchestration.
        type:
          type: string
          enum:
            [
              agent,
              tool,
              transform,
              knowledge,
              memory_write,
              condition,
              human,
              loop,
              poll,
              delay,
              webhook,
              sub_orchestration,
            ]
          description: Node execution type.
        agent_id:
          x-soat-ref: agents
          type: string
          description: For agent nodes — public ID of the agent to invoke.
        tool_id:
          x-soat-ref: tools
          type: string
          description: For tool and poll nodes — public ID of the tool to call.
        operation_id:
          type: string
          description: For tool and poll nodes — specific operation/action on MCP/SOAT tools.
        expression:
          type: object
          description: For transform/condition nodes — JSON Logic rule (https://jsonlogic.com) evaluated against the run state.
        exit_condition:
          type: object
          description: >
            For poll nodes — JSON Logic stop condition, evaluated each attempt
            against the run state augmented with `response` (the latest tool
            result) and `attempt` (1-based count); a truthy result stops
            polling.
        prompt:
          type: string
          description: For human nodes — prompt shown to the human reviewer.
        options:
          type: array
          items:
            type: string
          description: For human nodes — constrained choices.
        memory_id:
          x-soat-ref: memories
          type: string
          description: For memory_write nodes — public ID of the target memory.
        input_mapping:
          type: object
          additionalProperties: true
          description: >
            Maps node input keys to values. Each value is JSON Logic
            (https://jsonlogic.com), the same evaluator used by transform and
            condition nodes. A single-key object is evaluated against the run
            state — `{"var": "key"}` reads `state.key`, `{"cat": [...]}` and
            `{">": [...]}` compute derived values. Any other value (string,
            number, boolean, array, multi-key object) is passed through as a
            literal.
        output_mapping:
          type: object
          additionalProperties:
            type: string
          description: Maps node artifact keys to state paths.
        output_schema:
          type: object
          description: For agent nodes — JSON Schema for structured output parsing.
        collection:
          type: string
          description: For loop nodes — state path to the collection to iterate over.
        item_variable:
          type: string
          description: For loop nodes — variable name injected into state for each item.
        parallelism:
          type: integer
          description: For loop nodes — number of items to process in parallel.
        interval:
          type: string
          description: >
            For poll nodes — wait between attempts. Accepts a friendly suffix
            form (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
        fail_on_timeout:
          type: boolean
          description: >
            For poll nodes — when max_iterations is reached without the exit
            condition becoming true, fail the run (true) instead of completing
            with condition_met=false (default false).
        duration:
          type: string
          description: >
            For delay nodes — how long to wait. Accepts a friendly suffix form
            (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
        mode:
          type: string
          enum: [emit, receive]
          description: For webhook nodes — whether to emit or receive.
        webhook_url:
          type: string
          description: For webhook emit nodes — URL to POST to.
        orchestration_id:
          x-soat-ref: orchestrations
          type: string
          description: >
            Public ID of the orchestration this node runs — the child
            orchestration for sub_orchestration nodes, and the orchestration run
            once per item for loop nodes.
        max_iterations:
          type: integer
          description: >
            Maximum iterations before the node is aborted. For poll nodes this
            is the maximum number of attempts (default 10, ceiling 1000).
        retry:
          type: object
          description: >
            Retry-on-failure policy. When the node throws a transient error
            (unexpected/infrastructure errors and upstream 5xx) and attempts
            remain, the run parks as `sleeping` and re-executes the node after
            the backoff delay. Terminal errors (4xx business errors) fail
            immediately. Absent or `max_attempts <= 1` means fail-fast.
          properties:
            max_attempts:
              type: integer
              description: >
                Total attempts including the first (default 1, ceiling 20).
            backoff:
              type: object
              properties:
                strategy:
                  type: string
                  enum: [fixed, exponential]
                  description: >
                    `fixed` waits `delay_ms` between every attempt;
                    `exponential` doubles per prior attempt. Default `fixed`.
                delay_ms:
                  type: integer
                  description: Base delay between attempts in ms (default 1000).
                max_delay_ms:
                  type: integer
                  description: >
                    Cap on the computed backoff delay in ms (default 300000).

    OrchestrationEdge:
      type: object
      description: A directed connection between two nodes.
      required:
        - from
        - to
      properties:
        from:
          type: string
          description: Source node ID.
        to:
          type: string
          description: Target node ID.
        condition:
          type: string
          description: For condition node routing — label to match against condition output.
        activation_group:
          type: string
          description: Groups edges for join semantics.
        activation_condition:
          type: string
          enum: [all, any]
          description: Whether all or any edges in the activation group must fire.

    Orchestration:
      type: object
      required:
        - id
        - project_id
        - name
        - nodes
        - edges
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Public ID (orch_...).
        project_id:
          x-soat-ref: projects
          type: string
          description: Public ID of the owning project.
        name:
          type: string
          description: Human-readable name.
        description:
          type: string
          nullable: true
          description: Optional description.
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationNode'
        edges:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationEdge'
        state_schema:
          type: object
          nullable: true
          description: Optional JSON Schema for state validation.
        input_schema:
          type: object
          nullable: true
          description: Schema for run inputs (initial state).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    CreateOrchestrationRequest:
      type: object
      required:
        - name
        - nodes
        - edges
      properties:
        project_id:
          x-soat-ref: projects
          type: string
          description: Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.
        name:
          type: string
          description: Human-readable name.
        description:
          type: string
          nullable: true
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationNode'
        edges:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationEdge'
        state_schema:
          type: object
          nullable: true
        input_schema:
          type: object
          nullable: true

    UpdateOrchestrationRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
          nullable: true
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationNode'
        edges:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationEdge'
        state_schema:
          type: object
          nullable: true
        input_schema:
          type: object
          nullable: true

    OrchestrationRun:
      type: object
      required:
        - id
        - orchestration_id
        - project_id
        - status
        - state
        - active_nodes
        - artifacts
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Public ID (run_...).
        orchestration_id:
          x-soat-ref: orchestrations
          type: string
          description: Public ID of the parent orchestration.
        project_id:
          x-soat-ref: projects
          type: string
          description: Public ID of the owning project.
        status:
          type: string
          description: >-
            Run lifecycle state. `queued` awaits a worker; `running` is actively
            executing; `sleeping` is parked on a delay/poll wait (no worker);
            `awaiting_input` is parked on a human node; `succeeded`/`failed`/
            `cancelled` are terminal; `expired` is a wait that passed its
            deadline.
          enum:
            [
              queued,
              running,
              sleeping,
              awaiting_input,
              succeeded,
              failed,
              cancelled,
              expired,
            ]
        state:
          type: object
          description: Current accumulated state.
        active_nodes:
          type: array
          items:
            type: string
          description: Node IDs currently active.
        artifacts:
          type: object
          description: Map of node ID to output artifact.
        error:
          type: object
          nullable: true
          description: Error details when status is failed.
        trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
        input:
          type: object
          nullable: true
          description: Initial input provided at run creation.
        output:
          type: object
          nullable: true
          description: Terminal node artifact(s) when the run has succeeded.
        node_executions:
          type: array
          description: >-
            Per-node execution records in chronological order. Each entry
            captures the resolved input, output, status, and error for a single
            node execution — the orchestration analogue of an LLM trace.
          items:
            $ref: '#/components/schemas/NodeExecution'
        required_action:
          allOf:
            - $ref: '#/components/schemas/RequiredAction'
          nullable: true
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    NodeExecution:
      type: object
      description: >-
        Record of a single node execution within a run, used to debug which
        node failed, what input it received, and what it produced.
      required:
        - node_id
        - attempt
        - status
        - created_at
      properties:
        node_id:
          type: string
          description: ID of the executed node.
        node_type:
          type: string
          nullable: true
          description: Type of the executed node (e.g. agent, transform).
        attempt:
          type: integer
          description: >
            1-based attempt number. A node with a retry policy produces one
            record per attempt (failed attempts followed by a final record).
        status:
          type: string
          enum: [completed, failed, requires_action, skipped]
        input:
          type: object
          nullable: true
          description: Resolved input_mapping the node received.
        output:
          type: object
          nullable: true
          description: Output artifact the node produced (null when failed).
        error:
          type: object
          nullable: true
          description: Error details when status is failed.
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time

    RequiredAction:
      type: object
      description: Details for an awaiting_input run waiting for human input.
      required:
        - type
        - node_id
        - prompt
        - context
      properties:
        type:
          type: string
          enum:
            - human_input
            - webhook_receive
          description: Discriminator identifying the kind of pause.
        node_id:
          type: string
        prompt:
          type: string
        context:
          type: object
        options:
          type: array
          items:
            type: string
          nullable: true

    HumanInputRequest:
      type: object
      required:
        - node_id
      properties:
        node_id:
          type: string
          description: ID of the human node to satisfy.
        output:
          type: object
          description: Output/response provided by the human reviewer.

    StartRunRequest:
      type: object
      required:
        - orchestration_id
      properties:
        orchestration_id:
          x-soat-ref: orchestrations
          type: string
          description: Orchestration to run (orch_...).
          example: orch_V1StGXR8Z5jdHi6B
        input:
          type: object
          description: Initial state for the run (merged with orchestration defaults).
        wait:
          type: boolean
          default: false
          description: >-
            When true, block until the run reaches a terminal (succeeded/failed)
            or awaiting_input state and return the settled run. When false
            (default), return immediately with status "running" and execute the
            run in the background.

    ValidateOrchestrationRequest:
      type: object
      properties:
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationNode'
        edges:
          type: array
          items:
            $ref: '#/components/schemas/OrchestrationEdge'
        input_schema:
          type: object
          nullable: true
          description: Optional JSON Schema for run inputs; its top-level properties seed state.

    ValidationError:
      type: object
      properties:
        path:
          type: string
          description: Location of the issue (e.g. nodes[1].input_mapping.val).
        message:
          type: string
          description: Human-readable description of the issue.

    ValidationResult:
      type: object
      required:
        - valid
        - errors
        - warnings
      properties:
        valid:
          type: boolean
          description: True when there are no blocking errors.
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ValidationError'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/ValidationError'
