Skip to main content

End Users

Every person who talks to your agent has an end-user record: identity claims copied from your identity provider, plus the handful of settings they control themselves — where they are, what language they read, and what model defaults they prefer.

The platform reads this record on every turn. Setting a user's location means the agent knows their timezone without being told; setting their locale means it answers in their language without a prompt instruction.

Every call is scoped to the caller

There is deliberately no user identifier in any request body. The user is derived server-side from the gateway-verified x-user-id / x-tenant-id headers, so a caller can only read and write their own record — there is no way to address someone else's.

That means these endpoints need an end-user identity: a Firebase ID token, or a secret key with X-On-Behalf-Of. A bare sk_… key returns 401 authenticated user_id is required.

Read the Profile

curl -X POST https://api.travila.ai/api/v1/enduser/get \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{}'

Response:

{
"endUser": {
"subject": "user_123",
"tenantId": "tenant_abc",
"email": "jane@example.com",
"emailVerified": true,
"name": "Jane Doe",
"givenName": "Jane",
"picture": "https://…",
"signInProvider": "google.com",
"locale": "en-US",
"zoneinfo": "Europe/London",
"location": {
"latitude": 51.5072,
"longitude": -0.1276,
"timezone": "Europe/London",
"updatedAt": "2026-08-14T09:31:02Z"
},
"localeOverride": "es-MX",
"defaultGenerationConfig": {
"model": "google/gemini-3.6-flash",
"temperature": 0.4,
"languagePreference": "es"
},
"metadata": {"plan": "pro"},
"firstSeenAt": "2026-01-14T09:12:00Z",
"lastSeenAt": "2026-08-14T09:30:58Z",
"updatedAt": "2026-08-14T09:31:02Z",
"createdAt": "2026-01-14T09:12:00Z"
}
}

The record mixes two kinds of field:

SourceFieldsWho writes them
Identity providersubject, email, emailVerified, name, givenName, familyName, preferredUsername, picture, phoneNumber, signInProvider, authMethod, locale, zoneinfo, customClaimsCopied from the verified token — read-only here
User settingslocation, localeOverride, defaultGenerationConfig, metadataThe four update endpoints below
A user with no profile is not a 404

If no row exists yet, the call still succeeds and returns an empty endUser object. Treat "no profile yet" and "profile with no overrides" identically — do not branch on the difference.

Remember that unset, empty, zero and false fields are omitted from the response rather than sent as null. Read emailVerified with a default of false, not with a presence check.

Update Location

curl -X POST https://api.travila.ai/api/v1/enduser/update-location \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"latitude": 51.5072,
"longitude": -0.1276
}'

Response:

{
"location": {
"latitude": 51.5072,
"longitude": -0.1276,
"timezone": "Europe/London",
"updatedAt": "2026-08-14T09:31:02Z"
}
}

Do not send a timezone. The IANA zone is derived server-side from the coordinates and returned on the response — that derivation is the main reason to call this endpoint at all, since it is what lets the agent reason about "this evening" correctly.

city is reserved for a later release and is currently always empty; there is no reverse geocoding yet.

Update Locale

curl -X POST https://api.travila.ai/api/v1/enduser/update-locale \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{"locale": "es-MX"}'

Response: empty body on success.

A BCP-47 tag that overrides the locale supplied by the identity provider — for when the user picks a language in your app that differs from their account language.

Send an empty string to clear the override and fall back to the identity-provider locale:

{"locale": ""}

Update Default Generation Config

Sets the user's own generation defaults, applied as the base for their conversations. A conversation's own config still layers on top.

curl -X POST https://api.travila.ai/api/v1/enduser/update-generation-config \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"config": {
"model": "google/gemini-3.6-flash",
"temperature": 0.4,
"maxOutputTokens": 2048,
"languagePreference": "es"
}
}'

Response: empty body on success.

The accepted field list is a closed allowlist

Only user-settable fields are accepted: model, models, the core sampling parameters (temperature, topP, maxOutputTokens, frequencyPenalty, presencePenalty, stopSequences, seed), and languagePreference.

Operational fields — systemPrompt, tools, clientTools, toolPolicy, MCP settings, timeouts, anything else — are rejected, not silently dropped: a config carrying any of them fails the whole request. The allowlist is closed by default, so fields added to the generation config in future releases are rejected here until they are explicitly opened up.

model and every entry in models are additionally checked against the platform model allowlist; an unpermitted entry fails the request. This is the endpoint to expose behind a user-facing "preferred model" setting — it cannot be used to smuggle in a system prompt.

Update Metadata

Free-form key/value pairs you attach to the user — plan tier, feature flags, anything your own product needs alongside the platform's own fields.

curl -X POST https://api.travila.ai/api/v1/enduser/update-metadata \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"plan": "pro",
"onboardingComplete": "true"
}
}'

Response: empty body on success.

This is a merge, not a replace: keys you omit are left untouched. To delete a key, send it with an empty-string value:

{
"metadata": {"onboardingComplete": ""}
}
  • Authentication — Firebase tokens, secret keys, and X-On-Behalf-Of
  • Model Routing — how defaultGenerationConfig interacts with per-turn overrides
  • Conversations — where the user context is applied
  • Notifications — the separate subscriber record used for delivery