Skip to content

Service accounts

View as Markdown

Service account authentication is for backend services that call TIE programmatically — webhooks, cron jobs, internal microservices, or any server-side code. Instead of going through the user sign-in flow, your service authenticates with a shared secret called AUTH_SECRET.

Before You Start

What Where to get it
AUTH_SECRET Issued by the TIE team. Contact them to request one for your service.
TIE AI Gateway URL The base URL for TIE’s API (e.g. https://your-tie-host).
User UUID (if acting on behalf of users) The user.id from TIE Auth responses. Store this in your database when users sign up.

Authentication Modes

There are three ways to use service auth, depending on whether your service needs a user context and where your user IDs come from.

Mode Headers Memory & Persona Use case
Service + TIE user Authorization: Bearer <AUTH_SECRET> + X-On-Behalf-Of: <user-uuid> Yes Backend acting as a TIE-registered user
Service + external user Authorization: Bearer <AUTH_SECRET> + X-External-User-Id: <your-user-id> Yes 3rd-party app with its own user IDs — no TIE sign-up needed
Service only Authorization: Bearer <AUTH_SECRET> No Stateless one-shot calls

Service acting as a user

Your service calls TIE on behalf of a specific user. TIE loads that user’s persona and memory, and records the conversation — the same as if the user called directly. The service does not need to go through Firebase auth; it just needs to know the user’s TIE UUID.

Pass AUTH_SECRET as the bearer token and the user’s UUID in the X-On-Behalf-Of header:

curl -X POST https://your-tie-host/v1/chat/completions \
  -H "Authorization: Bearer $AUTH_SECRET" \
  -H "X-On-Behalf-Of: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-App-Id: whatsapp-webhook" \
  -H "Content-Type: application/json" \
  -d '{"model": "claude-haiku-4-5", "messages": [{"role": "user", "content": "What did we discuss yesterday?"}]}'
Header Required Description
Authorization Yes Bearer <AUTH_SECRET>
X-On-Behalf-Of Yes The user’s UUID (from user.id in auth responses)
X-App-Id Recommended Identifies the calling app and scopes threads

Where to get the user UUID

When a user signs up through TIE Auth, the response includes user.id — a deterministic UUID derived from their Firebase UID. Store this in your database alongside the user record. When your backend needs to call TIE on that user’s behalf, pass it in X-On-Behalf-Of.

sequenceDiagram
accTitle: Service-as-user authentication flow
accDescr: Shows how a backend service calls TIE on behalf of a user using AUTH_SECRET

    participant User
    participant App as Your App
    participant Backend as Your Backend
    participant TIE as TIE AI Gateway

    User->>App: Sends message (e.g. WhatsApp)
    App->>Backend: Forward message + user identifier
    Backend->>Backend: Look up user's TIE UUID from database
    Backend->>TIE: POST /v1/chat/completions<br/>Authorization: Bearer AUTH_SECRET<br/>X-On-Behalf-Of: user-uuid
    TIE->>TIE: Load user's memory & persona
    TIE-->>Backend: AI response
    Backend-->>App: Forward response to user

Acting for users with external IDs

If your users never sign in to TIE, you don’t need to create TIE accounts for them. Pass any stable string identifier from your own system in the X-External-User-Id header — TIE deterministically derives a user UUID from it (UUID5 of ext:<your-id>), so the same ID always maps to the same memory graph. No registration call, no ID mapping table.

# Write a memory for your user "crm-user-8841"
curl -X POST https://your-tie-host/memories/graph \
  -H "Authorization: Bearer $AUTH_SECRET" \
  -H "X-External-User-Id: crm-user-8841" \
  -H "Content-Type: application/json" \
  -d '{"data": "User prefers morning meetings and is based in Kuala Lumpur.", "type": "text", "agent_id": "chatbot"}'

# Retrieve memory context for the same user
curl -X POST https://your-tie-host/memories/retrieve \
  -H "Authorization: Bearer $AUTH_SECRET" \
  -H "X-External-User-Id: crm-user-8841" \
  -H "Content-Type: application/json" \
  -d '{"query": "when should I schedule the call?", "agent_id": "chatbot"}'

Rules for X-External-User-Id values:

  • Non-empty, max 256 characters, no control characters — otherwise 400 AUTH_INVALID_USER_ID.
  • Case-sensitive: Alice and alice derive different users. Pick one canonical form and always send it.
  • Cannot be combined with X-On-Behalf-Of — sending both returns 400 AUTH_SUBJECT_CONFLICT.
  • Requires service auth — sending it with a user token returns 400 AUTH_SUBJECT_CONFLICT.

Service only (stateless)

Your service makes a call with no user context. No memory, no persona, no turn recording. This is useful for utility tasks like title generation, text summarization, or content classification where there is no meaningful user to attach to.

curl -X POST https://your-tie-host/v1/chat/completions \
  -H "Authorization: Bearer $AUTH_SECRET" \
  -H "X-App-Id: title-generator" \
  -H "Content-Type: application/json" \
  -d '{"model": "gemini-2.5-flash", "messages": [{"role": "user", "content": "Generate a title for: ..."}]}'

Restrictions in service-only mode:

  • persona_id is not allowed (returns 400)
  • Memory tools (memory_search, memory_write) are disabled
  • No conversation turns are recorded
  • Threads still work for multi-turn within the same request chain, but have no user association

Security

  • Store AUTH_SECRET in environment variables or a secret manager (e.g. GCP Secret Manager), not in code.
  • Rotate the secret by coordinating with the TIE team. During rotation, both old and new values can be active briefly.
  • If you suspect the secret has been compromised, contact the TIE team immediately to revoke and reissue it.

Error Codes

Code Status When
AUTH_TOKEN_MISSING 401 No Authorization header provided
AUTH_TOKEN_INVALID 401 AUTH_SECRET value does not match
AUTH_INVALID_USER_ID 400 X-On-Behalf-Of is not a valid UUID, or X-External-User-Id is empty, longer than 256 characters, or contains control characters
AUTH_SUBJECT_CONFLICT 400 Both X-On-Behalf-Of and X-External-User-Id sent, or X-External-User-Id sent with a user token
VALIDATION_ERROR 400 persona_id used in service-only mode (no subject user)
Navigation

Type to search…

↑↓ navigate↵ selectEsc close