IronStratum

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, messages carries 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_id creates 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 (limit default 25, clamped to 100; total is 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} — idempotent 204 for your id, live or already deleted; a foreign or unknown id is the same 404 conversation_not_found as 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.