# Bring Your Own Auth (Pre-Authenticated Sessions) Sign users into your Chipp app from your own backend -- no Chipp login screen -- with server-side identity that works across devices and inside iframe embeds. --- If your application already authenticates its users (WorkOS, Auth0, Clerk, Okta, a custom SSO, or your own signed links), you do not need to make them sign in again inside Chipp. Your backend can mint a Chipp session for a user it has already verified, and the chat opens pre-authenticated: no login screen, no OTP, no Chipp password. This is the recommended pattern for white-label deployments, portals behind your own login, and governed workflows (for example, sending a patient or client a protected link that opens a pre-authenticated chat). > **Note:** Because the consumer identity created this way lives on Chipp's servers (keyed by email), it is portable across devices: any device where your backend mints a token for the same email is the same consumer, with the same conversation history. ## How It Works ``` User opens your app or your protected link 1. Your backend verifies the user (your auth, your signature checks) 2. Your backend calls POST /api/v1/apps/{appId}/consumers/auth -> Chipp finds or creates the consumer and returns a bearer token 3. Your page passes the token to the embedded chat -> the user is signed in, no Chipp login screen ``` Chipp never sees or validates your credentials or signed links. Your backend is the authority: it decides who gets a token, and Chipp trusts the Builder API call because it carries your secret API key. ## Step 1: Mint a Token on Your Server Call the Builder API endpoint [`POST /api/v1/apps/{appId}/consumers/auth`](/docs/builder-api/consumers#authenticate-a-consumer-server-side) from your backend. It requires a Builder API key with `consumers:write` scope (ask the Alchemist: "Create a Builder API key for this app"). ```bash curl -X POST https://build.chipp.ai/api/v1/apps/YOUR_APP_ID/consumers/auth \ -H "Authorization: Bearer chipp_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "jane@example.com", "name": "Jane Doe", "metadata": { "accountTier": "enterprise", "projectId": "PRJ-4821" } }' ``` Response: ```json { "data": { "token": "eyJhbGciOi...", "consumer": { "id": "...", "email": "jane@example.com", "name": "Jane Doe" }, "expires_at": "2026-08-16T10:00:00Z" } } ``` What happens server-side: Chipp finds or creates the consumer by email (lower-cased and trimmed), applies any `name` and `metadata` you send, creates a session, and returns a bearer token for it. **Token lifetime:** sessions last 30 days by default. When **HIPAA mode** is enabled on the app, sessions last 4 hours instead. Either way, the simplest robust pattern is to mint a fresh token on every page load: the call is idempotent, cheap, and `metadata` is upserted per key on each call. > **Warning:** Never call this endpoint from the browser. The Builder API key must stay on your server -- anyone holding it can mint a session for any email on your app. ### Users Without a Real Email The email is the identity key, not a delivery address. If your users should not be identified by their real email (or do not have one), use a stable synthetic email per user, for example `user-8f3a@yourdomain.com`. As long as your backend always maps the same user to the same synthetic address, their identity and history stay consistent. ## Step 2: Pass the Token to the Chat For the embed widget, call [`ChippWidget.setUser`](/docs/guides/embed-widget#server-side-secure) once the widget is loaded: ```javascript ChippWidget.setUser({ email: "jane@example.com", name: "Jane Doe", token: "eyJhbGciOi..." // from your backend }); ``` The token authenticates the user inside the chat iframe **without cookies**, so it works reliably in cross-origin iframe embeds even under Safari ITP and Chrome third-party cookie restrictions. ## Auth Modes Server-side tokens work with every [auth mode](/docs/guides/access-control). The mode only controls what a user sees when no token is present: | Auth mode | Without a token | With a server-minted token | |-----------|-----------------|----------------------------| | Open | Anonymous chat | Signed in as the identified user | | Encouraged | Inline sign-in card, can dismiss | Signed in, no card | | Required / Restricted | Full-screen sign-in gate | Signed in, no gate | For governed deployments where nobody should ever chat anonymously, use **Required**: if your token wiring fails, users hit a login gate instead of silently chatting as anonymous consumers. ## Cross-Device Behavior and Session Continuity Understanding what is and is not continuous across devices: - **Identity and history are server-side.** The same email on any device is the same consumer. All of their past conversations are available in the in-chat history panel on every device. - **A new visit starts a fresh conversation thread by default.** Within the same browser tab, an in-progress session is restored automatically, but opening the app on a new device (or after the idle timeout) starts a new thread. The user can reopen any prior conversation from history. - **There is no session-addressing parameter.** You cannot pass a session ID in the URL or embed configuration to force the chat to open a specific conversation, and the embed does not expose session IDs to the parent page via postMessage. If your application needs to continue a structured workflow mid-stream across devices (for example, a multi-step interview), the supported pattern is to keep workflow state in **your** backend and have the agent reconstruct it: pass your own reference ID on the arrival URL, have a custom action fetch current progress on the first turn (or seed it via the `metadata` field when minting the token), and treat each Chipp session as an execution context rather than the source of truth. ## Tracking Sessions From Your Backend You do not need anything in-conversation to learn which Chipp session belongs to which of your users: - **[Session webhooks](/docs/guides/session-webhooks)** send your endpoint a signed `session.created` event the moment any session starts, containing the `sessionId`, `consumerId`, `externalId`, and all captured URL query params. This is the deterministic way to build a `your-user-id -> chipp-session-id` mapping, with no model involvement. - **`?chipp_external_id=`** on the arrival URL is promoted to a first-class `externalId` field on the session (colon-free values only) and appears in the Conversations tab, session webhooks, the [Sessions API](/docs/builder-api/sessions), and all exports. Use it to stamp sessions with your own user or case reference. - **The [Sessions API](/docs/builder-api/sessions)** lets your backend fetch any session's transcript by `sessionId` at any time, and the [Org Export API](/docs/api/org-export) covers org-wide retrieval. ## Per-User Context for the Agent The optional `metadata` object on the mint call is upserted per key onto the consumer and rendered under an `## About this user` block in the agent's system prompt on every session. Use it for things your backend already knows (role, account tier, project, internal IDs) so the agent has them from the first message, with no custom action round-trip. Full schema and limits: [Authenticate a Consumer (Server-Side)](/docs/builder-api/consumers#authenticate-a-consumer-server-side). ## Alternative: Magic Links Chipp apps also support email magic links: `POST /consumer/YOUR_APP_SLUG/auth/magic-link` with an email causes Chipp to email that user a single-use sign-in link (valid 7 days). Opening it signs them in with the same server-side, cross-device identity, no password. This is useful when you want Chipp to handle link delivery. The server-side identify flow above is strictly more flexible for white-label integrations because your backend controls exactly when and for whom sessions are created, and nothing depends on email delivery. ## Related - [Access Control](/docs/guides/access-control) - auth modes and HIPAA mode - [Embed Widget](/docs/guides/embed-widget) - the full embed and `setUser` reference - [Session Lifecycle Webhooks](/docs/guides/session-webhooks) - signed `session.created` / `session.ended` events - [Builder API: Consumers](/docs/builder-api/consumers) - the `/consumers/auth` endpoint reference - [Builder API: Sessions](/docs/builder-api/sessions) - fetch transcripts by session ID