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

# Calling your API endpoints

Use `POST` requests to invoke capabilities on an API Surface from your application. Before you start, [set up an API Surface](/user-guide/products-surfaces/setting-up-an-api-surface) and create a [product API key](/user-guide/products-surfaces/authenticating-with-product-api-keys).

## Endpoint structure

Use the following URL pattern for a capability endpoint:

**`Capability endpoint URL`**

```plaintext title="Capability endpoint URL"
https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/{CAPABILITY_SLUG}
```

Replace the placeholders with these values:

* `PRODUCT_ID`: ID of the Product.
* `SURFACE_ID`: ID of the API Surface.
* `CAPABILITY_SLUG`: Configured slug for the capability endpoint.

Runtype uses the configured endpoint slug. If you don't set one, Runtype lowercases the capability name and replaces spaces and underscores with hyphens. For example, **Summarize Article** becomes `summarize-article`. Punctuation in the name stays in the derived slug, so set an explicit slug for a capability whose name contains punctuation.

The slugs `capabilities`, `dispatch`, `chat`, and `info` are reserved for other endpoints on the surface. Choose a different endpoint slug for a capability with one of these names.

Find the endpoint URL on the API Surface's **Endpoints** tab or **Ship** tab.

## Make a request

Send a `POST` request with the capability's input as a JSON object. The endpoint passes top-level fields directly to the capability.

Use the following request to send `topic` and `tone` fields:

**`Send a capability request`**

```bash title="Send a capability request"
curl -X POST "https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/{CAPABILITY_SLUG}" \
  -H "Authorization: Bearer papi_YOUR_PRODUCT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "machine learning",
    "tone": "casual"
  }'
```

Replace `papi_YOUR_PRODUCT_KEY` with your API key. Use the `PRODUCT_ID`, `SURFACE_ID`, and `CAPABILITY_SLUG` values for your Product, API Surface, and capability endpoint. The other examples on this page use the same placeholders.

You can also send the key in an `X-API-Key` header. If the surface's **Authentication Mode** is **Public (No Auth)**, requests don't need a key. For details, see [Authenticating with product API keys](/user-guide/products-surfaces/authenticating-with-product-api-keys).

To stream a capability response, set `options.stream` to `true` in the request body. On a capability endpoint, you can also add `stream=true` to the URL. The following request uses the query parameter:

**`Stream a capability response`**

```bash title="Stream a capability response"
curl -X POST "https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/{CAPABILITY_SLUG}?stream=true" \
  -H "Authorization: Bearer papi_YOUR_PRODUCT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"topic": "machine learning"}'
```

## Request body

The capability endpoint accepts a JSON object. Each top-level field becomes an input parameter. The following table lists the supported fields:

| Field               | Type      | Required | Description                                  |
| ------------------- | --------- | -------- | -------------------------------------------- |
| `YOUR_INPUT_FIELDS` | `any`     | No       | Input parameters defined by your capability. |
| `options.stream`    | `boolean` | No       | Streams the response when set to `true`.     |

The top-level `options` field is reserved for request options and isn't passed to the capability. To send an input named `options`, use the dispatch endpoint's `input` object.

## Call a capability by name

To select a capability by name instead of by its endpoint slug, use the dispatch endpoint:

**`Dispatch URL`**

```plaintext title="Dispatch URL"
https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/dispatch
```

Send the capability name in `capability` and its inputs in `input`:

**`Dispatch a capability by name`**

```bash title="Dispatch a capability by name"
curl -X POST "https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/dispatch" \
  -H "Authorization: Bearer papi_YOUR_PRODUCT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capability": "Summarize Article",
    "input": {
      "topic": "machine learning",
      "tone": "casual"
    },
    "messages": [
      { "role": "user", "content": "Keep it under 100 words." }
    ]
  }'
```

The dispatch endpoint accepts these fields:

| Field            | Type      | Required | Description                                                                                                |
| ---------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `capability`     | `string`  | Yes      | Name of the capability to run.                                                                             |
| `input`          | `object`  | No       | Input parameters for the capability.                                                                       |
| `messages`       | `array`   | No       | Conversation history. Each message has `role` (`system`, `user`, or `assistant`) and `content` (a string). |
| `options.stream` | `boolean` | No       | Streams the response when set to `true`.                                                                   |

## Send a chat message

Every API Surface also has a chat endpoint. It sends one message to a capability on the surface that your key can access. You can't choose which capability runs, so use the chat endpoint on a surface with one flow capability, or with a key scoped to one capability:

**`Chat URL`**

```plaintext title="Chat URL"
https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/chat
```

