pinecallDocs
Key concepts / The call

The call

this.call: who is on the line, on which channel, and what the agent can do to the call.

this.call is the one call this instance serves: who is on the line, on which channel, what has been said, and the commands that act on the call. Tools, hooks and render() all read it.

What is on this.call?#

property what it is
id the call's id
contact the other party: a phone number, a contact id, or a web visitor's id
from the number or identity the call came from, when there is one
channel phone, web or whatsapp
medium voice or text. A web call can be either; WhatsApp is text
today the day the call opened, YYYY-MM-DD. Count "next Tuesday" from this, never from the machine's clock
history the finished turns: turns, last and length. Each turn has who (user or agent), text and interrupted
room who is in the call: participants, caller, has(kind), get(identity)
claimed the page code this call claimed, or null

this.call exists only while a call is being served. Read outside one, it throws.

Speaking#

TypeScript
await this.say("One moment, let me check.");                 // these words, as written
await this.reply("Apologise and offer another time.");        // the model speaks, guided by words the caller never hears
await this.say("Your code is 4 4 1 9.", { allowInterruptions: false });
Python
await self.say("One moment, let me check.")                  # these words, as written
await self.reply("Apologise and offer another time.")        # the model speaks, guided by words the caller never hears
await self.say("Your code is 4 4 1 9.", allow_interruptions=False)

A verb that waits answers with an Answer: await it in an async def tool, or call .result() in a def one, which runs on a thread of its own.

Ruby
say "One moment, let me check."                       # these words, as written; blocks until spoken
reply "Apologise and offer another time."             # the model speaks, guided by words the caller never hears
say "Your code is 4 4 1 9.", allow_interruptions: false

Both resolve true once the turn has been spoken, and false if it was not within 30 seconds or the call ended first. allowInterruptions: false keeps the caller from cutting the sentence short.

The room#

this.call.room.has("supervisor");                           // caller, agent, supervisor, listener or sip
this.call.participant(identity).mute();                     // for the rest of the call
this.call.participant(identity).remove();                   // removing the caller ends the call
this.call.invite("+34910000099", { kind: "sip" });          // dial a number into the call
this.call.send("cart", { total: 42 }, { to: identity });    // a payload to a browser in the call
this.log("appointment.booked", booking);                    // one named entry in the call's log
self.call.room.has("supervisor")                            # caller, agent, supervisor, listener or sip
self.call.participant(identity).mute()                      # for the rest of the call
self.call.participant(identity).remove()                    # removing the caller ends the call
self.call.invite("+34910000099", "sip")                     # dial a number into the call
self.call.send("cart", {"total": 42}, to=identity)          # a payload to a browser in the call
self.log("appointment.booked", booking)                     # one named entry in the call's log
call.participants.any? { |one| one.kind == "supervisor" }   # caller, agent, supervisor, listener or sip
call.participant(identity).mute                             # for the rest of the call
call.participant(identity).remove                           # removing the caller ends the call
call.invite("+34910000099", kind: :sip)                     # dial a number into the call
call.send_to("cart", { total: 42 }, to: identity)           # a payload to a browser in the call
log "appointment.booked", booking                           # one named entry in the call's log

Searching from a tool#

this.knowledge.search(question, { k }) searches the knowledge bases attached to the agent and returns { path, heading, text } per chunk. this.call.search does the same.

TypeScript
/** Looks up what the patient asks in the clinic's documents. */
@tool()
async lookUp(question: string): Promise<Found[]> {
  return await this.knowledge.search(question, { k: 3 });
}

A class that reads this.knowledge is refused at registration when no base is attached to it, so it never meets a call where the search finds nothing. See Knowledge bases.

Handing the call on#

These verbs act on the call itself. Each one that can fail answers with what really happened, so the tool decides what the caller is told.

verb does
transfer(to, { mode }) sends the caller to a number. Answers { ok, to, mode, error? }; ok: false means the caller is still with the agent. Python: transfer(to, mode=…); Ruby: transfer(to, mode:)
attention(reason, { waitS }) asks your team to take the line, and waits up to waitS seconds. Answers { ok, by, error? }. Python and Ruby: attention(reason, wait_s=…)
hold() · unhold() puts the caller on hold music, and takes them off it
dtmf(digits) sends touch tones: 0-9, *, #, and , for a pause
callback(number, { when, note }) records that the caller wants a call back (callback(number, when=…, note=…) in Python, callback(number, at:, note:) in Ruby)
optOut(note) adds the caller to the org's do-not-call list (opt_out in Python and Ruby)
hangup(reason) ends the call. Say goodbye first: nothing after it is heard

The whole of it is Handing a call to a person and Callbacks.

A caller who is also on your page#

A page can show a phone number and a four-digit code. The caller rings and keys the code, and from then on the page follows the call. When the call is claimed, this.call.claimed holds the code and the view is rendered again at once, so render() can tell a caller who sees the slots on screen from one who only hears them. A caller who says the code instead is the class's to claim, from a tool: this.call.claim(code). An unknown, expired or taken code is refused with no_code. See Codes.

Long calls and callers who come back#

method does
this.collapse(summary) replaces the instance's change log with one sentence. The state is untouched, and the call's log keeps every turn
this.last(contact) the state this contact's last call left. Needs a store, given where you mount the agent yourself: mount(Class, { pc, last })
this.restore(snapshot) puts a whole state back; fields missing from the snapshot are cleared
this.startIn(partial) writes only the fields given, over the current state

last is for an agent you run inside your own server: Run it on your own server.

A call that changes hands#

A deploy stops one process and starts the next, and a call in the middle of it goes on in the next one. What the new instance gets is the call's state, restored whole, and this.call. What it does not get: a field that is not state, a promise or timer the old instance held, and what memory recalled (this.remembers() is false until the next recall). onCall does not run again; onEnd runs in whichever process the call ends in.

Keep everything a call needs in state, and let onCall only open it. See A deploy never cuts a call.

Next steps#