# Statefulness on the OpenAI-compatible API

> previous_response_id, the conversation extension param, and stateless calls compared.

Both `/v1/responses` and `/v1/chat/completions` can run **stateful** (Octo
remembers the conversation server-side) or **stateless** (a one-shot call with
no memory). Which one you get depends on which field you send.

## The statefulness table

| Call | Behavior | Persistence |
|---|---|---|
| `POST /v1/responses` with `previous_response_id` | Continues the conversation the referenced response ran in. | Persisted (unless `store:false` was used). |
| `POST /v1/responses` without `previous_response_id` | Starts a new conversation. | Persisted (unless `store:false`). |
| `POST /v1/chat/completions` **with** `conversation` (`mc-...` short id or uuid) | Continues that server-side conversation; **only the last message in `messages[]` is consumed** — the server owns history. | Persisted. |
| `POST /v1/chat/completions` **without** `conversation` | A new conversation is seeded from the client's full `messages[]`. | **Ephemeral** — not stored, and runs **safe tools only** (see [Confirmations](/openai-api/confirmations/)). |

In every case, client `system` / `developer` messages (and the Responses API's
`instructions` field) become an **additional-instructions** block appended
after Octo's own system prompt — they never replace it.

## `previous_response_id` (Responses API)

```python
from openai import OpenAI
client = OpenAI(api_key=OMNI_OCTO_API_KEY, base_url="https://api.omniocto.com/v1")

a = client.responses.create(model="claude-sonnet-4-6", input="Remember the number 7.")
b = client.responses.create(
    model="claude-sonnet-4-6",
    input="What number did I say?",
    previous_response_id=a.id,
)
print(b.output_text)   # "You said 7."
```

Continuation is **workspace-scoped**: any API key in the workspace may
continue any conversation in that workspace — Octo's work belongs to the
workspace, not to the individual key that started it. See
[Authentication](/getting-started/authentication/).

## `conversation` (Chat Completions extension param)

A stateful Chat Completions response echoes the conversation id back in a
non-standard `x_octo_conversation` field (top-level; OpenAI clients tolerate
extra fields), so a client can opt into continuation on its next call:

```jsonc
{
  "id": "chatcmpl-...", "object": "chat.completion", "model": "claude-sonnet-4-6",
  "choices": [ { "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" } ],
  "usage": { "prompt_tokens": 812, "completion_tokens": 143, "total_tokens": 955 },
  "x_octo_conversation": "mc-0000000001"   // present only on stateful (persisted) turns
}
```

```python
# Stateless one-shot (safe tools only):
r = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "What can you help me with?"}],
)
conv = r.model_extra.get("x_octo_conversation")   # None here (stateless)

# Stateful — continue a server-side conversation for consequential work:
r2 = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[{"role": "user", "content": "Draft a follow-up to yesterday's leads."}],
    extra_body={"conversation": conv or "mc-0000000001"},
)
```

```bash
curl https://api.omniocto.com/v1/chat/completions \
  -H "Authorization: Bearer $OMNI_OCTO_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-4-6","messages":[{"role":"user","content":"hi"}],"conversation":"mc-0000000001"}'
```

## `background` mode (Responses API)

```python
r = client.responses.create(
    model="claude-sonnet-4-6",
    input="Launch the recall campaign for list B.",
    background=True,
)
# r.status == "queued", r.id == "resp_..."

done = client.responses.retrieve(r.id)   # status -> "completed" | "failed"
```

On completion (or failure), Octo writes a `meta_response.completed` event.
To be told, subscribe to it on a webhook endpoint (`POST /v1/webhooks` with
`"event_types": ["meta_response.completed"]`, or `"*"`, or under Settings →
API Keys & Webhooks), or read it from `GET /v1/events`. See
[Events & webhooks](/openai-api/events/). The delivery is signed and retried
like every other event:

```jsonc
{
  "id": "evt_…",
  "type": "meta_response.completed",
  "workspace_id": "…",
  "created_at": "…",
  "seq": 48213,
  "data": { "response_id": "resp_3f9a1c2b7d", "conversation_id": "…", "status": "completed", "error": null }
}
```

When the response failed, `status` is `failed` and `error` is
`{ "type", "code", "message" }`, the error `GET /v1/responses/{id}` returns.

Background execution is best-effort in-process: a boot-time sweep transitions
any response stuck past a TTL to `failed` (with an error envelope + the
event), so a background response always completes or fails — it never hangs
forever. Long-running tools (campaigns, calls) are durably executed regardless
of this TTL.

## `store: false`

Set `store: false` on `/v1/responses` to run and return inline **without**
creating a retrievable record — it's not fetchable via `GET
/v1/responses/:id`, can't be used as a later `previous_response_id`, and is
rejected if combined with `background: true`.

## Idempotency

Send an `Idempotency-Key` header on a non-background `POST /v1/responses`; a
retry with the same key returns the **stored** response instead of re-running
the turn. Reusing the key with a different body returns `422`.

```bash
curl https://api.omniocto.com/v1/responses \
  -H "Authorization: Bearer $OMNI_OCTO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"model":"claude-sonnet-4-6","input":"Summarize today'"'"'s meetings."}'
```

## Next

- [Confirmations](/openai-api/confirmations/) — how a parked risky action gets
  confirmed on the next turn of the same stateful conversation.
- [Streaming](/openai-api/streaming/) for SSE token delivery.