The following request sends a message:

**`Send a chat message`**

```bash title="Send a chat message"
curl -X POST "https://api.runtype.com/v1/products/{PRODUCT_ID}/surfaces/{SURFACE_ID}/api/chat" \
  -H "Authorization: Bearer papi_YOUR_PRODUCT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message": "Summarize recent machine learning research."}'
```

The chat endpoint accepts these fields:

| Field            | Type      | Required | Description                                                                        |
| ---------------- | --------- | -------- | ---------------------------------------------------------------------------------- |
| `message`        | `string`  | Yes      | The user message. The capability receives it as a single user message.             |
| `conversationId` | `string`  | No       | An ID stored with the run. The endpoint doesn't load earlier messages for this ID. |
| `options.stream` | `boolean` | No       | Streams the response when set to `true`.                                           |

The chat endpoint runs flow capabilities only. If the selected capability doesn't run a flow, the API returns `500` with the error `Capability not configured`. If the key can't access any capability on the surface, the API returns `404` with the error `No capabilities available`. Responses use the same JSON and streaming formats as the other endpoints.

## Response shape

Non-streaming requests return JSON. The response shape depends on the **Response Shape** setting on your API Surface.

The **Raw** response shape, which is the default, returns only the capability output:

**`Raw response`**

```json title="Raw response"
"AI-generated response text"
```

The **Wrapped** response shape includes the capability output and execution metadata:

**`Wrapped response`**

```json title="Wrapped response"
{
  "data": "AI-generated response text",
  "meta": {
    "executionId": "exec_YOUR_EXECUTION_ID",
    "capability": "Summarize Article",
    "durationMs": 1823,
    "tokens": {
      "input": 150,
      "output": 95,
      "total": 245
    }
  }
}
```

To choose the shape, set **Response Shape** to **Raw** or **Wrapped** on the API Surface's **Overview** tab.

The response contains the output of the step selected by **Return Step** on the same tab: **Last step** (the default), **First step**, or **Specific step** with a step that you choose. If the chosen step doesn't run, the API returns the last step's output.

### Run ID header

When a request to a flow or agent capability starts a run, the response carries the run's ID in the `X-Runtype-Execution-Id` header. JSON and streaming responses both carry it, whatever the **Response Shape** setting, including a response for a run that started and then failed. A request refused before a run starts, such as one over its rate limit, carries no header. Claude-managed and external-agent capabilities don't create a Runtype run, so their responses carry none.

Store the ID next to your own request to correlate logs, quote it in a support request, or open the run with `GET /v1/runs/{executionId}` and an API key from your Runtype account. The ID gives no access on its own: run lookups need your account's credentials.

## Streaming responses

Streaming responses use Server-Sent Events (SSE). The API emits a unified event stream with a named event and a JSON payload for each event. The following example shows a successful flow stream:

**`Successful flow stream`**

```text title="Successful flow stream"
id: 0
event: execution_start
data: {"type":"execution_start","executionId":"exec_YOUR_EXECUTION_ID","seq":0,"kind":"flow","flowId":"flow_YOUR_FLOW_ID","flowName":"Summarize Article","totalSteps":1,"startedAt":"2026-07-23T00:00:00Z"}

id: 1
event: step_start
data: {"type":"step_start","executionId":"exec_YOUR_EXECUTION_ID","seq":1,"id":"step_1","name":"Summarize","stepType":"prompt","index":0,"totalSteps":1,"startedAt":"2026-07-23T00:00:00Z"}

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

id: 3
event: text_delta
data: {"type":"text_delta","executionId":"exec_YOUR_EXECUTION_ID","seq":3,"id":"text_1","delta":"Machine learning is"}

id: 4
event: text_complete
data: {"type":"text_complete","executionId":"exec_YOUR_EXECUTION_ID","seq":4,"id":"text_1","text":"Machine learning is a method for training models with data."}

id: 5
event: step_complete
data: {"type":"step_complete","executionId":"exec_YOUR_EXECUTION_ID","seq":5,"id":"step_1","name":"Summarize","stepType":"prompt","success":true,"durationMs":1823,"completedAt":"2026-07-23T00:00:02Z"}

id: 6
event: execution_complete
data: {"type":"execution_complete","executionId":"exec_YOUR_EXECUTION_ID","seq":6,"kind":"flow","success":true,"durationMs":1823,"completedAt":"2026-07-23T00:00:02Z"}
```

The `event` name matches `data.type`. The `seq` value starts at 0 and increments by 1, and the SSE `id` carries the same number. The `execution_start` event and the terminal event carry the same `executionId`.

