Developer guide

This guide shows how to use the End Users API alongside the Conversations API to build context-aware, authenticated custom channel integrations. It covers setting user context before the first AI Agent turn, passing sensitive metadata securely, and common integration patterns.

The POST /v2/end-users/POST /v2/conversations/ flow described in this guide is for custom channel (Conversations API) integrations only. For native chat, use the Chat SDK setMetaFields() and setSensitiveMetaFields() instead.

Channel eligibility

FeatureAvailable channels
Pre-greeting context (POST /v2/end-users/POST /v2/conversations/)Custom channels only
Secure metadata on POST /v2/end-users/Custom channels only
Secure metadata on PATCH /v2/end-users/:idAll channels (endpoint is channel-agnostic)
  • Native chat: The Chat SDK provides setSensitiveMetaFields() as the primary path. PATCH /v2/end-users/:id with sensitive_metadata is also available if you have the end_user_id.
  • Social and email channels: PATCH /v2/end-users/:id with sensitive_metadata is the only API pathway. Obtain the end_user_id from a v1.end_user.created or v1.conversation.created webhook event.

Before you begin

Multi-language support from the first turn

This example creates an end user with a language preference and metadata before starting a conversation. The AI Agent receives the correct language and context from the greeting onward.

1

Create the end user with language and metadata

$curl -X POST https://EXAMPLE.ada.support/api/v2/end-users/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "profile": {
> "first_name": "Maria",
> "last_name": "Santos",
> "language": "pt-BR",
> "metadata": {
> "region": "latam",
> "plan_type": "enterprise"
> }
> }
> }'

The response includes the end_user_id:

1{
2 "end_user_id": "67a8b2c1d3f4e5a6b7c8d9e0",
3 "profile": {
4 "first_name": "Maria",
5 "last_name": "Santos",
6 "language": "pt-BR",
7 "metadata": {
8 "region": "latam",
9 "plan_type": "enterprise"
10 },
11 ...
12 },
13 "created_at": "2026-03-31T10:30:00.000Z",
14 "updated_at": "2026-03-31T10:30:00.000Z"
15}
2

Start a conversation with the end user

Pass the end_user_id from the previous step. The AI Agent associates the conversation with this end user, and the greeting fires with the correct language, locale-aware knowledge base lookups, and personalized content.

$curl -X POST https://EXAMPLE.ada.support/api/v2/conversations/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "channel_id": "YOUR_CHANNEL_ID",
> "end_user_id": "67a8b2c1d3f4e5a6b7c8d9e0"
> }'

The AI Agent now has full user context from the first turn. Language is set to pt-BR, knowledge base lookups use the correct locale, and metadata values like region and plan_type are available as metavariables in Playbooks, Actions, and article rules.

Authenticated Actions without re-login

This example passes an auth token securely at user creation time so the AI Agent can run authenticated Actions (such as account lookups or order changes) without prompting the end user to log in again.

1

Create the end user with sensitive metadata

$curl -X POST https://EXAMPLE.ada.support/api/v2/end-users/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "profile": {
> "language": "en-US",
> "metadata": {
> "account_tier": "premium"
> },
> "sensitive_metadata": {
> "fields": {
> "auth_token": "eyJhbGciOiJIUzI1NiIs...",
> "session_id": "sess_abc123"
> }
> }
> }
> }'

The sensitive_metadata values are encrypted at rest, redacted from the dashboard, excluded from LLM context, and automatically deleted after 24 hours. They do not appear in the response body.

2

Start a conversation

$curl -X POST https://EXAMPLE.ada.support/api/v2/conversations/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "channel_id": "YOUR_CHANNEL_ID",
> "end_user_id": "END_USER_ID_FROM_STEP_1"
> }'

The AI Agent can now use the auth_token to execute authenticated Actions on behalf of the end user.

3

Refresh the token mid-conversation (if needed)

If the token expires during the conversation, update it with a PATCH request:

$curl -X PATCH https://EXAMPLE.ada.support/api/v2/end-users/END_USER_ID \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "profile": {
> "sensitive_metadata": {
> "fields": {
> "auth_token": "eyJhbGciOiJSUzI1NiIs..."
> }
> }
> }
> }'

