Alchemist Operations

Alchemist: Embedded Agent - Identity & Billing

Understand authenticated vs. anonymous sessions and how billing is attributed.

|View as Markdown
Chipp EngineeringEngineering at Chipp
||1 min read
#alchemist#embedded-agent#identity#billing#authentication

The embedded-Chipp-agent supports two identity modes for end users: authenticated (your app’s own signed-in users) and anonymous (pseudonymous, no login required). This guide covers when to use each, how they work, and billing attribution.

Identity modes

Authenticated mode

Authenticated users are the people who are signed in to your app. Chipp does not show its own login. Your project’s session route (POST /api/embedded-agent/session, generated by the pack) requires your app’s login, reads the current user, and mints a chat session for that user’s id, email, and name.

When to use:

  • SaaS applications with registered user accounts
  • When you want to attribute conversations to a specific person
  • When users have account-specific context (order history, preferences, etc.)

Flow:

  1. User signs in to your app as usual
  2. User opens the chat panel or widget
  3. The component calls your session route, which mints a short-lived chat token on the server
  4. Conversations are attributed to that user’s email

Session TTL: chat tokens last 1 hour. The generated panel and widget mint a fresh token every 10 minutes while open, so a long chat keeps working. If the user signs out of your app, your session route stops minting tokens.

Conversation history: Conversations are stored under the user’s email. If the same user opens the chat from a different device, they see their conversation history.

Anonymous mode

Anonymous users are not required to log in. Instead, a pseudonymous visitor session is created for them.

When to use:

  • Public websites or landing pages
  • When you want low friction (no login required)
  • Floating widgets for general visitors
  • Evaluation / freemium experiences

Flow:

  1. User opens the chat panel or widget (no login prompt)
  2. The component calls POST /api/embedded-agent/session/anonymous, which mints a token for a new pseudonymous visitor
  3. Conversations begin immediately

Session TTL: 1 hour. The generated components mint a new anonymous session each time they load, so a page reload starts a new visitor and a new conversation.

Conversation history: By default, anonymous conversations are stored like any other conversation. A binding can instead be set to ephemeral retention, which keeps anonymous conversations in a short-lived store (up to 4 hours) and never writes them to the database. Choose this in the project’s Settings tab, under Embedded agent settings > Anonymous visitors. Anonymous access must be on for the binding; generating the integration in anonymous or both mode turns it on, and a binding without it rejects anonymous sessions.

Choosing a mode

ScenarioRecommendedWhy
SaaS app with user accountsAuthenticatedLink conversations to user identity; retain history
Public website / marketing siteAnonymousLow friction; no login required
Floating support widgetEitherAuthenticated if you want to track user history; anonymous for open access
Free trial / freemiumAnonymous, then AuthenticatedStart anonymous, upgrade when user signs up
Internal tool / employee appAuthenticatedLink to employee identity for compliance and support

Session upgrading (anonymous to authenticated)

A common pattern is to start users as anonymous (low friction) and upgrade them to authenticated when they sign up or log in. Choose the Both identity mode for this. The pack then generates the whole flow.

What “Both” mode generates

  • Session start: the panel and widget try POST /api/embedded-agent/session first. If the visitor is not signed in (401), they start an anonymous session with POST /api/embedded-agent/session/anonymous.
  • Upgrade route: POST /api/embedded-agent/session/upgrade in src/services/embedded-agent/router.ts. It requires your app’s login. The browser sends only { "anonymousToken" }. The route takes the user’s id, email, and name from your server session (never from the request body) and calls the platform’s upgrade endpoint through upgradeEmbedSession() in src/services/embedded-agent/client.ts. It returns { token, applicationId, expiresAt, runtimeBaseUrl, merged }.
  • Browser helpers in web/src/lib/embedded-agent/session-client.ts: upgradeEmbeddedAgentSession(anonymousToken) and notifyEmbeddedAgentSignedIn().

What you do when a visitor signs in

  • Sign-in with a page reload (a redirect or a form post): nothing. The components keep the anonymous token for the browser tab (sessionStorage) and upgrade it on the next load.
  • Sign-in without a reload (an SPA login): call notifyEmbeddedAgentSignedIn() right after login succeeds. Any anonymous panel or widget on the page upgrades and keeps the conversation.

After the upgrade, the components mint a normal signed-in session too, so the agent gets your app’s agent tools.

The upgrade endpoint requires a full-scope project client key. The Builder preview’s narrower key cannot upgrade, so in the preview the anonymous conversation does not carry over; the deployed app carries it over.

If your integration was generated in “Authenticated users only” or “Anonymous visitors only” mode, it has no upgrade route. Generate it again in “Both” mode to get one.

What happens after upgrade

  • The user’s anonymous conversation is merged into their authenticated identity (merged: true)
  • The anonymous token is revoked
  • Future messages are attributed to the authenticated email
  • If the user opens the chat from another device, they see the full conversation history
  • A missing or expired anonymousToken is not an error: the user still gets an authenticated session, with nothing to merge (merged: false)

Session endpoints (backend)

