Chat API
The Chat API enables you to create chat sessions, send messages, and receive responses from your AI agents programmatically.
Base Authentication
All examples in this page assume:
x-api-key: YOUR_API_KEY
For an end-user Web Channel, Codeer can also authenticate the client token with Authorization: Bearer YOUR_CLIENT_TOKEN or the codeer_token cookie. Do not expose a workspace API key in browser code.
Check Billing Block Status
Check whether the current external user can start an AI-powered turn before enabling the message composer.
Endpoint: GET /api/v1/chats/billing-block-status
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
agent_id |
UUID | - | Optional Agent to evaluate. Include it so Human replies only and other non-AI interactions are not blocked by an AI-credit restriction. |
For API-key requests, pass the external user consistently with x-external-user-id. Client-token requests resolve the user and workspace from the token.
Example
curl -X GET "https://api.codeer.ai/api/v1/chats/billing-block-status?agent_id=550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-external-user-id: user_123"
Response
{
"data": {
"blocked": true,
"reason": "end_user_credit_cap_reached",
"message": "This workspace has reached its limit for this user.",
"contact_email": null,
"end_user_credit_cap": {
"enabled": true,
"cap": 10,
"usage": "10.00",
"reached": true,
"cycle_start": "2026-08-01T00:00:00+00:00"
}
},
"message": null,
"error_code": 0
}
reason can be billing_locked, org_budget_locked, org_budget_insufficient, or end_user_credit_cap_reached. Optional fields are returned only when they apply. Treat blocked=true as the decision signal; use message and contact_email to explain the next step to the user.
List Published Agents
Retrieve all published agents available in your workspace.
Endpoint: GET /api/v1/chats/published-agents
Request
curl -X GET "https://api.codeer.ai/api/v1/chats/published-agents" \
-H "x-api-key: YOUR_API_KEY"
Response
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Customer Support Agent",
"description": "Handles customer inquiries",
"workspace_id": "workspace-uuid-here",
"agent_type": "basic",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"message": null,
"error_code": 0
}
Response Fields
The response includes additional fields like system_prompt, unified_tools, suggested_questions, use_search, meta, llm_model, and versioning fields. See the full schema in the codebase for complete details.
Create Chat
Create a new chat session with a specific agent.
Endpoint: POST /api/v1/chats
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Chat session name (max 256 characters) |
agent_id |
UUID | Yes | The agent's ID in the current workspace |
external_user_id |
string | Conditional | Your system's user identifier. Required when whitelist mode is enforced for API-key clients. Pass it consistently even when API-key whitelist bypass is enabled. |
Agent Must Be Published
Chat creation accepts any valid workspace agent_id, but sending messages requires the agent to have a published version. If the agent has no published version, later message requests return 404 Not Found with message Agent version not found. Use the List Published Agents endpoint to fetch publish-ready agent IDs.
Example
curl -X POST "https://api.codeer.ai/api/v1/chats" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Support Session",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"external_user_id": "user_123"
}'
Response
{
"data": {
"id": 12345,
"name": "Support Session",
"meta": {
"external_user_id": "user_123",
"conversation_agent_id": "550e8400-e29b-41d4-a716-446655440000"
},
"external_user_id": "user_123",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
"message": null,
"error_code": 0
}
List Chats
Retrieve chat sessions with pagination and filtering.
Endpoint: GET /api/v1/chats
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 50 | Results per page (max 1000) |
offset |
integer | 0 | Number of results to skip |
order_by |
string | -created_at |
Sort order: created_at, -created_at, asc, desc |
agent_id |
UUID | - | Filter by agent |
external_user_id |
string | - | Filter by external user |
Example
curl -X GET "https://api.codeer.ai/api/v1/chats?limit=10&agent_id=550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: YOUR_API_KEY"
Response
{
"data": [
{
"id": 12345,
"name": "Support Session",
"meta": { ... },
"external_user_id": "user_123",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}
],
"pagination": {
"limit": 10,
"offset": 0,
"total_records": 100,
"total_pages": 10,
"current_page": 1,
"next_page": "https://api.codeer.ai/api/v1/chats?offset=10&limit=10",
"prev_page": null
},
"message": null,
"error_code": 0
}
Get Chat Metadata
Retrieve one chat's metadata without loading its messages.
Endpoint: GET /api/v1/chats/{chat_id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
Example
curl -X GET "https://api.codeer.ai/api/v1/chats/12345" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-external-user-id: user_123"
Response
The response uses the same chat object shown by List Chats. Access is checked against the workspace and, when present, the external user identity.
Update Chat
Rename an existing chat session.
Endpoint: PUT /api/v1/chats/{chat_id}
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | New chat session name (max 256 characters) |
external_user_id |
string | Conditional | Your system's user identifier. Required when you need to update a chat owned by a specific external user. For client-token authentication, this value is derived from the token instead of the request body. |
Example
curl -X PUT "https://api.codeer.ai/api/v1/chats/12345" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Renamed Support Session",
"external_user_id": "user_123"
}'
Response
{
"data": {
"id": 12345,
"name": "Renamed Support Session",
"meta": {
"external_user_id": "user_123",
"conversation_agent_id": "550e8400-e29b-41d4-a716-446655440000"
},
"external_user_id": "user_123",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:40:00Z"
},
"message": null,
"error_code": 0
}
Get Chat Messages
Retrieve the message history for a specific chat.
Endpoint: GET /api/v1/chats/{chat_id}/messages
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 50 | Results per page |
offset |
integer | 0 | Number of results to skip |
include_tool_tags |
boolean | false |
Whether to keep <tool ...>...</tool> blocks in assistant messages. When false, tool blocks are removed from assistant content. |
external_user_id |
string | - | Your system's user identifier. Optional for API key authentication. For client-token authentication, this value is derived from the token instead of this query parameter. Can also be passed via x-external-user-id header for API key requests. |
Example
curl -X GET "https://api.codeer.ai/api/v1/chats/12345/messages?external_user_id=user_123" \
-H "x-api-key: YOUR_API_KEY"
Response
{
"data": [
{
"id": 1001,
"role": "user",
"group_id": "group_abc",
"content": "Hello, I need help with my order",
"meta": {},
"attached_files": [],
"feedback_counts": {}
},
{
"id": 1002,
"role": "assistant",
"group_id": "group_abc",
"content": "Hello! I'd be happy to help you with your order. Could you please provide your order number?",
"meta": {
"reasoning_steps": [...],
"token_usage": {
"total_prompt_tokens": 150,
"total_completion_tokens": 45,
"total_tokens": 195,
"total_calls": 1
}
},
"attached_files": [],
"feedback_counts": {
"sys_helpful": 1
}
}
],
"pagination": { ... },
"message": null,
"error_code": 0
}
Assistant Tool Block Visibility
include_tool_tags=false(default) removes<tool ...>...</tool>blocks from assistant messages in this endpoint.- Set
include_tool_tags=trueif your client needs the raw assistant tool blocks.
Download a Chat Attachment
Download an attachment referenced by a chat message or output component.
Endpoint: GET /api/v1/chats/{chat_id}/attachments/{attachment_id}/download
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
attachment_id |
UUID | The attachment ID returned in message metadata, attached_files, or an output component |
The endpoint checks workspace, chat, and external-user access, then returns 302 Found to a short-lived storage URL. Prefer the chat-scoped placeholder_url or attachment URL returned by the messages API instead of constructing the path yourself.
Do not forward credentials to the storage URL
Follow the redirect in your HTTP client, but do not copy x-api-key or Authorization credentials onto the redirected storage request.
Send Message
Send a message to a chat and receive the agent's response. Supports both streaming (SSE) and non-streaming modes.
Endpoint: POST /api/v1/chats/{chat_id}/messages
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
message |
string | Yes | The user's message |
stream |
boolean | No | Enable SSE streaming (default: true) |
include_tool_tags |
boolean | No | Keep <tool ...>...</tool> blocks in non-stream data and SSE delta/completed text (default: false) |
agent_id |
UUID | Yes | The agent ID (must match chat's agent) |
attached_file_uuids |
array | No | UUIDs of uploaded files to attach |
resume_form_request_id |
string | No | Resume the last user turn after a form request is submitted. Rejected forms are terminal and cannot be resumed. |
external_user_id |
string | Conditional | Your system's user identifier. Required when whitelist mode is enforced for API-key clients. Pass it consistently even when API-key whitelist bypass is enabled. |
Possible Error Responses
404 Not Foundwith "Agent not found" - The agent_id doesn't exist in this workspace404 Not Foundwith "Agent version not found" - The agent exists but has no published version404 Not Foundwith "Chat not found" - The chat_id doesn't exist400 Bad Requestwith "Agent mismatch" - The agent_id doesn't match the agent used to create the chat403 Forbiddenwith error code14103- Workspace enforces whitelist mode for this API-key request andexternal_user_idis missing or not whitelisted
Non-Streaming Example
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/messages" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "What are your business hours?",
"stream": false,
"agent_id": "550e8400-e29b-41d4-a716-446655440000"
}'
Non-Streaming Response
{
"data": "Our business hours are Monday to Friday, 9 AM to 6 PM EST. We're closed on weekends and major holidays. Is there anything specific you'd like to know about our availability?",
"structured_message": {
"final_text": "Our business hours are Monday to Friday, 9 AM to 6 PM EST. We're closed on weekends and major holidays. Is there anything specific you'd like to know about our availability?",
"conversation_group_id": "group_abc",
"user_conversation_id": 1001,
"assistant_conversation_id": 1002
},
"message": null,
"error_code": 0
}
Non-Streaming Response Shape
dataremains the plain text response for backward compatibility.structured_messageincludes IDs you can use without making an extra list-messages request.
Streaming Example
curl -N -X POST "https://api.codeer.ai/api/v1/chats/12345/messages" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"message": "What are your business hours?",
"stream": true,
"agent_id": "550e8400-e29b-41d4-a716-446655440000"
}'
For detailed information on streaming responses, see SSE Streaming.
Tool Tag Visibility Rollout
- External API callers get
<tool>blocks hidden by default (include_tool_tagsomitted orfalse). - Internal clients that need tool blocks must explicitly pass
include_tool_tags=true. - The same flag applies to
POST /chats/{chat_id}/messages,POST /chats/{chat_id}/regenerate, andGET /chats/{chat_id}/messages.
Regenerate Message
Regenerate a conversation turn by reprocessing the original user message in a group.
Endpoint: POST /api/v1/chats/{chat_id}/regenerate
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
conversation_group_id |
string | Yes | Target conversation group ID to regenerate |
stream |
boolean | No | Enable SSE streaming (default: true) |
include_tool_tags |
boolean | No | Keep <tool ...>...</tool> blocks in non-stream data and SSE delta/completed text (default: false) |
external_user_id |
string | Conditional | Your system's user identifier. Required when whitelist mode is enforced for API-key clients. Pass it consistently even when API-key whitelist bypass is enabled. |
Non-Streaming Example
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/regenerate" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_group_id": "group_abc",
"stream": false
}'
Non-Streaming Response
{
"data": "Our business hours are Monday to Friday, 9 AM to 6 PM EST.",
"structured_message": {
"final_text": "Our business hours are Monday to Friday, 9 AM to 6 PM EST.",
"conversation_group_id": "group_abc",
"user_conversation_id": 1001,
"assistant_conversation_id": 1002
},
"message": null,
"error_code": 0
}
Streaming Example
curl -N -X POST "https://api.codeer.ai/api/v1/chats/12345/regenerate" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"conversation_group_id": "group_abc",
"stream": true
}'
For detailed information on streaming responses, see SSE Streaming.
Submit or Reject Form Request
Submit a pending form request or mark it as rejected.
Endpoint: POST /api/v1/chats/{chat_id}/form-requests/{form_request_id}/submit
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
form_request_id |
UUID | The pending form request ID |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
conversation_group_id |
string | Yes | The conversation group that opened the form request |
rejected |
boolean | No | Set true to reject the form request instead of submitting data |
submission |
object | Conditional | Required when rejected=false; must be empty when rejected=true |
external_user_id |
string | Conditional | Your system's user identifier. Required when whitelist mode is enforced for API-key clients. Pass it consistently even when API-key whitelist bypass is enabled. |
Submit Example
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/form-requests/form-uuid-here/submit" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_group_id": "group_abc",
"rejected": false,
"submission": {
"email": "user@example.com"
},
"external_user_id": "user_123"
}'
Reject Example
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/form-requests/form-uuid-here/submit" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_group_id": "group_abc",
"rejected": true,
"submission": null,
"external_user_id": "user_123"
}'
Response
{
"data": null,
"message": null,
"error_code": 0
}
Continuing After Form Submission
If you want the agent to continue from the original user turn after a form request is submitted, call POST /api/v1/chats/{chat_id}/messages and pass the same form_request_id in resume_form_request_id.
Rejecting a form is a terminal action. After rejection, send a normal new message instead of using resume_form_request_id.
Possible Error Responses
404 Not Found-form_request_iddoes not exist in this chat400 Bad Request-conversation_group_iddoes not match, or the form request is no longer pending
Upload File
Upload a file to attach to chat messages.
Endpoint: POST /api/v1/chats/upload-file
Constraints
| Constraint | Value |
|---|---|
| Maximum file size | 50 MB |
| Allowed MIME types | application/pdf, text/*, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.presentationml.presentation, image/jpeg, image/png, image/gif, image/webp, text/html, audio/mpeg, audio/mp4, audio/wav, audio/ogg, audio/webm |
| Allowed extensions | .pdf, .txt, .md, .csv, .docx, .doc, .pptx, .jpeg, .jpg, .png, .gif, .webp, .html, .mp3, .mp4, .m4a, .wav, .ogg, .webm |
Chat attachments are separate from Knowledge Base
This allowlist applies only to files attached to chat messages. It includes standalone image and audio files and does not define Knowledge Base support. See Knowledge Base supported sources and file types.
Audio uploads can be attached to messages. If you use audio attachments in an agent setup, choose an LLM model that supports audio input.
Request
Use multipart/form-data encoding:
| Field | Type | Required | Description |
|---|---|---|---|
file |
binary | Yes | The file to upload |
data |
JSON string | No | JSON object with scope (persistent or ephemeral, default: persistent) and optional external_user_id |
For API key requests, include external_user_id in data when you want to associate the upload with a specific external user. For client-token requests, external_user_id is derived from the token, so you do not need to send it.
Example
curl -X POST "https://api.codeer.ai/api/v1/chats/upload-file" \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@document.pdf" \
-F 'data={"scope":"persistent","external_user_id":"user_123"}'
Response
{
"data": {
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"original_name": "document.pdf",
"content_type": "application/pdf",
"size": 102400,
"file_url": "https://storage.codeer.ai/files/...",
"scope": "persistent"
},
"message": null,
"error_code": 0
}
Error Response
When validation fails, the response includes an error_msg field in the data:
{
"data": {
"original_name": "large-file.pdf",
"content_type": "application/pdf",
"size": 104857600,
"scope": "persistent",
"error_msg": "File size check failed (max size: 50 MB)"
},
"message": "File size check failed (max size: 50 MB)",
"error_code": 10006
}
Common validation errors:
"File size check failed (max size: 50 MB)"- File exceeds 50 MB limit"Unsupported content type: {type}"- MIME type not in allowed list"Unsupported file extension: {ext}"- Extension not in allowed list"File name is missing"- No filename provided"File is empty (size must be greater than 0 bytes)"- Empty file
Using Uploaded Files
After uploading, include the file UUID when sending a message:
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/messages" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Please analyze this document",
"stream": true,
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"attached_file_uuids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}'
Create Message Feedback
Submit feedback on an assistant's response.
Endpoint: POST /api/v1/chats/{chat_id}/messages/{message_id}/feedbacks
Path Parameters
| Parameter | Type | Description |
|---|---|---|
chat_id |
integer | The chat session ID |
message_id |
integer | The message ID to provide feedback on |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
external_user_id |
string | Yes | Your system's user identifier |
type |
string | Yes | Feedback type: sys_helpful or sys_improve |
content |
string | No | Additional feedback text (only for sys_improve type, max 500 characters) |
Example
curl -X POST "https://api.codeer.ai/api/v1/chats/12345/messages/1002/feedbacks" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_123",
"type": "sys_helpful"
}'
Response
{
"message": "Feedback created",
"error_code": 0
}
Code Examples
Python
import requests
API_KEY = "your-api-key"
BASE_URL = "https://api.codeer.ai/api/v1"
headers = {
"x-api-key": API_KEY,
"Content-Type": "application/json"
}
# Create a chat
chat_response = requests.post(
f"{BASE_URL}/chats",
headers=headers,
json={
"name": "API Test",
"agent_id": "your-agent-id",
"external_user_id": "user_123"
}
)
chat_id = chat_response.json()["data"]["id"]
# Send a message (non-streaming)
message_response = requests.post(
f"{BASE_URL}/chats/{chat_id}/messages",
headers=headers,
json={
"message": "Hello!",
"stream": False,
"agent_id": "your-agent-id"
}
)
print(message_response.json()["data"])
JavaScript
const API_KEY = 'your-api-key';
const BASE_URL = 'https://api.codeer.ai/api/v1';
const headers = {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
};
// Create a chat
const chatResponse = await fetch(`${BASE_URL}/chats`, {
method: 'POST',
headers,
body: JSON.stringify({
name: 'API Test',
agent_id: 'your-agent-id',
external_user_id: 'user_123'
})
});
const { data: chat } = await chatResponse.json();
// Send a message (non-streaming)
const messageResponse = await fetch(`${BASE_URL}/chats/${chat.id}/messages`, {
method: 'POST',
headers,
body: JSON.stringify({
message: 'Hello!',
stream: false,
agent_id: 'your-agent-id'
})
});
const { data: reply } = await messageResponse.json();
console.log(reply);
For more examples including streaming implementations, see the official examples repository.