# Coral Inference API

> OpenAI-compatible inference for chat, interactive agents, and long-running autonomous agents. One API key, three surfaces, open models.

Coral Bricks runs open models against the OpenAI wire format. Point your
existing OpenAI SDK at the Coral gateway and you get three progressively
more powerful surfaces:

- **Chat Completions** — turn-based chat. Client owns the conversation.
- **Responses** — stateful, interactive agents. Server carries reasoning
  and thread state across turns.
- **Responses in background mode** — autonomous agents that run for
  minutes to tens of minutes, headless. Fire-and-poll, resumable
  streams, no HTTP hold.

Same auth. Same model ids. Same SDK. Choose the surface that matches the
shape of your workload.

> Coral Inference is currently a design-partner program. To get access,
> contact us at [hello@coralbricks.ai](mailto:hello@coralbricks.ai).

> **Building with a coding agent?** This page is served as raw markdown at
> [`https://www.coralbricks.ai/docs.md`](https://www.coralbricks.ai/docs.md).
> A machine-readable site index lives at [`/llms.txt`](https://www.coralbricks.ai/llms.txt), and the
> complete docs in one file at [`/llms-full.txt`](https://www.coralbricks.ai/llms-full.txt).

> **Using OpenCode, Cursor, Cline, or Claude Code?** One config stanza (or
> one click) routes their agent loops through Coral — see the setup guides
> for [OpenCode](https://www.coralbricks.ai/docs/opencode), [Cursor](https://www.coralbricks.ai/docs/cursor),
> [Cline](https://www.coralbricks.ai/docs/cline), [Claude Code](https://www.coralbricks.ai/docs/claude-code), and
> [Kilo Code](https://www.coralbricks.ai/docs/kilo-code).

---

## Coding agent setup guides

Point your coding agent at Coral with one config change:

- **[OpenCode](https://www.coralbricks.ai/docs/opencode)** — open-source, multi-provider terminal agent
- **[Cline](https://www.coralbricks.ai/docs/cline)** — VS Code extension for autonomous coding
- **[Claude Code](https://www.coralbricks.ai/docs/claude-code)** — Anthropic's terminal coding agent
- **[Kilo Code](https://www.coralbricks.ai/docs/kilo-code)** — open-source VS Code agent

---

## Which API should I use?

| Your workload | Use | Why |
|---|---|---|
| Turn-based chat, single-shot generation, you already have OpenAI Chat code | **Chat Completions** | Stateless per request. Drop-in for any Chat Completions client. |
| Multi-step interactive agent, tool use, reasoning that should carry across turns, a human is waiting | **Responses** | Server-side state via `previous_response_id`. Reasoning preserved between turns — measurably better on reasoning models. |
| Long-running headless agent, runs in CI / cron / a cloud box / a coding agent, no human waiting on the socket | **Responses (background)** | Fire-and-poll. Resumable streams. Survives caller death, network drops, and multi-minute runs. |

All three share the same base URL (`https://inference.coralbricks.ai/v1`),
the same API key, and the same model ids.

---

## Authentication

Every request needs:

```
Authorization: Bearer <CORAL_API_KEY>
```

- Mint and rotate keys at [/api-keys](https://www.coralbricks.ai/api-keys). Keys look like `cb_…` (older `ak_…` keys remain valid).
- Keep keys server-side. Never embed in a browser bundle or a mobile
  app — anyone with the key can spend against your account.
- Newly-minted keys may take up to ~30 seconds to be honoured.

Access to the Coral Inference API is gated per-account. If you see
`403 access_denied`, your account hasn't been granted yet — contact us.

---

## Models

| Model | Description | Status | Limitations |
|---|---|---|---|
| `glm-5.2-fp4` | Zhipu's GLM 5.2, FP4 weights | available | Text input only |
| `gpt-oss-120b` | OpenAI's open-weight 120B MoE | available | Text input only |
| `kimi-k3` | Moonshot AI's Kimi K3 | available | `frequency_penalty` and `presence_penalty` must be `0` |

Pass the model id verbatim as the `model` field on any request. The
exact set enabled for your key is also available programmatically:

```bash
curl -sS https://inference.coralbricks.ai/v1/models \
  -H "Authorization: Bearer $CORAL_API_KEY"
```

The same model ids work across Chat Completions, Responses, and
background mode.

`kimi-k3` is the only model that accepts image input. Sending an
`image_url`, `input_audio`, or `video_url` content part to a text-only
model returns `400 unsupported_content_type` rather than being silently
dropped, so a mis-routed multimodal turn fails loudly at the first call.
The `supports_image_input` flag on each `/v1/models` row carries the same
information programmatically.

### Pricing and cached input

**Cached input tokens are free** — a prefix that is already resident is billed
at $0, on every model and both APIs, with no cache-management flags to set.
Agent loops that re-send a growing conversation each turn benefit most: the
repeated prefix costs nothing after the first turn.

Cached reads and physical writes are reported per response. Cache writes are the novel
input for that request: `prompt_tokens - cached_tokens`. The separate billable counter
is non-zero only when a paid retention request is both requested and deliverable.

```json
"usage": {
  "prompt_tokens": 20767,
  "prompt_tokens_details": {
    "cached_tokens": 20032,
    "cache_write_tokens": 735,
    "billable_cache_write_tokens": 0
  },
  "completion_tokens": 246
}
```

---

## 1. Chat Completions

Turn-based chat. Stateless per request — you send the full message
history on every call, and the server returns one assistant turn. The
foundational OpenAI-compatible chat endpoint, unchanged from what your
existing SDK expects.

**Use when:** you already have working `chat.completions` code, you're
generating a single response, or you don't need the server to remember
anything about the conversation.

### `POST /v1/chat/completions`

```bash
curl -sS -X POST https://inference.coralbricks.ai/v1/chat/completions \
  -H "Authorization: Bearer $CORAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "messages": [
      {"role": "system", "content": "You are a careful research assistant."},
      {"role": "user",   "content": "What are the key metrics to watch for a Series A SaaS company?"}
    ]
  }'
```

Streaming is supported the OpenAI way — pass `stream: true` and consume
the SSE. Tool calling (`tools` / `tool_choice`) works the same as
upstream. Background mode is **not** available on this endpoint — use
`/v1/responses` for that.

```python
from openai import OpenAI

client = OpenAI(base_url="https://inference.coralbricks.ai/v1", api_key="cb_...")

resp = client.chat.completions.create(
    model="gpt-oss-120b",
    messages=[
        {"role": "system", "content": "You are a careful research assistant."},
        {"role": "user",   "content": "Explain KV attention in one paragraph."},
    ],
)
print(resp.choices[0].message.content)
```

---

## 2. Responses — interactive agents

Stateful. The server holds reasoning and conversation state between
turns, so subsequent calls can reference an earlier response by id
instead of resending the full history. This preserves the model's
reasoning context across turns, which measurably improves multi-step
tool-using agents.

**Use when:** you're building an interactive agent — multi-step tool
calling, iterative reasoning, threaded conversation — and a human (or
an upstream agent) is waiting on the reply. Timescale: seconds.

### `POST /v1/responses`

```bash
curl -sS -X POST https://inference.coralbricks.ai/v1/responses \
  -H "Authorization: Bearer $CORAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "input": "Sketch a plan to refactor the auth module."
  }'
```

Follow-up turns pass `previous_response_id` to continue the thread. The
server carries state; you don't have to replay the transcript:

```python
first = client.responses.create(
    model="gpt-oss-120b",
    input="Sketch a plan to refactor the auth module.",
)

# Same thread, no history replay needed.
next_turn = client.responses.create(
    model="gpt-oss-120b",
    input="OK, now do step 1 in detail.",
    previous_response_id=first.id,
)
print(next_turn.output_text)
```

| Field | Type | Notes |
|---|---|---|
| `model` | string | Required. One of the ids from `GET /v1/models`. |
| `input` | string or array | Required. Same shape as OpenAI Responses. |
| `instructions` | string | Optional system / developer instructions. |
| `previous_response_id` | string | Continues an existing thread. Sticky-routed back to the same replica for locality. |
| `stream` | boolean | When `true`, SSE stream of Responses events. |
| ...other OpenAI fields | | Forwarded verbatim. |

### `GET /v1/responses/{id}`

Retrieve any prior response by id — useful for auditing, resuming a
UI after a page reload, or re-hydrating a thread from persistent
storage. Also serves stream resumption (see background mode).

```bash
curl -sS https://inference.coralbricks.ai/v1/responses/$RESPONSE_ID \
  -H "Authorization: Bearer $CORAL_API_KEY"
```

### `DELETE /v1/responses/{id}`

Delete a stored response.

---

## 3. Responses in background mode — autonomous agents

Same endpoint as above, plus one flag: `background: true`. The call
returns immediately with a response id and `status: "queued"`. The
model then runs on Coral's cluster — not on your socket — for as long
as the work takes. You poll (or resume the stream) on your own cadence.

**Use when:** the caller is not a human at a chat window. Long-running
research loops, coding agents, batch pipelines, CI jobs, cron tasks,
overnight runs. Timescale: minutes to tens of minutes. Nobody is
waiting on the next token.

Why the shape matters for autonomous agents:

- **No HTTP hold.** The socket closes when `create` returns. No proxy
  timeouts, no keepalive plumbing, no reconnect logic.
- **Survives caller death.** If your agent process crashes or your CI
  runner is preempted, the response keeps running server-side. A fresh
  process retrieves by id and gets the completed result.
- **Resumable streams.** If you were streaming and the socket dropped,
  a `GET /v1/responses/{id}?stream=true` from any process picks up
  where you left off.
- **Location-independent.** Kick off from your laptop, poll from a
  Lambda, render in a Slack bot. One id, three consumers.

### `POST /v1/responses` (with `background: true`)

```bash
curl -sS -X POST https://inference.coralbricks.ai/v1/responses \
  -H "Authorization: Bearer $CORAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "input": "Plan a 5-step refactor for the attached repo, then execute step 1.",
    "background": true
  }'
```

```json
{
  "id": "resp_01HXY…",
  "object": "response",
  "status": "queued",
  "model": "gpt-oss-120b"
}
```

### `GET /v1/responses/{id}` (polling)

The status moves through `queued → in_progress → completed | failed |
cancelled`. Poll on any cadence you like.

```python
import time

queued = client.responses.create(
    model="gpt-oss-120b",
    input="Run the full research loop. Take your time.",
    background=True,
)

while True:
    current = client.responses.retrieve(queued.id)
    if current.status in ("completed", "failed", "cancelled"):
        break
    time.sleep(5)

print(current.output_text)
```

### `POST /v1/responses/{id}/cancel`

Cooperative cancel of an in-progress background response. Queued
responses flip to `cancelled` synchronously.

```bash
curl -sS -X POST https://inference.coralbricks.ai/v1/responses/$RESPONSE_ID/cancel \
  -H "Authorization: Bearer $CORAL_API_KEY"
```

### Stream + background

You can combine `stream: true` with `background: true`. The initial
call returns an SSE stream that you can consume; if the socket drops or
you want a second consumer, `GET /v1/responses/{id}?stream=true` picks
up from where the last event left off. Useful when the agent loop and
the renderer live on different machines.

---

## Streaming

Every surface supports OpenAI-shape SSE streaming via `stream: true`.
Consume it exactly as you would from OpenAI's SDK. See the background
section above for stream resumption via GET.

---

## Errors

Errors follow the OpenAI error shape:

```json
{"error": {"message": "…", "type": "…", "code": "…"}}
```

| Status | `code` | Meaning |
|---|---|---|
| `400` | `bad_request` / `model_required` | Malformed body or missing required field. |
| `401` | `missing_api_key` / `invalid_api_key` | Auth header absent or rejected. Re-mint at [/api-keys](https://www.coralbricks.ai/api-keys). |
| `403` | `access_denied` | Account isn't on the Coral Inference allowlist — contact us. |
| `404` | `model_not_accepted` | The `model` id isn't enabled for your account or doesn't match a known id. |
| `404` | `response_not_found` | Wrong `response_id`, or it belongs to another account. |
| `429` | `rate_limit_exceeded` | Per-API-key rate limit. Slow down and retry with backoff. |
| `502` | `upstream_error` | Upstream model server returned an error. Safe to retry. |
| `503` | `backend_unconfigured` | Transient. Retry. |
| `504` | `timeout` | Sync request waited too long. Re-issue with `background: true`. |

---

## Limits

Rate, concurrency, and context-length limits are set per API key and
account. The defaults fit a single interactive agent or a single
background loop comfortably. If you're fanning out (multiple
background agents in parallel, a batch pipeline, a coding agent that
spawns sub-agents), talk to us so we can raise the cap on your key.

---

## Support

- **Email**: [hello@coralbricks.ai](mailto:hello@coralbricks.ai)
- **API keys**: [/api-keys](https://www.coralbricks.ai/api-keys)
