Logo
Search
Docs

Chat completions

POST https://app.sulus.ai/api/v1/chat/completions

Send a chat-completions request to an agent

Authentication

Send a bearer token created on your dashboard's Developer page:

Authorization: Bearer <YOUR_API_KEY>

Request body

model string Optional Nullable

Model id from GET /models. Omit for the default.

Validation: sometimes|nullable|string|max:255
Stored in ai_chats.model
messages array Required

Conversation turns. Must contain at least one user-role message.

Validation: required|array|min:1
messages.*.role string Required

Turn role.

Validation: required|string|in:system,user,assistant
Stored in ai_chat_messages.role
messages.*.content string Required

Turn text, max 20,000 characters.

Validation: required|string|max:20000
Stored in ai_chat_messages.content
agent string Optional Nullable

Sulus extension: which agent answers. Defaults to your most recently active agent.

Validation: sometimes|nullable|string|max:64
Stored in ai_projects.uid
chat string Optional Nullable

Sulus extension: continue this existing thread statefully.

Validation: sometimes|nullable|string|max:64
Stored in ai_chats.uid
stream boolean Optional Default: false

Must be false or omitted — streaming is not supported.

Validation: sometimes|boolean

Errors

StatusMeaningBody
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.
  • Passing both agent and chat continues that thread under that agent; passing agent alone (no chat) starts a fresh stateless thread.
  • model ids are account-specific; "sulus-chat-1" here is a placeholder — call GET /models for the real ids available to your account.
curl --request POST \
  --url https://app.sulus.ai/api/v1/chat/completions \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "sulus-chat-1",
  "messages": [
    {"role": "system", "content": "You are speaking with a returning customer."},
    {"role": "user", "content": "What are your opening hours?"}
  ],
  "agent": "a1b2c3d4-0000-4000-8000-000000000001",
  "chat": "b2c3d4e5-0000-4000-8000-000000000002",
  "stream": false
}'
import requests

url = "https://app.sulus.ai/api/v1/chat/completions"
headers = {"Authorization": "Bearer <YOUR_API_KEY>"}
payload = {
  "model": "sulus-chat-1",
  "messages": [
    {"role": "system", "content": "You are speaking with a returning customer."},
    {"role": "user", "content": "What are your opening hours?"}
  ],
  "agent": "a1b2c3d4-0000-4000-8000-000000000001",
  "chat": "b2c3d4e5-0000-4000-8000-000000000002",
  "stream": false
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	url := "https://app.sulus.ai/api/v1/chat/completions"
	payload := strings.NewReader(`{
  "model": "sulus-chat-1",
  "messages": [
    {"role": "system", "content": "You are speaking with a returning customer."},
    {"role": "user", "content": "What are your opening hours?"}
  ],
  "agent": "a1b2c3d4-0000-4000-8000-000000000001",
  "chat": "b2c3d4e5-0000-4000-8000-000000000002",
  "stream": false
}`)
	req, _ := http.NewRequest("POST", url, payload)
	req.Header.Add("Authorization", "Bearer <YOUR_API_KEY>")
	req.Header.Add("Content-Type", "application/json")
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
Response 200
{
    "id": "chatcmpl-7f3a9c2e-1111-4222-8333-000000000010",
    "object": "chat.completion",
    "created": 1769800000,
    "model": "sulus-chat-1",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "We're open Monday to Friday, 9am to 6pm Eastern, and Saturday 10am to 2pm."
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {
        "prompt_tokens": 210,
        "completion_tokens": 88,
        "total_tokens": 298
    },
    "sulus": {
        "agent": "a1b2c3d4-0000-4000-8000-000000000001",
        "chat": "b2c3d4e5-0000-4000-8000-000000000002",
        "tools_used": []
    }
}