Other events can appear depending on what the capability does. For example, tool calls emit `tool_start` and `tool_complete`, model reasoning emits `reasoning_delta`, and approvals emit `approval_start`. An agent capability also emits `turn_start` and `turn_complete` events. Its `execution_start` event has `kind` set to `agent`, with `agentId` and `agentName` in place of `flowId` and `flowName`.

If execution fails after the stream starts, the API sends `execution_error` as the terminal event:

**`Streaming error`**

```text title="Streaming error"
id: 6
event: execution_error
data: {"type":"execution_error","executionId":"exec_YOUR_EXECUTION_ID","seq":6,"kind":"flow","error":"Flow execution failed"}
```

The `error` field is either a string or an object with `code` and `message` fields. Handle both forms.

## Error responses

The API uses HTTP status codes to report authentication, authorization, endpoint, rate-limit, and execution errors. The following examples show common responses.

When authentication is missing, the API returns `401 Unauthorized`:

**`Authentication error`**

```json title="Authentication error"
{
  "error": "Authentication required",
  "message": "Provide API key via Authorization: Bearer papi_xxx or X-API-Key header"
}
```

When the key is rejected, the API returns `401 Unauthorized` with an `error` message and a `code`. The following table lists the codes:

| `code`            | `error`                                       | What to do                                                          |
| ----------------- | --------------------------------------------- | ------------------------------------------------------------------- |
| `INVALID_KEY`     | `Invalid API key` or `Invalid API key format` | Check that you copied the full key for this API Surface.            |
| `EXPIRED_KEY`     | `API key has expired`                         | Generate a new key on the API Surface's **Keys** tab.               |
| `INVALID_SURFACE` | `API surface is not active`                   | Set the API Surface's status to **Active** on its **Overview** tab. |

When the `PRODUCT_ID` or `SURFACE_ID` doesn't match an API Surface, the API returns `404 Not Found` with the error `API surface not found`.

When a key can't use the endpoint, the API returns `403 Forbidden`:

**`Authorization error`**

```json title="Authorization error"
{
  "error": "Forbidden",
  "message": "API key does not have access to this endpoint"
}
```

On the dispatch endpoint, the `message` names the capability, for example `API key does not have access to capability 'Summarize Article'`.

When the capability slug doesn't match an available endpoint, the API returns `404 Not Found`:

**`Endpoint error`**

```json title="Endpoint error"
{
  "error": "Endpoint not found",
  "message": "No capability found for endpoint 'summarize-article'"
}
```

When the dispatch endpoint can't find the capability name, the `404 Not Found` response lists the names you can use:

**`Dispatch capability error`**

```json title="Dispatch capability error"
{
  "error": "Capability not found",
  "message": "Capability 'Summarize Articles' is not available on this surface",
  "availableCapabilities": ["Summarize Article"]
}
```

When a key exceeds its configured limit, the API returns `429 Too Many Requests`:

**`Rate limit error`**

```json title="Rate limit error"
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMITED",
  "retryAfter": 60
}
```

The `retryAfter` value is the number of seconds before the rate-limit window resets. The response also includes a `Retry-After` header with the same value.

When capability execution fails, the API returns `500 Internal Server Error`:

**`Execution error`**

```json title="Execution error"
{
  "error": "Execution failed",
  "message": "Flow execution failed"
}
```

When your account reaches an execution or spend limit, the API returns `503 Service Unavailable` with the code `SERVICE_UNAVAILABLE`. The message doesn't name the limit, so your end users don't see billing details. To check your account's usage, see [Rate limits and usage](/user-guide/troubleshooting-faq/rate-limits-and-usage).

A failed non-streaming request might not include an execution ID. When the API returns an `executionId`, use it to find the run in execution logs. Streaming responses include the execution ID in their `execution_start` and terminal events.

## Rate limits

Configure per-minute and per-day limits for each API key in the API Surface's **Keys** tab. The API applies those limits to requests that use the key and returns `429 Too Many Requests` when a key exceeds them. Your account's execution and spend limits also apply to every request.

## Next steps

Use these links to continue:

* [Setting up an API Surface](/user-guide/products-surfaces/setting-up-an-api-surface): create and configure an API Surface
* [Auto-generated OpenAPI spec](/user-guide/products-surfaces/auto-generated-openapi-spec): inspect the generated request and response schemas
* [Scoping API keys to capabilities](/user-guide/products-surfaces/scoping-api-keys-to-capabilities): limit a key to specific capabilities
* [Authenticating with product API keys](/user-guide/products-surfaces/authenticating-with-product-api-keys): configure headers and manage API keys