Starting the Server
polpo serve --port 3000 --api-key my-secret
The server provides a REST API, an OpenAI-compatible chat endpoint, Server-Sent Events (SSE), and WebSocket streaming.
Base URL
http://localhost:3000/api/v1/projects/:projectId
All endpoints are scoped to a project. The project ID defaults to the working directory name.
Authentication
When --api-key is set, all requests must include the key:
REST Endpoints
Projects
| Method | Path | Description |
|---|
GET | /api/v1/projects | List all projects |
GET | /api/v1/projects/:id/state | Full state snapshot |
GET | /api/v1/projects/:id/config | Orchestrator config |
Tasks
| Method | Path | Description |
|---|
GET | /tasks | List all tasks (filterable) |
GET | /tasks/:id | Get single task |
POST | /tasks | Create task |
PATCH | /tasks/:id | Update task |
DELETE | /tasks/:id | Remove task |
POST | /tasks/:id/retry | Retry failed task |
POST | /tasks/:id/kill | Kill running task |
POST | /tasks/:id/reassess | Re-run assessment |
Filters (query params on GET /tasks):
?status=failed — filter by status
?group=feature-auth — filter by mission group
?assignTo=backend-dev — filter by agent
Missions
| Method | Path | Description |
|---|
GET | /missions | List all missions |
GET | /missions/resumable | List resumable missions |
GET | /missions/:id | Get mission details |
POST | /missions | Save mission (JSON) |
PATCH | /missions/:id | Update mission |
DELETE | /missions/:id | Delete mission |
POST | /missions/:id/execute | Execute mission |
POST | /missions/:id/resume | Resume mission |
POST | /missions/:id/abort | Abort mission |
Agents
| Method | Path | Description |
|---|
GET | /agents | List agents |
POST | /agents | Add agent |
DELETE | /agents/:name | Remove agent |
GET | /agents/team | Get team info |
PATCH | /agents/team | Rename team |
GET | /agents/processes | Active processes |
Memory
| Method | Path | Description |
|---|
GET | /memory | Get project memory |
PUT | /memory | Update project memory |
Files
| Method | Path | Description |
|---|
GET | /files/list | List directory contents (sandboxed to .polpo/ and project root) |
GET | /files/read | Stream file content with correct Content-Type |
GET | /files/preview | Get structured preview data (type, content for text, URL for binary) |
Query params on GET /files/list:
?path=.polpo/output — directory to list (defaults to project root)
Query params on GET /files/read:
?path=/abs/path/to/file — file path (required)
?download=1 — force download (Content-Disposition: attachment)
Query params on GET /files/preview:
?path=/abs/path/to/file — file path (required)
?maxLines=500 — max lines for text content (default: 500)
All paths are sandboxed — .. traversal is rejected, and only files under .polpo/ or the project working directory are accessible.
Chat (OpenAI-compatible)
The primary way to talk to Polpo programmatically:
| Method | Path | Description |
|---|
POST | /v1/chat/completions | Talk to Polpo (OpenAI-compatible, streaming + non-streaming) |
This endpoint accepts standard OpenAI-format messages and returns responses in OpenAI format. Polpo runs its full tool loop internally (38 tools) — you describe what you need, Polpo handles the rest. Supports "stream": true for SSE streaming.
curl http://localhost:3000/v1/chat/completions \
-H "Authorization: Bearer my-secret" \
-H "Content-Type: application/json" \
-d '{
"messages": [{"role": "user", "content": "Create a mission to build a REST API for user management"}],
"stream": false
}'
Sessions
| Method | Path | Description |
|---|
GET | /chat/sessions | List all sessions |
GET | /chat/sessions/:id/messages | Get messages for a session |
PATCH | /chat/sessions/:id | Rename a session ({ title }) |
DELETE | /chat/sessions/:id | Delete a session |
Logs
| Method | Path | Description |
|---|
GET | /logs | List log sessions |
GET | /logs/:sessionId | Get session log entries |
Health
| Method | Path | Description |
|---|
GET | /api/v1/health | Server health check |
Server-Sent Events (SSE)
Subscribe to real-time events:
const events = new EventSource(
'http://localhost:3000/api/v1/projects/my-project/events'
);
events.onmessage = (e) => {
const { event, data } = JSON.parse(e.data);
console.log(event, data);
};
Reconnection
SSE supports Last-Event-ID for reconnection. The server maintains a circular buffer of the last 1000 events:
// Resume from event 42
const events = new EventSource(
'http://localhost:3000/api/v1/projects/my-project/events?lastEventId=42'
);
WebSocket
WebSocket provides the same event stream with optional filtering:
const ws = new WebSocket(
'ws://localhost:3000/api/v1/projects/my-project/ws'
);
// Filter events with glob patterns
ws.send(JSON.stringify({ type: 'subscribe', pattern: 'task:*' }));
ws.send(JSON.stringify({ type: 'subscribe', pattern: 'assessment:*' }));
ws.onmessage = (e) => {
const { event, data } = JSON.parse(e.data);
console.log(event, data);
};
Filter Patterns
| Pattern | Matches |
|---|
task:* | All task events |
agent:* | All agent events |
assessment:* | Assessment events |
mission:* | Mission events |
* | All events |
All REST responses follow a consistent format:
// Success
{
"ok": true,
"data": { /* ... */ }
}
// Error
{
"ok": false,
"error": "Task not found",
"code": "NOT_FOUND"
}
The server uses Hono (14KB, zero dependencies) with @hono/node-server. It’s a pure translation layer over the Orchestrator public API — no core logic is duplicated.