> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Quickstart

> Make your first API call and run an AI flow

Use the Runtype API to make an authenticated request and run an inline flow.

## Before you begin

* [Create a Runtype account](https://use.runtype.com/sign-up).
* [Create an API key](/developer-guides/getting-started/authentication) that
  can run flows. The key needs full access or one of the `DISPATCH:*`,
  `FLOWS:*`, or `FLOWS:EXECUTE` permissions. Test keys (`rt_test_`) have daily
  and lifetime execution limits.
* Set the `RUNTYPE_API_KEY` environment variable to your API key.
* Make sure your account can use the model in the examples, either through
  platform keys or your own provider key. For more information, see
  [Platform keys vs. BYOK](/user-guide/getting-started/platform-keys-vs-byok).

## Test your API key

Verify your API key by requesting your profile. The following TypeScript example
sends the request:

**`TypeScript`**

```typescript title="TypeScript"
const response = await fetch('https://api.runtype.com/v1/users/profile', {
  headers: {
    Authorization: 'Bearer ' + process.env.RUNTYPE_API_KEY,
  },
})
const profile = await response.json()
console.log(profile)
```

The following Python example sends the same request:

**`Python`**

```python title="Python"
import os

import requests

response = requests.get(
    'https://api.runtype.com/v1/users/profile',
    headers={'Authorization': 'Bearer ' + os.environ['RUNTYPE_API_KEY']},
)
print(response.json())
```

The following cURL command sends the same request:

**`cURL`**

```bash title="cURL"
curl https://api.runtype.com/v1/users/profile \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Replace `YOUR_API_KEY` with your Runtype API key before you run the cURL
command.

A valid key returns `200` with your profile. The output is similar to the
following, with some fields left out:

**`Response`**

```json title="Response"
{
  "id": "user_abc123",
  "orgId": "org_abc123",
  "orgName": "Example Organization",
  "email": "dana@example.com",
  "createdAt": "2026-01-15T10:00:00.000Z",
  "dashboardUrl": "https://use.runtype.com"
}
```

A `401` response means that the key isn't valid. Check that you copied the
full key and that it hasn't been revoked. The profile request needs no
permission, so a successful response doesn't confirm that the key can run
flows.

## Define and run a flow

Send a `POST` request to `/v1/dispatch` with an inline flow definition. Set
`flowMode` and `recordMode` to `virtual` to run without creating a persisted
flow or record. The following TypeScript example sends the request, reads the
Server-Sent Events (SSE) stream, and prints the generated text:

**`TypeScript`**

```typescript title="TypeScript"
const response = await fetch('https://api.runtype.com/v1/dispatch', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.RUNTYPE_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    inputs: {
      customerName: 'Example Organization',
      topic: 'sales report',
    },
    flow: {
      name: 'Customer Greeting',
      steps: [
        {
          id: 'greeting',
          name: 'Greeting',
          type: 'prompt',
          order: 1,
          config: {
            model: 'claude-haiku-4-5',
            userPrompt: 'Greet {{customerName}} about their {{topic}}.',
            outputVariable: 'greeting',
          },
        },
      ],
    },
    options: {
      flowMode: 'virtual',
      recordMode: 'virtual',
      streamResponse: true,
    },
  }),
})

const reader = response.body?.getReader()
if (!reader) throw new Error('Response body is empty')

const decoder = new TextDecoder()
let buffer = ''
for (;;) {
  const { done, value } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const lines = buffer.split('\n')
  buffer = lines.pop() ?? ''
  for (const line of lines) {
    if (!line.startsWith('data: ')) continue
    const event = JSON.parse(line.slice(6))
    if (event.type === 'text_delta') process.stdout.write(event.delta)
  }
}
```

The following Python example sends the same request:

**`Python`**

```python title="Python"
import json
import os

import requests

response = requests.post(
    'https://api.runtype.com/v1/dispatch',
    headers={'Authorization': 'Bearer ' + os.environ['RUNTYPE_API_KEY']},
    json={
        'inputs': {
            'customerName': 'Example Organization',
            'topic': 'sales report',
        },
        'flow': {
            'name': 'Customer Greeting',
            'steps': [
                {
                    'id': 'greeting',
                    'name': 'Greeting',
                    'type': 'prompt',
                    'order': 1,
                    'config': {
                        'model': 'claude-haiku-4-5',
                        'userPrompt': (
                            'Greet {{customerName}} about their {{topic}}.'
                        ),
                        'outputVariable': 'greeting',
                    },
                },
            ],
        },
        'options': {
            'flowMode': 'virtual',
            'recordMode': 'virtual',
            'streamResponse': True,
        },
    },
    stream=True,
)

