Build / SDK
One file, one agent
The code-first path: define an agent — prompt, tools, hooks — in a single TypeScript file and start it. The SDK runs the voice pipeline and registers the worker with your VoiceLayer project; the dashboard picks it up from there.
Install
@voicelayer/sdk plus a plugin per provider you use — published on npm under Apache-2.0.
pnpm add @voicelayer/sdk \
@voicelayer/agents-plugin-deepgram \
@voicelayer/agents-plugin-openai \
@voicelayer/agents-plugin-silero \
@voicelayer/agents-plugin-livekitWrite the agent
A complete agent: a prompt, a tool the LLM can call, and a hook that fires on every utterance. Tools declare description, input, and an async run(args, ctx).
import { Agent } from '@voicelayer/sdk';
export default await new Agent({
name: 'fnol-simple',
prompt:
'You are a calm First Notice of Loss intake agent. ' +
'Gather: incident date, vehicles involved, injuries, police report. ' +
'If the caller mentions an attorney, transfer immediately.',
capabilities: ['fnol', 'inbound-sip'],
tools: {
transferToHuman: {
description: 'Transfer the caller to a human claims agent.',
input: { reason: 'string' },
run: async ({ reason }, ctx) => {
await ctx.handoff('+15555550100', { reason });
return 'transferred';
}
}
},
async onUtterance(text, ctx) {
if (/attorney|lawyer|lawsuit/i.test(text)) {
await ctx.handoff('+15555550100', { reason: 'legal_threat' });
}
}
}).start(import.meta.url);Run it
.start(import.meta.url) boots a worker in-process and registers it with the control plane. Within ~90 seconds of heartbeat it shows in your Agents list — test it in the Playground, attach a number when ready.
# .env
VOICELAYER_API_KEY=vl_… # Dashboard → Settings → API keys
LIVEKIT_URL=wss://…
LIVEKIT_API_KEY=…
LIVEKIT_API_SECRET=…
DEEPGRAM_API_KEY=… # keys for the providers your agent uses
OPENAI_API_KEY=…
# develop with hot reload
node --import tsx agent.ts dev
# production
node agent.ts startThe call context ctx
Every tool and hook receives ctx — the live call.
ctx.say(text)Speak a line via TTS.
ctx.ask(question)Ask and resolve with the caller’s transcribed answer.
ctx.respond(instructions?)Let the LLM compose the next reply.
ctx.handoff(to, opts)Transfer to a human — warm or cold SIP.
ctx.endCall(opts)Hang up, with guaranteed telephony teardown.
ctx.onDtmf / ctx.sendDtmfReact to or send keypad digits.
ctx.memory.get / setPer-caller memory that survives across calls.
ctx.processCaptured fields and completion state of the business process.
ctx.connectors.*Typed access to your connected systems.
ctx.recordEvent(event)Emit events onto the call timeline in the dashboard.
Lifecycle hooks
Attach behavior without owning the loop — or take the loop over entirely.
onUtterance(text, ctx)Every caller transcription.
onFieldCaptured(field, value, ctx)A process field was extracted.
onCallEnd(outcome, ctx)Call teardown.
onQuery(text)Bring your own brain — return (or stream) the reply text yourself.
onCall(ctx)Full control: own the entire conversation loop.
Provider plugins
Each provider ships as an optional peer — install only what your pipeline uses.
@voicelayer/agents-plugin-deepgramSTT · TTS@voicelayer/agents-plugin-openaiLLM · TTS · Realtime@voicelayer/agents-plugin-googleLLM · Realtime@voicelayer/agents-plugin-cartesiaTTS · STT@voicelayer/agents-plugin-elevenlabsTTS@voicelayer/agents-plugin-assemblyaiSTT@voicelayer/agents-plugin-sileroVAD@voicelayer/agents-plugin-livekitTurn detectionTest without a phone. testAgent() from @voicelayer/sdk/testing runs your agent over text in CI — assert on transcripts, tool calls, and captured fields before anything dials.