Skip to content

Running a Workflow

Once you’ve published a Workflow, you run it by calling POST /v1/workflows/{id} with the version ID (the Endpoint ID in the Developer Portal). The request starts an invocation and returns its ID. Then poll GET /v1/workflow_invocations/{id}, or use the SDK’s wait helper, until the invocation finishes. GET /v1/tasks/{id} doesn’t return invocations.

A request sets an input by its node ID and the output key on that node: prompt for text inputs, and image, video, or audio for media inputs.

The version’s page in the Developer Portal lists each exposed input’s node ID. You can also read them from GET /v1/workflows/{id}, which returns the published graph. An input is exposed unless it’s marked exposedToApp: false, and the key is the name under the node’s nodeOutputs:

{
"id": "4f0c9a52-8a3e-4c5b-9d1e-2b7a6c3e9f10",
"nodeType": "constant-node",
"constantNodeType": "prompt",
"nodeOutputs": {
"prompt": {
"type": "string",
"label": "Scene",
"description": "Describe the scene in one sentence.",
"exposedToApp": true,
"value": "A ceramic mug on a marble counter, soft daylight"
}
}
}

Pass your values in nodeOutputs. Inputs you leave out run with the value saved in the graph.

ValueShapeUse for
Primitive{ "type": "primitive", "value": … }Text inputs
Media{ "type": "image" | "video" | "audio", "uri": … }Images, video, and audio. uri accepts an HTTPS URL, a Runway upload URI, or a base64 data URI of up to 5 MB.

The API doesn’t reject a node ID or key that doesn’t match an input; the run uses the saved value instead. Copy IDs from the version page.

import RunwayML, { WorkflowInvocationFailedError } from '@runwayml/sdk';
const client = new RunwayML();
try {
// Start the published version with your own inputs
const { id } = await client.workflows.run('YOUR_WORKFLOW_ID', {
nodeOutputs: {
// Replace with the node IDs from the Developer Portal
'TEXT_NODE_ID': {
prompt: { type: 'primitive', value: 'A timelapse on a sunny day with clouds flying by' },
},
'IMAGE_NODE_ID': {
image: {
type: 'image',
uri: 'https://upload.wikimedia.org/wikipedia/commons/8/85/Tour_Eiffel_Wikimedia_Commons_(cropped).jpg',
},
},
},
});
// Wait up to 30 minutes for the whole graph to finish
const invocation = await client.workflowInvocations
.retrieve(id)
.waitForTaskOutput({ timeout: 30 * 60 * 1000 });
console.log('Outputs:', invocation.output);
console.log('Node errors:', invocation.nodeErrors);
} catch (error) {
if (error instanceof WorkflowInvocationFailedError) {
console.error('The workflow failed.');
console.error(error.invocationDetails);
} else {
console.error(error);
}
}

The wait helpers give up after 10 minutes by default. A graph with several video steps can take longer, so pass a longer timeout: milliseconds in Node, seconds in Python. On timeout they throw WorkflowInvocationTimedOutError in Node and WorkflowInvocationTimeoutError in Python; the invocation keeps running, so you can keep polling.

StatusMeaning
PENDINGThe invocation is queued.
THROTTLEDThe invocation is queued and hasn’t started. Treat it like PENDING.
RUNNINGThe graph is running. progress rises from 0 to 1.
SUCCEEDEDNo more nodes can run. Individual nodes can still have failed or been skipped; see Partial runs.
FAILEDThe invocation stopped as a whole, usually because the graph couldn’t start. Read failure and, when present, failureCode.

Poll no more often than once every five seconds. The SDK wait helpers return when the invocation succeeds and throw WorkflowInvocationFailedError when it fails.

An invocation succeeds once no more nodes can run, so a node can fail, for example on a moderated prompt or a provider outage, while the invocation as a whole succeeds. Nodes downstream of a failed node don’t run, and neither does a node missing a required input. Don’t rely on status alone: check nodeErrors, and check that output has every node you expect.

{
"id": "f1a2b3c4-2c4d-4c6d-9e57-2d2e8ff97903",
"status": "SUCCEEDED",
"createdAt": "2024-06-27T19:49:32.335Z",
"output": {
"a1b2c3d4-e5f6-7890-abcd-ef1234567890": ["https://example.com/output1.jpg"]
},
"nodeErrors": {
"b2c3d4e5-f6a7-8901-bcde-f12345678901": {
"nodeName": "Generate image",
"message": "Image generation failed. Please try again later.",
"failureCode": "THIRD_PARTY.UNAVAILABLE"
}
},
"cost": { "credits": 45 }
}

nodeErrors lists only the nodes that failed, not the nodes skipped after them. failureCode uses the same codes as task failures, and can be absent.

output maps node IDs to arrays of URLs for the outputs you exposed when you published. While the invocation is running, it includes exposed nodes that have already finished.

These URLs expire within 24–48 hours. Fetch the invocation again for fresh URLs, and download the files to your own storage rather than exposing the URLs in your product. See Outputs.

A run uses your Runway Dev credits, not your Runway app workspace’s. It’s billed for each node that runs, at that node’s own price, with no separate charge for the Workflow. When every node has finished, cost.credits reports the total on a SUCCEEDED or FAILED invocation. It’s omitted until then, so fetch the invocation again if it’s missing. See Pricing for model rates.

To stop a RUNNING invocation, call DELETE /v1/tasks/{id} with the invocation ID, or client.tasks.delete() in the SDKs. No new nodes start, but nodes that have already started or are queued still run and are billed.

  • POST /v1/workflows/{id} returns 404 if the version doesn’t exist, was deleted, or was published to a different project than your API key’s.
  • POST /v1/workflows/{id} returns 400 if a node in the published graph uses a model that is no longer available. Replace the node in the Runway app and publish a new version.
  • POST /v1/workflows/{id} returns 400 if a media input can’t be fetched or is over the input limits.
  • GET /v1/workflow_invocations/{id} returns 404 if the invocation doesn’t exist or was cancelled or deleted. There is no CANCELLED status to poll for.

See HTTP errors for the codes every endpoint can return.