Executions

Execute AI workflows, poll for results, and view execution history via the Wireflow API.

Last updated View as Markdown

Executions are the result of running a workflow. The API uses an async pattern: you start an execution, then poll until it completes.

Execute a Workflow

POST /api/v1/workflows/{id}/execute

Starts a workflow execution. Returns 201 immediately with an execution ID. Use the poll endpoint to check for completion.

Access

You need edit-level access to the workflow: you own it, you are an editor on it (a shared editor, or anyone when its link is set to edit), you hold an edit or owner grant on its media, or you are an owner or admin of its team. View-only grants (a view share, a view link, a view-only media grant) are refused with 403 and { "error": "Forbidden" } before any credit is read and before any node in the request is used. To run a board you can only view, duplicate it and run your copy.

One known exception: a board filed in a team folder can be run by every member of that team whatever their team role, a team viewer included, and that run bills the team's credit pool.

Request Body

Field Type Required Description
nodes Node[] Yes Workflow nodes with current input values
edges Edge[] Yes Connections between nodes
targetNodeId string No Execute only this node and its dependencies
triggerData object No Data passed from an external trigger
iteratorConfigs IteratorConfig[] No Iterator execution settings

Request

curl -X POST https://www.wireflow.ai/api/v1/workflows/cm1abc123/execute \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "nodes": [
      {
        "id": "node-1",
        "type": "model",
        "position": { "x": 0, "y": 0 },
        "data": {
          "label": "Text to Image",
          "nodeType": "model:fal:text-to-image",
          "category": "model",
          "prompt": "A sunset over mountains"
        }
      }
    ],
    "edges": []
  }'

Response 201 Created

{
  "workflowId": "cm1abc123",
  "batchId": "550e8400-e29b-41d4-a716-446655440000",
  "executionId": "exec_456",
  "executionRuns": [
    {
      "id": "exec_456",
      "startTime": "2025-03-20T12:01:00.000Z",
      "endTime": null,
      "status": "RUNNING",
      "error": null,
      "nodeRuns": [
        {
          "nodeId": "node-1",
          "status": "COMPLETED",
          "output": {
            "images": [{ "url": "https://cdn.atomu.ai/..." }]
          }
        }
      ],
      "batchId": "550e8400-e29b-41d4-a716-446655440000",
      "runIndexInBatch": 0,
      "inputIndex": 0
    }
  ]
}

executionId is the id to poll. It is the same value as executionRuns[0].id, which stays in the response for existing clients. Error responses carry no executionId.

The response includes a Location header pointing to the execution status endpoint.

Insufficient Credits

If you don't have enough credits, the API returns 402:

{
  "error": "Insufficient credits. Required: 10, Available: 3",
  "insufficientCredits": true,
  "requiredCredits": 10,
  "availableCredits": 3,
  "breakdown": [
    { "nodeId": "node-1", "nodeLabel": "Text to Image", "credits": 10 }
  ]
}

Poll Execution Status

GET /api/v1/workflows/executions/{executionId}/poll

Checks the status of an async execution. For long-running AI models (video generation, etc.), poll this endpoint until status is COMPLETED or FAILED. This is the URL the execute response's Location header points to.

Request

curl https://www.wireflow.ai/api/v1/workflows/executions/exec_456/poll \
  -H "Authorization: Bearer sk-your-api-key"

Response — Running 200 OK

{
  "status": "RUNNING",
  "nodeResults": {
    "nodeRuns": [
      {
        "nodeId": "node-1",
        "status": "RUNNING",
        "output": null
      }
    ]
  }
}

Response — Completed 200 OK

{
  "status": "COMPLETED",
  "nodeResults": {
    "nodeRuns": [
      {
        "nodeId": "node-1",
        "status": "COMPLETED",
        "output": {
          "images": [{ "url": "https://cdn.atomu.ai/generated-image.png" }]
        }
      }
    ]
  }
}

Response — Failed 200 OK

