Skip to main content
Version: 1.11.x

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