Conversations
Server-side conversation state for the Responses API. Create a conversation, append items, reference it by id. The model loads the history; you never resend it.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /:project_id/v1/conversations | Create a conversation |
| GET | /:project_id/v1/conversations | List conversations |
| GET | /:project_id/v1/conversations/{id} | Get a conversation |
| POST | /:project_id/v1/conversations/{id} | Update a conversation |
| DELETE | /:project_id/v1/conversations/{id} | Delete a conversation |
| POST | /:project_id/v1/conversations/{id}/items | Add an item |
| GET | /:project_id/v1/conversations/{id}/items | List items |
| GET | /:project_id/v1/conversations/{id}/items/{item_id} | Get an item |
| DELETE | /:project_id/v1/conversations/{id}/items/{item_id} | Delete an item |
Conversations are project-scoped. All paths are relative to your project base URL:
https://api.xerotier.ai/proj_ABC123
Quick Start
Create a conversation, add a system prompt, then use it with the Responses API:
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"metadata": {"title": "Weather assistant"},
"items": [
{
"type": "message",
"role": "system",
"content": "You are a helpful weather assistant."
}
]
}'
# Returns: {"id": "conv_abc123", ...}
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "message",
"role": "user",
"content": "What is the weather in Paris?"
}'
# Responses API uses endpoint-scoped path
curl -X POST https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/responses \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.1-8b",
"input": "What is the weather in Paris?",
"conversation": {"id": "conv_abc123"}
}'
The Responses API automatically loads all conversation items as context and appends the model's response to the conversation.
Lifecycle
The two surfaces meet at the conversation id; the model writes back into the same store you read from.
flowchart LR
Create["POST /v1/conversations
conv_abc123"]
AddUser["POST .../items
user message"]
Respond["POST /v1/responses
conversation: {id: ...}"]
LoadCtx["Server loads items
as prior context"]
Generate["Model generates"]
AutoAppend["Server appends
assistant + function_call items"]
Harvest["Client reads new items
for call_id, content, ..."]
Create --> AddUser
AddUser --> Respond
Respond --> LoadCtx
LoadCtx --> Generate
Generate --> AutoAppend
AutoAppend --> Harvest
Harvest -.-> AddUser
Manage Conversations
Create Conversation
POST /:project_id/v1/conversations
| Parameter | Type | Description |
|---|---|---|
| metadataoptional | object | Up to 16 key-value pairs. Keys max 64 characters, values max 512 characters. |
| itemsoptional | array | Initial items to add (max 20). Useful for seeding system prompts or conversation history. |
Response
{
"id": "conv_abc123def456",
"object": "conversation",
"created_at": 1709000000,
"updated_at": 1709000000,
"metadata": {"title": "Weather assistant"}
}
List Conversations
GET /:project_id/v1/conversations
Returns a paginated list of conversations. Use cursor-based pagination with after and limit parameters.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| limitoptional | integer | Maximum conversations to return (1-100, default 20). |
| afteroptional | string | Cursor for pagination. Pass the last_id from the previous page to get the next page. |
curl "https://api.xerotier.ai/proj_ABC123/v1/conversations?limit=10" \
-H "Authorization: Bearer xero_myproject_your_api_key"
Response
{
"object": "list",
"data": [
{
"id": "conv_abc123def456",
"object": "conversation",
"created_at": 1709000000,
"updated_at": 1709000100,
"metadata": {"title": "Weather assistant"}
}
],
"first_id": "conv_abc123def456",
"last_id": "conv_abc123def456",
"has_more": false
}
Get Conversation
GET /:project_id/v1/conversations/{conversation_id}
curl https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123 \
-H "Authorization: Bearer xero_myproject_your_api_key"
Update Conversation
POST /:project_id/v1/conversations/{conversation_id}
Updates the conversation metadata. The new metadata replaces the existing metadata entirely.
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123 \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"metadata": {"title": "Updated title", "category": "support"}
}'
Delete Conversation
DELETE /:project_id/v1/conversations/{conversation_id}
Soft-deletes the conversation. It is excluded from list queries but retained for auditing.
curl -X DELETE https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123 \
-H "Authorization: Bearer xero_myproject_your_api_key"
Response
{
"id": "conv_abc123def456",
"object": "conversation",
"deleted": true
}
Manage Items
Add Item
POST /:project_id/v1/conversations/{conversation_id}/items
| Parameter | Type | Description |
|---|---|---|
| typerequired | string | Item type: "message", "function_call", or "function_call_output". |
| roleconditional | string | Required (non-null) for message type. By convention one of "user", "assistant", "system", or "tool"; the server stores the value as-is and does not currently reject other strings, but downstream consumers (including the Responses API) expect the conventional values. |
| contentrequired | string | Text content of the item. Required at decode time for every item type. For function_call items the call's semantic payload is in arguments; supply an empty string or a short label for content. |
| call_idrecommended | string | Recommended for function_call and function_call_output items. Links a function call to its output so the Responses API can pair them in subsequent turns. Not enforced by the server today, but omitting it will break tool-result correlation. |
| namerecommended | string | Function name. Recommended for function_call items. Not enforced by the server today, but the model will not be able to resolve the tool without it. |
| argumentsrecommended | string | Serialized JSON arguments. Recommended for function_call items. Not enforced by the server today. |
For function_call_output items, the call_id must
match the one the model chose; harvest it from the conversation rather than
inventing it. See Multi-Turn with Function Calling
below for the harvest pattern.
Message Item
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "message",
"role": "user",
"content": "Hello, how are you?"
}'
Response
{
"id": "item_xyz789abcdef",
"object": "conversation.item",
"type": "message",
"role": "user",
"content": "Hello, how are you?",
"call_id": null,
"name": null,
"arguments": null,
"sequence_number": 1,
"created_at": 1709000100
}
Function Call Output Item
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "function_call_output",
"call_id": "call_abc123",
"content": "{\"temperature\": 18, \"condition\": \"sunny\"}"
}'
List Items
GET /:project_id/v1/conversations/{conversation_id}/items
Returns items ordered by sequence number (ascending). Supports cursor-based pagination with after and limit parameters.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| limitoptional | integer | Maximum items to return (1-100, default 20). |
| afteroptional | string | Cursor for pagination. Pass the last_id from the previous page. |
curl "https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items?limit=50" \
-H "Authorization: Bearer xero_myproject_your_api_key"
Response
{
"object": "list",
"data": [
{
"id": "item_xyz789abcdef",
"object": "conversation.item",
"type": "message",
"role": "user",
"content": null,
"call_id": null,
"name": null,
"arguments": null,
"sequence_number": 0,
"created_at": 1709000100
}
],
"first_id": "item_xyz789abcdef",
"last_id": "item_xyz789abcdef",
"has_more": false
}
Note: The content field is null
in list responses for efficiency. Use the Get Item endpoint to retrieve full
content for a specific item.
Get Item
GET /:project_id/v1/conversations/{conversation_id}/items/{item_id}
Returns the item with full content loaded from tiered storage.
curl https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items/item_xyz789 \
-H "Authorization: Bearer xero_myproject_your_api_key"
Delete Item
DELETE /:project_id/v1/conversations/{conversation_id}/items/{item_id}
Soft-deletes the item. It is excluded from future conversation context but the sequence number gap is preserved.
curl -X DELETE https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items/item_xyz789 \
-H "Authorization: Bearer xero_myproject_your_api_key"
Response
{
"id": "item_xyz789abcdef",
"object": "conversation.item",
"deleted": true
}
Delete response shape. Both delete endpoints
return the resource type in object
("conversation" or "conversation.item"),
not a synthetic ".deleted" suffix. Match on the
deleted: true flag, not on the object string.
Responses API Integration
Pass a conversation parameter when creating a response on the
Responses API to use the conversation's items
as context. The model's output is automatically appended to the conversation.
curl -X POST https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/responses \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.1-8b",
"input": "What is the weather in Paris?",
"conversation": {"id": "conv_abc123"}
}'
Note that conversations are project-scoped (at /:project_id/v1/conversations)
while the Responses API is endpoint-scoped (at /:project_id/:endpoint_slug/v1/responses).
How It Works
- All active items in the conversation are loaded and prepended to the input as context.
- The model generates a response using the full conversation history plus the new input.
- The model's output items (messages, function calls) are automatically appended to the conversation.
- The conversation's
updated_attimestamp is refreshed.
Precedence when both are supplied. If a request includes
both conversation and previous_response_id, the
conversation parameter wins: the server loads the conversation's
items and uses them as the chain context, ignoring the response chain that
previous_response_id would otherwise have selected. The request
is not rejected. Send only one of the two unless you specifically intend
this override behavior. Use conversation for explicit state
management and previous_response_id for simple response chaining.
Storage Tiers
Conversation content is stored using the platform's standard two-tier storage architecture. For details on storage tiers, encryption, retention, and billing, see Storage.
Conversation Storage Limits
| Limit | Value |
|---|---|
| Max items per conversation | 100 |
| Max individual item size | 1 MB |
| Max metadata keys per conversation | 16 |
| Max metadata key length | 64 characters |
| Max metadata value length | 512 characters |
| Max total metadata size | 16 KB |
Error Handling
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | invalid_request |
Missing or invalid parameters (e.g., missing role for message items). |
| 400 | invalid_request |
Initial items array exceeds 20 items. |
| 401 | authentication_error |
Invalid or missing API key. |
| 404 | not_found |
Conversation or item not found. |
| 429 | rate_limit_exceeded |
Too many requests. Check the Retry-After header. |
Examples
Python (requests)
import requests
headers = {
"Authorization": "Bearer xero_myproject_your_api_key",
"Content-Type": "application/json"
}
base = "https://api.xerotier.ai/proj_ABC123/v1"
# Create a conversation with a system prompt
conv = requests.post(f"{base}/conversations", headers=headers, json={
"metadata": {"title": "Demo chat"},
"items": [{
"type": "message",
"role": "system",
"content": "You are a helpful assistant."
}]
}).json()
print(f"Created conversation: {conv['id']}")
# Add a user message
requests.post(
f"{base}/conversations/{conv['id']}/items",
headers=headers,
json={
"type": "message",
"role": "user",
"content": "What is the capital of France?"
}
)
# List conversations
convs = requests.get(f"{base}/conversations?limit=10", headers=headers).json()
for c in convs["data"]:
print(f"Conversation: {c['id']}")
# List items in a conversation
items = requests.get(
f"{base}/conversations/{conv['id']}/items?limit=50",
headers=headers
).json()
for item in items["data"]:
print(f"[{item['role']}] {item['content']}")
# Delete a conversation
requests.delete(f"{base}/conversations/{conv['id']}", headers=headers)
Node.js (fetch)
const base = "https://api.xerotier.ai/proj_ABC123/v1";
const headers = {
"Authorization": "Bearer xero_myproject_your_api_key",
"Content-Type": "application/json"
};
// Create a conversation with a system prompt
const convRes = await fetch(`${base}/conversations`, {
method: "POST",
headers,
body: JSON.stringify({
metadata: { title: "Demo chat" },
items: [{
type: "message",
role: "system",
content: "You are a helpful assistant."
}]
})
});
const conv = await convRes.json();
console.log(`Created conversation: ${conv.id}`);
// Add a user message
await fetch(`${base}/conversations/${conv.id}/items`, {
method: "POST",
headers,
body: JSON.stringify({
type: "message",
role: "user",
content: "What is the capital of France?"
})
});
// List conversations
const listRes = await fetch(`${base}/conversations?limit=10`, { headers });
const list = await listRes.json();
list.data.forEach(c => console.log(`Conversation: ${c.id}`));
// List items in a conversation
const itemsRes = await fetch(
`${base}/conversations/${conv.id}/items?limit=50`,
{ headers }
);
const items = await itemsRes.json();
items.data.forEach(item => console.log(`[${item.role}] ${item.content}`));
// Delete a conversation
await fetch(`${base}/conversations/${conv.id}`, {
method: "DELETE",
headers
});
Multi-Turn with Function Calling (curl)
When the Responses API decides to call a tool, it auto-appends a
function_call item to the conversation. That appended item
carries the call_id the model chose; you must read it from
the conversation before posting the matching function_call_output.
Do not invent or hardcode call_id values.
# 1. Create conversation
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{"metadata": {"title": "Tool demo"}}'
# Returns: {"id": "conv_abc123", ...}
# 2. Generate response with tools (auto-appends a function_call item to the conversation)
curl -X POST https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/responses \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.1-8b",
"input": "What is the weather in Paris?",
"conversation": {"id": "conv_abc123"},
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
}
}]
}'
# 3. Harvest the assistant's call_id from the conversation.
# The Responses API auto-appended a function_call item; list the conversation
# items and pick the most recent function_call entry. Its call_id is the value
# the model chose and is the only value that will correlate with the output.
ASSISTANT_CALL_ID=$(curl -s \
"https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items?limit=100" \
-H "Authorization: Bearer xero_myproject_your_api_key" \
| jq -r '[.data[] | select(.type == "function_call")] | last | .call_id')
echo "captured call_id: $ASSISTANT_CALL_ID"
# 4. Post the function output using the captured call_id (NOT a hardcoded one)
curl -X POST https://api.xerotier.ai/proj_ABC123/v1/conversations/conv_abc123/items \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"function_call_output\",
\"call_id\": \"$ASSISTANT_CALL_ID\",
\"content\": \"{\\\"temperature\\\": 18, \\\"condition\\\": \\\"sunny\\\"}\"
}"
# 5. Continue with the function result in context
curl -X POST https://api.xerotier.ai/proj_ABC123/my-endpoint/v1/responses \
-H "Authorization: Bearer xero_myproject_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.1-8b",
"input": "Great, now summarize the weather.",
"conversation": {"id": "conv_abc123"}
}'
Note on the list response. List Items returns
content: null for efficiency. call_id,
name, type, and sequence_number
are populated in the list response, so harvesting the call_id
does not require a follow-up Get Item call. If you also need the function
arguments payload, fetch the specific item by id.