StrataDocs

Chat

The Chat resource exposes Strata's prompt loop and conversation history over HTTP. It is the main endpoint of the public API — send a message, get the AI's reply, either streamed or as a single JSON object. Every endpoint on this page requires an API key with the chat scope.

The base URL is https://app.strata.kronisys.com/v1 (or your org's deployment with /v1 appended). The examples below use <API_BASE_URL> for that prefix — for example, <API_BASE_URL>/chat resolves to https://app.strata.kronisys.com/v1/chat. Keys look like sk_strata_live_…; call from your backend only, never client-side code — the key is a secret that must never be exposed in a browser or mobile bundle.

POST /chat

Send a message to a Strata model and stream or collect the response. The same AI engine, tools, and per-tenant Azure AI Foundry routing the web app uses runs behind this endpoint.

Headers

HeaderRequiredDescription
AuthorizationyesBearer sk_strata_live_...
Content-Typeyesapplication/json
AcceptnoSet to text/event-stream for SSE. Any other value returns one-shot JSON.
Idempotency-KeynoReplay-safe key for non-streaming requests, 24h window. See Reliability.

Body

Send either message (a single string) or messages (a conversation array). They are mutually exclusive; if both are present, message wins.

FieldTypeRequiredDescription
messagestringone ofA single user message.
messagesarrayone ofConversation history as { role, content } objects. role is user, assistant, or system. The last message should be a user turn.
modelstringnoA model id the key is allowed to use. Defaults to the key's first allowed model. See Models.
modestringnoauto (default), technical, or simple. An org may pin a required mode that overrides this.
permissionstringnoauto (default), read, or write. Caps whether SQL tools may run mutating statements — but only takes effect when the key holds a SQL extension scope. A chat-only key has no SQL tools, so this field does nothing for it.
conversation_idstringnoA UUID to continue an existing conversation. Omit to start a new one.
{
  "message": "Top 10 customers by revenue last quarter",
  "model": "gpt-5.4",
  "mode": "auto"
}
Note

There is no connection_id field and no stream body flag. Streaming is chosen by the Accept header. SQL tools run against the actor user's own active SQL Server connection, configured in Strata, not a per-request value.

Key

The chat scope alone does not let a key run SQL. The run_sql tool only appears when the key also holds the sql.read (read-only) or sql.write (read + write) extension scope, and the actor (user or connection bot) has a SQL Server connection. A chat-only key answers prompts but can never touch the database. Atlassian works the same way: search_jira appears only when the key holds jira.read or jira.write and the actor can reach the organization's pinned Atlassian site, and search_confluence only when the key holds confluence.read or confluence.write. write_jira additionally requires the organization to allow Jira writes and write_confluence requires it to allow Confluence writes — resolved per product, so with a product's writes off its create and comment tools are never offered, whatever the key carries. See Extension access for the full scope model and Atlassian extensions for the organization's connection modes.

Example request

curl -X POST <API_BASE_URL>/chat \
  -H "Authorization: Bearer sk_strata_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Summarize Q1 revenue by region",
    "model": "claude-sonnet"
  }'

Example response

A non-streaming request returns a single JSON object:

{
  "id": "f04a72c1-...",
  "conversation_id": "9b1e4c8d-...",
  "model": "claude-sonnet",
  "content": "Q1 revenue totaled $14.2M across four regions...",
  "tool_calls": [],
  "finish_reason": "stop",
  "usage": { "prompt_tokens": 3120, "completion_tokens": 540, "total_tokens": 3660 },
  "created_at": "2026-06-24T18:22:14Z"
}
FieldDescription
idUnique id for this response.
conversation_idThe conversation this turn belongs to. Persist it to continue the thread.
modelThe model that produced the reply.
contentThe assistant's full text reply.
tool_callsArray of { name, args } objects — one entry per tool the AI invoked (e.g. run_sql, search_outlook, search_jira, search_confluence). Empty when no tools were called.
finish_reasonstop on a complete reply, length if the output was truncated.
usageToken counts, or null if the provider did not report them.
created_atISO 8601 timestamp.

