The Ask API consists of three parts:
- Ticket endpoint -- Issues a short-lived HMAC ticket for direct worker connection
- Worker SSE endpoint -- Receives the ticket and streams the AI answer via Server-Sent Events
- Chat Sessions CRUD -- Manage chat sessions and messages
Issues a signed HMAC ticket that the client uses to connect directly to the VPS worker for SSE streaming. This endpoint handles authentication and quota checks only -- the actual AI processing happens on the worker.
Session cookie or Bearer API key via deriveAuthV2().
| Field | Type | Required | Description |
|---|
projectId | string | Yes | The project to query |
question | string | Yes | The question to ask (min 3 characters) |
chatSessionId | string | No | Existing chat session ID to continue a conversation |
curl -X POST https://contox.dev/api/v2/ask \
-H "Content-Type: application/json" \
-H "Cookie: a_session_..." \
-d '{
"projectId": "proj_abc123",
"question": "How does the authentication middleware work?",
"chatSessionId": "sess_xyz789"
}'
{
"ticket": "eyJwaWQiOiJwcm9qX2FiYzEyMyIs...",
"workerUrl": "https://worker.example.com"
}
| Field | Type | Description |
|---|
ticket | string | HMAC-signed ticket in base64url(payload).signature format. Valid for 60 seconds. |
workerUrl | string | The VPS worker URL to connect to for SSE streaming |
The ticket contains the following claims, signed with WORKER_API_SECRET:
{
"pid": "proj_abc123",
"tid": "team_def456",
"uid": "user_ghi789",
"sid": "sess_xyz789",
"exp": 1705312260000
}
| Status | Error | Cause |
|---|
| 400 | projectId is required | Missing projectId in request body |
| 400 | question is required (min 3 chars) | Missing or too-short question |
| 401 | Unauthorized | Invalid or missing authentication |
| 403 | AI token limit reached | Team has exceeded AI token quota |
| 503 | Worker not configured | WORKER_URL or WORKER_API_SECRET not set |
The SSE streaming endpoint on the VPS worker. The client connects directly to this endpoint with the ticket obtained from /api/v2/ask.
| Field | Type | Required | Description |
|---|
ticket | string | Yes | The HMAC ticket from /api/v2/ask |
question | string | Yes | The question to ask (min 3 characters) |
curl -X POST https://worker.example.com/ask \
-H "Content-Type: application/json" \
-d '{
"ticket": "eyJwaWQiOiJwcm9qX2FiYzEyMyIs...",
"question": "How does the authentication middleware work?"
}'
The response is an SSE stream (Content-Type: text/event-stream). Each event is a data: line containing a JSON object.
Streamed incrementally as the LLM generates the answer. Each event contains a small chunk of text.
data: {"type":"token","content":"The authentication"}
data: {"type":"token","content":" middleware uses"}
data: {"type":"token","content":" JWT tokens..."}
| Field | Type | Description |
|---|
type | "token" | Event type identifier |
content | string | A chunk of the answer text |
Sent once when the answer is complete. Contains the full answer, sources, token usage, and metadata.
data: {"type":"done","answer":"The authentication middleware uses JWT tokens...","sources":[...],"avgSimilarity":0.78,"usage":{"promptTokens":1200,"completionTokens":350,"totalTokens":1550},"model":"gemini-2.0-flash"}
| Field | Type | Description |
|---|
type | "done" | Event type identifier |
answer | string | The complete answer text (markdown) |
sources | array | Array of search result objects (see below) |
avgSimilarity | number | Average cosine similarity across all sources |
usage | object | Token usage: promptTokens, completionTokens, totalTokens |
model | string | The model used (e.g., "gemini-2.0-flash") |
embedUsage | object | Embedding token usage: { totalTokens } |
error | string | undefined | If the LLM failed, this contains the error message instead of answer |
If the LLM encounters an error during streaming, the done event includes an error field instead of answer:
data: {"type":"done","error":"Model rate limited, please try again"}
The server sends SSE comments every 5 seconds to keep the connection alive:
Each entry in the sources array has this shape:
| Field | Type | Description |
|---|
itemId | string | The memory item's document ID |
type | string | Item type (e.g., architecture_pattern, implementation, convention, bug) |
title | string | Memory item title |
facts | string | Memory item facts text |
schemaKey | string | The item's schema key in the brain hierarchy |
similarity | number | Cosine similarity score (0.50--1.0) |
confidence | number | Item confidence score (0.0--1.0) |
files | string[] | File paths associated with the item |
used | boolean | Whether the AI explicitly cited this source in the answer |
| Status | Error | Cause |
|---|
| 400 | Invalid JSON | Malformed request body |
| 400 | question is required (min 3 chars) | Missing or too-short question |
| 401 | Invalid or expired ticket | Ticket signature invalid, expired, or missing |
Chat sessions group Ask conversations. All session endpoints use session cookie or Bearer API key authentication via deriveAuthV2().
List chat sessions for a project.
| Parameter | Type | Required | Default | Description |
|---|
projectId | string | Yes | -- | The project to list sessions for |
limit | number | No | 50 | Max sessions to return (1--200) |
offset | number | No | 0 | Pagination offset |
favorites | "true" | No | -- | Filter to only favorited sessions |
search | string | No | -- | Full-text search on session title (min 2 chars) |
curl "https://contox.dev/api/v2/chat-sessions?projectId=proj_abc123&limit=20&favorites=true" \
-H "Cookie: a_session_..."
{
"sessions": [
{
"id": "sess_abc123",
"projectId": "proj_abc123",
"userId": "user_def456",
"title": "How does authentication work?",
"messageCount": 6,
"aiTokensUsed": 4500,
"storageTokens": 1200,
"model": "gemini-2.0-flash",
"favoritedBy": ["user_def456"],
"status": "active",
"lastMessageAt": "2026-02-16T10:30:00.000Z",
"createdAt": "2026-02-16T10:00:00.000Z"
}
],
"total": 1
}
Create a new chat session.
| Field | Type | Required | Default | Description |
|---|
projectId | string | Yes | -- | The project to create the session for |
title | string | No | "New conversation" | Session title |
curl -X POST https://contox.dev/api/v2/chat-sessions \
-H "Content-Type: application/json" \
-H "Cookie: a_session_..." \
-d '{
"projectId": "proj_abc123",
"title": "Authentication deep dive"
}'
{
"ok": true,
"sessionId": "sess_abc123",
"session": {
"id": "sess_abc123",
"projectId": "proj_abc123",
"userId": "user_def456",
"title": "Authentication deep dive",
"messageCount": 0,
"aiTokensUsed": 0,
"storageTokens": 0,
"model": null,
"favoritedBy": [],
"status": "active",
"lastMessageAt": "2026-02-16T10:00:00.000Z",
"createdAt": "2026-02-16T10:00:00.000Z"
}
}
Get a single chat session's details including token statistics.
| Parameter | Type | Description |
|---|
id | string | The chat session ID |
Returns the same session object shape as the list endpoint.
| Status | Error | Cause |
|---|
| 401 | Unauthorized | Invalid or missing authentication |
| 404 | Session not found | No session with this ID exists |
Update a chat session's title or status.
| Parameter | Type | Description |
|---|
id | string | The chat session ID |
| Field | Type | Required | Description |
|---|
title | string | No | New session title (max 256 characters) |
status | "archived" | No | Set to "archived" to archive the session |
curl -X PATCH https://contox.dev/api/v2/chat-sessions/sess_abc123 \
-H "Content-Type: application/json" \
-H "Cookie: a_session_..." \
-d '{"title": "Auth middleware deep dive"}'
{
"ok": true,
"sessionId": "sess_abc123"
}
| Status | Error | Cause |
|---|
| 400 | No valid fields to update | Request body contained no valid fields |
| 401 | Unauthorized | Invalid or missing authentication |
| 404 | Session not found | No session with this ID exists |
Delete a chat session and all its messages. Storage tokens used by the session are decremented from the team's rollup.
| Parameter | Type | Description |
|---|
id | string | The chat session ID |
curl -X DELETE https://contox.dev/api/v2/chat-sessions/sess_abc123 \
-H "Cookie: a_session_..."
{
"ok": true,
"sessionId": "sess_abc123"
}
| Status | Error | Cause |
|---|
| 401 | Unauthorized | Invalid or missing authentication |
| 404 | Session not found | No session with this ID exists |
Toggle the favorite status for the current user on a chat session.
| Parameter | Type | Description |
|---|
id | string | The chat session ID |
curl -X POST https://contox.dev/api/v2/chat-sessions/sess_abc123/favorite \
-H "Cookie: a_session_..."
{
"ok": true,
"favorited": true
}
| Field | Type | Description |
|---|
favorited | boolean | true if the session is now favorited, false if unfavorited |
List messages for a chat session, ordered chronologically (oldest first).
| Parameter | Type | Description |
|---|
id | string | The chat session ID |
| Parameter | Type | Required | Default | Description |
|---|
limit | number | No | 200 | Max messages to return (1--500) |
cursor | string | No | -- | Cursor for pagination (message ID to start after) |
curl "https://contox.dev/api/v2/chat-sessions/sess_abc123/messages?limit=50" \
-H "Cookie: a_session_..."
{
"messages": [
{
"id": "msg_001",
"role": "user",
"content": "How does authentication work?",
"sources": [],
"promptTokens": 0,
"completionTokens": 0,
"totalTokens": 0,
"model": null,
"createdAt": "2026-02-16T10:00:00.000Z"
},
{
"id": "msg_002",
"role": "assistant",
"content": "The authentication system uses JWT tokens with...",
"sources": [
{
"itemId": "item_abc",
"type": "architecture_pattern",
"title": "JWT Auth Middleware",
"facts": "The auth middleware validates...",
"schemaKey": "root/architecture/auth",
"similarity": 0.87,
"confidence": 0.95,
"files": ["src/middleware/auth.ts"],
"used": true
}
],
"promptTokens": 1200,
"completionTokens": 350,
"totalTokens": 1550,
"model": "gemini-2.0-flash",
"createdAt": "2026-02-16T10:00:05.000Z"
}
],
"total": 2
}
| Field | Type | Description |
|---|
id | string | Message document ID |
role | "user" | "assistant" | Who sent the message |
content | string | The message text (markdown for assistant messages) |
sources | array | Array of source objects (empty for user messages) |
promptTokens | number | Prompt tokens used (0 for user messages) |
completionTokens | number | Completion tokens used (0 for user messages) |
totalTokens | number | Total tokens used (0 for user messages) |
model | string | null | Model used (null for user messages) |
createdAt | string | ISO 8601 timestamp |
- Ask (Concept) -- How Ask works architecturally
- HMAC Signing -- How HMAC signatures work in Contox
- Rate Limits -- API rate limit details