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

# Submit a batch flow execution

POST https://api.runtype.com/v1/batch/flow
Content-Type: application/json

Submits a batch of records for flow execution. Checks daily execution limits and feature gating, and refuses up front (422 RUNTIME_LANE_INELIGIBLE) a flow the batch host cannot run.

Reference: https://docs.runtype.com/api-reference/runtype-api/batch/submit-a-batch-flow-execution

## Authentication

- `Authorization` header (bearer token, required) — API key or Clerk session token

## Servers

- `https://api.runtype.com` (Production, default)
- `https://api.runtype-staging.com` (Staging)

## Request

### Body (application/json)

This endpoint expects an object.

- `agentId` (string, optional, nullable) — Agent to execute per record instead of a flow. Saved records are loaded and their conversation read from metadata.messages; inline records reach the agent only when their type is _agent_eval or _agent_schedule and metadata.messages carries the conversation. Any other inline record type fails per record.
- `flowDefinition` (BatchFlowDefinition, optional) — Inline flow definition executed per record without saving the flow.
- `flowId` (string, optional, nullable) — Saved flow to execute per record. One of flowId, flowDefinition, or agentId is required.
- `inputs` (map from string to any, optional, nullable) — Flow input variables applied to every record execution.
- `messages` (list of V1BatchFlowPostRequestBodyContentApplicationJsonSchemaMessagesItems, optional, nullable) — Conversation seed passed unchanged to every record execution. Records cannot see each other's history.
- `options` (V1BatchFlowPostRequestBodyContentApplicationJsonSchemaOptions, optional, nullable)
- `recordIds` (list of string, optional, nullable) — Saved record ids to process. May be combined with records; the batch runs the union. At least one of recordIds or records must be non-empty. Maximum 50000 records per batch across both.
- `records` (list of BatchInlineRecord, optional, nullable) — Inline records to process without saving them first. May be combined with recordIds. Maximum 50000 records per batch across both.

## Response

### 200

Batch submitted

- `batchExecutionId` (string, required)
- `status` (string, required)

## Errors

### 400 Bad Request Error

Bad request

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 401 Unauthorized Error

Unauthorized

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 402 Payment Required Error

Feature not available

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 403 Forbidden Error

Insufficient permissions

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 422 Unprocessable Entity Error

The flow uses capabilities the batch host cannot run (RUNTIME_LANE_INELIGIBLE, with `reasons` such as `ineligible:wait-until`). Nothing is queued and no executions are counted.

- `code` (enum, required) — The flow uses capabilities the batch host cannot run.
  - Allowed values: `RUNTIME_LANE_INELIGIBLE`
