Skip to main content
When embedding Dial AI into your application, you often need to pass context — such as the logged-in user’s ID, account number, or role — into the chat session so that functions can act on behalf of that user. Because the widget runs client-side, any context sent directly from the browser can be forged. A malicious user could change their userId to someone else’s and impersonate them. This page covers three approaches to passing context securely, each suited to different integration architectures.

The Problem

Consider a script-tag integration where you pass a user ID as an extra parameter:
// DON'T DO THIS — the user can change "user-123" to any value
window.renderModal("dial-ai-button", {
    chatId: "00000000-0000-0000-0000-000000000000",
    backendUrl: "https://your-instance.dialai.ca",
    iconUrl: "/icon.svg",
    extraInternalParameters: {
        userId: "user-123"
    }
});
Because this code runs in the user’s browser, nothing prevents them from opening DevTools and changing "user-123" to "user-456". Any downstream function that trusts this value is now operating on the wrong user’s data. All three approaches below solve this by ensuring user identity is verified server-side before it reaches your flow.

Approach 1: External JWT with Public Key Verification

If your application already issues JWTs to authenticated users (e.g. via an identity provider like Auth0, Cognito, or Keycloak), Dial AI can validate those tokens directly and extract claims from them.

How It Works

  1. Your identity provider issues a signed JWT to the user when they log in
  2. The user’s browser passes this JWT to Dial AI when creating a chat session
  3. Dial AI verifies the JWT signature against your provider’s public keys (via a JWKS endpoint)
  4. Dial AI checks the issuer claim matches your configured issuer
  5. Verified claims (e.g. sub, accountId) are extracted and made available as internal parameters
Because the JWT is cryptographically signed, the user cannot modify the claims without invalidating the signature.

Setup

To configure external JWT verification, provide Dial AI with:
  • JWKS URL: The endpoint where your identity provider publishes its public keys (e.g. https://your-idp.com/.well-known/jwks.json)
  • Issuer: The expected iss claim in the JWT (e.g. https://your-idp.com/)
  • Tenant: Which Dial AI tenant this JWT configuration applies to
Once configured, users authenticate to Dial AI by passing their JWT as a Bearer token. Dial AI verifies the signature and issuer, then makes the JWT’s claims available to your flow.

When to Use This

  • You already have an identity provider that issues JWTs with a JWKS endpoint
  • You want a zero-backend-work integration — the browser can pass the token directly
  • You need access to multiple claims from the token (e.g. user ID, roles, email)

Approach 2: Server-Side Token Verification

If your users authenticate with tokens that aren’t JWTs, or you need to verify additional state beyond what’s in a token (e.g. checking that an account is still active), you can verify the user’s identity via a server-side request before passing context to Dial AI.

How It Works

  1. The user logs in to your application and receives a session token
  2. When starting a chat, your frontend sends the session token to your backend
  3. Your backend validates the token against your system of record (database, auth service, etc.)
  4. Your backend creates a chat session via the Dial AI API, passing verified context as extraInternalParams
  5. The chat link or session ID is returned to the frontend
The user never passes their own identity to Dial AI — your backend does, after confirming who they are.

Example Flow

Browser                    Your Backend                 Dial AI
  │                            │                           │
  │─── start chat ────────────►│                           │
  │    (session token)         │                           │
  │                            │── verify token ──►        │
  │                            │◄── user: alice ───        │
  │                            │                           │
  │                            │── POST /api/chat/link ───►│
  │                            │   { extraInternalParams:  │
  │                            │     { userId: "alice" } } │
  │                            │◄── chat link UUID ────────│
  │                            │                           │
  │◄── chat link UUID ────────│                           │
  │                            │                           │
  │── open chat (X-DialAi-Chat-Link: Bearer <uuid>) ─────►│

When to Use This

  • Your auth system doesn’t use JWTs, or uses opaque tokens
  • You need to check live state (account status, permissions) at chat-creation time
  • You want full control over what context is passed to Dial AI

Dial AI supports creating chat links via API with context permanently embedded in them. This is especially useful for short-lived, user-specific sessions.

How It Works

  1. Your backend authenticates the user through your own auth system
  2. Your backend calls the Dial AI API to create a chat link, including verified user context in extraInternalParams
  3. The chat link UUID is returned to the frontend
  4. The frontend uses this link to open the chat — the baked-in context is automatically attached to every conversation created with this link
The context is stored server-side in Dial AI and cannot be modified by the client. Create a chat link with context that persists until explicitly deleted:
POST /api/chat/link
Authorization: Bearer <your-dial-ai-jwt>

{
    "flowId": "your-flow-id",
    "name": "Support chat for alice",
    "extraInternalParams": {
        "userId": "alice",
        "accountTier": "premium"
    },
    "settableInternalParams": ["preferredLanguage"]
}
  • extraInternalParams: Context baked into the link. Every chat created with this link will have these values as internal parameters. The client cannot override them.
  • settableInternalParams: Parameters the client is allowed to set when creating a chat. Use this for non-sensitive context like UI preferences. If a client attempts to set a parameter not in this list, the request is rejected.
For short-lived sessions, create a temporary chat link that automatically extracts claims from the caller’s JWT:
POST /api/v2/chat/link/temporary
Authorization: Bearer <user-jwt>

{
    "flowId": "your-flow-id",
    "name": "Session link",
    "duration": 3600,
    "verifiedExtraInternalParams": {
        "userId": { "type": "jwt-claim", "key": "sub" },
        "accountId": { "type": "jwt-claim", "key": "accountId" }
    }
}
  • duration: Link lifetime in seconds (must be positive, maximum 6 hours)
  • verifiedExtraInternalParams: Maps parameter names to JWT claim extraction rules. Dial AI reads the specified claim from the caller’s verified JWT and bakes it into the link. If the claim is missing or not a string, the request fails.
This combines the security of JWT verification with the simplicity of a pre-built chat link — the frontend just receives a UUID and doesn’t need to handle tokens at all.

When to Use This

  • You want the simplest possible frontend — just a chat link UUID, no token handling
  • You need per-user or per-session chat links with guaranteed context
  • You want to limit which parameters the client can influence via settableInternalParams

Comparison

External JWTServer-Side VerificationBackend-Created Links
Frontend complexityLow — pass existing JWTLow — call your backend firstLowest — just a UUID
Backend workNone (configuration only)Moderate — verification endpointModerate — link creation endpoint
Identity sourceJWT claimsYour system of recordYour system of record or JWT claims
Context flexibilityAll JWT claims availableFull controlBaked at link creation
Session lifetimePer-token expiryPer-link (optional expiry)Per-link (optional expiry)

Using Context in Flows

Regardless of which approach you use, the verified context arrives in your flow as internal parameters. Functions can access these values as inputs by declaring them as internal parameters (context parameters). For example, a “Look Up Account” function could take userId as an internal parameter and use it to query your database — knowing that the value was verified and cannot have been tampered with by the end user.