deerflow-code/offline-backend-20260512/backend/docs/WORKFLOW_OPENAPI_DRAFT_V1.yaml
2026-09-07 18:24:55 +08:00

741 lines
20 KiB
YAML

openapi: 3.0.3
info:
title: DeerFlow Workflow Studio API (Draft v1)
version: "1.0.0-draft"
description: |
Wire models: deerflow.workflows.schemas / events / errors.
Paths marked `x-implemented: false` are still sketches; everything else is
mounted today (app/gateway/routers/workflow*.py). Where the phase-0 sketch
and the implementation disagreed, this file follows the implementation.
servers:
- url: http://localhost:8001
description: Local Gateway
tags:
- name: workflows
- name: runs
- name: resources
- name: data-sources
- name: embed
- name: coze-compat
paths:
/api/workflows:
get:
tags: [workflows]
summary: List workflow definitions
responses:
"200":
description: OK
post:
tags: [workflows]
summary: Create workflow definition
responses:
"201":
description: Created
/api/workflows/{workflow_id}:
get:
tags: [workflows]
summary: Get workflow definition
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: OK
patch:
tags: [workflows]
summary: Update workflow metadata
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: OK
delete:
tags: [workflows]
summary: Soft-delete / archive
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"204":
description: Archived
/api/workflows/{workflow_id}/copy:
post:
tags: [workflows]
summary: Duplicate a workflow as a new draft owned by the caller
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: Created copy
/api/workflows/{workflow_id}/draft:
put:
tags: [workflows]
summary: Save draft (optimistic lock)
parameters:
- $ref: "#/components/parameters/WorkflowId"
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [expectedRevision, graph]
properties:
expectedRevision:
type: integer
graph:
$ref: "#/components/schemas/WorkflowGraph"
responses:
"200":
description: Saved
"409":
description: Draft revision conflict
/api/workflows/{workflow_id}/validate:
post:
tags: [workflows]
summary: Validate draft without publishing
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: Validation result
/api/workflows/{workflow_id}/publish:
post:
tags: [workflows]
summary: Publish immutable version
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"201":
description: Version created
/api/workflows/{workflow_id}/versions:
get:
tags: [workflows]
summary: List published versions
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: OK
/api/workflows/{workflow_id}/versions/{version_id}:
get:
tags: [workflows]
summary: Get one published version snapshot
parameters:
- $ref: "#/components/parameters/WorkflowId"
- $ref: "#/components/parameters/VersionId"
responses:
"200":
description: OK
/api/workflows/node-types:
get:
tags: [resources]
summary: List supported node types
responses:
"200":
description: OK
/api/workflows/resources/agents:
get:
tags: [resources]
summary: Agents available to workflow nodes
responses:
"200":
description: OK
/api/workflows/resources/skills:
get:
tags: [resources]
summary: Skills available to workflow nodes
responses:
"200":
description: OK
/api/workflows/resources/data-sources:
get:
tags: [resources]
summary: Registered read-only data sources
responses:
"200":
description: OK
/api/workflows/resources/subworkflows:
get:
tags: [resources]
summary: Published workflows usable as subworkflows
responses:
"200":
description: OK
/api/workflows/data-sources:
get:
tags: [data-sources]
summary: List data sources visible to the caller (own + shared)
description: |
Never returns the DSN or credential headers — only `masked_target`.
responses:
"200":
description: OK
post:
tags: [data-sources]
summary: Register a data source (`shared: true` is admin-only)
description: |
`kind: sql` requires `dsn`; `kind: http` requires `headers`. The secret
is encrypted on write and only ever decrypted inside the run executor.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/DataSourceCreate"
responses:
"200":
description: Created (masked view)
"503":
description: WORKFLOW_SECRET_KEY not configured
/api/workflows/data-sources/{id}:
get:
tags: [data-sources]
summary: Get one data source (masked view)
parameters:
- $ref: "#/components/parameters/DataSourceId"
responses:
"200":
description: OK
put:
tags: [data-sources]
summary: Update data source (owner or admin)
parameters:
- $ref: "#/components/parameters/DataSourceId"
responses:
"200":
description: OK
delete:
tags: [data-sources]
summary: Delete data source (owner or admin)
parameters:
- $ref: "#/components/parameters/DataSourceId"
responses:
"200":
description: '{ "deleted": true }'
/api/workflows/data-sources/{id}/introspect:
post:
tags: [data-sources]
summary: Introspect whitelisted schema/table/column metadata
description: Returns the configured table allowlist only. Never returns sample rows or secrets.
parameters:
- $ref: "#/components/parameters/DataSourceId"
responses:
"200":
description: '{ dataSourceId, kind, tables, schemas }'
/api/workflows/data-sources/{id}/validate-query:
post:
tags: [data-sources]
summary: Validate SQL against read-only policy (no execution)
parameters:
- $ref: "#/components/parameters/DataSourceId"
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [statement]
properties:
statement:
type: string
responses:
"200":
description: '{ ok: true, statement }'
"400":
description: WORKFLOW_SQL_POLICY_DENIED
/api/workflows/{workflow_id}/runs:
post:
tags: [runs]
summary: Start a run against a published version
parameters:
- $ref: "#/components/parameters/WorkflowId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/StartRunRequest"
responses:
"200":
description: |
Run view plus `created`; `created: false` means the idempotencyKey
matched an existing run (scoped per workflow).
"400":
description: WORKFLOW_INPUT_INVALID / WORKFLOW_VERSION_NOT_FOUND
"429":
description: WORKFLOW_LIMIT_EXCEEDED (per-user concurrent runs)
get:
tags: [runs]
summary: List runs for a workflow
parameters:
- $ref: "#/components/parameters/WorkflowId"
responses:
"200":
description: OK
/api/workflows/runs/{run_id}:
get:
tags: [runs]
summary: Get run status projection plus current node runs
description: |
Never includes `resumeToken` — that is delivered on the event stream only.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: OK
"403":
description: WORKFLOW_FORBIDDEN (not owner, not admin)
"404":
description: WORKFLOW_RUN_NOT_FOUND
/api/workflows/runs/{run_id}/events:
get:
tags: [runs]
summary: Page durable events
parameters:
- $ref: "#/components/parameters/RunId"
- name: after
in: query
description: Exclusive `seq` cursor. `after_seq` is accepted as an alias.
schema:
type: integer
default: 0
- name: after_seq
in: query
schema:
type: integer
- name: limit
in: query
schema:
type: integer
default: 200
maximum: 1000
responses:
"200":
description: '{ "events": [...], "lastSeq": 42 }'
/api/workflows/runs/{run_id}/stream:
get:
tags: [runs]
summary: SSE replay + live tail
description: |
Replays from `Last-Event-ID` (preferred) or `after` / `after_seq`, then
tails live. Closes on the first terminal event; if the run is already
terminal without one, a synthesised terminal frame is emitted so the
client never hangs.
parameters:
- $ref: "#/components/parameters/RunId"
- name: after
in: query
schema:
type: integer
default: 0
- name: after_seq
in: query
schema:
type: integer
- name: Last-Event-ID
in: header
schema:
type: string
responses:
"200":
description: text/event-stream
headers:
X-Workflow-Run-Status:
description: Run status at connection time.
schema:
type: string
content:
text/event-stream:
schema:
type: string
/api/workflows/runs/{run_id}/cancel:
post:
tags: [runs]
summary: Request cancellation (idempotent)
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: |
Run view plus `cancelled`; `false` means it was already terminal.
/api/workflows/runs/{run_id}/resume:
post:
tags: [runs]
summary: Resume after human_input
parameters:
- $ref: "#/components/parameters/RunId"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ResumeRunRequest"
responses:
"200":
description: Requeued
"409":
description: WORKFLOW_RUN_NOT_RESUMABLE / WORKFLOW_RESUME_TOKEN_INVALID
/api/workflows/runs/{run_id}/retry:
post:
tags: [runs]
summary: Retry a failed run from the first unfinished node
description: |
Creates a new queued run with `retryOfRunId` pointing at the failed
original. Completed `workflow_node_runs` are copied so the engine
resumes from the failed node; if nothing is replayable it is a full
re-run of the same version and inputs. Only `failed` runs are retryable.
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"201":
description: '{ ..., retryOfRunId, created: true }'
"409":
description: WORKFLOW_RUN_NOT_RETRYABLE
"429":
description: WORKFLOW_LIMIT_EXCEEDED
/api/workflows/runs/{run_id}/artifacts:
get:
tags: [runs]
summary: List artifacts for a run
parameters:
- $ref: "#/components/parameters/RunId"
responses:
"200":
description: OK
/api/workflows/embed/tickets:
post:
tags: [embed]
summary: Issue a short-lived iframe ticket (authenticated parent)
description: |
`origin` must exactly match `workflows.embed.allowed_origins`. The
response also carries `frameAncestors` for the host's CSP.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [workflowId, origin]
properties:
workflowId:
type: string
origin:
type: string
responses:
"200":
description: '{ ticket, expiresIn, origin, frameAncestors }'
"403":
description: WORKFLOW_EMBED_ORIGIN_DENIED / WORKFLOW_FORBIDDEN
"503":
description: No signing key configured
/api/workflows/embed/verify:
post:
tags: [embed]
summary: Verify a ticket (single-use, best effort per process)
description: |
A ticket proves "this origin may embed this workflow for this user right
now". It is NOT an API credential — embedded pages still authenticate
their API calls with the normal session.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [ticket]
properties:
ticket:
type: string
origin:
type: string
responses:
"200":
description: '{ ok, workflowId, userId, origin, expiresAt }'
"401":
description: WORKFLOW_EMBED_TICKET_INVALID (expired or replayed)
"403":
description: WORKFLOW_EMBED_ORIGIN_DENIED (origin mismatch)
/api/workflow_api/canvas:
post:
tags: [coze-compat]
summary: Coze canvas load adapter
responses:
"200":
description: Coze DTO
/api/workflow_api/save:
post:
tags: [coze-compat]
summary: Coze canvas save adapter
responses:
"200":
description: Saved
/api/workflow_api/create:
post:
tags: [coze-compat]
summary: Coze create workflow adapter
responses:
"200":
description: Created
/api/workflow_api/node_template_list:
post:
tags: [coze-compat]
summary: Coze node template list adapter
responses:
"200":
description: Templates
components:
parameters:
WorkflowId:
name: workflow_id
in: path
required: true
schema:
type: string
VersionId:
name: version_id
in: path
required: true
schema:
type: string
RunId:
name: run_id
in: path
required: true
schema:
type: string
DataSourceId:
name: id
in: path
required: true
schema:
type: string
schemas:
WorkflowGraph:
type: object
description: Internal standard graph (see WORKFLOW_SCHEMA_V1_ZH.md)
required: [schemaVersion, id, nodes, edges]
properties:
schemaVersion:
type: string
enum: ["1.0"]
id:
type: string
name:
type: string
description:
type: string
inputSchema:
type: object
outputSchema:
type: object
nodes:
type: array
items:
$ref: "#/components/schemas/WorkflowNode"
edges:
type: array
items:
$ref: "#/components/schemas/WorkflowEdge"
settings:
$ref: "#/components/schemas/WorkflowGraphSettings"
WorkflowNode:
type: object
required: [id, type]
properties:
id:
type: string
type:
type: string
enum:
- start
- output
- agent
- skill
- http
- sql_read
- code
- transform
- condition
- merge
- human_input
- subworkflow
- loop
name:
type: string
config:
type: object
WorkflowEdge:
type: object
required: [id, source, target]
properties:
id:
type: string
source:
type: string
target:
type: string
sourcePort:
type: string
nullable: true
WorkflowGraphSettings:
type: object
properties:
runTimeoutSeconds:
type: integer
default: 1800
nodeTimeoutSeconds:
type: integer
default: 300
maxSteps:
type: integer
default: 200
maxLoopIterations:
type: integer
default: 5
maxParallelism:
type: integer
default: 4
NodeResult:
type: object
properties:
data:
type: object
messages:
type: array
items:
type: object
artifacts:
type: array
items:
$ref: "#/components/schemas/ArtifactRef"
metadata:
type: object
warnings:
type: array
items:
type: string
ArtifactRef:
type: object
required: [artifactId, name]
properties:
artifactId:
type: string
name:
type: string
mimeType:
type: string
path:
type: string
sizeBytes:
type: integer
nullable: true
preview:
type: string
nullable: true
StartRunRequest:
type: object
properties:
versionId:
type: string
description: Omit to use the latest published version.
inputs:
type: object
idempotencyKey:
type: string
maxLength: 128
executionMode:
type: string
enum: [normal]
default: normal
ResumeRunRequest:
type: object
required: [resumeToken]
properties:
resumeToken:
type: string
nodeId:
type: string
description: Defaults to the paused node from `pendingInput`.
action:
type: string
default: submit
values:
type: object
DataSourceCreate:
type: object
required: [name]
properties:
name:
type: string
description:
type: string
kind:
type: string
enum: [sql, http]
default: sql
dsn:
type: string
description: Write-only; required for `kind: sql`.
headers:
type: object
additionalProperties:
type: string
description: Write-only; required for `kind: http`.
maxRows:
type: integer
default: 1000
allowedTables:
type: array
items:
type: string
enabled:
type: boolean
default: true
shared:
type: boolean
default: false
description: Admin-only. Shared sources have a null owner.
WorkflowEventEnvelope:
type: object
required: [schemaVersion, runId, workflowId, versionId, seq, event]
properties:
schemaVersion:
type: string
enum: ["1.0"]
runId:
type: string
workflowId:
type: string
versionId:
type: string
seq:
type: integer
event:
type: string
nodeId:
type: string
nullable: true
nodeRunId:
type: string
nullable: true
timestamp:
type: string
format: date-time
data:
type: object
WorkflowErrorBody:
type: object
required: [code, message]
properties:
code:
type: string
message:
type: string
retryable:
type: boolean
nodeId:
type: string
nullable: true
details:
type: object