npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pine-voice

v0.1.4

Published

Official Pine AI Voice SDK — make phone calls via Pine's AI voice agent

Readme

Pine Voice SDK for JavaScript / TypeScript

Official SDK for Pine AI voice calls. Make phone calls via Pine's AI voice agent from any Node.js application — no MCP client or OpenClaw required.

Install

npm install pine-voice

Quick start

import { PineVoice } from "pine-voice";

const client = new PineVoice({
  accessToken: "your-access-token",
  userId: "your-user-id",
});

const result = await client.calls.createAndWait({
  to: "+14155551234",
  name: "Dr. Smith Office",
  context:
    "Local dentist office. I'm an existing patient (Jane Doe, DOB 03/15/1990). " +
    "Open Mon-Fri 9am-5pm. Dr. Smith is my preferred dentist but Dr. Lee is also fine.",
  objective: "Schedule a dental cleaning for next Tuesday afternoon, ideally 2-4pm",
  instructions:
    "If Tuesday afternoon is unavailable, try Wednesday or Thursday afternoon. " +
    "If no afternoons are open this week, take the earliest available afternoon next week. " +
    "Confirm the appointment date, time, and dentist name before hanging up.",
});

console.log(result.transcript);

Authentication

Option A: Pass credentials directly

const client = new PineVoice({
  accessToken: "your-access-token",
  userId: "your-user-id",
});

Option B: Use environment variables

export PINE_ACCESS_TOKEN="your-access-token"
export PINE_USER_ID="your-user-id"
const client = new PineVoice(); // reads from env

Getting credentials

If you don't have credentials yet, use the auth helpers:

import { PineVoice } from "pine-voice";

// Step 1: Request a verification code (sent to your Pine AI account email)
const { requestToken } = await PineVoice.auth.requestCode("[email protected]");

// Step 2: Enter the code from your email
const { accessToken, userId } = await PineVoice.auth.verifyCode(
  "[email protected]",
  requestToken,
  "1234", // code from email
);

// Step 3: Use the credentials
const client = new PineVoice({ accessToken, userId });

Making calls

Fire and poll

// Initiate (returns immediately)
const { callId } = await client.calls.create({
  to: "+14155552345",
  name: "Bay Area Auto Care",
  context:
    "Local auto repair shop. My car is a 2019 Honda Civic, ~45,000 miles. " +
    "Due for a routine oil change and tire rotation. No warning lights or known issues.",
  objective:
    "Schedule an oil change and tire rotation for this Friday morning, ideally before noon",
  instructions:
    "If Friday morning is full, try Friday afternoon. " +
    "If Friday is completely booked, try next Monday or Tuesday morning. " +
    "Ask for a price estimate for both services combined. " +
    "Ask how long the service will take so I know when to pick up the car. " +
    "Confirm the appointment date, time, services, and estimated cost before hanging up.",
  caller: "communicator",
  voice: "female",
  maxDurationMinutes: 10,
});

// Poll until complete
const status = await client.calls.get(callId);

Call and wait (SSE with polling fallback)

const result = await client.calls.createAndWait(
  {
    to: "+14155559876",
    name: "Bella Italia Restaurant",
    context:
      "Italian restaurant in downtown SF. Reservation for Mike Chen. " +
      "Party of 4 adults, no children. One guest is vegetarian, one has a nut allergy.",
    objective: "Make a dinner reservation for tonight at 7pm for 4 people",
    instructions:
      "If 7pm is not available, try 7:30pm or 8pm. " +
      "If tonight is fully booked, try tomorrow (Saturday) at the same times. " +
      "Request a booth or quiet table if possible, but not required. " +
      "Mention the nut allergy and ask if they can accommodate it. " +
      "Confirm the reservation date, time, party size, and name on the reservation.",
  },
  {
    // SSE is used by default to wait for the final result.
    // Falls back to polling if SSE is unavailable.
    pollIntervalMs: 10_000, // polling fallback interval (default 10s)
    signal: abortController.signal, // optional cancellation
  },
);

