---
title: "Create response"
description: "Create a model response (OpenAI Responses protocol)."
canonical: "https://pirouter.ai/docs/api/responses"
last-updated: "2026-10-13"
lang: "en"
---

# Create response

Create a model response (OpenAI Responses protocol).

`POST https://api.pirouter.ai/v1/responses`

OpenAI-compatible Responses API. A model is served only on the protocols it is registered for; calling one that does not accept this protocol is `validation_failed` (422). With `"stream": true` the response is a Server-Sent Events stream of typed response events; otherwise a single JSON body. Only the fields listed in the request schema are read by the platform; every other field is forwarded to the upstream provider as-is.

## Authorization

Send your API key in one of these headers.

| Header | Value | Description |
| --- | --- | --- |
| `Authorization` | `Bearer sk-pi-…` | Platform-issued API key with the `sk-pi-` prefix, sent as a Bearer token. |
| `X-Api-Key` | `sk-pi-…` | The same platform-issued `sk-pi-` key, sent in `X-Api-Key`. Read only when `Authorization` is absent or empty; when both are sent, `Authorization` wins. A bare key without the `Bearer ` prefix is accepted in either header. |

## Body

`application/json`

OpenAI Responses request. Only the fields the platform reads are listed; the rest of the protocol's fields are forwarded upstream unchanged. The routing fields `provider` and `models` are consumed by routing and removed before forwarding.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | string | Yes | Model slug in "vendor/model" form. |
| `input` | string \| object[] | No | The input to the model, a string or an array of input items as the protocol defines. Forwarded unchanged. |
| `stream` | boolean | No | When true, respond as a Server-Sent Events stream. Default: `false`. |
| `models` | string[] · max 3 | No | Models to fall back to, tried after `model` in array order when every deployment of the current model fails before its first byte (see Runtime fallback). Entries repeating `model` or an earlier entry are dropped; with no `model` the first entry is the primary. Every entry must exist (404 model_not_found naming it); more than 3 is validation_failed. Not forwarded upstream. |
| `provider` | object | No | Per-request provider routing preferences, applied when several providers serve the requested model. Limits (`only`, `ignore`, `data_collection`, `zdr`, `quantizations`) can only tighten the workspace routing settings; the sort mode (`order` and `sort`) is a default the request replaces as a whole. Provider names are platform ids, matched case-insensitively, plus the aliases `google-vertex` (gcp), `google-ai-studio` (gemini) and `amazon-bedrock` (aws). A known but unimplemented field is refused with `provider.<field> is not supported yet`; any other unknown key with `provider: unrecognized key "<key>"`. |
| `provider.order` | string[] | No | Providers to try first, in this order; providers not listed come after them. |
| `provider.only` | string[] | No | Use only these providers (intersected with the workspace allowlist). |
| `provider.ignore` | string[] | No | Never use these providers (added to the workspace ignore list). Wins over `only`. |
| `provider.allow_fallbacks` | boolean | No | With `false` and an `order`, only the providers in `order` may serve. A workspace `false` cannot be relaxed. Default: `true`. |
| `provider.sort` | string \| object | No | Rank providers within each `order` tier by `input_text + output_text` unit price, lowest first. Without it, deployment weight decides. |
| `provider.sort.by` | string | Yes | One of: `price`. |
| `provider.data_collection` | string | No | `deny` excludes providers that may train on prompts or retain them beyond a limited window (undeclared counts as may). One of: `allow`, `deny`. Default: `"allow"`. |
| `provider.zdr` | boolean | No | Only zero-data-retention providers. Default: `false`. |
| `provider.quantizations` | string[] | No | Only deployments at one of these precisions. One of: `fp4`, `int4`, `int8`, `fp8`, `fp16`, `bf16`, `fp32`, `unknown`. |

## Response

`200` `application/json`, `text/event-stream`

The response object, in the shape the OpenAI Responses protocol defines; the body is the upstream's, passed through. Streaming requests get an SSE stream of that protocol's typed events (`event:` plus `data:` lines).

## Errors

| Status | Code | Description |
| --- | --- | --- |
| `400` | `invalid_request` | The request is missing a required field (`invalid_request`): a body with neither `model` nor `models` answers `model is required`. |
| `401` | `invalid_api_key`, `api_key_not_found`, `api_key_revoked`, `api_key_expired` | API key is missing or malformed (`invalid_api_key`), unknown (`api_key_not_found`), revoked (`api_key_revoked`), or expired (`api_key_expired`). |
| `402` | `insufficient_balance` | Wallet balance cannot cover the requested call (`insufficient_balance`). |
| `404` | `model_not_found`, `no_eligible_provider` | Requested model slug is unknown or not enabled (`model_not_found`), or the request's `provider` preferences, merged with the workspace routing settings, leave no provider that serves it (`no_eligible_provider`, with `error.metadata` carrying the routing funnel). |
| `422` | `validation_failed` | The request failed validation (`validation_failed`): the model is not served on this endpoint (the message lists the endpoints it is served on), or a routing field (`provider`, `models`, `route`, `preset`) breaks its rules. The message names the cause. |
| `429` | `rate_limited` | Request rejected by rate limiting (`rate_limited`). |
| `500` | `internal_error` | Other failures: `provider_unavailable` (503) when no healthy upstream can serve the request, `internal_error` (500) for unexpected platform-side failures. |
| `503` | `provider_unavailable` | Other failures: `provider_unavailable` (503) when no healthy upstream can serve the request, `internal_error` (500) for unexpected platform-side failures. |