for line in response.iter_lines(decode_unicode=True):
    if not line or not line.startswith('data: '):
        continue
    event = json.loads(line[len('data: '):])
    if event['type'] == 'text_delta':
        print(event['delta'], end='', flush=True)
```

The following cURL command sends the same request:

**`cURL`**

```bash title="cURL"
curl https://api.runtype.com/v1/dispatch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {
      "customerName": "Example Organization",
      "topic": "sales report"
    },
    "flow": {
      "name": "Customer Greeting",
      "steps": [
        {
          "id": "greeting",
          "name": "Greeting",
          "type": "prompt",
          "order": 1,
          "config": {
            "model": "claude-haiku-4-5",
            "userPrompt": "Greet {{customerName}} about their {{topic}}.",
            "outputVariable": "greeting"
          }
        }
      ]
    },
    "options": {
      "flowMode": "virtual",
      "recordMode": "virtual",
      "streamResponse": true
    }
  }'
```

Replace `YOUR_API_KEY` with your Runtype API key before you run the cURL
command.

> **Tip**
>
> Use `inputs` to pass values that change between runs. Templates reference
> them as `{{variableName}}`.

### Save the flow instead

The `virtual` mode runs the flow once and saves nothing. To save the flow so
that you can run it again and see it in the dashboard, set `flowMode` to
`create` or `upsert`. To save the run's data on a record, set `recordMode` to
`create` and pass a `record` object with a `name` and a `type`. Put data that
must persist with the record in `record.metadata`. For more information, see
[Manage flows as code](/user-guide/flows/manage-flows-as-code).

## Read the response

Because `streamResponse` is `true`, the dispatch endpoint returns an SSE stream.
The output is similar to the following, with some fields left out:

**`SSE stream`**

```text title="SSE stream"
event: execution_start
data: {"type":"execution_start","executionId":"exec_abc123","seq":0,"kind":"flow","startedAt":"2026-01-15T10:00:00.000Z"}

event: step_start
data: {"type":"step_start","executionId":"exec_abc123","seq":1,"id":"greeting","name":"Greeting","stepType":"prompt"}

event: text_start
data: {"type":"text_start","executionId":"exec_abc123","seq":2,"id":"text_1"}

event: text_delta
data: {"type":"text_delta","executionId":"exec_abc123","seq":3,"id":"text_1","delta":"Hello!"}

event: text_complete
data: {"type":"text_complete","executionId":"exec_abc123","seq":4,"id":"text_1"}

event: step_complete
data: {"type":"step_complete","executionId":"exec_abc123","seq":5,"id":"greeting","name":"Greeting","success":true}

event: execution_complete
data: {"type":"execution_complete","executionId":"exec_abc123","seq":6,"kind":"flow","success":true,"outcome":"completed"}
```

Each `text_delta` event carries the `id` of the text block that it belongs to.
The `success` field on `execution_complete` tells you whether the run
succeeded. A run that fails can end with an `execution_error` event instead of
`execution_complete`. Both terminal events carry an `outcome` field:
`completed`, `error`, or `aborted`. The value `aborted` means that the caller
or the host stopped the run, for example by a cancel, a client disconnect, or a
newer turn that superseded it. Use `outcome` to settle a turn instead of
parsing `stopReason`. A paused run is not terminal: it ends the stream on an
`approval_start` or `await` event, and the resumed run ends on the terminal
event. For all event types and fields, see the
[API reference](/api-reference/overview).

To get a single JSON response instead of a stream, set `streamResponse` to
`false`. The response includes `success`, `status`, and an `events` array with
the same events. If the run fails, `success` is `false` and the `error` and
`code` fields describe the failure.

### Troubleshoot the request

The following table lists the most common error statuses from the dispatch
endpoint:

| Status | Cause                                                                          |
| ------ | ------------------------------------------------------------------------------ |
| `401`  | The API key isn't valid.                                                       |
| `402`  | Your account reached a spending limit.                                         |
| `403`  | The API key doesn't have a permission that can run flows.                      |
| `429`  | You sent too many requests, or a test key reached its daily or lifetime limit. |

When `streamResponse` is `true`, a spending or rate limit can arrive as an
error event in the stream instead of as an HTTP status. After a rate-limit
`429`, wait for the time in the `Retry-After` header before you retry.

For more errors, see
[Common errors and solutions](/user-guide/troubleshooting-faq/common-errors-and-solutions)
and [Rate limits and usage](/user-guide/troubleshooting-faq/rate-limits-and-usage).

## Use the SDK

Use an SDK to build and run the same flow. The TypeScript SDK needs Node.js 22
or later, and the examples use top-level `await`, so run them as an ES module.
The Python SDK needs Python 3.10 or later.

#### TypeScript

Install the SDK package:

**`Install the SDK`**

```bash title="Install the SDK"
npm install @runtypelabs/sdk
```

The following TypeScript example builds a flow, passes input values, and
runs it:

**`TypeScript`**

```typescript title="TypeScript"
import { FlowBuilder, RuntypeClient } from '@runtypelabs/sdk'

