Skip to main content

Workflows

A workflow is a saved node graph — inputs, generation steps, text steps, and outputs wired together — that runs as one pipeline. This page covers managing workflow definitions. To execute one, see Workflow runs.

Most API callers build workflows on the canvas and only ever call GET /v1/workflows/:id (to discover input node ids) and execute. Full CRUD is here for tooling that generates pipelines programmatically.

The workflow object

FieldTypeDescription
idstringWorkflow id (UUID).
namestringDisplay name. Defaults to "Untitled Workflow".
graphobject{ nodes, edges }. See The graph.
projectIdstring | nullThe project the workflow belongs to. Every workflow has one; a personal workflow lives in your default project.
createdAtstringISO 8601.
updatedAtstringISO 8601.

List responses return a lighter summary instead:

FieldTypeDescription
id · name · projectId · updatedAtAs above.
nodeCountnumberNumber of nodes in the graph.
previewUrlstring | nullTile visual: the latest complete run's first media output, else an attached input image. Signed and expiring.
previewKind"image" | "video" | nullWhat previewUrl points at.

The graph

graph.nodes[]

FieldTypeDescription
idstringNode id — this is the key you pass run inputs under.
typestringNode type, e.g. prompt, imageInput, generateImage, output.
x · ynumberCanvas coordinates.
dataobjectNode-specific configuration. Opaque to the server.

graph.edges[]

FieldTypeDescription
idstringEdge id.
source · targetstringNode ids.
sourceHandle · targetHandlestring | nullPort ids on each end.

The server validates structure only — that nodes and edges are arrays, that each node has id/type/x/y, that each edge has id/source/target, and that the serialized graph is under 1 MB. Node semantics belong to the client, which is why hand-authoring a graph is harder than it looks. Build on the canvas, then read it back.

Input node types

Four node types accept per-run overrides. These are the ones worth finding in graph.nodes:

typeTakesOverrides
prompt{ "text": "…" }the node's data.text
generateText{ "text": "…" }the node's data.instruction
imageInput{ "storageId": "…" }the node's data.storageId
videoInput{ "storageId": "…" }the node's data.storageId

Passing an input for any other node type is invalid_params (400).


GET /v1/workflows

List workflows in one project.

Query parameters

NameTypeDefaultDescription
projectIdstringyour default projectWhich project's workflows to list. You must be a member.
limitnumberunsetPage size, 1500. Omit for the full list (the app gallery renders everything).
offsetnumber0Only meaningful with limit.
sortstringupdatedupdated (newest first) · created (newest first) · name (alphabetical).

Because scoping is by project rather than by author, this returns every member's workflows in a shared project — not just yours.

Returns { "workflows": WorkflowSummary[], "total": number }total counts every workflow in the project, not the page.

Request
curl -s "https://api.corte.so/v1/workflows?sort=name" \
-H "Authorization: Bearer corte_sk_…"
Response
{
"workflows": [
{
"id": "7c93b1de-2a48-4f60-91b7-5d0e6a2c8f13",
"name": "Product Reshoot Pipeline",
"nodeCount": 6,
"updatedAt": "2026-07-27T09:41:22.006Z",
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"previewUrl": "https://media.corte.so/…/frame.jpg?X-Amz-Signature=…",
"previewKind": "image"
}
],
"total": 12
}

GET /v1/workflows/:id

Fetch one workflow with its full graph. This is how you discover input node ids before executing.

Returns { "workflow": Workflow }.

Request
curl -s https://api.corte.so/v1/workflows/7c93b1de-2a48-4f60-91b7-5d0e6a2c8f13 \
-H "Authorization: Bearer corte_sk_…"
Response
{
"workflow": {
"id": "7c93b1de-2a48-4f60-91b7-5d0e6a2c8f13",
"name": "Product Reshoot Pipeline",
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"createdAt": "2026-07-14T11:02:55.310Z",
"updatedAt": "2026-07-27T09:41:22.006Z",
"graph": {
"nodes": [
{ "id": "productPhoto", "type": "imageInput", "x": 40, "y": 120, "data": { "storageId": "b0e7c9a1-…", "previewUrl": "https://media.corte.so/…" } },
{ "id": "concept", "type": "prompt", "x": 40, "y": 300, "data": { "text": "Alpine trail-running capsule, cold dawn light" } },
{ "id": "reshoot", "type": "generateImage", "x": 360, "y": 200, "data": { "modelId": "nano-banana-2", "aspectRatio": "4:5" } },
{ "id": "final", "type": "output", "x": 680, "y": 200, "data": {} }
],
"edges": [
{ "id": "e1", "source": "productPhoto", "target": "reshoot", "targetHandle": "image" },
{ "id": "e2", "source": "concept", "target": "reshoot", "targetHandle": "prompt" },
{ "id": "e3", "source": "reshoot", "target": "final", "targetHandle": "in" }
]
}
}
}

Here the run-input keys are productPhoto (takes storageId) and concept (takes text).

:::note Preview URLs are re-signed on every read data.previewUrl on media input nodes is regenerated each time you fetch the workflow. Don't cache it. :::


POST /v1/workflows

Create a workflow.

Body

NameTypeRequiredDescription
graphobjectno{ nodes, edges }. Defaults to an empty graph.
namestringnoDefaults to "Untitled Workflow".
projectIdstringnoBind to a shared project you belong to. Defaults to your default project.

Returns { "workflow": Workflow }.

Request
curl -s -X POST https://api.corte.so/v1/workflows \
-H "Authorization: Bearer corte_sk_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Nightly Ad Variants",
"graph": { "nodes": [], "edges": [] }
}'
Response
{
"workflow": {
"id": "e51f7a09-c3b6-4e82-a97d-4b810c2f6d55",
"name": "Nightly Ad Variants",
"graph": { "nodes": [], "edges": [] },
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"createdAt": "2026-07-28T16:11:03.887Z",
"updatedAt": "2026-07-28T16:11:03.887Z"
}
}

PATCH /v1/workflows/:id

Update a workflow. Partial: omitted fields are left alone.

Body

NameTypeDescription
graphobjectReplaces the whole graph. Not merged.
namestringNew name. Cannot be empty or whitespace.

Returns { "workflow": Workflow }.

Request
curl -s -X PATCH https://api.corte.so/v1/workflows/e51f7a09-… \
-H "Authorization: Bearer corte_sk_…" \
-H "Content-Type: application/json" \
-d '{ "name": "Nightly Ad Variants (EU)" }'
Response
{
"workflow": {
"id": "e51f7a09-c3b6-4e82-a97d-4b810c2f6d55",
"name": "Nightly Ad Variants (EU)",
"graph": { "nodes": [], "edges": [] },
"projectId": "1b6d4f90-77c2-4a5e-b3d8-2e9a0c15f7b4",
"createdAt": "2026-07-28T16:11:03.887Z",
"updatedAt": "2026-07-28T16:24:40.512Z"
}
}

DELETE /v1/workflows/:id

Delete a workflow and all of its run history. Not reversible.

Returns { "ok": true }.

Request
curl -s -X DELETE https://api.corte.so/v1/workflows/e51f7a09-… \
-H "Authorization: Bearer corte_sk_…"
Response
{ "ok": true }

Access rules

Access follows the workflow's project. Any member of a shared project can read, run, and update its workflows. Deleting is narrower: only the project owner or the workflow's creator. A workflow in your own default project is yours alone.

CodeWhen
not_foundUnknown id, or a workflow in a project you don't belong to.
forbiddenDeleting a project workflow you didn't create, without being the project owner.
invalid_paramsMalformed graph, graph over 1 MB, or an empty name.