Endpoints
Every Agents API endpoint with exact parameters, validation rules, and error responses.
Chat completions
The system answers conversation requests through POST /chat/completions, which follows the industry-standard chat-completions request format. Two Sulus-specific fields extend it: agent (which agent answers; defaults to the account’s most recently active agent) and chat (continue an existing thread statefully — only the newest user message is sent).
Streaming is not supported: "stream": true returns 400. System-role messages in stateless mode are stored as user-visible preamble turns.
Models and usage
GET /models lists the model ids you may pass as model. GET /usage returns the prepaid-token balance and account counts; balance fields are null when prepaid billing is not active for your account.
Agents
Standard CRUD on agents. The list includes your own agents and agents shared with you. Deleting an agent removes its chats, files, memory, and search index — it requires ownership, not just write access.
Chats
Threads inside an agent. All chat operations are limited to the token that created the chat.
Messages
Listing returns messages oldest → newest with has_more. Sending runs the full assistant turn and returns both the stored user message and the assistant reply; the reply carries a usage token block.
Knowledge files
Upload reference documents to an agent (multipart, field name file, max 20 MB). Listing requires view access; uploading and deleting require edit access.
Connectors and tools
List the connectors available to an agent, enable or disable a connector for it, and switch individual tools of an enabled connector on or off. All three require edit access to the agent.
POST/chat/completions
Send a chat-completions request to an agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
model |
body |
string |
no |
yes |
sometimes|nullable|string|max:255 |
ai_chats.model |
Model id from GET /models. Omit for the default. |
messages |
body |
array |
yes |
no |
required|array|min:1 |
|
Conversation turns. Must contain at least one user-role message. |
messages.*.role |
body |
string |
yes |
no |
required|string|in:system,user,assistant |
ai_chat_messages.role |
Turn role. |
messages.*.content |
body |
string |
yes |
no |
required|string|max:20000 |
ai_chat_messages.content |
Turn text, max 20,000 characters. |
agent |
body |
string |
no |
yes |
sometimes|nullable|string|max:64 |
ai_projects.uid |
Sulus extension: which agent answers. Defaults to your most recently active agent. |
chat |
body |
string |
no |
yes |
sometimes|nullable|string|max:64 |
ai_chats.uid |
Sulus extension: continue this existing thread statefully. |
stream |
body |
boolean |
no |
no |
sometimes|boolean |
|
Must be false or omitted — streaming is not supported. |
Request example
{
"agent": "a1b2c3d4-0000-4000-8000-000000000001",
"messages": [{"role": "user", "content": "What are your opening hours?"}]
}
Response example 200
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1750000000,
"model": "<model-id>",
"choices": [{"index": 0, "message": {"role": "assistant", "content": "…"}, "finish_reason": "stop"}],
"usage": {"prompt_tokens": 210, "completion_tokens": 88, "total_tokens": 298},
"sulus": {"agent": "a1b2c3d4-…", "chat": "b2c3d4e5-…", "tools_used": []}
}
Errors
| Status | Meaning | Body |
400 |
invalid_request_error — streaming requested, or no user-role message |
{"error":{"message":"Streaming is not supported yet. Set \"stream\": false.","type":"invalid_request_error","code":400}} |
404 |
invalid_request_error — agent or chat not found |
|
402 |
insufficient_quota — prepaid token balance exhausted |
|
500 |
api_error — the assistant could not generate a response |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
Notes
- Errors use the chat-completions envelope {"error":{"message","type","code"}} — not the plain {"message"} envelope.
- Stateless mode (no chat field) creates a fresh thread seeded with your messages; system-role turns are stored as user-visible preamble.
GET/models
List available model ids
Response example 200
{
"object": "list",
"data": [{"id": "<model-id>", "object": "model", "owned_by": "<provider>", "label": "<Model label>", "available": true}]
}
Errors
| Status | Meaning | Body |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
GET/usage
Prepaid-token balance and account counts
Response example 200
{
"object": "usage",
"billing_active": true,
"balance_tokens": 1250000,
"balance_usd": 6.25,
"agents": 4,
"chats": 31
}
Errors
| Status | Meaning | Body |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
- balance_tokens and balance_usd are null when prepaid billing is not active for the account.
GET/agents
List agents you can access
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
per_page |
query |
integer |
no |
no |
integer, clamped 1–100 |
|
Items per page. |
q |
query |
string |
no |
yes |
string (name/description contains-filter) |
|
Search filter. |
sort |
query |
string |
no |
no |
in: updated, name |
|
Sort order. |
Response example 200
{
"object": "list",
"data": [{"id": "a1b2c3d4-…", "object": "agent", "name": "Support Agent", "description": "…", "instructions": "…", "memory": null, "created_at": "…", "last_activity_at": "…"}]
}
Errors
| Status | Meaning | Body |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
POST/agents
Create an agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
name |
body |
string |
yes |
no |
required|string|max:255 |
ai_projects.name |
Agent display name. |
description |
body |
string |
no |
yes |
sometimes|nullable|string|max:5000 |
ai_projects.description |
What the agent is for. |
instructions |
body |
string |
no |
yes |
sometimes|nullable|string|max:10000 |
ai_projects.instructions |
Persistent system prompt. |
Request example
{
"name": "Support Agent",
"description": "Answers customer support questions.",
"instructions": "Be concise and friendly."
}
Response example 201
{"object": "agent", "data": {"id": "a1b2c3d4-…", "object": "agent", "name": "Support Agent", "description": "…", "instructions": "…", "memory": null, "created_at": "…", "last_activity_at": "…"}}
Errors
| Status | Meaning | Body |
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
GET/agents/{agent_id}
Retrieve one agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
Response example 200
{"object": "agent", "data": {"id": "a1b2c3d4-…", "object": "agent", "name": "…", "description": "…", "instructions": "…", "memory": "…", "created_at": "…", "last_activity_at": "…"}}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
PATCH/agents/{agent_id}
Update an agent (PUT also accepted)
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
name |
body |
string |
no |
no |
sometimes|required|string|max:255 |
ai_projects.name |
New name. When present it must not be empty. |
description |
body |
string |
no |
yes |
sometimes|nullable|string|max:5000 |
ai_projects.description |
New description; null clears it. |
instructions |
body |
string |
no |
yes |
sometimes|nullable|string|max:10000 |
ai_projects.instructions |
New instructions; null clears them. |
Request example
{"description": "Handles pre-sales questions too."}
Response example 200
{"object": "agent", "data": { … }}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
- Requires edit access.
- memory is read-only: a memory field in the request body is ignored.
DELETE/agents/{agent_id}
Delete an agent and everything in it
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
Response example 200
{"object": "agent.deleted", "id": "a1b2c3d4-…", "deleted": true}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you are not its owner (delete requires ownership, not just write access) |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
GET/agents/{agent_id}/chats
List your chats in an agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
Response example 200
{"object": "list", "data": [{"id": "b2c3d4e5-…", "object": "chat", "title": "New chat", "model": "<model-id>", "provider": "<provider>", "last_message_at": "…", "created_at": "…"}]}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
POST/agents/{agent_id}/chats
Start a new chat thread
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
model |
body |
string |
no |
yes |
sometimes|nullable|string|max:255 |
ai_chats.model |
Model id for this thread; omit for the default. |
Response example 201
{"object": "chat", "data": {"id": "b2c3d4e5-…", "object": "chat", "title": "New chat", "model": "<model-id>", "provider": "<provider>", "last_message_at": null, "created_at": "…"}}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
402 |
Out of prepaid tokens — blocked before the thread is created |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
GET/agents/{agent_id}/chats/{chat_id}
Retrieve one chat
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
chat_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_chats.uid |
The chat id (UUID). |
Response example 200
{"object": "chat", "data": { … }}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Chat not found in this agent, or it was created by a different token |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
PATCH/agents/{agent_id}/chats/{chat_id}
Rename a chat (PUT also accepted)
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
chat_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_chats.uid |
The chat id (UUID). |
title |
body |
string |
yes |
no |
required|string|max:255 |
ai_chats.title |
New chat title. |
Request example
{"title": "Pricing questions"}
Response example 200
{"object": "chat", "data": { … }}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Chat not found in this agent, or it was created by a different token |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
DELETE/agents/{agent_id}/chats/{chat_id}
Delete a chat and its messages
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
chat_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_chats.uid |
The chat id (UUID). |
Response example 200
{"object": "chat.deleted", "id": "b2c3d4e5-…", "deleted": true}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Chat not found in this agent, or it was created by a different token |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
GET/agents/{agent_id}/chats/{chat_id}/messages
List messages (oldest first)
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
chat_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_chats.uid |
The chat id (UUID). |
per_page |
query |
integer |
no |
no |
integer, clamped 1–100 |
|
Messages per page. |
before_id |
query |
integer |
no |
yes |
integer — fetch messages with id < before_id |
ai_chat_messages.id |
Cursor for older pages: pass the id of the first message from the previous response. |
Response example 200
{"object": "list", "data": [{"id": 9001, "object": "message", "role": "user", "content": "…", "model": null, "created_at": "…"}], "has_more": false}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Chat not found in this agent, or it was created by a different token |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
- usage token counts appear only on freshly generated assistant replies, never in this history listing.
POST/agents/{agent_id}/chats/{chat_id}/messages
Send a message and get the assistant reply
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
chat_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_chats.uid |
The chat id (UUID). |
content |
body |
string |
yes |
no |
required|string|max:20000 |
ai_chat_messages.content |
Your message, max 20,000 characters. |
model |
body |
string |
no |
yes |
sometimes|nullable|string|max:255 |
ai_chat_messages.model |
Override the thread model for this turn. |
Request example
{"content": "Summarise our refund policy."}
Response example 201
{
"object": "message",
"data": {
"user_message": {"id": 9001, "object": "message", "role": "user", "content": "…", "model": null, "created_at": "…"},
"assistant_message": {"id": 9002, "object": "message", "role": "assistant", "content": "…", "model": "<model-id>", "created_at": "…", "usage": {"prompt_tokens": 210, "completion_tokens": 88, "total_tokens": 298}}
},
"model": "<model-id>",
"tools_used": [],
"wallet": null
}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Chat not found in this agent, or it was created by a different token |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
402 |
Out of prepaid tokens |
{"error":true,"insufficient_tokens":true,"payer_is_self":true,"message":"You are not subscribed or your token limit is reached. Please recharge to continue chatting."} |
422 |
Generation failed — your message was rolled back so a retry re-runs cleanly |
{"error":true,"message":"The assistant could not generate a response. Please try again.","content":"<your original message>"} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
GET/agents/{agent_id}/files
List an agent's knowledge files
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
Response example 200
{"object": "list", "data": [{"id": 51, "project_id": 7, "original_name": "faq.md", "mime": "text/markdown", "size": 6120, "created_at": "…", "extractable": true, "vector_count": 12, "indexed": true, "url": "/storage/…", "preview_kind": "text"}]}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
POST/agents/{agent_id}/files
Upload a knowledge file (multipart)
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
file |
file |
file |
yes |
no |
required|file|max:20480 |
ai_project_files.path |
The document, max 20 MB. Allowed extensions: txt, md, markdown, csv, json, log, text, pdf, doc, docx, rtf. |
Response example 201
{"object": "file", "data": {"id": 51, "project_id": 7, "original_name": "faq.md", "mime": "text/markdown", "size": 6120, "created_at": "…", "extractable": true, "extracted": true, "indexed": true}}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
422 |
Disallowed extension |
{"message":"Unsupported file type. Allowed: txt, md, markdown, csv, json, log, text, pdf, doc, docx, rtf."} |
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
- Requires edit access.
- Text is extracted and indexed only for txt, md, markdown, csv, json, log, text (capped at 200,000 characters); other allowed types are stored for reference.
- The create response uses extracted; the list response uses vector_count / url / preview_kind instead.
DELETE/agents/{agent_id}/files/{file_id}
Delete a knowledge file
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
file_id |
path |
integer |
yes |
no |
integer (route-constrained) |
ai_project_files.id |
The file id from the list response. |
Response example 200
{"object": "file.deleted", "id": 51, "deleted": true}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
File not found in this agent |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
GET/agents/{agent_id}/connectors
List connectors available to an agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
Response example 200
{"object": "list", "data": [{"uid": "c3d4e5f6-…", "name": "Docs search", "description": "…", "server_url": "https://…", "enabled": true, "reachable": true, "tools_count": 9, "tools": [{"name": "search-docs", "description": "…", "enabled": true}]}]}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes
- reachable and the live tools list appear only for connectors currently enabled on this agent.
PUT/agents/{agent_id}/connectors/{connector_id}
Enable or disable a connector for this agent
Parameters
| Name | In | Type | Required | Nullable | Rules | DB column | Description |
agent_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_projects.uid |
The agent id (UUID). |
connector_id |
path |
string |
yes |
no |
uuid (route-constrained) |
ai_mcp_connectors.uid |
The connector uid from the list response. |
enabled |
body |
boolean |
yes |
no |
required|boolean |
ai_project_mcp_connectors.is_enabled |
Desired state. |
Request example
{"enabled": true}
Response example 200
{"message": "\"Docs search\" enabled for this agent.", "data": {"uid": "c3d4e5f6-…", "enabled": true}}
Errors
| Status | Meaning | Body |
404 |
Agent not found, or you do not have access to it |
|
404 |
Connector not found, inactive, or not available to you |
|
422 |
Validation failed |
{"message":"…","errors":{"field":["…"]}} |
401 |
Missing or invalid bearer token |
|
403 |
Feature ai_chat not enabled for the account |
|
Notes