{
  "status": "FAILED",
  "error": "Model inference failed",
  "nodeResults": {
    "nodeRuns": [
      {
        "nodeId": "node-1",
        "status": "FAILED",
        "error": "Provider returned 500"
      }
    ]
  }
}

Polling Strategy

We recommend polling with exponential backoff:

async function pollExecution(executionId, apiKey) {
  const baseUrl = 'https://www.wireflow.ai/api/v1';
  let delay = 1000; // Start at 1 second

  while (true) {
    const res = await fetch(
      `${baseUrl}/workflows/executions/${executionId}/poll`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    const data = await res.json();

    if (data.status === 'COMPLETED') return data;
    if (data.status === 'FAILED') throw new Error(data.error);

    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 1.5, 10000); // Cap at 10 seconds
  }
}

Get Execution Details

GET /api/v1/workflows/executions/{executionId}

Returns the full execution record including status, node results, and metadata.

Request

curl https://www.wireflow.ai/api/v1/workflows/executions/exec_456 \
  -H "Authorization: Bearer sk-your-api-key"

Response 200 OK

{
  "id": "exec_456",
  "workflowId": "cm1abc123",
  "status": "COMPLETED",
  "triggeredBy": "manual",
  "triggerData": {},
  "nodeResults": { "nodeRuns": [...] },
  "currentNode": null,
  "output": null,
  "error": null,
  "errorNode": null,
  "executionTime": 15000,
  "creditsUsed": 10,
  "startedAt": "2025-03-20T12:01:00.000Z",
  "completedAt": "2025-03-20T12:01:15.000Z",
  "workflowSnapshot": { ... }
}

Runs on a Workflow You Do Not Own

A run you start on somebody else's workflow (a published app you called with your own key, a template, or a run from an MCP client) is your run. You pay for it and you can read it. What you read back is the run and its outputs, not the author's workflow.

When you neither own the workflow nor have edit access to it, the execution reads below return an outputs-only projection. Fields are omitted or set to null, so a client should treat every one of them as optional. Owners and editors get the full responses documented above, unchanged.

Route What a cross-account caller gets
GET /workflows/executions/{id}/poll executionId, status, progress, creditsUsed, error, outputs, outputsWithheld. No nodes, result or nodeStates.
GET /workflows/executions/{id} id, workflowId, status, triggeredBy, currentNode, errorNode, executionTime, creditsUsed, startedAt, completedAt, error, outputs, outputsWithheld.
PATCH /workflows/executions/{id} (cancel) The same fields as the GET above.
GET /workflows/executions/{id}/node-runs/{nodeRunId}/details The run's status, timings, error, credit ledger and run list. The node's output is its media and text, only when the app designates that node (a blueprint answers { ports }). input and result are null, and each provider submission loses inputPayload, providerResponse and inputHash.
GET /workflows/executions/poll?ids= Per node: nodeId, status, error, progress and skip fields. Nothing a node produced; use the single poll for results.

Not returned to a cross-account caller: nodes, edges, nodeResults, workflowSnapshot, triggerData, the stored output, any node's resolved input, and anything produced by an Input node.

The MCP tool get_execution reads a run on a workflow you do not own through the same projection. It answers id, status, error, creditsUsed, startedAt, completedAt, outputs and outputsWithheld as described below, plus nodeStates that carry each node's status and error (with the author's saved values replaced by [redacted]) and nothing a node produced. If the workflow cannot be read, the tool answers a retryable error.

{
  "executionId": "exec_456",
  "status": "COMPLETED",
  "creditsUsed": 10,
  "outputs": [
    {
      "nodeId": "node-video",
      "label": "Final reel",
      "nodeType": "video:seedance",
      "status": "success",
      "url": "https://cdn.wireflow.ai/renders/reel-final.mp4"
    }
  ],
  "outputsWithheld": 0
}

Each entry in outputs is one finished node run of this execution. An iterator fan-out yields one entry per take, with iteration set. A node that produced several files carries them in urls, and url is the first. Template executions (template_api) use an older projection of the same shape: every node that ran except Input and utility nodes, with no value check and no outputsWithheld.