The updated token is immediately available to the AI Agent for subsequent Actions.

Campaign-driven personalization

This example sets campaign context on an end user so the AI Agent can personalize the greeting and route the conversation based on the campaign that brought the user in.

1

Create the end user with campaign metadata

$curl -X POST https://EXAMPLE.ada.support/api/v2/end-users/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "profile": {
> "metadata": {
> "campaign_id": "spring_2026_promo",
> "offer_code": "SAVE20",
> "source_channel": "email"
> }
> }
> }'
2

Start a conversation

$curl -X POST https://EXAMPLE.ada.support/api/v2/conversations/ \
> -H "Authorization: Bearer YOUR_API_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "channel_id": "YOUR_CHANNEL_ID",
> "end_user_id": "END_USER_ID_FROM_STEP_1"
> }'

The metavariables campaign_id, offer_code, and source_channel are available from the greeting onward. Use them in Playbooks to route to a campaign-specific flow, in article rules to surface relevant content, or in Actions to apply the offer.

Security considerations

sensitive_metadata vs. metadata

metadatasensitive_metadata
EncryptionNot encryptedEncrypted at rest
Dashboard visibilityVisibleRedacted
LLM contextIncludedExcluded
TTLPersists until overwrittenAutomatically deleted after 24 hours
API responsesReturned in GET and PATCH responsesNever returned (write-only)
Use casesRegion, plan type, preferencesAuth tokens, session IDs, PII (name, phone, email)

Use sensitive_metadata for any value that should not persist long-term or be visible to operators and LLMs. Use standard metadata for non-sensitive context that should be readable and persistent.

End-user scoped storage

sensitive_metadata values are stored at the end-user level, not the conversation level. If an end user has multiple active conversations, a value set in one conversation is accessible in all of them.

For the most common integration pattern (one end user per conversation), this is not observable. For integrations where a single end user has concurrent conversations across channels, be aware that sensitive values propagate to all active conversations for that end user.

Write-only behavior

sensitive_metadata values are never returned in API responses. If your integration needs to reference a token it previously set, store it on your side. Ada stores the value only for use by the AI Agent during the conversation.

FAQ

No. The POST /v2/end-users/POST /v2/conversations/ flow is for custom channel integrations only. For native chat, use the Chat SDK setMetaFields() to set user context before the conversation starts.

Only if your WhatsApp integration uses the Conversations API (making it a custom channel). Social channels managed through Ada’s native integrations (such as SunCo-based WhatsApp) do not support this flow because the end user initiates the conversation and there is no opportunity to call POST /v2/conversations/.

Yes. PATCH /v2/end-users/:id with sensitive_metadata is channel-agnostic and works for any end user as long as you have the end_user_id. For native chat, the Chat SDK setSensitiveMetaFields() is the primary path, but the API also works. For social and email channels, the API is the only pathway.

Values in standard metadata are stored unencrypted, visible in the dashboard, included in LLM context, and returned in API responses. Always use sensitive_metadata for auth tokens, session IDs, and personally identifiable information.

End users created through POST /v2/end-users/ that are not associated with a conversation within 24 hours of creation are automatically deleted. If your integration encounters an error between creating the end user and starting a conversation, reuse the same end_user_id for the retry rather than creating a new one. End users are not billed (billing is per conversation, not per end user).

No. Each call creates a new end user. Your integration is responsible for storing and reusing the end_user_id from the first successful call to avoid duplicates.

If no language is provided in the profile, the end user’s language defaults to the AI Agent’s configured default language (typically English). If you explicitly provide a language value, that value is used.

End-user metadata (set via profile.metadata) persists across conversations for the same end user. Conversation metadata (set on the conversation object) does not carry over. If you need fresh metadata for each conversation, update the end user via PATCH /v2/end-users/:id before starting a new conversation.

Set the key to null in a PATCH /v2/end-users/:id request:

1{
2 "profile": {
3 "sensitive_metadata": {
4 "fields": {
5 "auth_token": null
6 }
7 }
8 }
9}