Sai API & CLI Documentation | Sai by Simular AI
Sai HTTP API
Send tasks to your Sai agent programmatically over HTTPS. The same REST surface that powers the sai CLI, available for CI pipelines, devices, and custom integrations.
Overview
The Sai HTTP API exposes your agent over HTTPS. It is the same REST surface the sai CLI is built on — use it directly to integrate Sai into CI pipelines, devices, or your own applications.
All endpoints share a single base URL:
https://api.sai.simular.ai
Agent endpoints live under /v1/agents, account (API key) endpoints under /v1/account, and the sai CLI’s own channel under /v1/cli. All requests and responses are JSON unless noted otherwise.
Authentication
Every request requires a bearer token in the Authorization header. Two credential types are accepted:
- API key — a long-lived secret prefixed
sapi_. Best for servers, CI, and devices. Generate one withsai key generateor from Settings → API Keys in the desktop app. - Firebase ID token — a short-lived token from an interactive Google sign-in. Required for managing API keys.
curl https://api.sai.simular.ai/v1/agents/auth \
-H "Authorization: Bearer sapi_your_api_key_here"
{ "ok": true, "userId": "abc123", "authType": "apiKey" }
Conventions
- Base path — agent routes are under
/v1/agents; account routes under/v1/account; the CLI channel under/v1/cli. - Content type — send and receive
application/json, exceptPOST /v1/agents/upload(raw binary) and the two/messageroutes (an SSE response stream). - Ownership — every
machineIdis verified against the authenticated user. Passing another user’s machine returns403or404. - Scoping — sessions are scoped to the authenticated account, not just to the machine. A machine can be registered to more than one account, so
GET /v1/agents/sessionsfilters on both and lists only your own conversations. Nowhere do you name a session: no endpoint takes a session id, and the one every session-aware endpoint acts on is resolved server-side from a record no client can read or write. Another account’s conversation on the same machine isn’t addressable rather than being refused. - Errors — non-2xx responses return
{ "error": "…" }. See error responses.
Sessions
A session is a conversation — a transcript plus the address replies are sent back to. The API gets one dedicated session per user, per machine, created lazily on your first POST /v1/agents/message and reused by every call after it. You never send a session id: the server resolves it from the route. Which session a given route resolves is not uniform yet, which is the subject of the last part of this section.
One session per channel, not per client
Sessions are keyed by the channel a message arrived on, not by who sent it. Each channel gets its own conversation on a machine, and the channel is decided server-side from the route you posted to. The channels you can reach today:
| Channel | Who lands in it |
|---|---|
| API | Your curl scripts, cron jobs, CI runners, voice clients — anything posting to /v1/agents/message |
| CLI | The sai command, via /v1/cli/message |
| Desktop app | Whatever the person at the keyboard is doing |
| Telegram | That channel’s own conversation |
| iMessage | That channel’s own conversation |
⚠
All of your API clients on one machine share one conversation. There is no way to ask for a separate one: the surface is assigned server-side from the route, and no endpoint lets you choose or address a different session. An API key does not split it — the key is recorded on each message, so you can attribute a message to it, but a second key lands in the same transcript. Two clients streaming at once will interleave there and can cross each other’s turn boundaries, because completion is read from shared session state. Serialise concurrent callers, or point them at different workspaces if you have more than one.
Named channels and sessions
Name your channel when required. The session-aware endpoints predate the split, so they answered about the CLI whoever asked.
Fetch recent messages
curl "https://api.sai.simular.ai/v1/agents/context?machineId=m_1a2b&channel=api&limit=10" \
-H "Authorization: Bearer $SAI_API_KEY"
{
"messages": [
{ "role": "user", "content": "list my unread emails", "timestamp": 1750000000000 },
{ "role": "assistant", "content": "You have 3 unread emails...", "timestamp": 1750000005000 }
]
}
Rate limits
| Action | Limit |
|---|---|
| Send a message | 60 / hour |
| Upload a file | 30 / hour |
| Resolve an approval | 20 / minute |
| Abort a task | 20 / hour |
| Start a new conversation | 20 / hour |
| Restart agent or machine | 10 / hour |
| Wake a machine | 10 / minute |
| Generate an API key | 5 / hour |
Limits are per user on a rolling window — one hour unless the table says otherwise — and shared across all clients. Exceeding one returns 429 with a message naming the limit.
Error responses
Errors use standard HTTP status codes with a JSON body of the form { "error": "message" }.
| Status | Meaning |
|---|---|
400 |
Bad request — missing or invalid parameters. |
401 |
Missing, invalid, or expired credential. |
403 |
User not active, machine not owned. |
404 |
Machine not found. |
429 |
Rate limit exceeded. |
503 |
Auth service temporarily unavailable — retry shortly. |