> ## Documentation Index
> Fetch the complete documentation index at: https://support.myapps.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream Workflow API

> Execute a workflow and receive a real-time streamed response, fully compatible with Vercel AI SDKs such as useChat.

### 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](/docs/stream-token) endpoint.

### Features

<CardGroup cols={2}>
  <Card title="Real-time Streaming" icon="stream">
    Receive and display **progressive responses** as the AI generates content — no waiting for the full result.
  </Card>

  <Card title="Single-Use Token Auth" icon="key">
    Secure your requests with **single-use tokens** — no API key exposed on the client side.
  </Card>

  <Card title="Vercel AI SDK Compatible" icon="react">
    Integrates directly with `ai/react` hooks like **useChat** for reactive chat interfaces.
  </Card>

  <Card title="Flexible Input Processing" icon="input-text">
    Support for **various input formats** including text variables and image URLs.
  </Card>
</CardGroup>

### Authentication

Uses a **stream token** passed as a query parameter (not a Bearer key). Get a token from [`GET /api/v1/token`](/docs/stream-token) first.

```
POST /api/v1/run/{workflow_id}/stream?token=pub_tok_xxx
```

### Request

**Path Parameters**

| Name         | Required | Type   | Description                                      |
| ------------ | -------- | ------ | ------------------------------------------------ |
| workflow\_id | Yes      | string | The ID of the workflow to run (e.g. `wf_abc123`) |

**Query Parameters**

| Name  | Required | Type   | Description                                    |
| ----- | -------- | ------ | ---------------------------------------------- |
| token | Yes      | string | A single-use stream token from `/api/v1/token` |

**Request Body**

Content-Type: `application/json`

Pass your workflow's input variables as key/value pairs.

```json theme={null}
{
  "story": "a story about a dog named charlie"
}
```

### Implementation Guide

<AccordionGroup>
  <Accordion title="Step 1: Generate Authentication Token" icon="key">
    First, obtain a single-use token from the token endpoint using your API key.

    ```bash theme={null}
    curl --request GET \
         --url "https://aitutor-api.vercel.app/api/v1/token?ttl=60" \
         --header "Authorization: Bearer YOUR_API_KEY"
    ```

    Response:

    ```json theme={null}
    {
      "success": true,
      "token": "pub_tok_a1b2c3d4e5f6",
      "ttl": 60
    }
    ```
  </Accordion>

  <Accordion title="Step 2: Make Streaming Request" icon="code">
    Use the token to make a streaming request to the workflow endpoint:

    ```bash theme={null}
    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.
  </Accordion>

  <Accordion title="Step 3: Implement React Component" icon="react">
    Use this React component to handle streaming responses in your UI:

    ```typescript theme={null}
    "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>
      );
    }
    ```
  </Accordion>

  <Accordion title="Step 4: Use with Vercel AI SDK" icon="bolt">
    For chat-style interfaces, integrate with the Vercel AI SDK's `useChat` hook:

    ```typescript theme={null}
    "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>
        </>
      );
    }
    ```
  </Accordion>
</AccordionGroup>

### 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

<AccordionGroup>
  <Accordion title="401 Unauthorized" icon="lock">
    Missing, invalid, or already-used token.

    ```json theme={null}
    {
      "error": "Unauthorized. Please provide a valid secret key.",
      "success": false
    }
    ```
  </Accordion>

  <Accordion title="402 Payment Required" icon="credit-card">
    No credits remaining on the account.

    ```json theme={null}
    {
      "error": "No credits remaining. Please add credits to continue using the service.",
      "success": false,
      "code": "invalid_billing"
    }
    ```
  </Accordion>

  <Accordion title="404 Not Found" icon="circle-question">
    Workflow not found or not published.

    ```json theme={null}
    {
      "error": "Workflow not found",
      "success": false
    }
    ```
  </Accordion>

  <Accordion title="500 Internal Server Error" icon="triangle-exclamation">
    Something went wrong during execution.

    ```json theme={null}
    {
      "error": "Failed to run workflow",
      "success": false,
      "code": "internal_server_error"
    }
    ```
  </Accordion>
</AccordionGroup>

### Code Examples

<AccordionGroup>
  <Accordion title="Python" icon="python">
    ```python theme={null}
    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)
    ```
  </Accordion>

  <Accordion title="Node.js" icon="node-js">
    ```javascript theme={null}
    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));
    }
    ```
  </Accordion>

  <Accordion title="cURL" icon="terminal">
    ```bash theme={null}
    # 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"}'
    ```
  </Accordion>
</AccordionGroup>

### Best Practices

<AccordionGroup>
  <Accordion title="Error Handling" icon="shield-check">
    Implement **robust error handling** for network issues, expired tokens, and invalid responses. Always check `response.ok` before reading the stream.
  </Accordion>

  <Accordion title="Token Management" icon="key-skeleton">
    Tokens are **single-use** — generate a fresh token before each stream request. Never cache or reuse tokens.
  </Accordion>

  <Accordion title="Loading States" icon="spinner">
    Display appropriate **loading indicators** while waiting for the initial response chunk to arrive.
  </Accordion>

  <Accordion title="Performance Optimization" icon="gauge">
    Optimize your application to handle **continuous data streams** efficiently. Use `ReadableStream` readers and avoid buffering the entire response in memory.
  </Accordion>
</AccordionGroup>

### 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}`](/docs/run-workflow) endpoint if you don't need real-time output.
