VoiceLayer
Dashboard

Ship / Deploy & hosting

How an agent actually runs

Two ways to put an agent on a phone line. One needs no infrastructure from you; the other runs a process you own. Knowing which you are on explains every credential question.

The short answer. Building in the flow builder means you never touch LiveKit or provider keys — the platform runs it. Writing an agent with the SDK means you run the worker process, so that process needs credentials.

The two paths

Who runs itFlow builderThe platform (shared runtime)SDK workerYou — a process you keep running
LiveKit keysFlow builderNot neededSDK workerRequired
Provider keysFlow builderNot neededSDK workerRequired (OpenAI, Deepgram, …)
Custom codeFlow builderTool nodes call your HTTP APISDK workerAnything you can write
Ship withFlow builderGo liveSDK workerRun the worker + attach a number

Path A — flow builder no infrastructure

The platform already runs a pool of workers that are connected to telephony and to the model providers. Deploying a flow points that pool at your published version — there is nothing to host and no keys to manage.

  1. 1

    Build the flow

    Compose it on the canvas and press Save. Nothing runs yet.

  2. 2

    Publish

    Snapshots an immutable version. Validation errors block publish; warnings ride along.

  3. 3

    Go live

    Save + publish + point the shared runtime at this version, in one step. The agent now appears in Agents.

  4. 4

    Attach a number

    Numbers → assign the number to the agent. Inbound calls to that number now run this flow.

🔌

Need custom logic — a lookup in your database, creating a ticket? You do not need the SDK for that. Add a Call an API step (or register an HTTP tool under Tools) that points at an endpoint you host. The platform calls it server-side, with the URL and credentials never leaving the backend.

Path B — SDK worker you host

.start() boots a worker process that connects to LiveKit and waits for calls dispatched to its agent name. That process is yours to run — locally while you develop, or as a long-running deployment in production.

.env — SDK worker
# Only needed for the SDK-worker path.
VOICELAYER_API_KEY=vl_...            # who you are
VOICELAYER_API_URL=https://api.vlayers.ai

LIVEKIT_URL=wss://<project>.livekit.cloud
LIVEKIT_API_KEY=API...
LIVEKIT_API_SECRET=...

OPENAI_API_KEY=sk-...                # provider keys the pipeline uses
DEEPGRAM_API_KEY=...

AGENT_ENVIRONMENT=prd                # MUST match where the agent is registered

Three things trip people up on this path, all of them silent:

.env is not read for you

.start() reads process.env and nothing else — in production the environment is injected. Locally, import a loader yourself or the worker exits with a missing-credentials error.

AGENT_ENVIRONMENT must match

An agent is identified by name + environment. If the worker boots as dev (the default) but the agent is registered in prd, you get two separate rows and calls go to neither.

Stopping the worker removes the agent

On SIGINT/SIGTERM the SDK deregisters so the dashboard reflects reality immediately. Pass { deregisterOnSignal: false } to keep the row.

agent.ts — loading .env locally
import 'dotenv/config'; // <- .start() does NOT read .env for you
import { defineAgent } from '@voicelayer/sdk';

export default await defineAgent({ name: 'support-desk', prompt: '…' })
  .start(import.meta.url);

Registered vs running

These are different things, and the difference explains most “why is my agent offline?” questions.

Registered — it exists

One authenticated call creates the agent record. It shows in Agents with its config. No worker, no LiveKit, no call-handling. The row persists until you delete it.

Running — it can answer

A live process heartbeats every ~30s. Miss the heartbeats for ~90s and the agent reads Offline — the record stays, it just can’t take a call. Flow agents mirror the shared pool’s health, so they are online whenever the platform is.

register without running a worker
import { createClient } from '@voicelayer/sdk';

const client = createClient({ apiKey: process.env.VOICELAYER_API_KEY });

await client.agents.register(
  { name: 'support-desk', version: '1.0.0', environment: 'prd' },
  { attachSignalHandlers: false }
);
🧭

Which should you choose? Start with the flow builder — it covers most agents and has nothing to operate. Reach for the SDK when you need logic the canvas can’t express, and remember you can mix: a flow can call your API for the hard parts.