Skip to main content
CID222 Docs

Sessions API

Server-side conversation state under /sessions — creation, message streaming, history, summarisation, and the JWT-only guard.

  • Version: 0.4
  • Role: admin_user, normal_user
  • Type: reference

A session is conversation state CID222 keeps for you: message history, cumulative token usage and an automatically summarised context sent to the model. You send one message at a time and CID222 supplies the history.

Warning

Every /sessions route is guarded by JWT validation alone. A gateway API key (cid_key_…) returns 401 here, however it is presented. Use a token from POST /auth/login.

Endpoints

MethodPathReturnsDescription
POST/sessionsJSONCreate an empty session
GET/sessionsJSONList the tenant's chat sessions
GET/sessions/:idJSONOne session with its full history
PATCH/sessions/:idJSONRename a session
POST/sessions/:id/messagesSSESend a message and stream the reply
DELETE/sessions/:idJSONDelete a session

POST /sessions and POST /sessions/:id/messages are writes and are refused for the auditor role with 403 and ROLE_NOT_FOR_CHAT. Reads and deletes are scoped to the caller's own tenant.

Create a session

POST /sessions

FieldTypeRequiredNotes
session_namestringNoLabel. Defaults to Chat Session - <local timestamp>
user_idstringNoYour own end-user identifier, stored for attribution

The model and provider are chosen per message, not at creation time.

curl -sS -X POST "https://<appliance-fqdn>/sessions" \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"session_name":"Support chat","user_id":"user-123"}'
{
  "id": "6f1c2e7a-9b3d-4f5a-8c21-0d9a1b2c3d4e",
  "tenant_id": "b2c3d4e5-6f70-4a81-9b2c-3d4e5f607182",
  "user_id": "user-123",
  "session_name": "Support chat",
  "context": [],
  "token_usage": { "total_tokens": 0, "total_cost": 0 },
  "last_used_at": "2026-09-10T10:30:00.000Z",
  "created_at": "2026-09-10T10:30:00.000Z"
}

There is no updated_at. Recency is carried by last_used_at.

Send a message

POST /sessions/:id/messages returns text/event-stream.

FieldTypeRequiredNotes
contentstringYesThe user's message text
modelstringYesModel for this turn
providerstringNoDisambiguates a model name offered by two providers
temperaturenumberNo0 to 2
max_tokensnumberNo1 or greater
top_pnumberNo0 to 1
imageBase64stringNoBase64 image for a vision model, redacted before the model sees it
imageAnalysisSummaryobjectNoResult of a prior image analysis, to skip re-filtering
documentAnalysisSummaryobjectNoResult of a prior document analysis, to skip re-filtering
curl -N -sS -X POST \
  "https://<appliance-fqdn>/sessions/6f1c2e7a-9b3d-4f5a-8c21-0d9a1b2c3d4e/messages" \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"content":"I need help with my order 12345","model":"gpt-4o","provider":"openai"}'

The stream

The contract matches POST /chat/completions — buffered, with the whole reply delivered in one filtered-response event followed by data: [DONE] — with two differences.

user_message_processed is forwarded here. The session path passes this control event through so a UI can show what was masked in the prompt before the model saw it:

data: {"type":"user_message_processed","content":"My name is [NAME] and my email is [EMAIL]","original_had_issues":true,"entities_count":2,"masked_count":2,"flagged_count":0,"action_taken":"masked","filter_duration_ms":142}

data: {"type":"token_usage","usage":{"prompt_tokens":320,"completion_tokens":12,"total_tokens":332}}

data: {"id":"filtered-response","content":"Hello. How can I help you today?","finish_reason":"stop","filtered":false,"entities_masked":0}

data: [DONE]

The pre-filter rejection carries a different shape. When the session's own input filter rejects the message before the chat service is called, the event has no error field:

data: {"type":"content_rejected","reason":"Content policy violation: jailbreak","categories":["jailbreak"],"scores":{"jailbreak":0.96},"filter":"content-safety-v2 v2","filter_duration_ms":138}

data: [DONE]

