--- title: 'Authentication and sessions' description: 'The credential the chat wires expect, and the session key that turns a stateless wire into a durable thread.' position: 3 --- # 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 ` (the convention the OpenAI SDK sends) or `x-api-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//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](/docs/cli/protocol/tools#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.