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_...

Full account access. Manage agents, initiate calls, access recordings, generate tokens. 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. Even if someone extracts it from your bundle, the worst they can do is start a session with your agent (which costs you a call, but exposes nothing).

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 the secret key for full API access. Manage agents, calls, numbers, and contacts.

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: 'agent_xxx', to: '+91...' });
const agents = await converse.agents.list();

Security Summary

pk_live_...

Where: Browser, mobile, client code

Can do: Create voice/chat sessions only

Risk: Low — someone could start calls to your agent

sk_live_...

Where: Server ONLY (env vars)

Can do: Full account access

Risk: High — full control of your account

tokenProvider

Where: Client calls your backend

Can do: Whatever you authorize

Risk: None — you control access