Skip to main content

Server SDK quickstart

This page shows you how to build a server that starts and stops a voice AI agent using the Agora Conversational AI SDKs. The SDKs wrap the Conversational AI REST API with typed vendor configuration, automatic token generation, and a session lifecycle API.

Prerequisites

Before you begin, make sure you have:

  • Node.js 18 or later.

Install the SDK


_1
npm install agora-agent-server-sdk

The package name on npm is agora-agent-server-sdk. Imports use agora-agent-sdk.

Set up environment variables

Store credentials in a .env file. Never commit this file or expose its contents client-side; your App Certificate must stay on the server.

Sync with Console to see your credentials

AGORA_APP_ID=<your_agora_app_id> AGORA_APP_CERT=<your_agora_app_cert> # ASR,LLM and TTS vendor keys DEEPGRAM_API_KEY=<your_deepgram_key> OPENAI_API_KEY=<your_openai_key> ELEVENLABS_API_KEY=<your_elevenlabs_key> ELEVENLABS_VOICE_ID=<your_voice_id>

Implementation

The following steps set up a client, configure an agent, and manage session lifecycle.

Create a client

AgoraClient holds your credentials and handles authentication. Pass your App ID and App Certificate to use app-credentials mode. The SDK auto-generates a temporary token scoped to each channel before every request.


_7
import { AgoraClient, Area } from 'agora-agent-sdk';
_7
_7
const client = new AgoraClient({
_7
area: Area.US, // Sets the regional API endpoint
_7
appId: process.env.AGORA_APP_ID!,
_7
appCertificate: process.env.AGORA_APP_CERT!,
_7
});

Use the region closest to your users: Area.US, Area.EU, Area.AP, or Area.CN.

Define an agent

Agent is a reusable configuration object. Define it once at startup and call createSession() on it for each user conversation.


_23
import { Agent, OpenAI, ElevenLabsTTS, DeepgramSTT } from 'agora-agent-sdk';
_23
_23
const agent = new Agent({
_23
name: 'support-assistant',
_23
instructions: 'You are a helpful voice assistant. Keep responses concise.', // LLM system prompt
_23
greeting: 'Hello! How can I help you today?', // the first thing the agent says
_23
maxHistory: 20,
_23
})
_23
.withLlm(new OpenAI({
_23
apiKey: process.env.OPENAI_API_KEY!,
_23
model: 'gpt-4o-mini',
_23
}))
_23
.withTts(new ElevenLabsTTS({
_23
key: process.env.ELEVENLABS_API_KEY!,
_23
modelId: 'eleven_flash_v2_5',
_23
voiceId: process.env.ELEVENLABS_VOICE_ID!,
_23
sampleRate: 24000,
_23
}))
_23
.withStt(new DeepgramSTT({
_23
apiKey: process.env.DEEPGRAM_API_KEY!,
_23
model: 'nova-2',
_23
language: 'en-US',
_23
}));

For the full list of Agent options and vendor classes, see the SDK reference.

Create and start a session

Bind the agent to a specific RTC channel, register event listeners, then start the session.

Register listeners before starting the session. Otherwise you may miss the started event.


_24
import { ExpiresIn } from 'agora-agent-sdk';
_24
_24
const session = agent.createSession(client, {
_24
channel: 'your-channel-name',
_24
agentUid: '1',
_24
remoteUids: ['100'],
_24
idleTimeout: 120,
_24
expiresIn: ExpiresIn.hours(1),
_24
});
_24
_24
// Register before start()
_24
session.on('started', ({ agentId }) => {
_24
console.log('Agent is live:', agentId);
_24
});
_24
_24
session.on('stopped', ({ agentId }) => {
_24
console.log('Agent has left:', agentId);
_24
});
_24
_24
session.on('error', (err) => {
_24
console.error('Session error:', err);
_24
});
_24
_24
const agentId = await session.start();

start() transitions the session through idle → starting → running states and returns the agentId. The agentId is the same value as the agent_id returned by the REST API.

You can check the session state at any time with session.status.

Interact with a running session

Once a session is in the running state, you can instruct the agent or update its configuration mid-session.


