Core API

Responses API

Build OpenAI-compatible Responses requests for new clients and agent workflows.

On this page

This page is for developers building a new OpenAI-compatible client or an agent workflow such as Codex CLI.

Send POST https://api.clssai.com/v1/responses with a JSON body. The endpoint has been verified with both non-streaming and streaming requests.

Minimal request#

BashSyntax highlighted
curl https://api.clssai.com/v1/responses \
  -H "Authorization: Bearer $CLSSAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-nano",
    "input": "Reply with exactly: Responses works."
  }'

Read the response#

Assistant output is represented as typed items in output. For a text response, find the message item and read its content[0].text. When the upstream response supplies usage.cost, that value is the charge authority.

JSONSyntax highlighted
{
  "id": "resp_example",
  "object": "response",
  "model": "openai/gpt-5.4-nano",
  "output": [{
    "type": "message",
    "role": "assistant",
    "content": [{"type": "output_text", "text": "Responses works."}]
  }],
  "usage": {
    "input_tokens": 12,
    "output_tokens": 5,
    "total_tokens": 17,
    "cost": 0.00001
  }
}

Do not assume that the first output item is always a message or that the first content item is always text. Agent and tool workflows can return other typed items.

Python#

Install the official client with pip install openai, then run this on your server.

PythonSyntax highlighted
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.clssai.com/v1",
    api_key=os.environ["CLSSAI_API_KEY"],
)

response = client.responses.create(
    model="openai/gpt-5.4-nano",
    input="Reply with exactly: Responses works.",
)
print(response.output[0].content[0].text)
print(response.usage)

JavaScript#

Install the official client with npm install openai. This example runs in Node.js.

JavaScriptSyntax highlighted
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.clssai.com/v1",
  apiKey: process.env.CLSSAI_API_KEY,
});

const response = await client.responses.create({
  model: "openai/gpt-5.4-nano",
  input: "Reply with exactly: Responses works.",
});
console.log(response.output[0].content[0].text);
console.log(response.usage);

Streaming#

Set stream: true to receive server-sent events. Every frame arrives as a data: line whose JSON carries a type field; the gateway does not send a named SSE event: field, so dispatch on type. Append the delta from each response.output_text.delta frame, read the final response object from response.completed, and stop when the stream sends data: [DONE].

BashSyntax highlighted
curl -N https://api.clssai.com/v1/responses \
  -H "Authorization: Bearer $CLSSAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-5.4-nano",
    "input": "Count from one to three.",
    "stream": true
  }'
Plain textPlain rendering
data: {"type":"response.created","response":{"id":"resp_example","status":"in_progress"},"sequence_number":0}

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

data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":"One","sequence_number":4}

data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":", two, three.","sequence_number":5}

data: {"type":"response.completed","response":{"id":"resp_example","status":"completed"},"sequence_number":16}

data: [DONE]

A full turn uses this order: response.createdresponse.in_progressresponse.output_item.addedresponse.content_part.addedresponse.output_text.delta (one or more frames) → response.output_text.doneresponse.content_part.doneresponse.output_item.doneresponse.completeddata: [DONE]. Ignore frame types you do not handle rather than treating them as errors.

Stream with Python#

This raw HTTP example uses requests so the data: framing and [DONE] marker remain visible.

PythonSyntax highlighted
import json
import os
import requests

with requests.post(
    "https://api.clssai.com/v1/responses",
    headers={
        "Authorization": f"Bearer {os.environ['CLSSAI_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "openai/gpt-5.4-nano",
        "input": "Count from one to three.",
        "stream": True,
    },
    stream=True,
    timeout=120,
) as response:
    response.raise_for_status()
    final_response = None
    for raw_line in response.iter_lines():
        line = raw_line.decode("utf-8")
        if not line.startswith("data:"):
            continue
        data = line.removeprefix("data:").strip()
        if data == "[DONE]":
            break
        event = json.loads(data)
        if event.get("type") == "response.output_text.delta":
            print(event["delta"], end="", flush=True)
        elif event.get("type") == "response.completed":
            final_response = event["response"]

print()
print(final_response)

Stream with JavaScript#

This Node.js example buffers partial network chunks, parses each data: line, dispatches on the JSON type, and exits on [DONE].

JavaScriptSyntax highlighted
const response = await fetch("https://api.clssai.com/v1/responses", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLSSAI_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "openai/gpt-5.4-nano",
    input: "Count from one to three.",
    stream: true,
  }),
});
if (!response.ok) throw new Error(await response.text());

const decoder = new TextDecoder();
let buffer = "";
let finalResponse = null;

stream: for await (const chunk of response.body) {
  buffer += decoder.decode(chunk, { stream: true });
  const lines = buffer.split(/\r?\n/);
  buffer = lines.pop() ?? "";

  for (const line of lines) {
    if (!line.startsWith("data:")) continue;
    const data = line.slice("data:".length).trim();
    if (data === "[DONE]") break stream;

    const event = JSON.parse(data);
    if (event.type === "response.output_text.delta") {
      process.stdout.write(event.delta);
    } else if (event.type === "response.completed") {
      finalResponse = event.response;
    }
  }
}

process.stdout.write("\n");
console.log(finalResponse);

Choose Responses or Chat Completions#

Use Responses for a new client or a Codex-style agent integration. Its typed output and event model are designed for evolving agent workflows. Use Chat Completions when you already have an OpenAI chat integration built around messages and choices.

Common mistakes#

  • Include /v1 in the OpenAI SDK base URL.
  • Keep the API key and SDK on your server; a browser call exposes the key to every visitor.
  • The gateway injects user when you omit it, and the Responses API can echo that value as user. Pass your own user to override it.
  • previous_response_id and store: true are rejected with 400 invalid_prompt; the gateway requires previous_response_id: null and store: false. There is no server-side conversation persistence — resend the conversation in input and keep state in your application.
  • Production clients should use a complete author/model ID. Bare names are resolved as described on Models & pricing, while :free and :batch variants must be written in full.
  • Do not subscribe to named SSE events on /v1/responses; frames carry no event: field. Parse each data: payload's type, and stop at data: [DONE].
  • Parse typed output items and streaming frames instead of assuming every result uses one fixed array position.
  • Retain cf-ray when asking support to trace an inference request.
Need a hand?

Find answers to common questions or diagnose a failed request.

Frequently asked questions →Troubleshoot errors →