Skip to content

API Reference

All API endpoints require authentication unless noted otherwise. Unauthenticated requests receive a 401 Unauthorized response. Authenticate by including a valid session cookie (set during login via the web UI).

Some endpoints are admin only — non-admin users receive a 403 Forbidden response.

Agent chat is handled over WebSocket at /api/ws, not via REST. The browser sends JSON messages and receives streaming responses. For details on the WebSocket protocol, message types, and session management, see the Architecture page.

List agents visible to the current user. Admins see all agents. Non-admin users see shared agents and their own personal agents.

Response:

[
{
"id": "uuid",
"name": "HR Policy Assistant",
"model": "anthropic/claude-haiku-4-5-20251001",
"templateId": "knowledge-base",
"allowedTools": ["pinchy_ls", "pinchy_read"],
"pluginConfig": {
"allowed_paths": ["/data/hr-policies"]
},
"isPersonal": false,
"ownerId": "user-uuid",
"createdAt": "2025-01-15T10:00:00.000Z",
"updatedAt": "2025-01-15T10:00:00.000Z"
}
]

Create a new agent. The agent inherits default tool permissions from its template. Use PATCH /api/agents/:id to configure permissions after creation.

Request body:

FieldTypeRequiredDescription
namestringYesDisplay name for the agent
templateIdstringYesTemplate to use (knowledge-base or custom)

Example — Knowledge Base agent:

{
"name": "HR Policy Assistant",
"templateId": "knowledge-base"
}

Example — Custom agent:

{
"name": "General Assistant",
"templateId": "custom"
}

Response: 201 Created with the created agent object.

Errors:

  • 400 — missing or invalid name or templateId
  • 401 — not authenticated

Get a single agent by ID.

Response: The agent object, or 404 if not found.

Update an agent’s settings. Any combination of fields can be included.

Request body:

FieldTypeRequiredDescription
namestringNoNew display name
modelstringNoNew model identifier
allowedToolsstring[]NoList of tool IDs the agent can use (admin only)
pluginConfigobjectNoPlugin configuration, e.g. { "allowed_paths": ["/data/hr"] } (admin only)

Example — update permissions:

{
"allowedTools": ["pinchy_ls", "pinchy_read"],
"pluginConfig": {
"allowed_paths": ["/data/hr-policies"]
}
}

Response: The updated agent object.

Errors:

  • 400 — cannot change permissions for personal agents
  • 401 — not authenticated
  • 403 — only admins can change allowedTools or pluginConfig
  • 404 — agent not found

Delete an agent. Admin only. Personal agents (auto-created Smithers) cannot be deleted.

Response: 200 OK

{
"success": true
}

Errors:

  • 400 — attempting to delete a personal agent
  • 401 — not authenticated
  • 403 — not an admin
  • 404 — agent not found

List available agent templates.

Response:

{
"templates": [
{
"id": "knowledge-base",
"name": "Knowledge Base",
"description": "Answer questions from your docs"
},
{
"id": "custom",
"name": "Custom Agent",
"description": "Start from scratch"
}
]
}

List directories available under /data/ for agent configuration.

Response:

{
"directories": [
{ "path": "/data/hr-policies", "name": "hr-policies" },
{ "path": "/data/engineering-docs", "name": "engineering-docs" }
]
}

Returns an empty array if /data/ does not exist or contains no subdirectories. Hidden directories (starting with .) are excluded.

Get all application settings. Encrypted values (such as API keys) are masked in the response.

Response:

[
{ "key": "default_provider", "value": "anthropic", "encrypted": false },
{ "key": "anthropic_api_key", "value": "--------", "encrypted": true }
]

Update a setting.

Request body:

FieldTypeRequiredDescription
keystringYesSetting key
valuestringYesSetting value

Settings with api_key in the key name are automatically encrypted at rest.

Response: { "success": true }

Get the organization context. Admin only.

Response:

{
"content": "Acme Corp is a SaaS company focused on..."
}

Returns { "content": "" } if no org context has been set.

Update the organization context. This is synced to all shared agent workspaces and triggers a runtime restart.

Request body:

FieldTypeRequiredDescription
contentstringYesOrganization context (Markdown)

Response: { "success": true }

Errors:

  • 400 — content is not a string
  • 401 — not authenticated
  • 403 — not an admin

List all users.

Response:

[
{
"id": "uuid",
"name": "Alice",
"email": "alice@example.com",
"role": "admin",
"createdAt": "2025-01-15T10:00:00.000Z"
}
]

