Skip to content
Ensoul

Code Examples

Ready-to-use examples for interacting with the Ensoul API. The examples below the hello world use the REST API directly with cURL and JavaScript fetch. No SDK required.

💡

Use an SDK

These examples use the REST API directly. For a simpler experience, use one of our official SDKs, available for Python, TypeScript, Kotlin, Swift, Unity, C++, and Godot.

Hello World

Create a persona with no world, send it one message as one of your end users, and end the conversation.

# pip install ensoul
from ensoul import Ensoul

client = Ensoul(api_key="YOUR_API_KEY")

# A persona needs no world. This one stands alone.
persona = client.personas.create(name="Ada")

# user_id is your own id for the person talking.
# Each end user gets their own conversations with the persona.
reply = client.chat.send(
    persona.id,
    "Hi! Who are you?",
    user_id="YOUR_END_USER_ID",
)
print(reply.response)

# End the conversation when the person is done.
client.chat.end_conversation(
    persona.id,
    reply.conversation_id,
    user_id="YOUR_END_USER_ID",
)

Create a Persona

Create a persona. Only a name is required, and a persona needs no world. To place it in one, add the world's name as domain.

curl -X POST "https://api.ensoul-ai.com/v1/personas" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Maya Okonkwo"
  }'

Chat with a Persona

Send a message to a persona as one of your end users and receive their response. user_id is your own id for the person talking. Every response is generated through real-time inference.

curl -X POST "https://api.ensoul-ai.com/v1/personas/PERSONA_UUID/chat" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "What do you think about remote work?",
    "user_id": "YOUR_END_USER_ID"
  }'

Example Response

json
{
"response": "Honestly, I've really grown to appreciate remote work. The flexibility has been amazing for my productivity...",
"conversation_id": "conversation-uuid",
"latency_ms": 842,
"model": "claude-sonnet-5-5",
"timestamp": "2026-01-15T10:40:00Z"
}

End a Conversation

End a conversation when the person is done. Send the same user_id the conversation was chatted under. The API answers 204 with no body, and a later turn on the conversation gets 409 conversation_ended.

curl -X POST "https://api.ensoul-ai.com/v1/personas/PERSONA_UUID/conversations/CONVERSATION_UUID/end" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "YOUR_END_USER_ID"
  }'

Forget an End User

Erase everything kept for one end user: their conversations and messages, and the memories personas formed from them.

curl -X POST "https://api.ensoul-ai.com/v1/end-users/forget" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "YOUR_END_USER_ID"
  }'

Example Response

json
{
"user_id": "YOUR_END_USER_ID",
"conversations_erased": 3,
"memories_erased": 12
}
âš 

Full keys only

Forget takes a team key. A key bound to one persona gets 403.

List Personas

List personas with pagination and optional filters (region, archetype, country, city). Results come back in a paginated envelope.

curl -X GET "https://api.ensoul-ai.com/v1/personas?page=1&per_page=20" \
  -H "X-API-Key: YOUR_API_KEY"

List Worlds

Retrieve the worlds your account can reach. The API calls a world a domain.

curl -X GET "https://api.ensoul-ai.com/v1/domains" \
  -H "X-API-Key: YOUR_API_KEY"

Ask Many Personas

Ask many personas the same question at once. Answers stream back as server-sent events.

curl -X POST "https://api.ensoul-ai.com/v1/aggregate/stream" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain_id": "DOMAIN_UUID",
    "question": "What are your thoughts on work-life balance?",
    "sample_size": 10
  }'

Error Handling

All examples should include proper error handling:

javascript
async function apiCall(path, options = {}) {
const response = await fetch(path, {
...options,
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
...options.headers
}
});
if (!response.ok) {
const body = await response.json().catch(() => ({}));
// The body is flat: error is the code on a typed refusal, such as
// conversation_ended (409), and message explains a plain HTTP error.
// A 402 quota body names its quota in resource, such as persona_calls.
if (response.status === 401) {
throw new Error("Invalid API key");
} else if (response.status === 429) {
throw new Error("Rate limited: wait before retrying");
} else {
throw new Error(body.message || body.error || `HTTP ${response.status}`);
}
}
return response.json();
}

Next Steps