Authentication and sessions
The three chat wires take the same credential. Durability is a separate choice: the OpenAI and Anthropic wires are stateless until you opt in, and AG-UI is durable from the first turn.
Authentication
All three chat wires expect a bearer credential: Authorization: Bearer <key>
(the convention the OpenAI SDK sends) or x-api-key: <key> (the Anthropic
SDK's header) both work on any of them. A missing or empty credential
returns 401 with the matching wire's error shape ({"error": {...}}
for OpenAI, {"type": "error", "error": {...}} for Anthropic,
{"type": "RUN_ERROR", "message": "..."} for AG-UI).
The workflow routes are not on this bearer. On a deployed container they sit
behind the container's own gate, which the control plane and Stackbone Studio
satisfy for you. Under stackbone dev that gate is off, so
POST /api/workflows/<name>/start answers an un-credentialed request.
Session keys
A plain chat call is stateless: you replay the full messages[] array each
turn, the same as calling OpenAI or Anthropic directly. The server keeps no
transcript and records each turn on its own, so a stateless conversation is
never grouped into a single session.
Two optional headers change that, and each does a different job:
| Header | What it does | Where history lives |
|---|---|---|
x-stackbone-session |
Durable server-side session. The agent threads the conversation's history and tool state across calls, so you send only the newest user turn and the server carries the rest. Required for tool approvals. | On the server |
x-stackbone-conversation |
Grouping only. You still replay the full messages[], but the server joins the turns into one session so they show up together instead of as separate one-off runs. No durable state, no tool approvals. |
On the client (you replay it) |
Both take any non-empty string you choose, stable for the life of one
conversation. Send one, not both. A request carrying both counts as a durable
session, and the server ignores x-stackbone-conversation. With neither
header, each turn is an independent run and the server groups nothing.
AG-UI uses neither header. Its threadId field on the request body carries
the same durable, only-send-the-newest-turn semantics as
x-stackbone-session, and it is mandatory rather than opt-in. A non-empty
threadId is always a durable session. Leave it empty and you get one
stateless turn with no pause capability.