Skip to content

19PINE-AI/pine-voice-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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("you@example.com");

// Step 2: Enter the code from your email
const { accessToken, userId } = await PineVoice.auth.verifyCode(
  "you@example.com",
  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

About

TypeScript SDK for Pine Voice — make real phone calls, handle IVR, and get full transcripts from any Node.js app

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors