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
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
{"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
{"user_id": "YOUR_END_USER_ID","conversations_erased": 3,"memories_erased": 12}
Full keys only
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:
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();}