Conversations API
Read and manage conversations between your characters and players. Each unique player_id + character_id pair creates a persistent conversation with full memory.
Base path: /api/v1/characters/{character_id}/players
:::info character_id
The character_id in the URL path is the id returned by the Characters API.
:::
:::note Migrating from /api/agents
The older /api/agents/{agent_id}/players... routes still work, but they are deprecated. Their responses carry Deprecation, Link and Sunset headers pointing at their replacement. See the Legacy migration guide for the full mapping and the wire differences.
:::
Query parameter names on these routes are camelCase. Unknown query parameters are ignored, so a snake_case name such as sort_by silently has no effect.
Get Conversation Stats
GET /api/v1/characters/{character_id}/players/stats
Returns aggregate statistics for all conversations with a character.
Response
{
"totalConversations": 156,
"totalMessages": 4230,
"firstActivity": "2026-01-15T10:30:00",
"lastActivity": "2026-03-01T14:22:00"
}
| Field | Type | Description |
|---|---|---|
totalConversations | integer | Total number of player conversations |
totalMessages | integer | Total messages across all conversations |
firstActivity | string | null | ISO 8601 timestamp of earliest conversation |
lastActivity | string | null | ISO 8601 timestamp of most recent activity |
Example
curl https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/players/stats \
-H "X-API-Key: est_your_api_key"
List Conversations
GET /api/v1/characters/{character_id}/players
Returns a paginated list of player conversations for a character.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Results per page (1-100) |
offset | integer | 0 | Number of results to skip (minimum 0) |
sortBy | string | "last_activity" | Sort field: "last_activity", "created_at", "message_count", "player_id" |
sortOrder | string | "desc" | "asc" or "desc" |
playerSearch | string | none | Case-insensitive substring match on player_id (max 128 characters) |
minMessages | integer | none | Only include conversations with at least this many messages |
The parameter names are camelCase; the sortBy values are snake_case, as shown.
Response
{
"conversations": [
{
"id": "conv_abc123",
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"playerId": "player_xyz",
"apiKeyId": "key_abc123",
"createdAt": "2026-01-20T09:00:00",
"lastActivity": "2026-03-01T14:22:00",
"messageCount": 42,
"lastMemoryExtractionAt": "2026-03-01T14:25:00"
}
],
"total": 156,
"limit": 50,
"offset": 0,
"sortBy": "last_activity",
"sortOrder": "desc"
}
| Field | Type | Description |
|---|---|---|
id | string | Conversation ID |
characterId | string | The character in this conversation |
playerId | string | The player in this conversation |
apiKeyId | string | null | The API key that created the conversation |
createdAt | string | null | ISO 8601 timestamp |
lastActivity | string | null | ISO 8601 timestamp of the most recent message |
messageCount | integer | null | Messages in this conversation |
lastMemoryExtractionAt | string | null | ISO 8601 timestamp of the last memory extraction |
Example
curl "https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/players?limit=10&sortBy=message_count&sortOrder=desc" \
-H "X-API-Key: est_your_api_key"
const characterId = "550e8400-e29b-41d4-a716-446655440000";
const res = await fetch(
`https://api.estuary-ai.com/api/v1/characters/${characterId}/players?limit=10&minMessages=5`,
{ headers: { "X-API-Key": "est_your_api_key" } }
);
const data = await res.json();
console.log(`${data.total} conversations found`);
Get a Conversation
GET /api/v1/characters/{character_id}/players/{player_id}
Returns details for a specific player's conversation. Responds with 404 when this player has never spoken to this character.
Response
{
"id": "conv_abc123",
"characterId": "550e8400-e29b-41d4-a716-446655440000",
"playerId": "player_xyz",
"apiKeyId": "key_abc123",
"createdAt": "2026-01-20T09:00:00",
"lastActivity": "2026-03-01T14:22:00",
"messageCount": 42,
"lastMemoryExtractionAt": "2026-03-01T14:25:00"
}
Example
curl https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/players/player_xyz \
-H "X-API-Key: est_your_api_key"
Get Messages
GET /api/v1/characters/{character_id}/players/{player_id}/messages
Returns messages for a player conversation, ordered chronologically (newest page first, messages within each page in chronological order).
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 100 | Messages per page (1-200) |
page | integer | 1 | Page number (1-indexed, minimum 1) |
Response
{
"messages": [
{
"id": 1001,
"conversationId": "conv_abc123",
"role": "user",
"content": "Hey, remember what we talked about last time?",
"speaker": null,
"timestamp": "2026-03-01T14:20:00",
"imageUrl": null,
"imageDescription": null
},
{
"id": 1002,
"conversationId": "conv_abc123",
"role": "assistant",
"content": "Of course! You were telling me about your hiking trip to Rocky Mountain National Park.",
"speaker": null,
"timestamp": "2026-03-01T14:20:05",
"imageUrl": null,
"imageDescription": null
}
],
"pagination": {
"page": 1,
"limit": 100,
"total": 42,
"hasMore": false
}
}
| Field | Type | Description |
|---|---|---|
id | integer | Message ID |
conversationId | string | The conversation this message belongs to |
role | string | "user" or "assistant" |
content | string | Message text |
speaker | string | null | Display name of the person speaking, for shared multi-person conversations |
timestamp | string | null | ISO 8601 timestamp |
imageUrl | string | null | Image the player sent with this message |
imageDescription | string | null | What the character saw in that image |
:::info A player with no history returns an empty page
A player who has never spoken to this character gets a 200 with messages: [] and total: 0, not a 404. The conversation record is created when the player first connects, and your app may load history before that happens.
:::
Example
curl "https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/players/player_xyz/messages?limit=50&page=1" \
-H "X-API-Key: est_your_api_key"
Delete a Conversation
DELETE /api/v1/characters/{character_id}/players/{player_id}
Permanently deletes a conversation, all its messages, and all memories this character holds about the player. Uploaded chat images are soft-deleted.
Response
204 No Content, with an empty body.
:::warning Changed from the legacy route
The older DELETE /api/agents/{agent_id}/players/{player_id} returned 200 with {"message": "Player conversation deleted"}. The canonical route returns 204 and no body, so do not try to parse the response.
:::
Responds with 404 when this player has no conversation with the character.
Example
curl -X DELETE https://api.estuary-ai.com/api/v1/characters/550e8400-e29b-41d4-a716-446655440000/players/player_xyz \
-H "X-API-Key: est_your_api_key"