A rejection raised further down, inside the chat service, uses the {"error":…,"type":"content_rejected","entities":[…]} shape documented on the Chat API page. Handle both: match on type === "content_rejected" rather than on the presence of error.

Everything else — user_locked, security_warning, token_usage, output_content_rejected, output_content_warning, [DONE] — behaves as on /chat/completions. There is no hallucination event.

Get one session

GET /sessions/:id returns the session with its complete context.

{
  "id": "6f1c2e7a-9b3d-4f5a-8c21-0d9a1b2c3d4e",
  "tenant_id": "b2c3d4e5-6f70-4a81-9b2c-3d4e5f607182",
  "user_id": "user-123",
  "session_name": "Support chat",
  "context": [
    { "role": "user", "content": "I need help with my order 12345", "model": "gpt-4o", "delta_tokens": 12 },
    { "role": "assistant", "content": "Let me look that up for you.", "model": "gpt-4o", "delta_tokens": 9 }
  ],
  "token_usage": { "total_tokens": 21, "total_cost": 0.00019 },
  "summarization_info": {
    "ai_context_tokens": 21,
    "summarization_threshold": 4000,
    "tokens_until_summarization": 3979,
    "summarization_enabled": true
  },
  "summarization_history": [],
  "has_hallucination_warning": false,
  "hallucination_warnings": [],
  "last_used_at": "2026-09-10T10:35:00.000Z",
  "created_at": "2026-09-10T10:30:00.000Z"
}

Each entry in context can also carry timestamp, content_hash, had_detections, detection_summary, filter_duration_ms, llm_duration_ms, is_rejected, rejection_reason and imageBase64.

The response type also declares context_window_usage, but the session routes do not populate it. Do not depend on it being present.

List sessions

GET /sessions returns every chat session belonging to the authenticated tenant, ordered by last_used_at descending.

Note

This endpoint accepts no query parameters and does not paginate. page and limit are for admin list endpoints; here they are ignored.

List rows deliberately omit history — a session can hold megabytes of base64 images. Each row returns context as an empty array plus a message_count:

[
  {
    "id": "6f1c2e7a-9b3d-4f5a-8c21-0d9a1b2c3d4e",
    "tenant_id": "b2c3d4e5-6f70-4a81-9b2c-3d4e5f607182",
    "user_id": "user-123",
    "session_name": "Support chat",
    "context": [],
    "message_count": 2,
    "token_usage": { "total_tokens": 21, "total_cost": 0.00019 },
    "has_hallucination_warning": false,
    "last_used_at": "2026-09-10T10:35:00.000Z",
    "created_at": "2026-09-10T10:30:00.000Z"
  }
]

Only sessions of type chat are listed. Sessions created implicitly for image scans and for sessionless API traffic stay out of the list and appear under detections instead.

Rename a session

PATCH /sessions/:id takes session_name, maximum 100 characters, and returns the updated session.

Delete a session

DELETE /sessions/:id removes the session. Related detection records are removed with it by cascade.

{ "message": "Session deleted successfully" }

Context management

  • Full history is stored in context and returned by GET /sessions/:id.
  • The model context is separate. CID222 keeps a second, summarised context and sends that to the provider.
  • Summarisation is threshold-driven, not model-driven. When the summarised context reaches context_summarization_threshold_tokens — default 4000 — CID222 summarises the older messages and records the event in summarization_history. The trigger is that configured token count, not the model's context window. Set context_summarization_enabled to false to turn it off.
  • Token usage accumulates per session in token_usage.total_tokens and token_usage.total_cost.
  • Hallucination warnings land on the session. The background retrieval-grounding check writes has_hallucination_warning and appends to hallucination_warnings. It never appears on the stream.

Errors

StatusCause
400Body failed validation, for example a session_name over 100 characters
401Missing or invalid JWT — including a gateway API key, which is never accepted here
403ROLE_NOT_FOR_CHAT for auditor on a write, READ_ONLY_ROLE for viewer on any mutation
404No session with that id belongs to the calling tenant
  • Chat API — the stateless equivalent and the full SSE event list.
  • Authentication — obtaining the JWT these routes need.
  • API overview — base paths and error shapes across every surface.

Last updated on

On this page

Download PDF