Skip to main content
Version: 1.13.x (Next)

Workflow API quickstart

The Workflow API executes Langflow flows over POST /api/v2/workflows in three modes.

Use the default sync mode when you want to wait for the full JSON result in one response. Use stream when you need live Server-Sent Events (SSE) emitted as the flow runs. Use background when you want to queue a job and then poll or re-attach to its event stream.

This quickstart runs the same flow with each mode, so you can use the mode that fits your application.

Prerequisites​

  • Install and start Langflow with the developer API enabled

    The Workflow API endpoints require the developer_api_enabled setting 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:

    1. In the Langflow .env file, set the environment variable to true:
      LANGFLOW_DEVELOPER_API_ENABLED=true
    2. Start your Langflow server with the .env file enabled:
      uv run langflow run --env-file .env

    For more information about configuring environment variables, see Environment variables.

  • Create a Langflow API key

  • 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.

HeaderDescriptionExample
Content-TypeSpecifies the JSON format.application/json
x-api-keyYour Langflow API key.sk-...
acceptOptional. Specifies the response format.application/json

The examples on this page also expect FLOW_ID to be set to your flow's UUID. Use a chat-style flow (for example Basic Prompting) so output.text contains the assistant reply.

Run a flow synchronously​

Omit mode or set "mode": "sync" to run inline. When the flow finishes, the response includes a top-level output.text field with the primary answer.

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",
}

response = requests.post(f"{base}/api/v2/workflows", headers=headers, json=payload, timeout=120)
response.raise_for_status()
body = response.json()
print(body["output"]["text"])
Example sync response
{
"flow_id": "67ccd2be-17f0-8190-81ff-3bb2cf6508e6",
"session_id": "session-123",
"object": "response",
"status": "completed",
"output": {
"reason": "single",
"text": "2 + 2 equals 4.",
"source": "ChatOutput-xyz"
},
"has_errors": false
}

Pass the same session_id on the next request to continue chat history for that conversation.

Stream LLM tokens​

Set "mode": "stream" to open an SSE stream. Choose the wire format with stream_protocol:

  • langflow (default): Langflow EventManager frames for existing Langflow clients.
  • agui: AG-UI lifecycle events for compatible clients.

For request examples, event types, and protocol details, see Execute flow with selected stream mode in the Workflow API reference.

Langflow EventManager (default)​

Each data: line is JSON shaped like {"event": "<type>", "data": {...}}. The example below prints only token events (data.chunk) as they arrive.

import json
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": "Tell me a short joke.",
"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 not line or not line.startswith("data:"):
continue
frame = json.loads(line.removeprefix("data:").strip())
if frame.get("event") == "token":
print(frame["data"]["chunk"], end="", flush=True)
print()

AG-UI​

Send "stream_protocol": "agui". Each data: line contains one AG-UI event as JSON. The example below handles TEXT_MESSAGE_CONTENT, TOOL_CALL_RESULT, and RUN_FINISHED, extracts a number from the first reply, then chains a follow-up prompt in the same session_id.

import json
import os
import re

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", "")
session_id = os.environ.get("AGUI_SESSION_ID", "thread-123")
number = re.compile(r"-?\d+(?:\.\d+)?")


def extract_number(text: str, tool_results: list) -> float | None:
"""Prefer calculator tool output, then the last number in the assistant reply."""
for raw in reversed(tool_results):
if matches := number.findall(str(raw)):
return float(matches[-1])
if matches := number.findall(text):
return float(matches[-1])
return None


def ask(prompt: str) -> tuple[str, list]:
"""Stream one AG-UI run; return assistant text and tool results."""
text = ""
tool_results = []

with requests.post(
f"{base}/api/v2/workflows",
headers={
"Content-Type": "application/json",
"x-api-key": api_key,
"Accept": "text/event-stream",
},
json={
"flow_id": flow_id,
"input_value": prompt,
"mode": "stream",
"stream_protocol": "agui",
"session_id": session_id,
},
stream=True,
timeout=120,
) as response:
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
event = json.loads(line.removeprefix("data:").strip())
match event.get("type"):
case "TEXT_MESSAGE_CONTENT":
text += event.get("delta", "")
case "TOOL_CALL_RESULT":
tool_results.append(event.get("content") or event.get("result"))
case "RUN_ERROR":
raise RuntimeError(event.get("message") or "Run failed")
case "RUN_FINISHED":
break

return text, tool_results


prompt1 = os.environ.get("AGUI_PROMPT1", "What is 847 divided by 7?")
multiplier = int(os.environ.get("AGUI_MULTIPLIER", "3"))

print(f"User: {prompt1}")
reply1, tools1 = ask(prompt1)
quotient = extract_number(reply1, tools1)
if quotient is None:
raise SystemExit("Could not extract a number from run 1.")
print(f"Assistant: {reply1.strip()}")
print(f"Extracted: {quotient:g}")

prompt2 = os.environ.get("AGUI_PROMPT2") or f"Now multiply {quotient:g} by {multiplier}."
print(f"\nUser: {prompt2}")
reply2, tools2 = ask(prompt2)
product = extract_number(reply2, tools2)
if product is None:
raise SystemExit("Could not extract a number from run 2.")
print(f"Assistant: {reply2.strip()}")

print("\n=== Calculation chain ===")
print(f"847 ÷ 7 = {quotient:g}")
print(f"{quotient:g} × {multiplier} = {product:g}")

Run in the background​

Set "mode": "background" to queue a job and receive a job_id immediately. Poll GET /api/v2/workflows?job_id=… until status is completed, then read output.text from the result.

import os
import time

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",
"session_id": "session-456",
"mode": "background",
},
timeout=60,
)
start.raise_for_status()
job_id = start.json()["job_id"]
print(f"Queued job {job_id}")

while True:
status = requests.get(
f"{base}/api/v2/workflows",
headers=headers,
params={"job_id": job_id},
timeout=60,
)
status.raise_for_status()
body = status.json()

if body.get("object") == "response" and body.get("status") == "completed":
print(body["output"]["text"])
break

if body.get("status") in {"failed", "cancelled", "timed_out"}:
raise SystemExit(f"Job ended with status {body.get('status')}")

time.sleep(1)

While a job is still queued or in_progress, the poll endpoint returns a job object (object: "job") with links for status, events, and stop. When the job completes, the same endpoint returns the full execution response.

To connect to a background run, see Re-attach to a background run.

Next steps​

  • Workflow API reference — request body fields, response schemas, status polling, stop endpoint, tweaks, globals, and error codes.
  • Flow trigger endpoints — v1 /run and /webhook endpoints if you are migrating from or comparing with the v1 API.

Was this page helpful?

Support
Search