Your application’s backend calls these endpoints on CHIPP_PLATFORM_API_URL (default https://build.chipp.ai/alchemist-api). They are never exposed to the browser. Every response wraps its fields in data.

Create authenticated session

plaintext
POST /api/client/embed/session
Content-Type: application/json
Authorization: Bearer {projectClientKey}

{
  "externalUserId": "user_123",
  "email": "user@example.com",
  "name": "Ada"
}

externalUserId and email are required; name, workspaceId, and sessionScope are optional.

Returns:

json
{
  "data": {
    "token": "...",
    "expires_at": "2026-08-20T10:30:00Z",
    "application_id": "..."
  }
}

Create anonymous session

plaintext
POST /api/client/embed/session/anonymous
Content-Type: application/json
Authorization: Bearer {projectClientKey}

{}

Returns the same fields plus retention ("retained" or "ephemeral").

Upgrade anonymous to authenticated

plaintext
POST /api/client/embed/session/upgrade
Content-Type: application/json
Authorization: Bearer {projectClientKey}

{
  "anonymousToken": "...",
  "externalUserId": "user_123",
  "email": "user@example.com"
}

Returns the same fields plus merged (boolean).

Rotate session (refresh token)

plaintext
POST /api/client/embed/session/rotate
Content-Type: application/json
Authorization: Bearer {projectClientKey}

{
  "token": "..."
}

Returns a new token with a fresh TTL. An invalid, expired, or revoked token returns HTTP 400 with code invalid_session.

Revoke session

plaintext
POST /api/client/embed/session/revoke
Content-Type: application/json
Authorization: Bearer {projectClientKey}

{
  "token": "..."
}

Immediately invalidates the token and returns { "data": { "revoked": true } }. Revoking an already invalid token is not an error.

Upgrade, rotate, and revoke require a full-scope project client key.

Billing attribution

Usage is billed through the normal Chipp metering path. Every message sent to the bound agent is metered and attributed to the organization that owns the agent’s application.

How it works

  1. Your Alchemist project binds a Chipp agent (e.g., app_abc123)
  2. End users interact with that agent (via UI, backend client, or API)
  3. Chipp records each interaction (message, token count, etc.)
  4. The application owner’s organization (org_xyz) receives the invoice
  5. You see usage in Settings > Billing for that organization

Example

  • Organization: org_acme (your company, Acme Inc.)
  • Agent: app_support (customer support bot)
  • Bound to: Alchemist project proj_website
  • Users: 1,000 customers using the floating widget on www.acme.com
  • Billing: org_acme is billed for all 1,000 customer interactions (token usage, etc.)

Cost estimation

Costs depend on:

  • Number of messages: Each user message = 1 metered interaction
  • Token usage: Agent responses consume tokens based on model (GPT-4, Claude, etc.)
  • Custom actions: Tool calls and integrations may incur additional costs
  • Rate: Chipp’s standard metering rates apply (see Pricing)

To estimate costs, multiply:

plaintext
(messages per user) x (avg tokens per response) x (rate per 1K tokens) x (# users)

Cost control strategies

  1. Set reasonable timeouts - Prevent long-running requests that consume unnecessary tokens
  2. Monitor usage - Check the Chipp dashboard weekly to track token consumption
  3. Limit capabilities - Disable expensive features (file uploads, long knowledge bases) if unneeded
  4. Rate limiting - Implement application-level rate limits on chat endpoints
  5. Anonymous mode - For public websites, use anonymous mode to keep exploration costs low

Privacy & data retention

Authenticated sessions

  • User email and conversations are stored in Chipp
  • Conversations are attributed to the email address
  • User can request data export or deletion (contact support)

Anonymous sessions

  • No personally identifiable information is collected
  • Session ID is pseudonymous (not linked to user identity)
  • With the default retained setting, conversations are stored like any other; with ephemeral, they are kept only in a short-lived store
  • Inactive anonymous visitors are removed after 90 days
  • If upgraded to authenticated, the conversation is merged into the user’s identity

Multi-tenant applications

For multi-tenant SaaS (multiple customers sharing one Alchemist project), ensure proper data isolation:

  1. Use a distinct identity per user. Conversations are scoped to the user’s identity (keyed by email), so one user never sees another user’s threads.

  2. Pass tenant context per message with hostContext, using only keys you list in contextKeys in chipp/agent.json:

    typescript
    transport.sendMessage(threadId, 'Help me', {
      clientMessageId: crypto.randomUUID(),
      hostContext: { tenantPlan: 'enterprise' },
    });

    The runtime drops any key not on that list. Never put personal data in it.

  3. Scope prompts to tenant - Write the bound agent’s system prompt to respect tenant context

Usage is billed to the agent owner’s organization as a whole; there is no per-tenant billing breakdown.

GDPR / data deletion

If a user requests data deletion:

  1. Revoke their session - Invalidates the active token

    plaintext
    POST /api/client/embed/session/revoke
    { "token": "..." }
  2. Contact Chipp support - Request deletion of the user’s conversation threads

    • Specify the email address and date range
  3. Delete local cache - Remove any session tokens your app stored client-side

Troubleshooting

”User sees different conversation history on different devices”

  • In authenticated mode, all devices see the same history (email-based). Verify the email is consistent.
  • In anonymous mode, each page load starts a new visitor. This is expected behavior.
  • To fix, use authenticated mode.

”Session expired”

  • Tokens last 1 hour in both modes. The generated components re-mint authenticated tokens every 10 minutes. If you mint tokens yourself, rotate before expiry: POST /api/client/embed/session/rotate
  • Anonymous: prompt users to sign in for persistent history.

”User upgraded from anonymous, but old conversation was lost”

  • With the generated integration, check the identity mode is “Both”, and that an SPA login calls notifyEmbeddedAgentSignedIn()
  • In the Builder preview the conversation never carries over (the preview key cannot upgrade); test on the deployed app
  • Verify the upgrade request sent the visitor’s current anonymous token
  • Check merged in the upgrade response: false means no valid anonymous token was presented
  • Each anonymous session is a separate visitor; upgrading one does not affect others

”Different organizations seeing each other’s conversations”

  • This should not happen (conversations are scoped by user and agent)
  • Contact Chipp support if you suspect a data leak

Next steps