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 (email-based, with login) 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 log in with their email address via OTP (one-time password).

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 clicks “Chat” or opens the chat panel
  2. If not logged in, they enter their email
  3. Chipp sends an OTP to that email
  4. User enters the OTP (valid for 10 minutes)
  5. Session is created, attributed to that email
  6. Conversations are retained across browser sessions (until logout)

Session TTL: 8 hours per device. After logout or expiry, the user must re-authenticate.

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

Anonymous mode

Anonymous users are not required to log in. Instead, a pseudonymous visitor session is created and stored in the browser.

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. A pseudonymous session ID is generated and stored in localStorage/sessionStorage
  3. Conversations begin immediately
  4. Subsequent messages in the same browser use the same session ID
  5. Conversations are retained for the duration of the session

Session TTL: 1 hour per session. If a user closes the browser or the session expires, the next chat is a new conversation.

Conversation history: Conversations are NOT permanently stored. They exist only for the duration of the session. If you need persistent history, upgrade to authenticated mode (see “Session upgrading” below).

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 → AuthenticatedStart anonymous, upgrade when user signs up
Internal tool / employee appAuthenticatedLink to employee identity for compliance and support

Session upgrading (anonymous → authenticated)

A common pattern is to start users as anonymous (low friction) and upgrade them to authenticated when they sign up or log in.

Frontend upgrade trigger

When a user completes authentication in your application, call the session upgrade endpoint:

typescript
// User just signed up or logged in
const userId = user.email;  // Or their ID

// Get the current anonymous session ID (stored by the chat panel)
const anonSessionId = getStoredSessionId();  // Implementation depends on your setup

// Upgrade to authenticated
const response = await fetch('/api/chat/session/upgrade', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    currentSessionId: anonSessionId,
    externalUserId: userId,
  }),
});

const { token, threadId } = await response.json();

// Update the chat panel's session
updateChatSession(token, threadId);

Backend upgrade handler

Implement /api/chat/session/upgrade in your backend:

typescript
import { ChippEmbedServerClient } from '@chipp/embed-client/server';

const client = new ChippEmbedServerClient({
  baseUrl: process.env.ALCHEMIST_API_URL,
  projectClientKey: process.env.ALCHEMIST_PROJECT_CLIENT_KEY,
});

app.post('/api/chat/session/upgrade', async (req) => {
  const { currentSessionId, externalUserId } = req.body;
  
  try {
    const response = await client.upgradeSession({
      currentAnonymousSessionId: currentSessionId,
      userId: externalUserId,
    });
    
    return res.json({
      token: response.token,
      threadId: response.threadId,  // Conversation is preserved
    });
  } catch (error) {
    return res.status(400).json({ error: error.message });
  }
});

What happens after upgrade

  • The user’s anonymous conversation is merged into their authenticated session
  • Future messages are attributed to the authenticated email
  • The user’s conversation history is now permanent (not lost when the session expires)
  • If the user logs out and logs back in from any device, they see the full conversation history

Session endpoints (backend)

Your application’s backend uses these endpoints (never exposed to the browser) to manage sessions:

Create authenticated session

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

{
  "externalUserId": "user@example.com",
  "metadata": { "plan": "pro", "account_id": "123" }
}

Returns:

json
{
  "token": "embed_session_...",
  "expiresAt": "2024-08-20T10:30:00Z",
  "threadId": "thread_..."
}

Create anonymous session

plaintext
POST /api/client/embed/session/anonymous
Authorization: Bearer {projectClientKey}

Returns:

json
{
  "token": "embed_session_...",
  "expiresAt": "2024-08-20T10:30:00Z"
}

Upgrade anonymous to authenticated

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

{
  "currentSessionId": "embed_session_...",
  "externalUserId": "user@example.com"
}

Rotate session (refresh token)

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

{
  "currentSessionId": "embed_session_..."
}

Returns a new token with a fresh TTL.

Revoke session

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

{
  "sessionId": "embed_session_..."
}

Immediately invalidates the session. The user must re-authenticate.

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 the Chipp dashboard → Billing under 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 Billing)

To estimate costs, multiply:

plaintext
(messages per user) × (avg tokens per response) × (rate per 1K tokens) × (# 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 permanently stored in Chipp
  • Conversations are attributed to the email address indefinitely
  • 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)
  • Conversations expire after 1 hour (not permanently stored unless upgraded)
  • If upgraded to authenticated, the conversation is merged and retained permanently

Multi-tenant applications

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

  1. Pass tenant context in metadata:

    typescript
    await client.sendMessageAndWait({
      userId: user.email,
      message: 'Help me',
      metadata: { tenant_id: user.tenantId },  // ← Isolate by tenant
    });
  2. Scope prompts to tenant — Train the bound agent’s system prompt to respect tenant context

  3. Monitor billing per tenant — Use the metadata.tenant_id field to track costs

GDPR / data deletion

If a user requests data deletion:

  1. Revoke their session — Invalidates any active sessions

    typescript
    await client.revokeSession(sessionId);
  2. Contact Chipp support — Request deletion of the user’s conversation threads

    • Specify the email address and date range
    • Conversations are permanently deleted
  3. Delete local cache — Remove session tokens from localStorage (if 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 device/browser has a separate session. This is expected behavior.
  • To fix, use authenticated mode or provide a login-to-sync feature.

”Session expired; user was logged out unexpectedly”

  • Authenticated: TTL is 8 hours. If longer, rotate the token before expiry: POST /api/client/embed/session/rotate
  • Anonymous: TTL is 1 hour. Prompt users to upgrade to authenticated for longer sessions.

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

  • Verify the upgrade endpoint used the same session ID as the active session
  • If a new anonymous session was created (e.g., in a different tab), they are separate conversations
  • Sessions are unique per browser; upgrading one session does not affect others

”Different organizations seeing each other’s conversations”

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

Next steps