Pinecall

Quickstart

From zero to a working voice agent in under 5 minutes.

1. Install#

npm install @pinecall/sdk

Node.js ≥ 18. The only runtime dependency is ws.

2. Get an API key#

Sign up at pinecall.io, grab your API key from the dashboard, and export it:

export PINECALL_API_KEY=pk_...

3. Deploy your first agent#

Create agent.js:

import { Pinecall } from "@pinecall/sdk";

const pc = new Pinecall({ apiKey: process.env.PINECALL_API_KEY });
await pc.connect();

const mara = pc.deploy("mara", {
  prompt: "You are Mara, a friendly voice assistant. Be concise.",
  model: "gpt-4.1-mini",
  voice: "elevenlabs:EXAVITQu4vr4xnSDxMaL",
  language: "en",
  channels: ["webrtc"],
});

mara.on("call.started", (call) => call.say("Hello! How can I help?"));
mara.on("call.ended", (call, reason) => {
  console.log(`Call ended: ${reason} (${call.duration}s)`);
});

console.log("Mara is live. Connect a browser via the widget to talk to her.");

4. Run it#

node agent.js

You should see:

Mara is live. Connect a browser via the widget to talk to her.

That's a running voice agent. It's connected to Pinecall's voice server, it has a personality, and it's waiting for someone to call.

5. Talk to it#

Connect from the browser using the @pinecall/voice-widget:

npm install @pinecall/voice-widget
import { VoiceWidget } from "@pinecall/voice-widget";

export default function App() {
  return (
    <VoiceWidget
      agent="mara"
      tokenProvider={async () => {
        const res = await fetch("/api/token");
        return res.json();
      }}
    />
  );
}

You'll need a tiny backend endpoint to mint the token:

app.get("/api/token", async (req, res) => {
  const token = await mara.createToken("webrtc");
  res.json(token);
});

Click the widget, talk to Mara. She'll respond.

What just happened#

You created an agent (mara), gave her a personality, exposed her over WebRTC, and connected a browser to her. The server handles STT (you speak → text), runs the LLM (text → response), and handles TTS (response → voice).

You didn't write a single line of WebSocket code, audio handling, or VAD logic. The SDK and the Pinecall server handle all of that.

Add a phone number#

Want Mara to answer phone calls too? Add a phone channel:

const mara = pc.deploy("mara", {
  // ...same as before
  channels: ["webrtc", "+13186330963"],
});

Now the same agent serves browser and phone calls. The events are identical — your code doesn't need to know which transport the call came in over.

Add a tool#

Tools are just local functions:

const mara = pc.deploy("mara", {
  prompt: "You are Mara. Look up orders when asked.",
  model: "gpt-4.1-mini",
  voice: "elevenlabs:EXAVITQu4vr4xnSDxMaL",
  language: "en",
  channels: ["webrtc"],
  tools: [
    {
      type: "function",
      function: {
        name: "lookupOrder",
        description: "Look up an order by ID",
        parameters: {
          type: "object",
          properties: { orderId: { type: "string" } },
          required: ["orderId"],
        },
      },
    },
  ],
});

mara.on("llm.tool_call", async (data, call) => {
  const results = [];
  for (const tc of data.toolCalls) {
    const args = JSON.parse(tc.arguments);
    const order = await db.orders.findOne(args.orderId); // your DB
    results.push({ toolCallId: tc.id, result: order });
  }
  call.toolResult(data.msgId, results);
});

No webhook URL to expose. No public endpoint. Just a function that runs in your process.

Where to go next#