Sessions
Server-side conversation continuity: send only the new turn, we assemble the context. Opt-in per request with one field — session_id — and every session-mode tool (list, inspect, cost, delete) lives under /v1/conversations.
The one-field contract
Add session_id (a UUID you mint, canonical hyphenated hex, case-insensitive) to any /v1/chat/completions request:
curl -sS $IRONSTRATUM_BASE_URL/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $IRONSTRATUM_API_KEY" \
-d '{
"model": "qwen3.8-27b",
"session_id": "7b9dc46e-1f0a-4c1e-8f2b-3a9d10e11f01",
"messages": [{"role": "user", "content": "Remember this number: 42"}]
}'
Two conventions do all the work:
- Delta-only messages. In session mode,
messagescarries ONLY the new turn. The gateway prepends the stored conversation prefix — you never resend history, so you pay network for one turn, not the whole transcript. - Unseen id auto-creates. The first request with a fresh
session_idcreates the conversation; subsequent requests append. No separate create call exists.
The response echoes session_id on session-mode turns and streams (an additive field — it is simply absent on full-context requests). Non-stream and stream: true behave identically; the id rides every chunk.
Without session_id, nothing changes: full-context requests work exactly as before, and are stored passively under a gateway-minted id (see privacy for what is kept and for how long).
Overflow: the context law
The assembled context — every stored turn plus your new delta — is checked against the model's context_tokens (from GET /v1/models) before anything is sent upstream. Over the limit is a fail-fast 400 context_length_exceeded:
{
"error": {
"message": "The assembled session context exceeds this model's context_tokens. Start a new session_id or trim the conversation.",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
}
No partial state is created — the conversation is untouched. Remediation is yours to choose: mint a new session_id (fresh context), or delete the conversation and start over. Models without a published window are not enforced (the check is only as good as the catalog).
Same-session serialization
Concurrent requests against one session_id would interleave turns — the platform prevents it: the second request waits behind a bounded lock (30 s default), and if the wait expires it answers 429 rate_limited with nothing stored. Retry the same request shortly. This is the one live emitter of the 429 envelope (see rate posture); per-key throttling is separate, still future.
A client that serializes its own turns per session never sees this.
Prefix stability
The stored prefix is immutable history: turns append, they are never rewritten, reordered, or truncated by the platform. A retry that lands (same session_id, same new turn) appends once — the session transaction commits delta + assistant reply + usage row atomically, so an aborted or failed exchange stores nothing. Deleting the conversation is the only operation that removes history, and it is final: a session_id referencing a deleted conversation is not available for reuse, and never revives.
A session may switch models between turns: each request dispatches under the model it names, and the overflow check always uses that request's model window. The conversation's listed model_alias is the model that started it.
Inspecting and deleting
GET /v1/conversations— your key's live conversations, most-recently-active first (limitdefault 25, clamped to 100;totalis the unpaginated count).GET /v1/conversations/{id}— every turn in exchange order plus the per-conversation cost summary (itemized from its ledger rows).DELETE /v1/conversations/{id}— idempotent204for your id, live or already deleted; a foreign or unknown id is the same404 conversation_not_foundas any other not-found.DELETE /v1/conversations— every live conversation of the key, same ledger-integrity law.
Deletion removes content and tombstones the row; the anonymized financial ledger rows survive detached (billing integrity — see privacy). Inactive conversations age out on a 30-day default retention window (PLATFORM_RETENTION_DAYS internally).
Idempotency composes
Idempotency-Key and session_id work together: a replayed request restores the original response without appending a second turn — replay hits the cache before the session is touched. Responses larger than the 8 MiB replay cache are not cached — a retry re-executes, and in session mode that appends the turn again. Keep idempotent session requests under the cap.