Error body

```json
{
  "error": {
    "message": "No such model. Model names are vendor-qualified (vendor/model), so a name copied from a provider's own docs needs its vendor prefix added. Call GET /v1/models for the slugs this gateway serves.",
    "type": "not_found_error",
    "code": "model_not_found"
  }
}
```

## Response headers

Each header is listed with the response statuses it is sent with.

| Header | Sent with | Description |
| --- | --- | --- |
| `X-Pi-Trace-Id` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Platform request id, minted by the gateway. Present on every response of the inference endpoints; quote it when reporting a problem. |
| `Access-Control-Allow-Origin` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Always `*`. Credentials are the API key header, never a cookie. |
| `Access-Control-Expose-Headers` | `200`, `400`, `401`, `402`, `404`, `422`, `429`, `default` | Lists every `X-Pi-*` response header plus `Retry-After` and `Content-Encoding`, so browser code can read them. |
| `X-Pi-Provider` | `200`, `default` | Provider that served this response, in the same vocabulary as `provider.order` and `GET /v1/models/{vendor}/{model}/endpoints`. Present only when the upstream provider answered (a completion or an upstream error); after a fallback it names the provider that took over. |
| `X-Pi-Model` | `200`, `default` | Model slug that served this response - the same value as the body's `model`. When a fallback model took over, it is that model. Present only when the upstream provider answered. |
| `X-Pi-Upstream-Model` | `200`, `default` | The full model id the upstream provider itself reported in its response, passed on verbatim (on a stream, from the first data event). Absent when the upstream response named no model; never filled from the platform's own records. |
| `X-Pi-Upstream-Request-Id` | `200`, `default` | The upstream provider's own request id, passed on verbatim, for looking the call up on the provider's side. Absent when the provider returned none. |

## Request samples

cURL

```bash
curl https://api.pirouter.ai/v1/responses \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-sol",
    "input": "Hello!"
  }'
```

Python

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.pirouter.ai/v1",
    api_key=os.environ["PIROUTER_API_KEY"],
)

response = client.responses.create(
    model="openai/gpt-6-sol",
    input="Hello!",
)
print(response.output_text)
```

TypeScript

```ts
import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'https://api.pirouter.ai/v1',
  apiKey: process.env.PIROUTER_API_KEY,
})

const response = await client.responses.create({
  model: 'openai/gpt-6-sol',
  input: 'Hello!',
})
console.log(response.output_text)
```

## Response sample

`200` `application/json`

```json
{
  "id": "resp_0064aae7eb7c...",
  "object": "response",
  "created_at": 1791795973,
  "status": "completed",
  "model": "openai/gpt-6-sol",
  "output": [
    {
      "id": "msg_0064aae7eb7c...",
      "type": "message",
      "status": "completed",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "Hello! How can I help?",
          "annotations": []
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 8,
    "output_tokens": 11,
    "total_tokens": 19
  }
}
```

`200` `text/event-stream`

```text
event: response.created
data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_0c7f2fbaa808...","object":"response","created_at":1791795975,"status":"in_progress","model":"openai/gpt-6-sol","output":[]}}

event: response.in_progress
data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_0c7f2fbaa808...","object":"response","created_at":1791795975,"status":"in_progress","model":"openai/gpt-6-sol","output":[]}}

event: response.output_item.added
data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_0c7f2fbaa808...","type":"message","status":"in_progress","role":"assistant","content":[]}}

event: response.content_part.added
data: {"type":"response.content_part.added","sequence_number":3,"output_index":0,"content_index":0,"item_id":"msg_0c7f2fbaa808...","part":{"type":"output_text","text":"","annotations":[]}}

event: response.output_text.delta
data: {"type":"response.output_text.delta","sequence_number":4,"output_index":0,"content_index":0,"item_id":"msg_0c7f2fbaa808...","delta":"Hi"}

event: response.output_text.done
data: {"type":"response.output_text.done","sequence_number":6,"output_index":0,"content_index":0,"item_id":"msg_0c7f2fbaa808...","text":"Hi!"}

event: response.content_part.done
data: {"type":"response.content_part.done","sequence_number":7,"output_index":0,"content_index":0,"item_id":"msg_0c7f2fbaa808...","part":{"type":"output_text","text":"Hi!","annotations":[]}}

event: response.output_item.done
data: {"type":"response.output_item.done","sequence_number":8,"output_index":0,"item":{"id":"msg_0c7f2fbaa808...","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi!","annotations":[]}]}}

event: response.completed
data: {"type":"response.completed","sequence_number":9,"response":{"id":"resp_0c7f2fbaa808...","object":"response","created_at":1791795975,"status":"completed","model":"openai/gpt-6-sol","output":[{"id":"msg_0c7f2fbaa808...","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"Hi!","annotations":[]}]}],"usage":{"input_tokens":8,"output_tokens":6,"total_tokens":14}}}
```