const client = new RuntypeClient({
  apiKey: process.env.RUNTYPE_API_KEY,
})

const result = await new FlowBuilder()
  .createFlow({ name: 'Customer Greeting' })
  .withInputs({
    customerName: 'Alex',
    accountType: 'premium',
  })
  .prompt({
    name: 'Personalized Greeting',
    model: 'claude-haiku-4-5',
    userPrompt: 'Greet {{customerName}} as a {{accountType}} customer.',
  })
  .run(client, { streamResponse: true })

const greeting = await result.getResult('Personalized Greeting')
console.log(greeting)
```

#### Python

Install the SDK package:

**`Install the SDK`**

```bash title="Install the SDK"
pip install runtype-sdk
```

The following Python example builds a flow, passes input values, and runs
it:

**`Python`**

```python title="Python"
import os

from runtype import FlowBuilder, RuntypeClient

client = RuntypeClient(api_key=os.environ['RUNTYPE_API_KEY'])

result = (
    FlowBuilder()
    .create_flow(name='Customer Greeting')
    .with_inputs({'customerName': 'Alex', 'accountType': 'premium'})
    .prompt(
        name='Personalized Greeting',
        model='claude-haiku-4-5',
        user_prompt='Greet {{customerName}} as a {{accountType}} customer.',
    )
    .run(client)
)

print(result.get_result('Personalized Greeting'))
```

The `withInputs` method (`with_inputs` in Python) adds top-level variables
that prompts reference as `{{variableName}}`. Use it for values that change
between runs.

### Validate before running

Call the `validate` method with an authenticated client to check an inline flow
before running it. The method reports structural issues, undeclared-variable
warnings, and model recommendations without creating or running the flow. The
following TypeScript example reports validation errors:

**`TypeScript`**

```typescript title="TypeScript"
import { FlowBuilder, RuntypeClient } from '@runtypelabs/sdk'

const client = new RuntypeClient({
  apiKey: process.env.RUNTYPE_API_KEY,
})

const builder = new FlowBuilder()
  .createFlow({ name: 'Customer Greeting' })
  .prompt({
    name: 'Personalized Greeting',
    model: 'claude-haiku-4-5',
    userPrompt: 'Create a greeting for {{customerName}}.',
  })

const validation = await builder.validate(client)
if (!validation.valid) {
  for (const issue of validation.errors) {
    console.error(issue.code, issue.message)
  }
}
```

Validation doesn't count as an execution. With an authenticated client, it
also checks that the tools, flows, and agents that the flow references exist
in your account. Check `validation.context.accountChecksPerformed` to confirm
whether those checks ran.

## Next steps

#### [What are flows?](/user-guide/flows/what-are-flows)

Learn how flows, records, and steps work.

#### [Choose between a flow and an agent](/user-guide/flows/choosing-between-a-flow-and-an-agent)

Decide which one fits your task. The dispatch endpoint runs both.

#### [Create and edit flows](/user-guide/flows/creating-and-editing-flows)

Build and test a flow in the dashboard.

#### [API reference](/api-reference/overview)

Review every endpoint and event.

#### [Connect Runtype to coding agents](/developer-guides/integrations/connect-coding-agents)

Set up the Runtype CLI and MCP server for your coding agent.

#### [Import products](/developer-guides/guides/importing-products)

Create a product from an existing JSON definition.

#### [Bring your own agent](/developer-guides/guides/bring-your-own-agent)

Send traces from a Flue, Cloudflare, or AI SDK agent and capture eval
cases from them.

#### [Run flows and agents with dispatch](/developer-guides/guides/dispatch)

Stream results, run work asynchronously, and poll for status.

#### [Runtype CLI reference](/developer-guides/getting-started/cli-reference)

Run Flows, Agents, and account tasks from the `runtype` CLI.