Delete a user and cascade-delete their personal agents.

Response: 200 OK

{
"success": true
}

Errors:

  • 400 — attempting to delete yourself
  • 401 — not authenticated
  • 403 — not an admin
  • 404 — user not found

Generate a password reset token for a user. The admin constructs the reset URL from the returned token: {origin}/invite/{token}.

Response: 201 Created

{
"token": "abc123..."
}

Errors:

  • 401 — not authenticated
  • 403 — not an admin
  • 404 — user not found

Create an invite for a new user. Returns the full invite object including a plaintext token (shown only once).

Request body:

FieldTypeRequiredDescription
rolestringYesRole for the invited user (admin or user)
emailstringNoEmail address of the invited user (optional, for reference)

Response: 201 Created

{
"id": "uuid",
"token": "abc123...",
"email": "bob@example.com",
"role": "user",
"type": "invite",
"expiresAt": "2025-01-22T10:00:00.000Z",
"createdAt": "2025-01-15T10:00:00.000Z"
}

The admin constructs the invite URL from the returned token: {origin}/invite/{token}.

Errors:

  • 400 — missing or invalid role
  • 401 — not authenticated
  • 403 — not an admin

List all invites and their status.

Response:

[
{
"id": "uuid",
"email": "bob@example.com",
"status": "pending",
"expiresAt": "2025-01-22T10:00:00.000Z",
"createdAt": "2025-01-15T10:00:00.000Z"
}
]

Revoke a pending invite.

Response: 200 OK

{
"success": true
}

Errors:

  • 401 — not authenticated
  • 403 — not an admin
  • 404 — invite not found

Claim an invite token to create a new account or reset a password. This endpoint does not require authentication.

Request body:

FieldTypeRequiredDescription
tokenstringYesThe invite/reset token from the URL
namestringYesDisplay name for the new user
passwordstringYesPassword (min 8 characters)

Response: 201 Created

{
"success": true
}

Errors:

  • 400 — missing fields, invalid token, or token expired

Retrieve a paginated, filterable audit log. Admin only.

Query parameters:

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber50Entries per page
eventTypestringFilter by event type (e.g., auth.login, tool.execute)
actorIdstringFilter by user ID
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)

Response:

{
"entries": [
{
"id": "uuid",
"eventType": "auth.login",
"actorId": "user-uuid",
"actorEmail": "alice@example.com",
"metadata": {},
"hmac": "sha256-hex-string",
"createdAt": "2026-02-21T10:00:00.000Z"
}
],
"total": 142,
"page": 1,
"limit": 50
}

Errors:

  • 401 — not authenticated
  • 403 — not an admin

Verify the integrity of audit log entries by recomputing HMAC signatures. Admin only.

Query parameters:

ParameterTypeDefaultDescription
fromIdstringStart verification from this entry ID (optional)
toIdstringEnd verification at this entry ID (optional)

Response:

{
"verified": true,
"entriesChecked": 142,
"tamperedEntries": []
}

If tampered entries are found, verified is false and tamperedEntries contains the IDs of entries with mismatched signatures.

Errors:

  • 401 — not authenticated
  • 403 — not an admin

Export the audit log as a CSV file. Supports the same filters as GET /api/audit. Admin only.

Query parameters:

ParameterTypeDefaultDescription
eventTypestringFilter by event type
actorIdstringFilter by user ID
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)

Response: 200 OK with Content-Type: text/csv and Content-Disposition: attachment; filename="audit-log.csv".

Errors:

  • 401 — not authenticated
  • 403 — not an admin

Update your own display name.

Request body:

FieldTypeRequiredDescription
namestringYesNew display name

Response:

{
"success": true
}

Errors:

  • 400 — missing or empty name
  • 401 — not authenticated

Get your personal context.

Response:

{
"content": "I'm Alice, a product manager at Acme Corp..."
}

Returns { "content": "" } if no context has been set.

Update your personal context. This is synced to your Smithers agent’s workspace and triggers a runtime restart.

Request body:

FieldTypeRequiredDescription
contentstringYesYour personal context (Markdown)

Response: { "success": true }

Errors:

  • 400 — content is not a string
  • 401 — not authenticated

Change your own password.

Request body:

FieldTypeRequiredDescription
currentPasswordstringYesCurrent password
newPasswordstringYesNew password (min 8 characters)

Response: { "success": true }

Errors:

  • 400 — missing fields or new password too short
  • 401 — not authenticated or current password incorrect