console.log(result.status);         // "completed" | "failed" | "cancelled"
console.log(result.transcript);     // full conversation
console.log(result.summary);        // LLM summary (empty unless enableSummary: true)
console.log(result.creditsCharged); // credits used

Error handling

import { PineVoice, AuthError, RateLimitError, CallError } from "pine-voice";

try {
  const result = await client.calls.createAndWait({ ... });
} catch (err) {
  if (err instanceof AuthError) {
    // Token expired or invalid — re-authenticate
    console.error("Auth failed:", err.code, err.message);
  } else if (err instanceof RateLimitError) {
    // Too many calls — wait and retry
    console.error("Rate limited:", err.message);
  } else if (err instanceof CallError) {
    // Call-specific issue (invalid phone, DND, policy, etc.)
    console.error("Call error:", err.code, err.message);
  }
}

API reference

new PineVoice(config?)

| Option | Type | Default | Description | |---|---|---|---| | accessToken | string | PINE_ACCESS_TOKEN env | Pine access token | | userId | string | PINE_USER_ID env | Pine user ID | | gatewayUrl | string | https://agent3-api-gateway-staging.19pine.ai | Voice API gateway URL |

PineVoice.auth.requestCode(email)

Request a verification code. Returns { requestToken }.

PineVoice.auth.verifyCode(email, requestToken, code)

Verify the code and get credentials. Returns { accessToken, userId }.

client.calls.create(params)

Initiate a call. Returns { callId, status }.

| Param | Type | Required | Description | |---|---|---|---| | to | string | Yes | Phone number in E.164 format. Supported countries: US/CA/PR (+1), UK (+44), AU (+61), NZ (+64), SG (+65), IE (+353), HK (+852) | | name | string | Yes | Name of the person or business being called | | context | string | Yes | Background context about the callee and info needed during the call | | objective | string | Yes | Specific goal the call should accomplish | | instructions | string | No | Detailed strategy and instructions for the voice agent | | caller | "negotiator" \| "communicator" | No | Caller personality. "negotiator" for complex negotiations (requires thorough strategy in context/instructions). "communicator" for general tasks. Default: "negotiator" | | voice | "male" \| "female" | No | Voice gender. Default: "female" | | maxDurationMinutes | number | No | Max call duration in minutes (1-120). Default: 120 | | enableSummary | boolean | No | Request an LLM-generated summary after the call. Default: false. Most AI agents can process the full transcript directly, so the summary is opt-in to save latency and cost. |

client.calls.get(callId)

Get call status. Returns CallStatus or CallResult if terminal.

client.calls.createAndWait(params, options?)

Initiate and wait until complete. Returns CallResult.

Uses SSE to wait for the final call result. If the SSE connection fails or the server doesn't support it, automatically falls back to polling. Reconnects once on SSE connection drop before falling back.

Important: Real-time intermediate updates (partial transcripts, "call connected" events) are not currently available. The SSE stream delivers only the final transcript after the call completes. There are no intermediate progress events during the call.

| Option | Type | Default | Description | |---|---|---|---| | pollIntervalMs | number | 10000 | Polling interval in ms (fallback only) | | signal | AbortSignal | — | Abort signal for cancellation | | useSSE | boolean | true | Try SSE first. Set false to force polling. | | onProgress | (progress: CallProgress) => void | — | Callback invoked with a CallProgress object after each poll cycle during polling fallback. Note: real-time progress events are not currently available. |

Supported countries

The voice agent can only speak English. Calls can be placed to the following countries:

  • US, Canada, and Puerto Rico (+1)
  • United Kingdom (+44)
  • Australia (+61)
  • New Zealand (+64)
  • Singapore (+65)
  • Ireland (+353)
  • Hong Kong (+852)

Calls to numbers outside these country codes will be rejected with a POLICY_VIOLATION error.

Requirements

  • Node.js 18+ (uses native fetch)
  • Pine AI Pro subscription (19pine.ai)

License

MIT