Persons API
A person is a participant identity persisted across conversations. The story of identity in Roxels — what it gets you, when to trust it, how to verify it — lives in Identity and resumption. This page covers the REST endpoints for managing person records programmatically.
When you need this API
For most embed integrations, you don't call this API directly — passing external_id to Roxels.start(...) or creating a conversation with a persons array auto-creates the person.
You'll call this API when:
- You want to pre-register persons with extra context before their first conversation.
- You want to batch-import participants from an existing system.
- You're auditing which users have run which conversations.
- You need to delete a person's history.
List persons
curl https://api.roxels.ai/v1/persons \
-H "Authorization: Bearer sk-your-key-here"Response:
{
"items": [
{
"id": "person_xxx",
"external_id": "user_123",
"name": "Ada Lovelace",
"first_seen_at": "2026-05-01T12:00:00Z",
"last_seen_at": "2026-05-22T10:14:00Z",
"conversation_count": 4
}
],
"next_cursor": null,
"has_more": false
}Standard cursor pagination — see Pagination and errors.
Query parameters:
| Param | Description |
|---|---|
external_id |
Filter to one external id. Useful for "does this user already exist?" lookups. |
cursor, limit |
Pagination. |
Get a person
curl https://api.roxels.ai/v1/persons/person_xxx \
-H "Authorization: Bearer sk-your-key-here"Response: the full person record, including accumulated org_context.
{
"id": "person_xxx",
"external_id": "user_123",
"name": "Ada Lovelace",
"first_seen_at": "...",
"last_seen_at": "...",
"conversation_count": 4,
"org_context": {
"plan": "pro",
"joined_at": "2026-05-01",
"role": "engineer"
}
}Create or upsert a person
Pre-register a participant so the agent has context about them on first contact:
curl -X POST https://api.roxels.ai/v1/persons \
-H "Authorization: Bearer sk-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"external_id": "user_123",
"name": "Ada Lovelace",
"org_context": {
"plan": "pro",
"joined_at": "2026-05-01",
"role": "engineer"
}
}'Body fields:
| Field | Type | Description |
|---|---|---|
external_id |
string | Your stable identifier for this person. Required. |
name |
string | Display name. The agent uses this in conversation. |
org_context |
object | Arbitrary key/value context. Available to the agent during every conversation with this person, and accessible as {{context.<key>}} in webhook templates. |
If a person with that external_id already exists, the call upserts: the name is updated and org_context is shallow-merged. Existing keys you don't include are preserved.
Response: the created or updated person record (same shape as Get).
Patterns
Pre-register on sign-up
The cleanest path: when a new user signs up in your system, mirror them into Roxels with whatever context you have.
# Your sign-up handler, server-side
def on_signup(user):
requests.post(
"https://api.roxels.ai/v1/persons",
headers={"Authorization": f"Bearer {ROXELS_API_KEY}"},
json={
"external_id": user.id,
"name": user.full_name,
"org_context": {
"plan": user.plan,
"signed_up_at": user.signed_up_at.isoformat(),
"industry": user.industry,
},
},
)The user's first conversation has rich context. The agent can greet them by name, reference their plan, tune the conversation to their industry.
Refresh context periodically
org_context is a snapshot. If your user's data changes (they upgrade plans, change roles), update the person record:
curl -X POST https://api.roxels.ai/v1/persons \
-H "Authorization: Bearer sk-your-key-here" \
-d '{
"external_id": "user_123",
"org_context": { "plan": "enterprise" }
}'Same external_id → upsert. The plan key updates; other context preserved.
A common rhythm: refresh the person record once a day from your backend, or on significant events (upgrade, role change, account merge).
Batch import
For migrating an existing user base:
for user in your_users:
requests.post(
"https://api.roxels.ai/v1/persons",
headers={...},
json={...},
)There's no bulk endpoint yet — N individual POSTs is the right pattern. Throttle to respect rate limits.
Look up before assuming
If you're unsure whether you've ever sent a particular user to Roxels:
curl "https://api.roxels.ai/v1/persons?external_id=user_123" \
-H "Authorization: Bearer sk-your-key-here"Returns either a one-item list (exists) or empty (doesn't).
Deletion (right to be forgotten)
Currently, deletion is done by Roxels support — email support@roxels.ai with the external_id or person.id to delete. A self-service endpoint is on the roadmap.
When a person is deleted:
- All conversation transcripts and findings tied to them are removed.
- The
external_idis freed; future conversations with the same id create a fresh person. - Outputs that already fired (webhooks delivered, callbacks executed) are NOT recalled — they're already in your system.
For ongoing data minimization, delete promptly when a user closes their account.
How external_id flows through the rest of the API
When you pass external_id to:
Roxels.start({ externalId: "user_123" })— Embed creates the session linked to that person.Roxels.init({ externalId: "user_123" })— Same; propagated to the nextstart().POST /v1/interviewswithpersons: [{ external_id }]— Same.
The person is looked up by external_id in your org and the conversation is attached. The full identity story, including the trust model and server-side verification pattern, is in Identity and resumption.
Read next
- Identity and resumption — When to trust
external_id, server-side patterns, deletion. - Conversations API — Start conversations bound to a person.
- Embed install — Pass
external_idfrom the browser. - Headless mode — Resumption patterns in custom UIs.