Streaming with Accept: text/event-stream

When you set Accept: text/event-stream, the response is a text/event-stream connection. Each event is a data: line carrying a JSON object with a type. The event types are:

Event typePayload
meta{ conversation_id, model } — always the first event
content{ content } — an incremental text chunk
tool_call_start{ name, args } — emitted when the AI begins invoking a tool (e.g. run_sql)
step{ status, label, tool } — progress detail as the AI runs a tool/action step
done{ conversation_id, model, finish_reason } — final event
error{ error, code } — terminal failure (5xx details are scrubbed to a generic message)
Tip

Read the meta event first and persist conversation_id before processing content. If the stream drops, reconnect by sending the next prompt with the same conversation_id — do not blind-retry the original POST.

Parsing SSE

In the browser, fetch the stream and parse it manually (native EventSource cannot send an Authorization header):

const res = await fetch(`${API_BASE_URL}/chat`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    Accept: "text/event-stream",
  },
  body: JSON.stringify({ message: "hello" }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  const frames = buffer.split("\n\n");
  buffer = frames.pop();
  for (const frame of frames) {
    const line = frame.split("\n").find(l => l.startsWith("data:"));
    if (!line) continue;
    const event = JSON.parse(line.slice(5).trim());
    handle(event); // switch on event.type
  }
}

From the command line, pass -N to disable curl's output buffering:

curl -N -X POST <API_BASE_URL>/chat \
  -H "Authorization: Bearer sk_strata_live_..." \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{"message":"hello"}'

Models

The model field accepts any model id in the API key's allowed list — a subset an admin chose when minting the key, which itself is a subset of the org's enabled models. Strata runs a DB-driven Model Catalog that Kronisys curates centrally, served through Microsoft Azure AI Foundry; each organization enables the models it wants and admins manage that set in the Model Catalog (Admin → Organization Settings → Models). The catalog spans many models across OpenAI, Anthropic, xAI, DeepSeek, Mistral AI, Cohere, and Microsoft.

Common chat model ids include gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.4, gpt-5.4-mini, claude-sonnet, claude-opus, grok-4.3, and grok-4-fast. Rather than hard-coding a list, call GET <API_BASE_URL>/health — it returns the calling key's allowed_models — or check the in-app Model Catalog for the live set. A model that the key, the org, or the actor's per-user access does not allow returns 403 model_not_allowed; if an admin removes a model after the key was minted, that model is rejected at request time too.

Errors

StatusSlugWhen
400invalid_requestNeither message nor messages supplied, or a malformed conversation_id
401invalid_token / missing_tokenKey missing, malformed, expired, or revoked
403insufficient_scopeKey lacks the chat scope
403model_not_allowedRequested model not allowed by the key or the org's current policy
403org_suspendedThe key's organization is suspended
404conversation_not_foundconversation_id does not exist or belongs to a different actor
409request_in_progressAn identical Idempotency-Key request is still running
429rate_limit_exceeded / budget_exhausted / usage_limit_exceededPer-key rate window, monthly budget, or org usage pool exhausted
503policy_lookup_failed / budget_check_failedPolicy or budget enforcement temporarily unavailable — retry

See Errors for the full reference and the problem+json shape.

POST /query

A convenience wrapper for one-shot questions. It sends a single prompt, runs the same AI loop, and returns the answer as JSON. The conversation is ephemeral — it is never persisted, so there is no conversation_id to follow up on. Always returns JSON regardless of the Accept header.

Note

/query is for natural-language questions, not raw SQL. The AI decides whether to call its SQL tools. There is no endpoint to run SQL yourself without the model in the loop; constrain mutations by sending permission: "read", which forces read-only tools.

Headers

HeaderRequiredDescription
AuthorizationyesBearer sk_strata_live_...
Content-Typeyesapplication/json

Body

FieldTypeRequiredDescription
querystringyesThe question to ask.
modelstringnoA model id the key is allowed to use. Defaults to the key's first allowed model.
modestringnoauto, technical, or simple.
{
  "query": "What was total Q1 revenue, and which region led?",
  "model": "gpt-5.4"
}

