openapi: 3.0.3
info:
  title: YakPipes API
  version: "1.0.0"
  description: >
    Workflow automation API. Create and manage workflows (trigger-to-action
    node graphs), import/export portable definitions, publish versions, start
    runs, and read step-by-step run history. Authenticate with an API key from
    the YakPipes "API keys" page. API-triggered runs count toward the manual
    run quota. An MCP server (Model Context Protocol, Streamable HTTP) exposing
    the same operations as tools lives at POST /mcp with the same API-key auth.
servers:
  - url: /api/v1
security:
  - ApiKeyAuth: []
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: "Also accepted as `Authorization: Bearer <key>`."
  schemas:
    Error:
      type: object
      properties:
        errors:
          type: object
          properties:
            detail: { type: string }
    Graph:
      type: object
      description: Workflow node graph (the builder's persisted format).
      properties:
        nodes:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              type: { type: string, description: "e.g. webhook-trigger, schedule-trigger, http-action, condition, map, filter, ai-transform" }
              x: { type: number }
              y: { type: number }
              config: { type: object }
        edges:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              from: { type: string }
              from_port: { type: string }
              to: { type: string }
              to_port: { type: string }
    Workflow:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        status: { type: string, enum: [draft, active] }
        version: { type: integer }
        graph: { $ref: "#/components/schemas/Graph" }
        notify_on_failure: { type: boolean }
        inserted_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    WorkflowDetail:
      allOf:
        - $ref: "#/components/schemas/Workflow"
        - type: object
          properties:
            webhook_url:
              type: string
              nullable: true
              description: The workflow's inbound webhook trigger URL (owner-only; not included in exports).
    WorkflowExport:
      type: object
      description: Portable definition (secrets and webhook tokens omitted).
      properties:
        format: { type: string, example: yak_pipes.workflow }
        format_version: { type: integer }
        name: { type: string }
        graph: { $ref: "#/components/schemas/Graph" }
        notification_settings: { type: object }
        connector_slugs: { type: array, items: { type: string } }
        versions: { type: array, items: { type: object } }
    Run:
      type: object
      properties:
        id: { type: integer }
        workflow_id: { type: integer }
        status: { type: string, enum: [queued, running, succeeded, failed, canceled] }
        trigger_source: { type: string, enum: [manual, webhook, schedule, replay] }
        error: { type: string, nullable: true }
        started_at: { type: string, format: date-time, nullable: true }
        finished_at: { type: string, format: date-time, nullable: true }
        inserted_at: { type: string, format: date-time }
    RunStep:
      type: object
      properties:
        node_id: { type: string }
        op_id: { type: string, nullable: true }
        sequence: { type: integer }
        status: { type: string }
        output: { type: object, nullable: true }
        error: { type: string, nullable: true }
        started_at: { type: string, format: date-time, nullable: true }
        finished_at: { type: string, format: date-time, nullable: true }
    RunDetail:
      allOf:
        - $ref: "#/components/schemas/Run"
        - type: object
          properties:
            trigger_payload: { type: object }
            steps:
              type: array
              items: { $ref: "#/components/schemas/RunStep" }
    RunQueued:
      type: object
      properties:
        data:
          type: object
          properties:
            run_id: { type: integer }
            status: { type: string }
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    PaymentRequired:
      description: The YakPipes plan is inactive.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Not found (or not owned by this account).
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unprocessable:
      description: Validation failed.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    TooManyRequests:
      description: A plan run quota was exceeded.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
paths:
  /workflows:
    get:
      summary: List workflows (most recently updated first)
      responses:
        "200":
          description: Workflows.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Workflow" }
        "401": { $ref: "#/components/responses/Unauthorized" }
    post:
      summary: Create a workflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                graph: { $ref: "#/components/schemas/Graph" }
                notify_on_failure: { type: boolean }
                notification_webhook_url: { type: string }
                notification_secret_header: { type: string }
      responses:
        "201":
          description: Created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/Workflow" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "422": { $ref: "#/components/responses/Unprocessable" }
        "429": { $ref: "#/components/responses/TooManyRequests" }
  /workflows/import:
    post:
      summary: Import a portable workflow definition
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              description: Either the export itself, or wrapped as {"data"{...}}.
              properties:
                data: { $ref: "#/components/schemas/WorkflowExport" }
      responses:
        "201":
          description: Imported as a draft workflow.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/Workflow" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "422": { $ref: "#/components/responses/Unprocessable" }
  /workflows/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: integer }
    get:
      summary: Fetch a workflow (includes its webhook trigger URL)
      responses:
        "200":
          description: Workflow detail.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/WorkflowDetail" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
    patch:
      summary: Update name/graph/notification settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string }
                graph: { $ref: "#/components/schemas/Graph" }
                notify_on_failure: { type: boolean }
                notification_webhook_url: { type: string }
                notification_secret_header: { type: string }
      responses:
        "200":
          description: Updated.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/Workflow" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "422": { $ref: "#/components/responses/Unprocessable" }
    delete:
      summary: Delete a workflow (and its runs)
      responses:
        "204": { description: Deleted. }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /workflows/{id}/export:
    get:
      summary: Export a portable definition (secrets omitted)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Export.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/WorkflowExport" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /workflows/{id}/publish:
    post:
      summary: Publish the draft graph as an immutable version
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Published; returns the workflow.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/Workflow" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "422": { $ref: "#/components/responses/Unprocessable" }
  /workflows/{id}/run:
    post:
      summary: Start a run (counts toward the manual run quota)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                payload:
                  type: object
                  description: Trigger payload available as {{trigger.*}} in node configs.
      responses:
        "202":
          description: Run queued.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RunQueued" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/TooManyRequests" }
  /workflows/{id}/runs:
    get:
      summary: List a workflow's runs (most recent first)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Runs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Run" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /runs:
    get:
      summary: List account runs (filterable, max 200)
      parameters:
        - name: status
          in: query
          schema: { type: string, enum: [queued, running, succeeded, failed, canceled] }
        - name: workflow_id
          in: query
          schema: { type: integer }
        - name: trigger
          in: query
          schema: { type: string, enum: [manual, webhook, schedule, replay] }
        - name: window
          in: query
          schema: { type: string, description: "e.g. 24h" }
      responses:
        "200":
          description: Runs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/Run" }
        "401": { $ref: "#/components/responses/Unauthorized" }
  /runs/{id}:
    get:
      summary: Fetch a run with step-by-step results
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "200":
          description: Run detail.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data: { $ref: "#/components/schemas/RunDetail" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
  /runs/{id}/replay:
    post:
      summary: Replay a run with its original payload and version
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        "202":
          description: Replay queued.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/RunQueued" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/TooManyRequests" }
