HTTP API

The endpoints the apps use: sign-in, models, chat, conversations and the agent protocol.

The Peridot apps (web, desktop, CLI and mobile) all talk to one HTTP API. This page describes the endpoints they use. It is the contract of the first-party clients: it can change, so for anything you build on it, pin the behaviour you need with tests.

Basics#

Base URL https://api.tryperidot.io/api
Format JSON requests and responses (Content-Type: application/json).
Authentication Authorization: Bearer <token>. Get a token from /auth/login, /auth/register or /auth/google.
Streaming Server-sent events (text/event-stream).
Errors A non-2xx status and { "error": "message" }. Some errors also carry a code.

Browsers can only call the API from origins it allows (CORS). Native clients and servers are not affected.

Authentication#

POST /auth/login#

json
{ "email": "[email protected]", "password": "••••••••" }

Returns { "token": "...", "user": { "id": 1, "email": "[email protected]", "name": "You", "role": "user", "plan": "free" } }.

POST /auth/register#

Body { "email", "password", "name" }. The password needs at least 8 characters. Returns the same shape as login.

POST /auth/google#

Body { "accessToken": "..." } or { "idToken": "..." }, a Google token issued for the Peridot application. Returns { token, user }.

GET /auth/me#

Returns the signed-in user, usage stats, and limits: your plan, daily messages used and allowed, the percentage of the monthly allowance used, and when it resets.

Models#

GET /models#

json
{
  "models": [
    { "id": "claude-sonnet-5-5", "name": "Claude Sonnet 5.5", "badge": null, "desc": "…", "locked": false }
  ]
}

locked is true for models your plan does not include. See Models and effort.

Chat#

Messages use the roles user and model (a system role is accepted for instructions).

POST /chat#

json
{
  "messages": [{ "role": "user", "content": "Hello" }],
  "model": "claude-sonnet-5-5",
  "effort": "Medium",
  "systemInstruction": "Answer briefly."
}

Returns the whole answer: { "text", "model", "usage": { "inputTokens", "outputTokens", "totalTokens" } }.

POST /chat/stream#

Same request as /chat, answered as a stream of events:

event: message
data: {"chunk":"Hel"}

event: message
data: {"chunk":"lo!"}

event: done
data: {"done":true}

An error event (data: {"error":"…"}) ends the stream on failure.

POST /chat/transcribe#

Speech to text: { "audio": "<base64>", "mimeType": "audio/webm" }. Returns { "text": "…" }.

Conversations#

Require a token. They store the history you see in the sidebar.

Endpoint Description
GET /conversations { "conversations": [{ id, title, updated_at }] }, newest first.
POST /conversations Body { "title" }. Returns 201 and { "conversation": { id, title } }.
GET /conversations/:id { "conversation", "messages": [{ role, content }] }.
POST /conversations/:id/messages Body { "messages": [{ "role": "user" | "model", "content" }] }. Appends messages.
PATCH /conversations/:id Body { "title" }. Renames it.
DELETE /conversations/:id Deletes one conversation.
DELETE /conversations Deletes all of yours.

The agent protocol#

POST /chat/agent is one model turn of the coding agent. The server does not run tools: the client does, and calls again with the results. This is the loop every Peridot client implements.

Request#

json
{
  "messages": [
    { "role": "user", "content": "How does login work?" }
  ],
  "model": "claude-sonnet-5-5",
  "effort": "Medium",
  "workspace": "my-project",
  "platform": "linux",
  "tools": []
}
Field Description
messages The conversation so far, in content block format: content is a string or an array of blocks (text, tool_use, tool_result). Send back the assistant's blocks exactly as received.
model, effort As in chat.
workspace The project's folder name, shown to the model.
platform The operating system, e.g. linux, win32, darwin.
tools Extra tools (from plugins), each { name, description, input_schema }. Names look like plugin__tool.
team, actor, caller, depth AI team fields, below.

Requires a signed-in user. The agent has the six built-in tools described in Tools and approvals.

Response#

A stream:

event: message
data: {"chunk":"Let me look at the code. "}

event: done
data: {"done":true,"stopReason":"tool_use","content":[
  {"type":"text","text":"Let me look at the code. "},
  {"type":"tool_use","id":"toolu_01","name":"search","input":{"pattern":"login"}}
]}

message events carry text as it is generated. The final done event carries the complete content blocks and the stopReason:

An error event ends the stream with { "error": "…" }.

Team fields#

To let the agent hand work to bots, send the project's team with every call:

Field Description
team { "lead": { "delegates": [ids] }, "bots": [{ id, name, role, prompt, tools, delegates }] }, as in the team file. The server validates it and drops what is invalid.
actor The id of the bot this call is for. Omit it (or send null) for the lead.
caller Who gave the bot its task: "lead" or a bot id.
depth 0 for the lead, plus one for every hand-off. Values above 2 are capped.

With a team and no actor, the lead's tool list gets one extra tool:

json
{
  "name": "delegate",
  "input_schema": {
    "type": "object",
    "properties": {
      "bot": { "type": "string", "enum": ["researcher", "reviewer"] },
      "task": { "type": "string" }
    },
    "required": ["bot", "task"]
  }
}

and its system prompt lists the bots it may use. With an actor, the server builds that bot's system prompt from its name, role and instructions, and offers only its allowed tools (plus delegate if it has bots to hand work to and depth is below 2). An unknown actor is rejected with 400.

The client runs the hand-off: when the lead's done event contains tool_use blocks named delegate, it starts a new conversation for that bot, with only the task as its first message, sets actor, caller and depth + 1, runs the same loop, and returns the bot's final text as the tool_result of the delegate call. See How hand-offs work.

Plugins#

Endpoint Description
GET /plugins { "plugins": [...] }, the approved plugins.
GET /plugins/:name One approved plugin.

Errors and limits#

Status code Meaning
400 The request is invalid, for example an empty messages.
401 auth Sign in first (agent endpoint).
403 model_not_allowed The model is not included in your plan.
413 input_too_large The conversation is too long for your plan.
413 too_long The agent task grew past your plan's request size. Start a new one.
429 daily_limit Today's message limit is reached. Resets at midnight UTC.
429 monthly_limit The monthly allowance is used up. Resets on the 1st.
429 Too many requests (rate limit). Wait and retry.
500, 502 Something went wrong on our side or with the model provider.

See Accounts and plans for the limits per plan.

← Plugins Troubleshooting →