Example request

curl -X POST <API_BASE_URL>/query \
  -H "Authorization: Bearer sk_strata_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "query": "How many active customers do we have right now?" }'

Example response

{
  "id": "a1b2c3d4-...",
  "model": "gpt-5.4",
  "answer": "There are 1,284 active customers as of today...",
  "finish_reason": "stop",
  "usage": { "prompt_tokens": 410, "completion_tokens": 96, "total_tokens": 506 },
  "created_at": "2026-06-24T18:30:00Z"
}

Errors

StatusSlugWhen
400invalid_requestMissing query
403model_not_allowedRequested model not allowed by the key or org
429rate_limit_exceeded / budget_exhausted / usage_limit_exceededLimits exhausted

GET /conversations

List conversations the API key's actor has created, newest first. Page backward in time with the before cursor.

Headers

HeaderRequiredDescription
AuthorizationyesBearer sk_strata_live_...

Query parameters

ParameterTypeDescription
limitintegerPage size, 1–100. Default 25.
beforestringISO 8601 timestamp. Returns conversations updated strictly before this time — pass the updated_at of the last row you saw to page.

Example request

curl "<API_BASE_URL>/conversations?limit=10" \
  -H "Authorization: Bearer sk_strata_live_..."

Example response

{
  "data": [
    {
      "id": "9b1e4c8d-...",
      "title": "Q1 revenue by region",
      "model": "claude-sonnet",
      "mode": "auto",
      "created_at": "2026-06-04T18:22:11Z",
      "updated_at": "2026-06-04T18:24:03Z"
    }
  ],
  "has_more": true
}

has_more is true when the page filled to limit, meaning there may be older conversations — request the next page with before set to the last row's updated_at.

GET /conversations/:id

Read a single conversation with its full message history.

Headers

HeaderRequiredDescription
AuthorizationyesBearer sk_strata_live_...

Example request

curl <API_BASE_URL>/conversations/9b1e4c8d-... \
  -H "Authorization: Bearer sk_strata_live_..."

Example response

{
  "id": "9b1e4c8d-...",
  "title": "Q1 revenue by region",
  "model": "claude-sonnet",
  "mode": "auto",
  "created_at": "2026-06-04T18:22:11Z",
  "updated_at": "2026-06-04T18:24:14Z",
  "messages": [
    {
      "id": 101,
      "role": "user",
      "content": "Summarize Q1 revenue by region",
      "created_at": "2026-06-04T18:22:11Z"
    },
    {
      "id": 102,
      "role": "assistant",
      "content": "Q1 revenue totaled $14.2M...",
      "created_at": "2026-06-04T18:22:14Z"
    }
  ]
}

Errors

StatusSlugWhen
404not_foundConversation does not exist or belongs to a different actor

DELETE /conversations/:id

Soft-delete a conversation. The record is hidden from list responses but retained in the platform database — Strata keeps chat history for employee-activity tracking and the retention-purge sweep, so this never hard-deletes.

Headers

HeaderRequiredDescription
AuthorizationyesBearer sk_strata_live_...

Example request

curl -X DELETE <API_BASE_URL>/conversations/9b1e4c8d-... \
  -H "Authorization: Bearer sk_strata_live_..."

Example response

204 No Content with an empty body.

Errors

StatusSlugWhen
404not_foundConversation does not exist or belongs to a different actor

Reliability

  • Idempotency. Send an Idempotency-Key header on a non-streaming POST /chat so a network-retry replays the original result instead of creating a duplicate conversation or double-billing. The key is scoped to your API key and held for 24 hours. A retry while the first request is still running returns 409 request_in_progress; once it finishes, the same key returns the cached result. Streaming requests are not idempotent — reconnect instead.
  • Rate limits and budgets. Each key has a per-hour rate window and an optional monthly token budget, and all spend counts toward your org's usage pool. Watch the X-RateLimit-* and X-Strata-Budget-* response headers. See Rate limits and budgets.
  • Streaming reconnects. On a dropped SSE connection, send your next prompt with the same conversation_id rather than re-POSTing the same request.

Related