Pinecall

Agents and Channels

The mental model: how Pinecall, Agent, Channel, and Call fit together.

The four nouns#

The four nouns — Pinecall, Agent, Channel, Call

Pinecall — the client#

One per process. Owns the WebSocket connection to voice.pinecall.io, handles auth and reconnection, and multiplexes events across multiple agents. Auto-connects on construction.

const pc = new Pinecall(); // reads PINECALL_API_KEY from env, connects automatically

Agent — a personality#

A configured assistant. Has a name (the agent ID), a voice, an STT provider, an LLM config, and a list of tools. Listens for events; owns channels.

const agent = pc.agent("support", {
  voice: "elevenlabs/sarah",
  language: "en",
  llm: "openai/gpt-5-chat-latest",
  stt: "deepgram/flux",
  prompt: "...",
});

You can have many agents on the same Pinecall instance — support, sales, intake — each with their own personality and channels.

Channel — a way to reach the agent#

A surface through which calls arrive. Some channels need explicit registration; others work automatically:

// Phone number — declared in config
const agent = pc.agent("support", {
  phoneNumber: "+13186330963",
  whatsapp: [{ phoneNumberId: "123", accessToken: "..." }],
});

// Or imperatively:
agent.addPhoneNumber("+13186330963");
agent.addWhatsapp({ phoneNumberId: "123", accessToken: "..." });

// WebRTC + Chat: work via tokens, no registration needed
const token = await agent.createToken("webrtc");

Channel types:

TypeRegistrationHow users connect
phonephoneNumber: "+1..."Call the number
whatsappwhatsapp: [{...}]Send a WhatsApp message
webrtcNone (automatic)Browser widget + token
chatNone (automatic)WebSocket + token

Call — a live session#

Created automatically when someone connects on a channel. You receive a Call object in the call.started event. Use it to:

  • Speak (call.say, call.reply, call.replyStream)
  • Control the call (call.hangup, call.forward, call.hold)
  • Update mid-call (call.update, call.setPrompt, call.addContext)
  • Read state (call.transcript, call.from, call.duration)

Creating an agent#

With phoneNumber (declarative)#

Pass a phone number directly in the config:

const mara = pc.agent("mara", {
  voice: "elevenlabs/sarah",
  language: "es",
  llm: "openai/gpt-5-chat-latest",
  stt: "deepgram/flux",
  prompt: "You are Mara. Be concise.",
  phoneNumber: "+13186330963",
});

WebRTC and Chat work automatically — no declaration needed. Just create tokens.

With agent.addPhoneNumber() (imperative)#

Use agent.addPhoneNumber() when you need per-number config overrides:

const mara = pc.agent("mara", {
  voice: "elevenlabs/sarah",
  language: "es",
  llm: "openai/gpt-5-chat-latest",
  stt: "deepgram/flux",
  prompt: "You are Mara. Be concise.",
});

mara.addPhoneNumber("+13186330963", {
  voice: "elevenlabs/daniel",
});

Per-number config overrides#

The agent has defaults. Each phone number can override voice, language, and STT. This is how you give the same agent different settings per number:

const agent = pc.agent("support", {
  llm: "openai/gpt-5-chat-latest",
  stt: "deepgram/flux",
  voice: "elevenlabs/sarah",
  phoneNumber: { number: "+34911234567", voice: "elevenlabs/valentina", language: "es" },
});

For multiple numbers with different overrides — including STT provider — use phoneNumbers:

const agent = pc.agent("global-support", {
  prompt: "You are a multilingual support agent.",
  llm: "openai/gpt-5-chat-latest",
  phoneNumbers: [
    { number: "+14155551234", language: "en", voice: "elevenlabs/sarah", stt: "deepgram/flux" },
    { number: "+34612345678", language: "es", voice: "elevenlabs/valentina", stt: "deepgram/flux" },
    // Arabic requires Nova-3 (Flux doesn't support it)
    { number: "+972501234567", language: "ar", voice: "elevenlabs/ahmad", stt: "deepgram/nova-3" },
  ],
});

The agent's prompt, tools, and LLM stay the same — only the audio surface changes per number. Turn detection and VAD are auto-derived from the STT provider (Flux → native, Nova-3 → smart_turn + silero).

Why this design#

The agent-and-channels split exists because voice agents have two completely different concerns:

  1. Who the agent is — personality, knowledge, tools, business logic
  2. How users reach it — a phone number, a SIP trunk, a browser widget, a WhatsApp chat

Most platforms conflate the two: you build a "Twilio bot" or a "WhatsApp bot." Pinecall keeps them separate so you can build the agent once and expose it through whatever channel you need today (or tomorrow).

What's next#