# Responses

> OpenAI's Responses API, served by every text model here.

Section: Core API. Canonical page: https://routehook.ai/docs/responses

`POST /v1/responses` is OpenAI's newer text API and the one their SDK now defaults to. It takes an `input` (a prompt or a conversation), and answers with an `output` array plus an `output_text` convenience field. If your code says `client.responses.create(...)`, changing the base URL is the whole migration.

> **Note: Every text model answers it, not just OpenAI's**
>
> The request is translated into a chat completion and run down the ordinary chat path (same routing, same failover, same billing), and the answer is translated back. A gateway that forwarded this route verbatim could only offer it on the one upstream that implements it; translating means Claude, Gemini, Llama and the rest answer here too.

## A request

**cURL**

```bash
curl https://api.routehook.ai/v1/responses \
  -H "Authorization: Bearer $ROUTEHOOK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-mini",
    "input": "Summarise the difference between a hold and a charge."
  }'
```

**JavaScript**

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.routehook.ai/v1",
  apiKey: process.env.ROUTEHOOK_API_KEY,
});

const response = await client.responses.create({
  model: "openai/gpt-5.4-mini",
  input: "Summarise the difference between a hold and a charge.",
});

// The one-liner most examples use.
console.log(response.output_text);
```

**Python**

```python
from openai import OpenAI

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

response = client.responses.create(
    model="openai/gpt-5.4-mini",
    input="Summarise the difference between a hold and a charge.",
)

print(response.output_text)
```

## POST /v1/responses

OpenAI's Responses API, served by every text model on the platform rather than one vendor's.

Status: Available. Authentication: any live API key on the account.

## Carrying a conversation

`input` takes a list of role/content items as well as a string, and that is how a conversation is continued here. Send the turns you already have; the reply is appended by your own code, not by us.

```javascript
const response = await client.responses.create({
  model: "openai/gpt-5.4-mini",
  instructions: "Answer in one sentence.",
  input: [
    { role: "user", content: "What is a credit hold?" },
    { role: "assistant", content: "A reservation taken before a request runs." },
    { role: "user", content: "When is it released?" },
  ],
});
```

> **Warning: previous_response_id is refused, not ignored**
>
> OpenAI can store a response and continue it by id. This gateway stores no conversation content at all (only a usage row), so there is nothing here to continue. Sending `previous_response_id` returns `invalid_request` naming the limitation, rather than quietly dropping it and answering with a model that had forgotten everything before it. `store` is accepted and has no effect, for the same reason.

## Streaming

Set `stream: true` and the answer arrives as server-sent events in this API's own vocabulary: `response.created`, then `response.output_text.delta` per token, then `response.output_text.done` and `response.completed` carrying the final object and its usage. A failure before the first byte is an ordinary JSON error with a real status; after it, an `error` event inside the stream. The split described on the [Errors](/docs/errors) page.

## Streamed

```javascript
const stream = await client.responses.create({
  model: "openai/gpt-5.4-mini",
  input: "Count to five.",
  stream: true,
});

for await (const event of stream) {
  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }
}
```

## Truncation is reported honestly

A model cut off by `max_output_tokens` comes back with `status: "incomplete"` and `incomplete_details.reason: "max_output_tokens"`, not `completed`. A client that retries on truncation depends on that distinction, and reporting a cut-off answer as finished is the one status mistake that silently corrupts whatever consumes the text.
