# Stateful Agent API

> Sessions, messages, and runs — persistent agentic conversations over REST


The stateful agent API is the first-class way to run **persistent agentic
conversations**: create a session, post messages, and each message executes a
real agent run — with sources, files, and full conversation continuity. It
replaces the deprecated session hints on `chat/completions` (see
[Deprecations](#chat-completions-and-the-stateless-split)).

All endpoints live under `/v1/agent/*`, authenticate with your `sk-rav-` key,
and are scoped (`sessions:*`, `messages:*`, `runs:*` — granted by default on
all keys).

## Model

| Resource | What it is |
| --- | --- |
| **Session** | The durable agentic container. Conversation state persists across runs. |
| **Message** | Your input (and the agent's replies) in the session history. |
| **Run** | One agentic execution. Posting a message queues exactly one run over it. |

Every run reports `credits` and `usage` **from the billing ledger receipt** —
the number you see is the number that was charged, never an estimate.

## Lifecycle

```bash
# 1. Create a session
curl -X POST https://api.rav.chat/v1/agent/sessions \
  -H "Authorization: Bearer $RAVCHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "ravchat"}'
# → {"id": "SESSION_ID", "object": "agent.session", ...}

# 2. Post a message — this queues and starts a run
curl -X POST https://api.rav.chat/v1/agent/sessions/$SESSION_ID/messages \
  -H "Authorization: Bearer $RAVCHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"role": "user", "content": "What does the candle-flame mashal teach about bittul?"}'
# → {"message": {...}, "run": {"id": "RUN_ID", "status": "queued", ...}}

# 3. Poll the run to a terminal state
curl https://api.rav.chat/v1/agent/runs/$RUN_ID \
  -H "Authorization: Bearer $RAVCHAT_API_KEY"
# → status: queued | in_progress | completed | failed | cancelled
#   on completed: output, credits (ledger-true), usage

# 4. Or stream lifecycle events (SSE)
curl -N https://api.rav.chat/v1/agent/runs/$RUN_ID/events \
  -H "Authorization: Bearer $RAVCHAT_API_KEY"
# events: run.started → message.completed → run.completed|run.failed|run.cancelled
# A stream opened on a finished run replays its terminal events and closes.

# 5. Continue the conversation — same session, full continuity
curl -X POST https://api.rav.chat/v1/agent/sessions/$SESSION_ID/messages \
  -H "Authorization: Bearer $RAVCHAT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"role": "user", "content": "Which sources did you base that on?"}'
```

## Endpoints

| Method & path | Purpose |
| --- | --- |
| `POST /v1/agent/sessions` | Create a session (`project_id` optional — your key's default project is used and created on demand). |
| `GET /v1/agent/sessions` | List your agent sessions. |
| `GET /v1/agent/sessions/{id}` | Fetch one session. |
| `POST /v1/agent/sessions/{id}/messages` | Post a user message → returns `{message, run}`. |
| `GET /v1/agent/sessions/{id}/messages` | Full history, oldest first. |
| `POST /v1/agent/sessions/{id}/runs` | Advanced: explicit input array → the messages are created and linked. |
| `GET /v1/agent/sessions/{id}/runs` | Runs for a session, newest first. |
| `GET /v1/agent/runs/{id}` | Run status, output, `credits`, `usage`, error. |
| `GET /v1/agent/runs/{id}/events` | SSE lifecycle stream (terminal replay supported). |
| `POST /v1/agent/runs/{id}/cancel` | Stop waiting on a run. |

## Semantics worth knowing

- **One active run per session.** Posting while a run is `queued` or
  `in_progress` returns `409` (enforced at the database level). Wait for the
  terminal state or cancel first.
- **Cancel means stop waiting.** A turn already executing server-side
  completes and its charge stands; the run's status becomes `cancelled` and
  is never overwritten.
- **Runs are billed as chat turns** (standard multiplier). The run object's
  `credits` comes from the charge receipt.
- **Restarts are safe, not silent.** A server restart fails stranded runs
  with `interrupted by server restart — resubmit the message`; you will
  never see a run stuck in `queued` forever.
- **Powered accounts** (own Claude token) get `403 powered_token_required`
  on execution until the account's Claude connection is set up in the app;
  their runs record zero-credit receipts.

## chat/completions and the stateless split

`POST /v1/chat/completions` is now the **stateless fast surface** — fast mode
is the default and needs no selector. The legacy agentic behavior over
chat/completions (`mode: "standard"` or `metadata.ravchat.session_id` hints)
is **deprecated**: responses carry `Deprecation: true` and
`Sunset: Fri, 14 Aug 2026` headers until the sunset date, after which those
requests return `invalid_request` pointing here. Migrate stateful workloads
to `/v1/agent/*` — the model above is strictly more capable.
