Skip to main content

Overview

This endpoint executes a workflow and streams the response in real-time as the model generates tokens. It works seamlessly with the Vercel AI SDK and is ideal for building interactive chat interfaces and real-time tutoring experiences.

Endpoint

  • HTTP Method: POST
  • URL: /api/v1/run/{workflow_id}/stream?token=STREAM_TOKEN
Replace {workflow_id} with your workflow ID and STREAM_TOKEN with a token from the Stream Token endpoint.

Features

Real-time Streaming

Receive and display progressive responses as the AI generates content — no waiting for the full result.

Single-Use Token Auth

Secure your requests with single-use tokens — no API key exposed on the client side.

Vercel AI SDK Compatible

Integrates directly with ai/react hooks like useChat for reactive chat interfaces.

Flexible Input Processing

Support for various input formats including text variables and image URLs.

Authentication

Uses a stream token passed as a query parameter (not a Bearer key). Get a token from GET /api/v1/token first.
POST /api/v1/run/{workflow_id}/stream?token=pub_tok_xxx

Request

Path Parameters
NameRequiredTypeDescription
workflow_idYesstringThe ID of the workflow to run (e.g. wf_abc123)
Query Parameters
NameRequiredTypeDescription
tokenYesstringA single-use stream token from /api/v1/token
Request Body Content-Type: application/json Pass your workflow’s input variables as key/value pairs.
{
  "story": "a story about a dog named charlie"
}

Implementation Guide

First, obtain a single-use token from the token endpoint using your API key.
curl --request GET \
     --url "https://aitutor-api.vercel.app/api/v1/token?ttl=60" \
     --header "Authorization: Bearer YOUR_API_KEY"
Response:
{
  "success": true,
  "token": "pub_tok_a1b2c3d4e5f6",
  "ttl": 60
}
Use the token to make a streaming request to the workflow endpoint:
curl --no-buffer --request POST \
     --url "https://aitutor-api.vercel.app/api/v1/run/wf_abc123/stream?token=pub_tok_a1b2c3d4e5f6" \
     --header 'Content-Type: application/json' \
     --data '{"story": "a story about a dog named charlie"}'
Use --no-buffer so curl prints tokens as they arrive.
Use this React component to handle streaming responses in your UI:
"use client";

import { useCallback, useEffect, useState } from "react";

export default function StreamingText({
  url,
  fallbackText,
  className,
}: {
  url: string;
  fallbackText: string;
  className?: string;
}) {
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState("");

  const getData = useCallback(async () => {
    setLoading(true);
    const response = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
    });

    if (!response.ok) return null;

    const data = response.body;
    if (!data) {
      setResult(fallbackText);
      return;
    }

    const reader = data.getReader();
    const decoder = new TextDecoder();
    let done = false;

    setLoading(false);

    while (!done) {
      const { value, done: doneReading } = await reader.read();
      done = doneReading;
      const chunkValue = decoder.decode(value);
      setResult((prev) => (prev ?? "") + chunkValue);
    }
  }, [url, fallbackText]);

  useEffect(() => {
    if (url) {
      getData();
    }
  }, []);

  return loading ? (
    <div className={className}>Loading...</div>
  ) : (
    <p className={className}>{result}</p>
  );
}
For chat-style interfaces, integrate with the Vercel AI SDK’s useChat hook:
"use client";

import { useChat } from "ai/react";

export default function ChatPage({ token }: { token: string }) {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: `https://aitutor-api.vercel.app/api/v1/run/wf_abc123/stream?token=${token}`,
    keepLastMessageOnError: true,
  });

  return (
    <>
      {messages.map((message) => (
        <div key={message.id}>
          {message.role === "user" ? "User: " : "AI: "}
          {message.content}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input name="prompt" value={input} onChange={handleInputChange} />
        <button type="submit">Submit</button>
      </form>
    </>
  );
}

Response

The response is a real-time text/plain stream. Text chunks arrive as they are generated:
Content-Type: text/plain; charset=utf-8
Once upon a time, there was a friendly dog named Charlie...

Error Responses

Missing, invalid, or already-used token.
{
  "error": "Unauthorized. Please provide a valid secret key.",
  "success": false
}
No credits remaining on the account.
{
  "error": "No credits remaining. Please add credits to continue using the service.",
  "success": false,
  "code": "invalid_billing"
}
Workflow not found or not published.
{
  "error": "Workflow not found",
  "success": false
}
Something went wrong during execution.
{
  "error": "Failed to run workflow",
  "success": false,
  "code": "internal_server_error"
}

Code Examples

import requests

API_KEY = "YOUR_API_KEY"
WORKFLOW_ID = "wf_abc123"
BASE_URL = "https://aitutor-api.vercel.app"

# Step 1: Get stream token
token_res = requests.get(
    f"{BASE_URL}/api/v1/token?ttl=60",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
token = token_res.json()["token"]

# Step 2: Stream the response
response = requests.post(
    f"{BASE_URL}/api/v1/run/{WORKFLOW_ID}/stream?token={token}",
    headers={"Content-Type": "application/json"},
    json={"story": "a story about a dog named charlie"},
    stream=True,
)

for chunk in response.iter_content(chunk_size=None, decode_unicode=True):
    print(chunk, end="", flush=True)
const API_KEY = "YOUR_API_KEY";
const WORKFLOW_ID = "wf_abc123";
const BASE_URL = "https://aitutor-api.vercel.app";

// Step 1: Get stream token
const tokenRes = await fetch(`${BASE_URL}/api/v1/token?ttl=60`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
});
const { token } = await tokenRes.json();

// Step 2: Stream the response
const response = await fetch(
  `${BASE_URL}/api/v1/run/${WORKFLOW_ID}/stream?token=${token}`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ story: "a story about a dog named charlie" }),
  }
);

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}
# Step 1: Get token
curl --request GET \
     --url "https://aitutor-api.vercel.app/api/v1/token?ttl=60" \
     --header "Authorization: Bearer YOUR_API_KEY"

# Step 2: Stream (replace TOKEN with the token from step 1)
curl --no-buffer --request POST \
     --url "https://aitutor-api.vercel.app/api/v1/run/wf_abc123/stream?token=TOKEN" \
     --header 'Content-Type: application/json' \
     --data '{"story": "a story about a dog named charlie"}'

Best Practices

Implement robust error handling for network issues, expired tokens, and invalid responses. Always check response.ok before reading the stream.
Tokens are single-use — generate a fresh token before each stream request. Never cache or reuse tokens.
Display appropriate loading indicators while waiting for the initial response chunk to arrive.
Optimize your application to handle continuous data streams efficiently. Use ReadableStream readers and avoid buffering the entire response in memory.

Additional Notes

  • Single-Use Token: The stream token is consumed on the first request — if it’s expired or already used, you’ll get a 401.
  • Max Duration: Streaming requests can run for up to 300 seconds before timing out.
  • Credits: The workflow owner’s credits are charged based on total tokens generated.
  • Non-Streaming Alternative: Use the POST /api/v1/run/{workflow_id} endpoint if you don’t need real-time output.