@pinecall/skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/build.mjs +204 -0
- package/package.json +29 -0
- package/skills/pinecall-concepts/SKILL.md +41 -0
- package/skills/pinecall-concepts/references/concepts/agents-and-channels.md +155 -0
- package/skills/pinecall-concepts/references/concepts/deployment-topologies.md +120 -0
- package/skills/pinecall-concepts/references/concepts/hot-reload.md +119 -0
- package/skills/pinecall-concepts/references/concepts/philosophy.md +100 -0
- package/skills/pinecall-concepts/references/concepts/server-vs-client-llm.md +119 -0
- package/skills/pinecall-examples/SKILL.md +59 -0
- package/skills/pinecall-examples/references/examples/browser-widget.md +206 -0
- package/skills/pinecall-examples/references/examples/chat-bot.md +184 -0
- package/skills/pinecall-examples/references/examples/headless-agent.md +121 -0
- package/skills/pinecall-examples/references/examples/index.md +183 -0
- package/skills/pinecall-examples/references/examples/multi-channel-bot.md +173 -0
- package/skills/pinecall-examples/references/examples/outbound-dispatch.md +109 -0
- package/skills/pinecall-examples/references/examples/turn-detection.md +150 -0
- package/skills/pinecall-guides/SKILL.md +68 -0
- package/skills/pinecall-guides/references/guides/call-ringing.md +149 -0
- package/skills/pinecall-guides/references/guides/conversation-history.md +377 -0
- package/skills/pinecall-guides/references/guides/dev-mode.md +130 -0
- package/skills/pinecall-guides/references/guides/events.md +677 -0
- package/skills/pinecall-guides/references/guides/human-takeover.md +184 -0
- package/skills/pinecall-guides/references/guides/inbound-voice.md +201 -0
- package/skills/pinecall-guides/references/guides/knowledge-bases.md +166 -0
- package/skills/pinecall-guides/references/guides/live-listening.md +199 -0
- package/skills/pinecall-guides/references/guides/multi-tenant.md +158 -0
- package/skills/pinecall-guides/references/guides/outbound-calls.md +279 -0
- package/skills/pinecall-guides/references/guides/sse-streaming.md +207 -0
- package/skills/pinecall-guides/references/guides/testing-agents.md +272 -0
- package/skills/pinecall-guides/references/guides/tools-and-functions.md +254 -0
- package/skills/pinecall-guides/references/guides/webrtc-browser.md +200 -0
- package/skills/pinecall-guides/references/guides/whatsapp.md +370 -0
- package/skills/pinecall-guides/references/guides/ws-streaming.md +235 -0
- package/skills/pinecall-quickstart/SKILL.md +54 -0
- package/skills/pinecall-quickstart/references/index.md +123 -0
- package/skills/pinecall-quickstart/references/quickstart.md +185 -0
- package/skills/pinecall-reference/SKILL.md +43 -0
- package/skills/pinecall-reference/references/reference/cli.md +578 -0
- package/skills/pinecall-reference/references/reference/events.md +366 -0
- package/skills/pinecall-reference/references/reference/llm-providers.md +263 -0
- package/skills/pinecall-reference/references/reference/rest-api.md +122 -0
- package/skills/pinecall-reference/references/reference/session-limits.md +119 -0
- package/skills/pinecall-reference/references/reference/stt-providers.md +174 -0
- package/skills/pinecall-reference/references/reference/tts-providers.md +149 -0
- package/skills/pinecall-sdk-api/SKILL.md +56 -0
- package/skills/pinecall-sdk-api/references/api/agent.md +328 -0
- package/skills/pinecall-sdk-api/references/api/call.md +324 -0
- package/skills/pinecall-sdk-api/references/api/pinecall.md +186 -0
- package/skills/pinecall-sdk-api/references/api/reply-stream.md +148 -0
- package/skills/pinecall-security/SKILL.md +37 -0
- package/skills/pinecall-security/references/security.md +138 -0
- package/skills/pinecall-web-chat/SKILL.md +38 -0
- package/skills/pinecall-web-chat/references/web/chat/chat-session.md +178 -0
- package/skills/pinecall-web-chat/references/web/chat/overview.md +98 -0
- package/skills/pinecall-web-components/SKILL.md +37 -0
- package/skills/pinecall-web-components/references/web/components/overview.md +128 -0
- package/skills/pinecall-web-voice/SKILL.md +40 -0
- package/skills/pinecall-web-voice/references/web/core/datachannel-protocol.md +149 -0
- package/skills/pinecall-web-voice/references/web/core/overview.md +70 -0
- package/skills/pinecall-web-voice/references/web/core/state-and-phases.md +153 -0
- package/skills/pinecall-web-voice/references/web/core/voice-session.md +279 -0
- package/skills/pinecall-web-widget/SKILL.md +41 -0
- package/skills/pinecall-web-widget/references/web/widget/overview.md +67 -0
- package/skills/pinecall-web-widget/references/web/widget/props.md +291 -0
- package/skills/pinecall-web-widget/references/web/widget/theming.md +131 -0
- package/skills/pinecall-web-widget/references/web/widget/tools-api.md +381 -0
- package/skills/pinecall-web-widget/references/web/widget/use-voice-session-hook.md +130 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Outbound Dispatch"
|
|
3
|
+
description: "CSV-driven outbound campaign with rate limiting, dedup, and result writeback."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Outbound Dispatch
|
|
7
|
+
|
|
8
|
+
> **Source:** [`examples/outbound-dispatch/`](https://github.com/pinecall/sdk/tree/main/examples/outbound-dispatch)
|
|
9
|
+
|
|
10
|
+
This example builds a complete outbound campaign system that:
|
|
11
|
+
|
|
12
|
+
1. Reads leads from a CSV file
|
|
13
|
+
2. Dispatches personalized appointment reminder calls
|
|
14
|
+
3. Uses per-call `promptVars` to inject contact details into the AI prompt
|
|
15
|
+
4. Lets the AI confirm/cancel via a tool that writes results back to CSV
|
|
16
|
+
5. Handles no-answer, busy, and rejection automatically
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
- A Pinecall API key with outbound calling enabled
|
|
25
|
+
- A phone number (Twilio) registered in your Pinecall organization
|
|
26
|
+
- Node.js ≥ 18
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
cd examples/outbound-dispatch
|
|
32
|
+
cp .env.example .env
|
|
33
|
+
# Edit .env with your API key and phone
|
|
34
|
+
npm install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## CSV format
|
|
38
|
+
|
|
39
|
+
The CSV must have a header row. The dispatcher skips rows that already have a `status` value:
|
|
40
|
+
|
|
41
|
+
```csv
|
|
42
|
+
name,phone,service,date,time
|
|
43
|
+
Maria,+14155551234,Eye Exam,June 12,10:00 AM
|
|
44
|
+
Carlos,+14155559876,Physiotherapy,June 15,5:30 PM
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
After calls complete, the CSV is updated:
|
|
48
|
+
|
|
49
|
+
```csv
|
|
50
|
+
name,phone,service,date,time,status
|
|
51
|
+
Maria,+14155551234,Eye Exam,June 12,10:00 AM,confirmed
|
|
52
|
+
Carlos,+14155559876,Physiotherapy,June 15,5:30 PM,no_answer
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Key concepts
|
|
56
|
+
|
|
57
|
+
### Content-based dedup
|
|
58
|
+
|
|
59
|
+
Records are identified by `phone + service + date`, not by row index. Two identical CSV entries produce the same ID and are dispatched only once:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
mapRow: (row) => ({
|
|
63
|
+
id: `${row.phone}-${row.service}-${row.date}`,
|
|
64
|
+
// ...
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Phone-level dedup
|
|
69
|
+
|
|
70
|
+
The hub tracks active phones. If Bernardo has two appointments (Eye Exam + Physio), only one call runs at a time. The second dispatches automatically after the first ends.
|
|
71
|
+
|
|
72
|
+
### Prompt variables
|
|
73
|
+
|
|
74
|
+
Each call sends per-call context via `promptVars` on the record returned by `mapRow`:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
promptVars: {
|
|
78
|
+
appointment_details: `Name: ${row.name}\nService: ${row.service}\nDate: ${row.date}\nTime: ${row.time}`,
|
|
79
|
+
},
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
These replace `{{appointment_details}}` in the agent prompt.
|
|
83
|
+
|
|
84
|
+
### Lifecycle callbacks
|
|
85
|
+
|
|
86
|
+
Handle calls that end without the AI calling a tool (rejected, no answer):
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
csv.onCompleted = (record, callId, reason) => {
|
|
90
|
+
// Don't overwrite if the tool already wrote a status
|
|
91
|
+
writeResultToCsv(record.phone, record.service, reason);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
csv.onFailed = (record, error) => {
|
|
95
|
+
writeResultToCsv(record.phone, record.service, "no_answer");
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Dial rejection
|
|
100
|
+
|
|
101
|
+
`agent.dial()` rejects immediately when Twilio reports `busy`, `no-answer`, `failed`, or `canceled` — no 30-second timeout. The hub catches this in `onFailed`.
|
|
102
|
+
|
|
103
|
+
## Run
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node server.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Add rows to `data/leads.csv` while the script is running — the dispatcher detects new rows on every poll cycle (default 5s) and places calls automatically.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Example: Turn Detection"
|
|
3
|
+
description: "Debug turn events in real-time — per-turn containers showing the full state machine lifecycle."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Example: Turn Detection
|
|
7
|
+
|
|
8
|
+
A dev-friendly debug tool that shows **every turn event in real-time**, grouped into visual containers that mirror the server-side turn state machine. Each turn container shows state transitions as they happen.
|
|
9
|
+
|
|
10
|
+
## State machine
|
|
11
|
+
|
|
12
|
+
The server maintains a 5-state machine for every call:
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
## What you'll see
|
|
17
|
+
|
|
18
|
+
Each turn is rendered as a bordered container:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌ Turn #1 · IDLE → LISTENING 08:53:08.000
|
|
22
|
+
│ 🎙 speech.started
|
|
23
|
+
│ 💬 "Hola, ¿qué tal?"
|
|
24
|
+
│ 📝 "Hola. ¿Qué tal?"
|
|
25
|
+
│
|
|
26
|
+
│ LISTENING → BOT_PENDING prob=96%
|
|
27
|
+
│
|
|
28
|
+
│ BOT_PENDING → BOT_SPEAKING
|
|
29
|
+
│ 🤖 bot.speaking "..."
|
|
30
|
+
│ 🗣 "¡Hola! Estoy bien, gracias. ¿Y tú?"
|
|
31
|
+
│ 📨 message.confirmed
|
|
32
|
+
│ 🔇 bot.finished 3846ms
|
|
33
|
+
│
|
|
34
|
+
└ 4.2s
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Interruptions (barge-in)
|
|
38
|
+
|
|
39
|
+
When the user cuts off the bot, a highlighted interruption section appears:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌ Turn #3 · IDLE → LISTENING 08:54:01.000
|
|
43
|
+
│ 🎙 speech.started
|
|
44
|
+
│ 📝 "Cuéntame un cuento largo"
|
|
45
|
+
│
|
|
46
|
+
│ LISTENING → BOT_PENDING prob=95%
|
|
47
|
+
│
|
|
48
|
+
│ BOT_PENDING → BOT_SPEAKING
|
|
49
|
+
│ 🤖 bot.speaking "..."
|
|
50
|
+
│ 🗣 "Érase una vez, en un reino muy lejano..."
|
|
51
|
+
│
|
|
52
|
+
├─── ⚡ INTERRUPTION ─────────────────────────────
|
|
53
|
+
│ BOT_SPEAKING → LISTENING barge-in after 2100ms
|
|
54
|
+
│ 🗣 said: "Érase una vez, en un reino muy lejano..."
|
|
55
|
+
│ ↻ continuation — user keeps talking
|
|
56
|
+
│
|
|
57
|
+
│ 💬 "No, algo más corto"
|
|
58
|
+
│ 📝 "No, algo más corto"
|
|
59
|
+
│
|
|
60
|
+
│ LISTENING → BOT_PENDING prob=97%
|
|
61
|
+
│
|
|
62
|
+
│ BOT_PENDING → BOT_SPEAKING
|
|
63
|
+
│ 🤖 bot.speaking "..."
|
|
64
|
+
│ 🗣 "¡Claro! Había una vez un gato que..."
|
|
65
|
+
│ 🔇 bot.finished 3200ms
|
|
66
|
+
│
|
|
67
|
+
└ 12.4s
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## The code
|
|
71
|
+
|
|
72
|
+
The key pattern: a `turn` tracker object that maps SDK events to server states:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const turn = {
|
|
76
|
+
id: 0, state: "IDLE", startTime: null, open: false,
|
|
77
|
+
|
|
78
|
+
log(icon, detail) {
|
|
79
|
+
console.log(` │ ${icon} ${detail}`);
|
|
80
|
+
},
|
|
81
|
+
transition(to, extra = "") {
|
|
82
|
+
const arrow = `${this.state} → ${to}`;
|
|
83
|
+
this.state = to;
|
|
84
|
+
console.log(` │\n │ ${arrow} ${extra}\n │`);
|
|
85
|
+
},
|
|
86
|
+
start(turnId) {
|
|
87
|
+
this.id = turnId;
|
|
88
|
+
this.state = "LISTENING";
|
|
89
|
+
this.startTime = Date.now();
|
|
90
|
+
this.open = true;
|
|
91
|
+
console.log(`\n ┌ Turn #${this.id} · IDLE → LISTENING`);
|
|
92
|
+
},
|
|
93
|
+
end() {
|
|
94
|
+
const dur = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
|
95
|
+
this.state = "IDLE";
|
|
96
|
+
this.open = false;
|
|
97
|
+
console.log(` └ ${dur}s`);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Map events to state transitions
|
|
102
|
+
agent.on("speech.started", (e) => { turn.start(e.turnId); });
|
|
103
|
+
agent.on("user.message", (e) => { turn.log("📝", `"${e.text}"`); });
|
|
104
|
+
agent.on("turn.end", () => { turn.transition("BOT_PENDING"); });
|
|
105
|
+
agent.on("bot.speaking", () => { turn.transition("BOT_SPEAKING"); });
|
|
106
|
+
agent.on("bot.word", (e, call) => { /* live preview via call.currentBotText */ });
|
|
107
|
+
agent.on("bot.finished", () => { turn.end(); });
|
|
108
|
+
agent.on("bot.interrupted", (e, call) => {
|
|
109
|
+
// Render interruption divider, show what was said
|
|
110
|
+
turn.interruption(e.playedMs, e.reason, call.currentBotText);
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The full runnable version is in [`examples/turn-detection/server.js`](https://github.com/pinecall/sdk/tree/master/examples/turn-detection) — with ANSI colors, timestamps, and the state machine diagram in the startup banner.
|
|
115
|
+
|
|
116
|
+
## Run it
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cd examples/turn-detection
|
|
120
|
+
cp .env.example .env # edit with your API key and phone number
|
|
121
|
+
node server.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
Set in `.env`:
|
|
127
|
+
|
|
128
|
+
| Variable | Default | Description |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `PINECALL_API_KEY` | required | Your API key |
|
|
131
|
+
| `PHONE` | required | Phone number to register |
|
|
132
|
+
| `MODEL` | `nova` | `nova` → SmartTurn + Silero, `flux` → native turns |
|
|
133
|
+
| `STT_LANG` | `es` | Language code (`en`, `es`, `ar`, `fr`, `de`, `pt`) |
|
|
134
|
+
|
|
135
|
+
## State transitions to observe
|
|
136
|
+
|
|
137
|
+
| SDK Event | State Before | State After | Notes |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| `speech.started` | IDLE | LISTENING | New turn opens |
|
|
140
|
+
| `turn.pause` | LISTENING | LISTENING | SmartTurn analyzing (nova only) |
|
|
141
|
+
| `turn.end` | LISTENING | BOT_PENDING | User finished, LLM fires |
|
|
142
|
+
| `bot.speaking` | BOT_PENDING | BOT_SPEAKING | TTS audio starts |
|
|
143
|
+
| `bot.finished` | BOT_SPEAKING | IDLE | Turn closes |
|
|
144
|
+
| `bot.interrupted` | BOT_SPEAKING | LISTENING | Barge-in, user keeps talking |
|
|
145
|
+
|
|
146
|
+
## What's next
|
|
147
|
+
|
|
148
|
+
- [Turn Detection guide](/concepts/turn-detection) — full explanation of the state machine
|
|
149
|
+
- [STT Providers](/reference/stt-providers) — language coverage and tuning parameters
|
|
150
|
+
- [Events reference](/reference/events) — all events including `bot.word` and `currentBotText`
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pinecall-guides
|
|
3
|
+
description: >-
|
|
4
|
+
Task guides for building Pinecall agent features. Use when the user is building, configuring, or debugging with @pinecall/sdk. Keywords: inbound, outbound, whatsapp, tools, function calling, events, live listening, conversation history, human takeover, webrtc, multi-tenant, dev mode, testing, agent.dial, call.say, tool().
|
|
5
|
+
license: MIT
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Guides
|
|
9
|
+
|
|
10
|
+
Task guides for building Pinecall agent features.
|
|
11
|
+
|
|
12
|
+
This skill bundles the official Pinecall documentation for **Guides**. The
|
|
13
|
+
table below indexes every page; open the `references/…` file for the full text
|
|
14
|
+
(loaded on demand). Source of truth: <https://docs.pinecall.io>.
|
|
15
|
+
|
|
16
|
+
| Page | What it covers | Open |
|
|
17
|
+
|------|----------------|------|
|
|
18
|
+
| **Inbound Voice** | Build a voice agent that answers phone calls. | [`references/guides/inbound-voice.md`](references/guides/inbound-voice.md) · [docs](https://docs.pinecall.io/guides/inbound-voice) |
|
|
19
|
+
| **Outbound Calls** | Make programmatic outbound phone calls with a greeting and metadata. | [`references/guides/outbound-calls.md`](references/guides/outbound-calls.md) · [docs](https://docs.pinecall.io/guides/outbound-calls) |
|
|
20
|
+
| **Events Guide** | Complete guide to every event in the Pinecall SDK — lifecycle, speech, turn, bot, tools, session, WhatsApp, and more. | [`references/guides/events.md`](references/guides/events.md) · [docs](https://docs.pinecall.io/guides/events) |
|
|
21
|
+
| **Live Listening** | Listen to active calls in real-time from a browser or custom client. | [`references/guides/live-listening.md`](references/guides/live-listening.md) · [docs](https://docs.pinecall.io/guides/live-listening) |
|
|
22
|
+
| **WhatsApp** | Build a WhatsApp messaging agent using Meta's Cloud API. | [`references/guides/whatsapp.md`](references/guides/whatsapp.md) · [docs](https://docs.pinecall.io/guides/whatsapp) |
|
|
23
|
+
| **Conversation History** | Save and restore conversations across calls so your agent remembers returning contacts. | [`references/guides/conversation-history.md`](references/guides/conversation-history.md) · [docs](https://docs.pinecall.io/guides/conversation-history) |
|
|
24
|
+
| **Human Takeover** | Pause the AI agent so a human can intervene in real-time conversations. | [`references/guides/human-takeover.md`](references/guides/human-takeover.md) · [docs](https://docs.pinecall.io/guides/human-takeover) |
|
|
25
|
+
| **WebRTC in the Browser** | Embed a Pinecall voice agent in your web app using the React widget. | [`references/guides/webrtc-browser.md`](references/guides/webrtc-browser.md) · [docs](https://docs.pinecall.io/guides/webrtc-browser) |
|
|
26
|
+
| **Tools and Functions** | Let your agent take actions: look up data, transfer calls, book appointments. | [`references/guides/tools-and-functions.md`](references/guides/tools-and-functions.md) · [docs](https://docs.pinecall.io/guides/tools-and-functions) |
|
|
27
|
+
| **Knowledge bases (RAG)** | Tutorial — ground a voice or chat agent on your own documents with retrieval-augmented generation. | [`references/guides/knowledge-bases.md`](references/guides/knowledge-bases.md) · [docs](https://docs.pinecall.io/guides/knowledge-bases) |
|
|
28
|
+
| **Multi-Tenant Dashboards** | Host many tenants on one Pinecall instance with scoped event streams. | [`references/guides/multi-tenant.md`](references/guides/multi-tenant.md) · [docs](https://docs.pinecall.io/guides/multi-tenant) |
|
|
29
|
+
| **SSE Event Streaming** | Stream agent events to your frontend in real time with Server-Sent Events. | [`references/guides/sse-streaming.md`](references/guides/sse-streaming.md) · [docs](https://docs.pinecall.io/guides/sse-streaming) |
|
|
30
|
+
| **WebSocket Event Streaming** | Stream agent events over WebSocket for bidirectional, real-time communication with your frontend. | [`references/guides/ws-streaming.md`](references/guides/ws-streaming.md) · [docs](https://docs.pinecall.io/guides/ws-streaming) |
|
|
31
|
+
| **Dev Mode** | Run dev and production agents on the same phone number, with zero extra Twilio cost. | [`references/guides/dev-mode.md`](references/guides/dev-mode.md) · [docs](https://docs.pinecall.io/guides/dev-mode) |
|
|
32
|
+
| **Call Ringing & Reject** | Screen incoming calls before answering — accept, reject, or route based on caller info. | [`references/guides/call-ringing.md`](references/guides/call-ringing.md) · [docs](https://docs.pinecall.io/guides/call-ringing) |
|
|
33
|
+
| **Testing Agents** | Automated QA for voice agents using YAML specs and LLM judges. | [`references/guides/testing-agents.md`](references/guides/testing-agents.md) · [docs](https://docs.pinecall.io/guides/testing-agents) |
|
|
34
|
+
|
|
35
|
+
## Canonical agent
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { Pinecall } from "@pinecall/sdk";
|
|
39
|
+
|
|
40
|
+
const pc = new Pinecall(); // reads PINECALL_API_KEY, auto-connects
|
|
41
|
+
|
|
42
|
+
const agent = pc.agent("mara", {
|
|
43
|
+
prompt: "You are Mara, a friendly voice assistant. Be concise.",
|
|
44
|
+
llm: "openai/gpt-5-chat-latest",
|
|
45
|
+
voice: "elevenlabs/sarah",
|
|
46
|
+
stt: "deepgram/flux",
|
|
47
|
+
language: "en",
|
|
48
|
+
greeting: "Hello! How can I help?",
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## House rules — always apply
|
|
53
|
+
|
|
54
|
+
- **Example defaults** (use these exact strings unless the user asks otherwise):
|
|
55
|
+
`stt: "deepgram/flux"`, `llm: "openai/gpt-5-chat-latest"`, `voice: "elevenlabs/sarah"`.
|
|
56
|
+
**NEVER use `deepgram/nova-2`** — it is not supported. Use `deepgram/nova-3`
|
|
57
|
+
only for languages Flux doesn't support (e.g. Arabic).
|
|
58
|
+
- **Turn detection & VAD are auto-derived from the STT provider — never set
|
|
59
|
+
`turnDetection` or `vad` manually.** Flux → native turns + native VAD;
|
|
60
|
+
every other STT → `smart_turn` + `silero`.
|
|
61
|
+
- **Greeting**: inbound → `greeting` field in `pc.agent()`; outbound → `greeting`
|
|
62
|
+
field in `agent.dial()`. It is sugar for `call.say()` in `call.started`.
|
|
63
|
+
- **Auth**: `new Pinecall()` reads `PINECALL_API_KEY` from env and auto-connects.
|
|
64
|
+
- Full documentation: <https://docs.pinecall.io>
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
*Generated from `sdk/docs/` by `@pinecall/skills` — do not edit by hand; edit the
|
|
68
|
+
docs and re-run `node build.mjs`.*
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Call Ringing & Reject"
|
|
3
|
+
description: "Screen incoming calls before answering — accept, reject, or route based on caller info."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Call Ringing & Reject
|
|
7
|
+
|
|
8
|
+
By default, Pinecall auto-accepts every incoming call. The **ringing** feature gives you a window to inspect the call _before_ it's answered, so you can:
|
|
9
|
+
|
|
10
|
+
- **Reject** spam or blacklisted callers
|
|
11
|
+
- **Route** calls to different agents based on caller ID
|
|
12
|
+
- **Log** incoming calls for analytics
|
|
13
|
+
- **Conditionally accept** based on time of day, capacity, etc.
|
|
14
|
+
|
|
15
|
+
## How it works
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
Without ringing enabled, the flow goes directly from ring → `call.started` (auto-accept).
|
|
20
|
+
|
|
21
|
+
## Enable ringing
|
|
22
|
+
|
|
23
|
+
Pass `ringing: true` in the phone number config:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
const agent = pc.agent("receptionist", {
|
|
27
|
+
phoneNumber: { number: "+13186330963", ringing: true },
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> **Warning:** Only phone channels support ringing. WebRTC and chat channels don't have a ringing phase.
|
|
32
|
+
|
|
33
|
+
## Handle `call.ringing`
|
|
34
|
+
|
|
35
|
+
When a call comes in, the SDK emits `call.ringing` with a `RingingCall` object. This object has caller info but no audio — the call isn't connected yet.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
agent.on("call.ringing", (call) => {
|
|
39
|
+
console.log(`Incoming: ${call.from} → ${call.to}`);
|
|
40
|
+
console.log(`Call SID: ${call.callId}`);
|
|
41
|
+
|
|
42
|
+
// Accept the call — proceeds to call.started
|
|
43
|
+
call.accept();
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### RingingCall API
|
|
48
|
+
|
|
49
|
+
| Property | Type | Description |
|
|
50
|
+
|----------|------|-------------|
|
|
51
|
+
| `call.callId` | `string` | Twilio Call SID |
|
|
52
|
+
| `call.from` | `string` | Caller phone number (E.164) |
|
|
53
|
+
| `call.to` | `string` | Called phone number (E.164) |
|
|
54
|
+
| `call.accept()` | `void` | Accept the call — triggers `call.started` |
|
|
55
|
+
| `call.reject(reason?)` | `void` | Reject the call. Reason: `"busy"` or `"rejected"` |
|
|
56
|
+
|
|
57
|
+
## Reject calls
|
|
58
|
+
|
|
59
|
+
Reject with an optional reason that maps to a Twilio rejection:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
agent.on("call.ringing", (call) => {
|
|
63
|
+
if (BLACKLIST.has(call.from)) {
|
|
64
|
+
call.reject("busy"); // caller hears busy signal
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
call.accept();
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Reason | Caller experience |
|
|
72
|
+
|--------|-------------------|
|
|
73
|
+
| `"busy"` | Hears busy tone |
|
|
74
|
+
| `"rejected"` | Call is dropped immediately |
|
|
75
|
+
| _(none)_ | Defaults to `"busy"` |
|
|
76
|
+
|
|
77
|
+
## Default behavior
|
|
78
|
+
|
|
79
|
+
If you don't call `accept()` or `reject()` within the timeout (configurable on the server, default ~5s), the call is **auto-accepted**. This prevents calls from hanging indefinitely if your handler crashes.
|
|
80
|
+
|
|
81
|
+
> **Note:** If you don't register a `call.ringing` handler at all, calls are auto-accepted immediately — same as before this feature existed. Ringing is fully opt-in.
|
|
82
|
+
|
|
83
|
+
## Full example
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Pinecall } from "@pinecall/sdk";
|
|
87
|
+
|
|
88
|
+
const pc = new Pinecall({ apiKey: process.env.PINECALL_API_KEY });
|
|
89
|
+
|
|
90
|
+
const BLACKLIST = new Set(["+15551234567", "+15559876543"]);
|
|
91
|
+
|
|
92
|
+
const agent = pc.agent("receptionist", {
|
|
93
|
+
voice: "elevenlabs/sarah",
|
|
94
|
+
language: "en",
|
|
95
|
+
stt: "deepgram/flux",
|
|
96
|
+
llm: "openai/gpt-5-chat-latest",
|
|
97
|
+
prompt: "You are a receptionist. Be brief and helpful.",
|
|
98
|
+
// Enable ringing on the phone channel
|
|
99
|
+
phoneNumber: { number: "+13186330963", ringing: true },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Screen calls before answering
|
|
103
|
+
agent.on("call.ringing", (call) => {
|
|
104
|
+
console.log(`🔔 Incoming: ${call.from}`);
|
|
105
|
+
|
|
106
|
+
if (BLACKLIST.has(call.from)) {
|
|
107
|
+
console.log(`❌ Rejected: ${call.from} (blacklisted)`);
|
|
108
|
+
call.reject("busy");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`✅ Accepted: ${call.from}`);
|
|
113
|
+
call.accept();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Normal call lifecycle
|
|
117
|
+
agent.on("call.started", (call) => {
|
|
118
|
+
call.say("Thanks for calling! How can I help?");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
agent.on("call.ended", (call, reason) => {
|
|
122
|
+
console.log(`📴 ${call.id} ended: ${reason} (${call.duration}s)`);
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Run the example from the SDK repo:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cd sdk/examples/ringing
|
|
130
|
+
PHONE=+13186330963 node server.js
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Wire protocol
|
|
134
|
+
|
|
135
|
+
The ringing handshake uses two new events and commands:
|
|
136
|
+
|
|
137
|
+
| Direction | Message | Payload |
|
|
138
|
+
|-----------|---------|---------|
|
|
139
|
+
| Server → SDK | `call.ringing` | `{ call_id, from, to }` |
|
|
140
|
+
| SDK → Server | `call.accept` | `{ call_id }` |
|
|
141
|
+
| SDK → Server | `call.reject` | `{ call_id, reason }` |
|
|
142
|
+
|
|
143
|
+
For full wire protocol details, see `sdk-server/PROTOCOL.md`.
|
|
144
|
+
|
|
145
|
+
## What's next
|
|
146
|
+
|
|
147
|
+
- [Inbound Voice](/guides/inbound-voice) — the standard (non-ringing) flow
|
|
148
|
+
- [Dev Mode](/guides/dev-mode) — route dev calls to your local agent
|
|
149
|
+
- [Events Reference](/reference/events) — all SDK events
|