Converse Logo
Developers

Authentication

Two types of keys for two different contexts. Use the right one for your use case.

Key Types

Client Safe

Publishable Key

pk_live_...

Safe to use in browsers, mobile apps, and any client code. Can only create voice/chat sessions — nothing else. Cannot read agent configs, make outbound calls, or access any admin APIs.

Server Only

Secret Key

sk_live_...

Permission-scoped server access. Read active agents, initiate and inspect calls, use chat, register webhooks, and mint voice sessions. NEVER use in client code — only in your backend (env vars, server SDK).

Client SDK — Publishable Key

The simplest way to integrate. Drop in your publishable key and agent ID — done. The key is safe to expose because it can only create sessions for agents YOU configured on the platform.

tsx
import { useConverseVoice, ConverseOrb } from '@converse/sdk/components';

function VoiceAgent() {
  const { agentState, voiceState, connect, disconnect } = useConverseVoice({
    publicKey: 'pk_live_abc123...',  // Safe in client code
    agentId: 'agent_xxx',
  });

  return (
    <ConverseOrb
      state={agentState}
      size="lg"
      onClick={voiceState === 'idle' ? connect : disconnect}
    />
  );
}

What can a publishable key do?

Create voice sessions and chat sessions — that's it. It cannot list agents, read prompts, make outbound calls, access recordings, or do anything administrative. A copied key can still create billable sessions. Configure exact allowed browser origins, keep the built-in rate limit, and use a backend token provider when you need user-level authorization.

Client SDK — Token Provider

For extra control (e.g., you want to authenticate YOUR user before allowing a call), use a token provider. Your backend validates the user, then generates a short-lived session token.

Your backend

typescript
import { getVoiceToken } from '@converse/sdk/mobile';

app.post('/api/voice-token', async (req, res) => {
  // Your auth: verify the user is logged in, has credits, etc.
  if (!req.user) return res.status(401).json({ error: 'Unauthorized' });

  const session = await getVoiceToken(req.body.agentId, {
    apiKey: process.env.CONVERSE_API_KEY!,
    allowedAgentIds: ['agent_xxx'],
  });
  res.json(session);
});

Your client

tsx
const { connect } = useConverseVoice({
  agentId: 'agent_xxx',
  tokenProvider: async () => {
    const res = await fetch('/api/voice-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + userToken },
      body: JSON.stringify({ agentId: 'agent_xxx' }),
    });
    return res.json();
  },
});

Server SDK — Secret Key

Your backend uses a permission-scoped secret key for the public server API. Agent authoring, channels, and CRM administration remain dashboard operations.

typescript
import { ConverseClient } from '@converse/sdk';

// Secret key in env var — never in client code
const converse = new ConverseClient({ apiKey: process.env.CONVERSE_API_KEY! });

const call = await converse.calls.create({
  agent_id: 'your-agent-uuid',
  to: '+919876543210',
  metadata: { task: 'Confirm the appointment' },
});
const agents = await converse.agents.list();

Security Summary

pk_live_...

Where: Browser or app client

Can do: Create voice/chat sessions only

Risk: Billable if copied; restrict browser origins and rotate when exposed

sk_live_...

Where: Server ONLY (env vars)

Can do: Only the permissions granted to that key

Risk: High — treat it as a credential and rotate on exposure

tokenProvider

Where: Voice client calls your backend

Can do: One short-lived voice session after your checks

Risk: Lowest when your backend enforces user authorization and quotas