Workflow API (Beta)
The Workflow API is currently in Beta. The API endpoints and response formats may change in future releases.
The Workflow API provides programmatic access to execute Langflow workflows in sync, stream, or background modes.
The Workflow API is part of the Langflow Developer v2 API and offers enhanced workflow execution capabilities compared to the v1 /run endpoint.
As of Langflow version 1.11, stream mode supports both Langflow's built-in EventManager and the AG-UI protocol.
Start with the Workflow API quickstart for a step-by-step walkthrough of sync, stream, and background modes.
Prerequisites
-
Install and start Langflow with the developer API enabled
The Workflow API endpoints require the
developer_api_enabledsetting to be enabled. If this setting is disabled, these endpoints will return a 404 Not Found error.To enable the developer API endpoint, do the following:
- In the Langflow
.envfile, set the environment variable totrue:LANGFLOW_DEVELOPER_API_ENABLED=true - Start your Langflow server with the
.envfile enabled:uv run langflow run --env-file .env
For more information about configuring environment variables, see Environment variables.
- In the Langflow
-
Create a flow that you want to execute
-
Get the flow ID or endpoint name of the flow you want to execute
Set environment variables
All code examples in this documentation assume you have set the following environment variables:
Python:
import os
LANGFLOW_SERVER_URL = os.getenv("LANGFLOW_SERVER_URL")
LANGFLOW_API_KEY = os.getenv("LANGFLOW_API_KEY")
TypeScript/JavaScript:
const LANGFLOW_SERVER_URL = process.env.LANGFLOW_SERVER_URL;
const LANGFLOW_API_KEY = process.env.LANGFLOW_API_KEY;
Set these environment variables before running the examples, or replace the variable references in the code examples with your actual Langflow server URL and API key.
The default LANGFLOW_SERVER_URL for a local Langflow deployment is http://localhost:7860.
For remote deployments, the domain is set by your hosting service, such as https://UUID.ngrok.app.
Authentication and headers
All Workflow API requests require authentication using a Langflow API key. The API key is passed in the x-api-key header.
For more information, see Create a Langflow API key.
| Header | Description | Example |
|---|---|---|
Content-Type | Specifies the JSON format. | application/json |
x-api-key | Your Langflow API key. | sk-... |
accept | Optional. Specifies the response format. | application/json |
Execute workflow endpoint
Endpoint:
POST /api/v2/workflows
Description: Execute a workflow in sync, stream, or background mode.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
flow_id | string | Yes | - | UUID of the flow to execute. |
input_value | string | No | "" | Chat-style input for the run. |
mode | string | No | sync | Execution mode: sync, stream, or background. |
stream_protocol | string | No | langflow | Stream adapter name used for stream responses and background event re-attachment. Unknown values return a 422 code for any request mode because the API validates the field against the registered adapters before execution starts. |
session_id | string | No | - | Scopes message memory and chat history to this session. |
tweaks | object | No | {} | Per-component parameter overrides keyed by component id. See Component tweaks. |
globals | object | No | {} | Request-level global variables. Available in sync mode only. Keys are limited to 256 characters and values to 64 KB. See Pass request-level global variables. |
output_ids | array | No | - | Output component ids to use when resolving sync-mode answers (sync mode only). |
data | object | No | - | Live-canvas override of nodes and edges (stream and background modes only). |
files | array | No | - | Pre-uploaded file paths to attach to the run (stream and background modes only). |
start_component_id | string | No | - | Partial-run start component id (stream and background modes only). |
stop_component_id | string | No | - | Partial-run stop component id (stream and background modes only). |
Execute flow in sync mode
Set "mode": "sync" or omit mode to run inline and receive the full response when execution completes.
- Python
- JavaScript
- curl
import os
import requests
base = os.environ.get("LANGFLOW_URL") or os.environ.get("LANGFLOW_SERVER_URL", "")
flow_id = os.environ.get("FLOW_ID", "")
api_key = os.environ.get("LANGFLOW_API_KEY", "")
headers = {"Content-Type": "application/json", "x-api-key": api_key}
payload = {
"flow_id": flow_id,
"input_value": "what is 2+2",
"session_id": "session-123",
"mode": "sync",
"globals": {
"FILENAME": "relatório—final.pdf",
"OWNER_NAME": "José",
},
}
response = requests.post(f"{base}/api/v2/workflows", headers=headers, json=payload, timeout=120)
response.raise_for_status()
print(response.text)
const url = `${process.env.LANGFLOW_SERVER_URL ?? ""}/api/v2/workflows`;
const options = {
method: 'POST',
headers: {
"Content-Type": `application/json`,
"x-api-key": `${process.env.LANGFLOW_API_KEY ?? ""}`,
},
body: JSON.stringify({
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "what is 2+2",
"session_id": "session-123",
"mode": "sync",
"globals": {
"FILENAME": "relatório—final.pdf",
"OWNER_NAME": "José"
}
}),
};
fetch(url, options)
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const text = await response.text();
console.log(text);
})
.catch((error) => console.error(error));
curl -X POST \
"$LANGFLOW_SERVER_URL/api/v2/workflows" \
-H "Content-Type: application/json" \
-H "x-api-key: $LANGFLOW_API_KEY" \
-d '{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "what is 2+2",
"session_id": "session-123",
"mode": "sync",
"globals": {
"FILENAME": "relatório—final.pdf",
"OWNER_NAME": "José"
}
}'
Execute flow with selected stream mode
Set "mode": "stream" to receive a Server-Sent Events (SSE) stream for the run.
stream_protocol is set to langflow by default, which emits the built-in Langflow EventManager output.
For more information, see Stream with Langflow EventManager
Set stream_protocol to agui to emit events using the AG-UI protocol instead.
For more information, see Stream with AG-UI.
If stream_protocol names an unknown adapter, the endpoint returns 422 with an available list of registered protocol names.
Stream with Langflow EventManager
stream_protocol defaults to langflow, which passes through the built-in Langflow EventManager events. Each SSE frame has a monotonic id: and a data: line containing JSON shaped like {"event": "<type>", "data": {...}}. This is the same wire format the v1 build stream uses, so existing Langflow clients can read it without changes.
Omit stream_protocol or set it explicitly to "langflow". Use curl -N so the client does not buffer the stream.
- Python
- JavaScript
- curl
import os
import requests
base = os.environ.get("LANGFLOW_URL") or os.environ.get("LANGFLOW_SERVER_URL", "")
flow_id = os.environ.get("FLOW_ID", "67ccd2be-17f0-8190-81ff-3bb2cf6508e6")
api_key = os.environ.get("LANGFLOW_API_KEY", "")
headers = {"Content-Type": "application/json", "x-api-key": api_key}
payload = {
"flow_id": flow_id,
"input_value": "Hello from a Langflow stream client",
"mode": "stream",
"session_id": "session-123",
}
with requests.post(
f"{base}/api/v2/workflows",
headers=headers,
json=payload,
stream=True,
timeout=120,
) as response:
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if line:
print(line)
const url = `${process.env.LANGFLOW_SERVER_URL ?? ""}/api/v2/workflows`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": `${process.env.LANGFLOW_API_KEY ?? ""}`,
},
body: JSON.stringify({
flow_id: "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
input_value: "Hello from a Langflow stream client",
mode: "stream",
session_id: "session-123",
}),
};
(async () => {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
process.stdout.write(decoder.decode(value, { stream: true }));
}
process.stdout.write(decoder.decode());
})().catch((error) => console.error(error));
curl -N -X POST \
"$LANGFLOW_SERVER_URL/api/v2/workflows" \
-H "Content-Type: application/json" \
-H "x-api-key: $LANGFLOW_API_KEY" \
-d '{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "Hello from a Langflow stream client",
"mode": "stream",
"session_id": "session-123"
}'
Common EventManager event types:
| Event | Purpose |
|---|---|
vertices_sorted | Execution order for the flow's components. |
add_message | Chat message added (user or assistant). |
token | Streaming LLM token chunk (data.chunk). |
end_vertex | A component finished building. |
output | Normalized terminal component output (same shape as sync-mode outputs). |
end | Run finished successfully. |
error | Run failed (data.error). |
cancelled | Run stopped by the client. |
For more examples of token and add_message events, see Stream LLM token responses.
Stream with AG-UI
Stream with the AG-UI protocol when your client expects agent lifecycle events such as RUN_STARTED and TEXT_MESSAGE_* instead of Langflow's internal events.
Read responses as an SSE stream. Each data: line contains one AG-UI event serialized as JSON. Close flow runs when you receive RUN_FINISHED, or handle failures on RUN_ERROR.
Send POST /api/v2/workflows with "mode": "stream" and "stream_protocol": "agui".
- Python
- JavaScript
- curl
import os
import requests
base = os.environ.get("LANGFLOW_URL") or os.environ.get("LANGFLOW_SERVER_URL", "")
flow_id = os.environ.get("FLOW_ID", "67ccd2be-17f0-8190-81ff-3bb2cf6508e6")
api_key = os.environ.get("LANGFLOW_API_KEY", "")
headers = {"Content-Type": "application/json", "x-api-key": api_key}
payload = {
"flow_id": flow_id,
"input_value": "Hello from an AG-UI client",
"mode": "stream",
"stream_protocol": "agui",
"session_id": "thread-123",
}
with requests.post(
f"{base}/api/v2/workflows",
headers=headers,
json=payload,
stream=True,
timeout=120,
) as response:
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if line:
print(line)
const url = `${process.env.LANGFLOW_SERVER_URL ?? ""}/api/v2/workflows`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": `${process.env.LANGFLOW_API_KEY ?? ""}`,
},
body: JSON.stringify({
flow_id: "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
input_value: "Hello from an AG-UI client",
mode: "stream",
stream_protocol: "agui",
session_id: "thread-123",
}),
};
(async () => {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
process.stdout.write(decoder.decode(value, { stream: true }));
}
process.stdout.write(decoder.decode());
})().catch((error) => console.error(error));
curl -N -X POST \
"$LANGFLOW_SERVER_URL/api/v2/workflows" \
-H "Content-Type: application/json" \
-H "x-api-key: $LANGFLOW_API_KEY" \
-d '{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "Hello from an AG-UI client",
"mode": "stream",
"stream_protocol": "agui",
"session_id": "thread-123"
}'
Common AG-UI event types:
| Event type | Purpose |
|---|---|
RUN_STARTED / RUN_FINISHED / RUN_ERROR | Run lifecycle. |
TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END | Streaming assistant text. |
TOOL_CALL_START / TOOL_CALL_ARGS / TOOL_CALL_END / TOOL_CALL_RESULT | Tool invocation lifecycle. |
STEP_STARTED / STEP_FINISHED | Per-component execution steps. |
STATE_SNAPSHOT / STATE_DELTA | Node graph state (status and output). |
CUSTOM | Langflow-specific extensions (see below). |
Langflow also emits CUSTOM events that generic AG-UI clients can ignore, such as langflow.log, langflow.content.*, langflow.message.removed, and langflow.run.cancelled.
For the full AG-UI event catalog, see the AG-UI specification.
For a longer client example that parses AG-UI frames and chains two runs in one session_id, see example-stream-agui-parse.py (and matching .js / .sh files) under docs/docs/API-Reference/.
Execute flow in background mode
Set "mode": "background" to queue a job and receive a job_id immediately. Poll GET /api/v2/workflows for status and results, re-attach to the event stream with GET /api/v2/workflows/{job_id}/events, or stop the job with POST /api/v2/workflows/stop.
Background runs honor the stream_protocol selected when the job was created.
- Python
- JavaScript
- curl
import os
import requests
base = os.environ.get("LANGFLOW_URL") or os.environ.get("LANGFLOW_SERVER_URL", "")
flow_id = os.environ.get("FLOW_ID", "")
api_key = os.environ.get("LANGFLOW_API_KEY", "")
headers = {"Content-Type": "application/json", "x-api-key": api_key}
payload = {
"flow_id": flow_id,
"input_value": "Process this in the background",
"session_id": "session-456",
"mode": "background",
}
response = requests.post(f"{base}/api/v2/workflows", headers=headers, json=payload, timeout=60)
response.raise_for_status()
print(response.text)
const url = `${process.env.LANGFLOW_SERVER_URL ?? ""}/api/v2/workflows`;
const options = {
method: 'POST',
headers: {
"Content-Type": `application/json`,
"x-api-key": `${process.env.LANGFLOW_API_KEY ?? ""}`,
},
body: JSON.stringify({
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "Process this in the background",
"session_id": "session-456",
"mode": "background"
}),
};
fetch(url, options)
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const text = await response.text();
console.log(text);
})
.catch((error) => console.error(error));
curl -X POST \
"$LANGFLOW_SERVER_URL/api/v2/workflows" \
-H "Content-Type: application/json" \
-H "x-api-key: $LANGFLOW_API_KEY" \
-d '{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"input_value": "Process this in the background",
"session_id": "session-456",
"mode": "background"
}'
See Background response fields in Response body for the job object returned when you queue a background run.
Re-attach to a background run
Endpoint: GET /api/v2/workflows/{job_id}/events (also returned as links.events on the background job response)
You can replay buffered SSE events from a background run and tail the stream until the job finishes.
Each SSE frame includes an id: line. Pass the last id value you received in a Last-Event-ID header to resume from the next event.
headers = {
"x-api-key": api_key,
"Accept": "text/event-stream",
"Last-Event-ID": last_event_id,
}
Response body
The response shape depends on the selected mode.
stream mode returns Server Sent Events.
For more information, see Execute flow with selected stream mode.
sync mode returns a completed WorkflowExecutionResponse, for example:
{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"session_id": "session-123",
"job_id": "a98d9753-9259-47ca-9783-69a9d8d26889",
"object": "response",
"created_timestamp": "2026-06-30T16:00:56.917538+00:00",
"status": "completed",
"output": {
"reason": "single",
"text": "2 + 2 equals 4.",
"source": "ChatOutput-xyz"
},
"has_errors": false,
"errors": [],
"inputs": {},
"globals": {
"FILENAME": "relatório—final.pdf",
"OWNER_NAME": "José"
},
"outputs": {
"ChatOutput-xyz": {
"type": "message",
"status": "completed",
"display_name": "Chat Output",
"content": "2 + 2 equals 4."
}
}
}
Sync response fields
| Field | Description |
|---|---|
output | Primary text answer for the run. |
output.reason | Why output.text was or wasn't populated: single, multiple, none, non_string, or failed. |
output.text | The answer when output.reason is single; otherwise null. |
output.source | Component id that produced output.text when output.reason is single. |
outputs | Map of component id → component result. |
has_errors | true when errors is non-empty. |
created_timestamp | ISO-8601 UTC timestamp (not a Unix epoch). |
session_id | Session used for the run; pass it on the next request to continue the same chat history. |
Each value in outputs includes:
| Field | Description |
|---|---|
type | Content type (message, image, sql, data, file, etc.). |
status | Component run status (completed, failed, …). |
display_name | Human-readable component name. |
content | Component payload. |
metadata | Optional extra metadata for the component output. |
Background response fields
background mode returns a job object immediately (object: "job"). Poll GET /api/v2/workflows until the job completes; the completed result uses the same WorkflowExecutionResponse shape as sync mode (see the example above).
{
"job_id": "a98d9753-9259-47ca-9783-69a9d8d26889",
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"object": "job",
"created_timestamp": "2026-06-30T16:00:56.917538+00:00",
"status": "queued",
"links": {
"status": "/api/v2/workflows?job_id=a98d9753-9259-47ca-9783-69a9d8d26889",
"events": "/api/v2/workflows/a98d9753-9259-47ca-9783-69a9d8d26889/events",
"stop": "/api/v2/workflows/stop"
},
"errors": [],
"globals": {}
}
| Field | Description |
|---|---|
object | Always "job". |
job_id | UUID for polling, re-attaching to events, and stop requests. |
links.status | GET /api/v2/workflows?job_id=… |
links.events | GET /api/v2/workflows/{job_id}/events |
links.stop | POST /api/v2/workflows/stop |
Get workflow status endpoint
Endpoint: GET /api/v2/workflows
Description: Retrieve the status and results of a workflow execution by job ID.
Example request
- Python
- JavaScript
- curl
import os
import requests
base = os.environ.get("LANGFLOW_URL") or os.environ.get("LANGFLOW_SERVER_URL", "")
flow_id = os.environ.get("FLOW_ID", "")
api_key = os.environ.get("LANGFLOW_API_KEY", "")
headers = {"Content-Type": "application/json", "x-api-key": api_key}
start = requests.post(
f"{base}/api/v2/workflows",
headers=headers,
json={
"flow_id": flow_id,
"input_value": "Process this in the background",
"mode": "background",
},
timeout=60,
)
start.raise_for_status()
print(start.text)
const url = `${process.env.LANGFLOW_SERVER_URL ?? ""}/api/v2/workflows?job_id=job_id_1234567890`;
const options = {
method: 'GET',
headers: {
"accept": `application/json`,
"x-api-key": `${process.env.LANGFLOW_API_KEY ?? ""}`,
},
};
fetch(url, options)
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const text = await response.text();
console.log(text);
})
.catch((error) => console.error(error));
curl -X GET \
"$LANGFLOW_SERVER_URL/api/v2/workflows?job_id=job_id_1234567890" \
-H "accept: application/json" \
-H "x-api-key: $LANGFLOW_API_KEY"
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | string | Yes | The job ID returned from a workflow execution. |
Example response
{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"session_id": "session-123",
"job_id": "a98d9753-9259-47ca-9783-69a9d8d26889",
"object": "response",
"created_timestamp": "2026-06-30T16:00:56.917538+00:00",
"status": "completed",
"output": {
"reason": "single",
"text": "Processing complete...",
"source": "ChatOutput-xyz"
},
"has_errors": false,
"errors": [],
"inputs": {},
"globals": {},
"outputs": {
"ChatOutput-xyz": {
"type": "message",
"status": "completed",
"display_name": "Chat Output",
"content": "Processing complete..."
}
}
}
Response body
Returns one of two JSON schemas depending on job state:
| Job state | Response | Notes |
|---|---|---|
queued, in_progress, cancelled | WorkflowJobResponse | Same shape as a background job create response (object: "job", links, …). |
completed | WorkflowExecutionResponse | Full results (example above). |
failed | HTTP 500 | Error detail in the response body; not a workflow result object. |
timed_out | HTTP 408 | Error detail in the response body. |
When the job is still active, poll GET /api/v2/workflows?job_id=… or re-attach to events for live output.
The status values are:
| Status | Description |
|---|---|
queued | Job is queued and waiting to start. |
in_progress | Job is currently executing. |
suspended | Job is paused and waiting for human input. See Human-in-the-Loop. |
completed | Job completed successfully. |
failed | Job failed during execution. |
cancelled | Job was stopped by the client. |
timed_out | Job exceeded the execution timeout. |