# Responses

> The OpenAI Responses format, used by Codex CLI.

Creates a model response in the OpenAI Responses format, the format Codex CLI uses.

## Method and path

`POST /v1/responses`

Base URL `https://api.relayvia.dev/v1`. Send the key as `Authorization: Bearer sk-...`.

## Common parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `model` | `string` | Yes | Official model name. |
| `input` | `string \| array` | Yes | Text, or a list of input items: messages, images, files and tool outputs. |
| `instructions` | `string` | No | System instructions. |
| `stream` | `boolean` | No | `true` to receive Server-Sent Events. |
| `max_output_tokens` | `integer` | No | Output limit; it decides how much is reserved before the call. |
| `tools` | `array` | No | Tools your code runs (function and custom tools). |
| `tool_choice` | `string \| object` | No | Whether the model must, may or must not call a tool. |
| `reasoning` | `object` | No | Reasoning effort and summary for reasoning models. |
| `include` | `array` | No | Extra output to return. Include `reasoning.encrypted_content` to carry reasoning into the next turn. |

Requests are forwarded in the original format, so the original API's parameters work unless the next section says otherwise. This table lists the common ones; see the [official reference](https://platform.openai.com/docs/api-reference/responses/create) for the rest.

## Differences from the original API

- There is no server-side conversation state. `previous_response_id`, `conversation`, input items referenced by id (`item_reference`, or an item with an `id` and no `type`), `reasoning` items without `encrypted_content` and a stored `prompt` template return 400 `unsupported_parameter`. Send the whole conversation in `input` each turn. `store` is always false, and `GET /v1/responses/<id>` is not provided.
- `tools[]` accepts `function` and `custom` tools, and `tool_search` with `execution: client`, which Codex CLI runs locally. Other hosted tools return 400 `unsupported_parameter`.
- Web search is not available. `web_search` tools are removed before forwarding and the request runs normally without search, so Codex CLI keeps working. If web search is the only tool, or `tool_choice` names it, the request returns 400 instead.
- Remote files (`input_file.file_url`) return 400 `unsupported_parameter`; send the file inline as `file_data`.
- `user`, `safety_identifier` and `prompt_cache_key` are scoped to your account before forwarding, so prompt caches and user ids are never shared with other accounts.
- Events are forwarded as they arrive, and `response.completed` carries the usage. A `: keepalive` comment is sent after every 15 seconds without data. The model name in the response is the one you sent.
- A non-streaming request with no response within 95 seconds returns 500 `upstream_timeout`; use streaming for long outputs. Or send the header `X-Robi-Keepalive: 1`: if there is no response within 30 seconds you get a 200 right away and a space every 15 seconds until the JSON arrives, and a later failure arrives as an error object in the body.

## Request examples

**cURL**

```bash
curl https://api.relayvia.dev/v1/responses \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-5.5","input":"Hello!"}'
```

**Python**

```python
from openai import OpenAI

client = OpenAI(base_url="https://api.relayvia.dev/v1", api_key="sk-...")
resp = client.responses.create(model="gpt-5.5", input="Hello!")
print(resp.output_text)
```

**JavaScript**

```javascript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://api.relayvia.dev/v1", apiKey: "sk-..." });
const resp = await client.responses.create({ model: "gpt-5.5", input: "Hello!" });
console.log(resp.output_text);
```

## Response and streaming examples

**Response**

```json
{
  "id": "resp_abc123",
  "object": "response",
  "status": "completed",
  "model": "gpt-5.5",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "content": [
        {
          "type": "output_text",
          "text": "Hello! How can I help?"
        }
      ]
    }
  ],
  "usage": {
    "input_tokens": 9,
    "output_tokens": 8,
    "total_tokens": 17
  }
}
```

**Streaming**

```text
event: response.created
data: {"type":"response.created","response":{"id":"resp_abc123","status":"in_progress","model":"gpt-5.5"}}

event: response.output_text.delta
data: {"type":"response.output_text.delta","delta":"Hello!"}

event: response.completed
data: {"type":"response.completed","response":{"id":"resp_abc123","status":"completed","model":"gpt-5.5","usage":{"input_tokens":9,"output_tokens":8,"total_tokens":17}}}
```

## Errors this endpoint returns

| HTTP | code | When |
| --- | --- | --- |
| `400` | `invalid_request` | A parameter is invalid. If the model rejected it, the message is this sentence, or `Invalid value for parameter <param>.` with `param` set. |
| `400` | `unsupported_parameter` | The request uses something Relayvia does not offer: stored server-side state, a hosted tool or a remote file. `param` names it. |
| `400` | `invalid_media` | The model reports that an input image or file format is not supported. |
| `400` | `media_unreachable` | The model could not read the media URL you sent. |
| `400` | `content_policy` | The input did not pass the model's safety review. If the model does not say whether the input or the output was refused, the message is `Content did not pass safety review.` |
| `400` | `content_policy_output` | The model generated a result, but the result did not pass safety review. Your input was not the problem. |
| `401` | `invalid_api_key` | The API key does not exist, is disabled or has expired. |
| `402` | `insufficient_quota` | Not enough credits: the account's credits, the key's limit, the team member's limit or an account spend limit cannot cover the request, or the account is frozen. |
| `403` | `forbidden` | The key may not use this model, the source IP is not on the allowlist, or the account is disabled. |
| `404` | `model_not_found` | The model is not listed. |
| `413` | `request_too_large` | The request body is over 20 MB, or the model reports that the input exceeds its context or size limit (then with this sentence). |
| `429` | `rate_limited` | Too many requests: per IP, per key, per account, or too many requests in progress at once. |
| `500` | `model_price_error` | The model has no price yet, so it cannot be called. |
| `500` | `upstream_error` | Relayvia tried other model services and every attempt failed. |
| `503` | `no_available_channel` | No model service is available for this model right now. |
| `503` | `service_busy` | Every model service for this model reported being busy. |
| `500` | `upstream_timeout` | Every attempt timed out. |

Every code, its fixed message and what to do are on the [error codes](https://relayvia.dev/docs/errors) page.
