This API authenticates with Client SDK keys (osk_ prefix).
This is a separate endpoint from the REST API Chat (OpenAI-compatible).
Send Message
POST /v1/chat/{persona_id}
Sends a message to a persona. Omit thread_id to create a new thread, or include it to continue an existing conversation.
Available models can be queried via GET /v1/models.
Request
New Thread (model required):
{
"model": "shopping-assistant",
"messages": [
{
"role": "user",
"content": "What should I have for dinner tonight?"
}
]
}
Continue Thread (model not needed):
{
"thread_id": "thr_a1b2c3d4e5f6",
"messages": [
{
"role": "user",
"content": "Tell me the curry ingredients"
}
]
}
| Field | Type | Required | Description |
|---|
model | string | Required for new thread | Model name (from GET /v1/models) |
thread_id | string | No | Existing thread ID (omit to create a new thread) |
messages | array | Yes | Message array |
messages[].role | string | Yes | user |
messages[].content | string | Yes | Message content |
The model specified when creating a new thread is fixed to that thread. Subsequent messages in the same thread do not need the model field.
Response
{
"success": true,
"data": {
"thread_id": "thr_a1b2c3d4e5f6",
"is_new_thread": true,
"user_message": {
"message_id": "msg_u1v2w3x4y5z6",
"role": "user",
"content": "What should I have for dinner tonight?",
"created_at": "2026-04-16T10:00:00Z"
},
"assistant_message": {
"message_id": "msg_a7b8c9d0e1f2",
"role": "assistant",
"blocks": [
{
"type": "text",
"content": "How about a warm beef curry on a rainy day like this?"
},
{
"type": "product_list",
"title": "Spicy Beef Curry Ingredients",
"badge": "AI Pick",
"items": [
{ "id": "p_001", "name": "Premium Beef Sirloin (300g)", "section": "Meat Counter", "price": 24500 },
{ "id": "p_002", "name": "Organic Potato & Carrot Set", "section": "Fresh Produce", "price": 4800 }
],
"actions": [
{ "id": "view_recipe", "label": "View details", "style": "primary" },
{ "id": "show_alternates", "label": "Other suggestions", "style": "secondary" }
]
},
{
"type": "suggestion",
"content": "Would you like a red wine recommendation to pair with the curry?",
"actions": [
{ "id": "wine_yes", "label": "Recommend", "style": "primary" },
{ "id": "wine_no", "label": "No thanks", "style": "secondary" }
]
}
],
"created_at": "2026-04-16T10:00:01Z"
}
}
}
Block Types
Assistant responses are returned as a structured blocks array.
| Type | Description | Key Fields |
|---|
text | Text message | content |
image | Image | url |
product_list | Product listing | title, items[], actions[] |
suggestion | AI recommendation/suggestion | content, actions[] |
Actions
The actions included in blocks provide information for handling user interactions on the client side.
| Field | Description |
|---|
id | Action identifier |
label | Button text |
style | primary / secondary / text |
Get Thread
GET /v1/chat/{persona_id}/threads/{thread_id}
Retrieves thread metadata (message bodies are not included).
Response
{
"success": true,
"data": {
"thread_id": "thr_a1b2c3d4e5f6",
"title": "Dinner Menu Recommendations",
"status": "ACTIVE",
"message_count": 4,
"started_at": "2026-04-16T10:00:00Z",
"last_active_at": "2026-04-16T10:30:00Z"
}
}
| Status | Description |
|---|
ACTIVE | In progress |
CLOSED | Ended |
Close Thread
POST /v1/chat/{persona_id}/threads/{thread_id}/close
Closes a thread. The persona status transitions from CHAT to READY.
Response
{
"success": true,
"data": {
"thread_id": "thr_a1b2c3d4e5f6",
"thread_status": "CLOSED",
"persona_status": "READY",
"closed_at": "2026-04-16T10:35:00Z"
}
}
Requesting on an already-closed thread returns 409 Conflict:
{
"success": false,
"error": {
"code": "ALREADY_CLOSED",
"message": "thread is already closed"
}
}
List Messages
GET /v1/chat/{persona_id}/threads/{thread_id}/messages
Retrieves the message list for a thread (oldest first). Supports cursor-based pagination.
Query Parameters
| Parameter | Type | Default | Description |
|---|
limit | integer | 50 | Number of messages to return (max 100) |
before | string | - | Load messages before this message_id (reverse paging) |
Response
{
"success": true,
"data": {
"thread_id": "thr_a1b2c3d4e5f6",
"messages": [
{
"message_id": "msg_u1v2w3x4y5z6",
"role": "user",
"content": "What should I have for dinner tonight?",
"created_at": "2026-04-16T10:00:00Z"
},
{
"message_id": "msg_a7b8c9d0e1f2",
"role": "assistant",
"blocks": [
{ "type": "text", "content": "How about beef curry?" }
],
"created_at": "2026-04-16T10:00:01Z"
}
],
"has_more": false,
"next_before": null
}
}
When has_more is true, pass the next_before value as the before parameter to load the next page.
Full Flow Example
# 1. Create persona
PERSONA_ID=$(curl -s -X POST https://api.ones1ght.com/v1/persona \
-H "Authorization: Bearer $SDK_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "John Doe", "locale": "en"}' | jq -r '.data.persona_id')
# 2. List available models
curl -s https://api.ones1ght.com/v1/models \
-H "Authorization: Bearer $SDK_KEY"
# 3. Start chat (new thread created automatically, model required)
THREAD_ID=$(curl -s -X POST https://api.ones1ght.com/v1/chat/$PERSONA_ID \
-H "Authorization: Bearer $SDK_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "shopping-assistant", "messages": [{"role": "user", "content": "What should I have for dinner?"}]}' | jq -r '.data.thread_id')
# 4. Continue conversation (model not needed, fixed to thread)
curl -X POST https://api.ones1ght.com/v1/chat/$PERSONA_ID \
-H "Authorization: Bearer $SDK_KEY" \
-H "Content-Type: application/json" \
-d "{\"thread_id\": \"$THREAD_ID\", \"messages\": [{\"role\": \"user\", \"content\": \"Tell me the curry ingredients\"}]}"
# 5. Retrieve message history
curl https://api.ones1ght.com/v1/chat/$PERSONA_ID/threads/$THREAD_ID/messages \
-H "Authorization: Bearer $SDK_KEY"
# 6. Close thread
curl -X POST https://api.ones1ght.com/v1/chat/$PERSONA_ID/threads/$THREAD_ID/close \
-H "Authorization: Bearer $SDK_KEY"