VoiceLayer
Dashboard

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.

terminal
pnpm add @voicelayer/sdk \
  @voicelayer/agents-plugin-deepgram \
  @voicelayer/agents-plugin-openai \
  @voicelayer/agents-plugin-silero \
  @voicelayer/agents-plugin-livekit

Write 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).

agent.ts
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.

terminal
# .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 start

The 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.sendDtmf

React to or send keypad digits.

ctx.memory.get / set

Per-caller memory that survives across calls.

ctx.process

Captured 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 detection
🧪

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