API reference

The workspace HTTP API served by ai.thundera.eu.

Conventions

  • Base URL: https://ai.thundera.eu. JSON in and out unless noted.
  • Auth is a Thundera session: the thundera_token / thundera_refresh cookies, or Authorization: Bearer <access token>. Other cookies are ignored. Refreshed cookies from the auth service are passed through in Set-Cookie.
  • Errors are {"error": "message"} with the status codes listed per endpoint.
  • Request bodies are limited to 64 KiB (413).
MethodPathAuth
GET/healthnone
GET/metricsnone
GET/api/plansnone
GET/api/modelsnone
GET/api/sessioncookie / bearer
GET POST/api/conversationsrequired
GET/api/conversations/:idrequired
POST/api/conversations/:id/messagesrequired
POST/api/chat/streamrequired
POST/api/chatrequired

GET /health

Liveness and readiness. 200 {"status":"ok","service":"thundera-ai-web"}.

GET /metrics

Prometheus text format: thundera_ai_http_requests_total.

GET /api/plans

A cached (60 s) proxy of the central catalogue for app=ai, plus billingUrl. If billing is unreachable the last good response is served, or an empty list.

{ "plans": [ { "id": "ai-pro", "app_slug": "ai", "tier": "pro", "billing_period": "monthly",
    "name": "AI Pro", "unit_amount_cents": 999, "currency": "USD", "features": [], "limits": {} } ],
  "billingUrl": "https://auth.thundera.eu/billing" }

GET /api/models

Alias to send as modelId in chat requests:

{ "models": [ { "id": "smart", "name": "Smart", "provider": "thundera", "modelId": "smart",
    "description": "Balanced quality for writing and analysis.", "available": true } ] }

GET /api/session

Always 200; 503 if the auth service is unavailable.

{ "logged": true, "user": { "id": "…", "name": "…", "email": "…" } }
{ "logged": false, "user": null }

GET, POST /api/conversations

GET returns your conversations, most recent first. POST body {"title"?: string ≤ 200} creates one and returns 201 with the conversation. Errors: 400 invalid title, 401, 503 storage unavailable.

GET /api/conversations/:id

The conversation with its messages[] (id, role, content, status, modelId, createdAt; status is partial, final, cancelled or error). Errors: 400 invalid id, 404 not yours or missing.

POST …/:id/messages

POST /api/conversations/:id/messages

Body {"content": string, "modelId"?: string} appends a user message; 201 with the message. 400 if content is empty, 404 if the conversation is not yours.

POST /api/chat/stream

Streams an answer as Server-Sent Events.

{ "message": "Explain CRDTs briefly", "modelId": "smart", "conversationId": "<uuid>", "disclosureAccepted": true }

message and disclosureAccepted: true are required; modelId defaults to smart and must be an alias from /api/models; conversationId is optional but, when given, must be one of yours — the exchange is saved to it. Events:

EventData
conversation{conversationId, messageId} — sent first when the conversation is persisted
delta{text} — next chunk of the answer
done{conversationId, messageId} — finished successfully
error{error, conversationId} — failed; no done follows

Errors before the stream starts: 400 (disclosure, message, model or conversation id), 401, 404 (conversation), 429 (20/min), 503 (auth, gateway or storage). Budget and rate failures inside a stream arrive as an error event with a readable message.

POST /api/chat

Same body, non-streaming. 200 {"provider":"thundera-models","modelId","message"}. Failures: 402 allowance used up, 429, 503.