Which nodes are outputs

outputs holds what the Apps API would give you for this workflow: the nodes its author picked as the app's outputs (publishSettings.exampleOutputs), or, when none were picked, the final nodes of the graph (the ones nothing consumes), as the graph was when your run started. A node in the middle of the graph is not an output of a run that completed, and an Input node never is.

  • A blueprint:invoke node answers one entry per output port, with the port id in port. If the blueprint failed after earlier steps had already run and charged you, those results come back as entries with status: "partial", whether or not the invoke is a final node.
  • A run you aimed at specific nodes (targetNodeIds) is read the same way: by what the app designates, not by the nodes you named. A run aimed at a node in the middle of the graph returns nothing for it, and neither does a run aimed at a final node the app did not pick.
  • If the run stopped before it finished (FAILED, CANCELLED or TIMEOUT), the image, video, audio and 3D files that finished generation, processing (upscalers, background removal, matte) and vector nodes produced come back too, with status: "partial", because you are still charged for them. Nothing else a node finished comes back on a stopped run: not its text, not fetched data, not a model's reply, not a utility's output. Those are counted in outputsWithheld. The status of an output the app itself designated stays success.
  • A utility or logic node returns the media it produced. It never returns text.
  • A text or media value equal to something the author saved in one of the workflow's inputs is held back, as is any text that contains a saved value with no spaces in it and 16 characters or more (a key, a cookie) or any saved value of 64 characters or more. A saved cookie header, JSON export or cookie file is also held back when only one pair or one value of it comes back, in its encoded or decoded form. A saved url is held back only when the value is exactly that url, so a caption that cites a plain image url is returned; a token in the url's query or path (a webhook url's secret) is held back wherever it appears. That is what keeps a router or a preview wired to a saved input from handing the input back to you. The check covers the values your run was started with, so it keeps holding after the author changes or deletes one. What you typed yourself comes back to you.

outputsWithheld counts the outputs the run owes you (the app's outputs, and on a run that stopped early every finished node outside them) that had something held back. 0 means none did. A positive number next to an empty outputs means something was produced and kept from you, not that nothing was produced. A node in the middle of a run that completed is not an output and is not counted. Error messages have the author's saved values replaced with [redacted].

If the server cannot read the workflow to decide this, the read answers 503 with a retryable error. The run itself is unaffected, so retry in a moment.

List Execution History

GET /api/v1/workflows/{id}/executions

Returns recent executions for a workflow, ordered by most recent first.

Query Parameters

Parameter Type Default Description
limit integer 10 Number of executions to return
offset integer 0 Offset for pagination

Request

curl "https://www.wireflow.ai/api/v1/workflows/cm1abc123/executions?limit=5" \
  -H "Authorization: Bearer sk-your-api-key"

Response 200 OK

[
  {
    "id": "exec_456",
    "workflowId": "cm1abc123",
    "status": "COMPLETED",
    "triggeredBy": "manual",
    "executionTime": 15000,
    "creditsUsed": 10,
    "startedAt": "2025-03-20T12:01:00.000Z",
    "completedAt": "2025-03-20T12:01:15.000Z",
    "error": null
  }
]

Async Execution Pattern

Wireflow uses an asynchronous execution model:

  1. POST to /workflows/{id}/execute — returns 201 with executionId
  2. GET to /workflows/executions/{id}/poll — returns current status
  3. Repeat step 2 until status is COMPLETED or FAILED

This design handles long-running AI models (video generation can take 5+ minutes) without holding HTTP connections open.

Retries and idempotency

/execute does not deduplicate on an Idempotency-Key header. It records the key in the audit log only, so a retry starts a second run and bills you again. After a timeout or a 5xx, check GET /workflows/{id}/executions (or poll the executionId you already have) before retrying. If you need a key that replays the original run, use POST /workflows/{id}/run.

For AI agents: the full documentation index is at /llms.txt, and most docs pages are available as Markdown by adding .md to the URL.

© 2026 Wireflow. All rights reserved.

Executions | Wireflow Docs