- `error` (string, required)
- `reasons` (list of string, required) — Stable reason codes: `ineligible:*` capabilities (e.g. `ineligible:wait-until`), or `eval-override:*` for an eval override the batch host cannot apply.
- `details` (list of BatchLaneRefusalErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 429 Too Many Requests Error

Rate limit exceeded

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

### 500 Internal Server Error

Internal server error

- `error` (string, required)
- `code` (string, optional) — Stable error code. `insufficient_scope` marks an API key that lacks a scope this request needs.
- `details` (list of ErrorDetailsItems, optional)
- `requiredScopes` (list of string, optional) — On `insufficient_scope`: every API key scope this request needs.

## Types

### BatchFlowDefinition

Inline flow definition executed per record without saving the flow.

- `name` (string, required)
- `steps` (list of map from string to any, required) — Flow steps, in the same shape as a saved flow definition.
- `description` (string, optional, nullable)
- `id` (string, optional) — Saved-flow identity retained when the definition is a pinned snapshot.

### V1BatchFlowPostRequestBodyContentApplicationJsonSchemaMessagesItems

- `role` (string, required)
- `content` (any, optional)

### V1BatchFlowPostRequestBodyContentApplicationJsonSchemaOptions

- `chunkSize` (integer, optional) — Records admitted to one processing chunk before progress is flushed and batch gates are re-checked.
- `flowTimeoutMs` (integer, optional) — Flow-wide deadline passed to every per-record flow execution.
- `maxRetries` (integer, optional) — Retries per failed record.
- `modelOverride` (string, optional) — Model id applied to every prompt step in this batch.
- `progressFlushSize` (integer, optional) — Deprecated and ignored. Completed results are now persisted per record as they finish, so no partial progress is held in memory and this threshold has no effect. Still accepted for backward compatibility.
- `recordConcurrency` (integer, optional) — Max records dispatched in parallel within a processing chunk.
- `skipCacheWarm` (boolean, optional) — Skip the serial prompt-cache canary before fan-out.
- `stepTimeoutMs` (integer, optional) — Per-step deadline passed to every per-record flow execution.
- `timeoutMs` (integer, optional) — Per-record execution timeout in milliseconds.

### BatchInlineRecord

An ad-hoc record processed in place of a saved record. Inline records never touch the records table; each is minted a synthetic id for the lifetime of the batch.

- `metadata` (map from string to any, required) — Arbitrary JSON payload exposed to the flow as \{\{\_record.metadata.\*}}. Any shape is accepted; nothing is written to the records table.
- `name` (string, required) — Display name for the record. Batch results and step results are keyed by this name, so keep it unique within one batch.
- `type` (string, required) — Record type label exposed to the flow as \{\{\_record.type}}.

### ErrorDetailsItems

- `message` (string, required)
- `code` (string, optional) — Stable Runtype validation code (e.g. MISSING_REQUIRED_FIELD, INVALID_ENUM).
- `path` (string, optional) — Dotted JSON path to the offending field.
- `suggestion` (string, optional) — Actionable hint (allowed values, accepted range, …).

### BatchLaneRefusalErrorDetailsItems

- `message` (string, required)
- `code` (string, optional) — Stable Runtype validation code (e.g. MISSING_REQUIRED_FIELD, INVALID_ENUM).
- `path` (string, optional) — Dotted JSON path to the offending field.
- `suggestion` (string, optional) — Actionable hint (allowed values, accepted range, …).

## Examples

### Run a saved flow over inline data

**Request**

```json
{
  "flowId": "flow_01jv0000000000000000000001",
  "options": {
    "recordConcurrency": 10
  },
  "records": [
    {
      "metadata": {
        "company": "Acme Corp",
        "employees": 120,
        "website": "https://acme.example"
      },
      "name": "Acme Corp",
      "type": "lead"
    },
    {
      "metadata": {
        "company": "Globex",
        "employees": 4800,
        "website": "https://globex.example"
      },
      "name": "Globex",
      "type": "lead"
    }
  ]
}
```

**Response**

```json
{
  "batchExecutionId": "string",
  "status": "string"
}
```

**SDK Code**

```python Run a saved flow over inline data
import requests

url = "https://api.runtype.com/v1/batch/flow"

payload = {
    "flowId": "flow_01jv0000000000000000000001",
    "options": { "recordConcurrency": 10 },
    "records": [
        {
            "metadata": {
                "company": "Acme Corp",
                "employees": 120,
                "website": "https://acme.example"
            },
            "name": "Acme Corp",
            "type": "lead"
        },
        {
            "metadata": {
                "company": "Globex",
                "employees": 4800,
                "website": "https://globex.example"
            },
            "name": "Globex",
            "type": "lead"
        }
    ]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Run a saved flow over inline data
const url = 'https://api.runtype.com/v1/batch/flow';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"flowId":"flow_01jv0000000000000000000001","options":{"recordConcurrency":10},"records":[{"metadata":{"company":"Acme Corp","employees":120,"website":"https://acme.example"},"name":"Acme Corp","type":"lead"},{"metadata":{"company":"Globex","employees":4800,"website":"https://globex.example"},"name":"Globex","type":"lead"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Run a saved flow over inline data
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.runtype.com/v1/batch/flow"

	payload := strings.NewReader("{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"recordConcurrency\": 10\n  },\n  \"records\": [\n    {\n      \"metadata\": {\n        \"company\": \"Acme Corp\",\n        \"employees\": 120,\n        \"website\": \"https://acme.example\"\n      },\n      \"name\": \"Acme Corp\",\n      \"type\": \"lead\"\n    },\n    {\n      \"metadata\": {\n        \"company\": \"Globex\",\n        \"employees\": 4800,\n        \"website\": \"https://globex.example\"\n      },\n      \"name\": \"Globex\",\n      \"type\": \"lead\"\n    }\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Run a saved flow over inline data
require 'uri'
require 'net/http'

url = URI("https://api.runtype.com/v1/batch/flow")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"recordConcurrency\": 10\n  },\n  \"records\": [\n    {\n      \"metadata\": {\n        \"company\": \"Acme Corp\",\n        \"employees\": 120,\n        \"website\": \"https://acme.example\"\n      },\n      \"name\": \"Acme Corp\",\n      \"type\": \"lead\"\n    },\n    {\n      \"metadata\": {\n        \"company\": \"Globex\",\n        \"employees\": 4800,\n        \"website\": \"https://globex.example\"\n      },\n      \"name\": \"Globex\",\n      \"type\": \"lead\"\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Run a saved flow over inline data
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.runtype.com/v1/batch/flow")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"recordConcurrency\": 10\n  },\n  \"records\": [\n    {\n      \"metadata\": {\n        \"company\": \"Acme Corp\",\n        \"employees\": 120,\n        \"website\": \"https://acme.example\"\n      },\n      \"name\": \"Acme Corp\",\n      \"type\": \"lead\"\n    },\n    {\n      \"metadata\": {\n        \"company\": \"Globex\",\n        \"employees\": 4800,\n        \"website\": \"https://globex.example\"\n      },\n      \"name\": \"Globex\",\n      \"type\": \"lead\"\n    }\n  ]\n}")
  .asString();
```

```php Run a saved flow over inline data
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.runtype.com/v1/batch/flow', [
  'body' => '{
  "flowId": "flow_01jv0000000000000000000001",
  "options": {
    "recordConcurrency": 10
  },
  "records": [
    {
      "metadata": {
        "company": "Acme Corp",
        "employees": 120,
        "website": "https://acme.example"
      },
      "name": "Acme Corp",
      "type": "lead"
    },
    {
      "metadata": {
        "company": "Globex",
        "employees": 4800,
        "website": "https://globex.example"
      },
      "name": "Globex",
      "type": "lead"
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Run a saved flow over inline data
using RestSharp;

var client = new RestClient("https://api.runtype.com/v1/batch/flow");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"recordConcurrency\": 10\n  },\n  \"records\": [\n    {\n      \"metadata\": {\n        \"company\": \"Acme Corp\",\n        \"employees\": 120,\n        \"website\": \"https://acme.example\"\n      },\n      \"name\": \"Acme Corp\",\n      \"type\": \"lead\"\n    },\n    {\n      \"metadata\": {\n        \"company\": \"Globex\",\n        \"employees\": 4800,\n        \"website\": \"https://globex.example\"\n      },\n      \"name\": \"Globex\",\n      \"type\": \"lead\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Run a saved flow over inline data
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "flowId": "flow_01jv0000000000000000000001",
  "options": ["recordConcurrency": 10],
  "records": [
    [
      "metadata": [
        "company": "Acme Corp",
        "employees": 120,
        "website": "https://acme.example"
      ],
      "name": "Acme Corp",
      "type": "lead"
    ],
    [
      "metadata": [
        "company": "Globex",
        "employees": 4800,
        "website": "https://globex.example"
      ],
      "name": "Globex",
      "type": "lead"
    ]
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runtype.com/v1/batch/flow")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Run a saved flow over existing records

**Request**

```json
{
  "flowId": "flow_01jv0000000000000000000001",
  "options": {
    "maxRetries": 3,
    "recordConcurrency": 10
  },
  "recordIds": [
    "record_01jv0000000000000000000001",
    "record_01jv0000000000000000000002",
    "record_01jv0000000000000000000003"
  ]
}
```

**Response**

```json
{
  "batchExecutionId": "string",
  "status": "string"
}
```

**SDK Code**

```python Run a saved flow over existing records
import requests

url = "https://api.runtype.com/v1/batch/flow"

payload = {
    "flowId": "flow_01jv0000000000000000000001",
    "options": {
        "maxRetries": 3,
        "recordConcurrency": 10
    },
    "recordIds": ["record_01jv0000000000000000000001", "record_01jv0000000000000000000002", "record_01jv0000000000000000000003"]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Run a saved flow over existing records
const url = 'https://api.runtype.com/v1/batch/flow';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"flowId":"flow_01jv0000000000000000000001","options":{"maxRetries":3,"recordConcurrency":10},"recordIds":["record_01jv0000000000000000000001","record_01jv0000000000000000000002","record_01jv0000000000000000000003"]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Run a saved flow over existing records
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.runtype.com/v1/batch/flow"

	payload := strings.NewReader("{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"maxRetries\": 3,\n    \"recordConcurrency\": 10\n  },\n  \"recordIds\": [\n    \"record_01jv0000000000000000000001\",\n    \"record_01jv0000000000000000000002\",\n    \"record_01jv0000000000000000000003\"\n  ]\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Run a saved flow over existing records
require 'uri'
require 'net/http'

url = URI("https://api.runtype.com/v1/batch/flow")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"maxRetries\": 3,\n    \"recordConcurrency\": 10\n  },\n  \"recordIds\": [\n    \"record_01jv0000000000000000000001\",\n    \"record_01jv0000000000000000000002\",\n    \"record_01jv0000000000000000000003\"\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Run a saved flow over existing records
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.runtype.com/v1/batch/flow")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"maxRetries\": 3,\n    \"recordConcurrency\": 10\n  },\n  \"recordIds\": [\n    \"record_01jv0000000000000000000001\",\n    \"record_01jv0000000000000000000002\",\n    \"record_01jv0000000000000000000003\"\n  ]\n}")
  .asString();
```

```php Run a saved flow over existing records
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.runtype.com/v1/batch/flow', [
  'body' => '{
  "flowId": "flow_01jv0000000000000000000001",
  "options": {
    "maxRetries": 3,
    "recordConcurrency": 10
  },
  "recordIds": [
    "record_01jv0000000000000000000001",
    "record_01jv0000000000000000000002",
    "record_01jv0000000000000000000003"
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Run a saved flow over existing records
using RestSharp;

var client = new RestClient("https://api.runtype.com/v1/batch/flow");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"flowId\": \"flow_01jv0000000000000000000001\",\n  \"options\": {\n    \"maxRetries\": 3,\n    \"recordConcurrency\": 10\n  },\n  \"recordIds\": [\n    \"record_01jv0000000000000000000001\",\n    \"record_01jv0000000000000000000002\",\n    \"record_01jv0000000000000000000003\"\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Run a saved flow over existing records
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "flowId": "flow_01jv0000000000000000000001",
  "options": [
    "maxRetries": 3,
    "recordConcurrency": 10
  ],
  "recordIds": ["record_01jv0000000000000000000001", "record_01jv0000000000000000000002", "record_01jv0000000000000000000003"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runtype.com/v1/batch/flow")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```