_13
// Make the agent speak a specific message
_13
await session.say('One moment while I look that up for you.');
_13
_13
// Interrupt the agent mid-speech
_13
await session.interrupt();
_13
_13
// Update agent configuration without restarting the session.
_13
// Accepts a partial properties object in REST API format.
_13
await session.update({
_13
llm: {
_13
system_messages: [{ role: 'system', content: 'You are now in concise mode.' }],
_13
},
_13
});

Stop the session

Call the session stop method when the user ends the conversation. This transitions the session through running → stopping → stopped states and removes the agent from the channel.


_1
await session.stop();

Complete example

The following is a minimal server your client app can call to manage agent sessions.

Express server example

_97
import 'dotenv/config';
_97
import express from 'express';
_97
import {
_97
AgoraClient,
_97
Area,
_97
Agent,
_97
ExpiresIn,
_97
OpenAI,
_97
ElevenLabsTTS,
_97
DeepgramSTT,
_97
AgoraError,
_97
} from 'agora-agent-sdk';
_97
_97
const app = express();
_97
app.use(express.json());
_97
_97
// Initialized once at startup; shared across all requests
_97
const client = new AgoraClient({
_97
area: Area.US,
_97
appId: process.env.AGORA_APP_ID!,
_97
appCertificate: process.env.AGORA_APP_CERT!,
_97
});
_97
_97
const agent = new Agent({
_97
name: 'support-assistant',
_97
instructions: 'You are a helpful voice assistant. Keep responses concise.',
_97
greeting: 'Hello! How can I help you today?',
_97
maxHistory: 20,
_97
})
_97
.withLlm(new OpenAI({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o-mini' }))
_97
.withTts(new ElevenLabsTTS({
_97
key: process.env.ELEVENLABS_API_KEY!,
_97
modelId: 'eleven_flash_v2_5',
_97
voiceId: process.env.ELEVENLABS_VOICE_ID!,
_97
sampleRate: 24000,
_97
}))
_97
.withStt(new DeepgramSTT({ apiKey: process.env.DEEPGRAM_API_KEY!, model: 'nova-2', language: 'en-US' }));
_97
_97
// Maps agentId → session object.
_97
// In production, use a database or Redis so any server process can stop any session.
_97
const sessions = new Map<string, ReturnType<typeof agent.createSession>>();
_97
_97
// POST /sessions/start
_97
// Called by your client app after the user joins the Agora channel.
_97
// Body: { channel: string, userId: string }
_97
// Returns: { agentId: string }
_97
app.post('/sessions/start', async (req, res) => {
_97
const { channel, userId } = req.body;
_97
_97
const session = agent.createSession(client, {
_97
channel,
_97
agentUid: '1',
_97
remoteUids: [userId],
_97
idleTimeout: 120,
_97
expiresIn: ExpiresIn.hours(1),
_97
});
_97
_97
session.on('error', (err) => console.error(`Session error [${channel}]:`, err));
_97
_97
try {
_97
const agentId = await session.start();
_97
sessions.set(agentId, session);
_97
res.json({ agentId });
_97
} catch (err) {
_97
if (err instanceof AgoraError) {
_97
res.status(err.statusCode).json({ error: err.message });
_97
} else {
_97
res.status(500).json({ error: 'Failed to start agent' });
_97
}
_97
}
_97
});
_97
_97
// POST /sessions/stop
_97
// Called by your client app when the user ends the conversation.
_97
// Body: { agentId: string }
_97
app.post('/sessions/stop', async (req, res) => {
_97
const { agentId } = req.body;
_97
const session = sessions.get(agentId);
_97
_97
if (!session) {
_97
return res.status(404).json({ error: 'Session not found' });
_97
}
_97
_97
try {
_97
await session.stop();
_97
sessions.delete(agentId);
_97
res.json({ status: 'stopped' });
_97
} catch (err) {
_97
if (err instanceof AgoraError) {
_97
res.status(err.statusCode).json({ error: err.message });
_97
} else {
_97
res.status(500).json({ error: 'Failed to stop agent' });
_97
}
_97
}
_97
});
_97
_97
app.listen(3000, () => console.log('Server listening on port 3000'));