Now onboarding Florida small businesses
VoiceaiAgentstable

AI receptionist

Hands the caller to an AI voice receptionist — a natural, speech-to-speech agent that greets the caller, answers questions from its knowledge base, runs t…

What it does

Hands the live caller to an AI voice receptionist — a natural, speech-to-speech agent that greets the caller, answers questions from its knowledge base, runs tools (look up a contact, take a message, check business hours, send an SMS, transfer, escalate), and decides how the call should end.

Under the hood the node emits the Protocol-v1 llm verb and breaks out of the flow for the entire conversation (just like dial waits for a bridge). When the session ends, the node resumes and routes on how the conversation ended — the receptionist's terminal outcome, stamped onto vars.agentOutcome by the tool the model called (or derived from the session-end reason when no tool stamped one).

You build the receptionist once — persona, tools, knowledge base, business hours, and the voice + model — in the dedicated AI receptionist builder, then point this node at it with agentId. The node is the in-flow handoff; the builder is where the brain is configured. The same receptionist can also be bound directly to a phone number ("point this number at the receptionist") without a flow at all; use this node when the receptionist is one step inside a larger call flow (e.g. after an IVR menu, or as the after-hours branch).

The override fields (greeting, instructions, voice, model, vendor) are escape hatches: leave them blank to run the receptionist exactly as saved, or set one when a single flow needs to differ (a different greeting for the after-hours line, say). instructions is kept as a stable, cacheable prompt prefix — caller-specific data is injected by the runtime as conversation items, never string-interpolated into the prompt.

When to use it

  • Replace a phone-tree IVR with a conversational front door: callers say what they want instead of pressing 1-2-3.
  • After-hours / overflow: route to the receptionist when no agent is available, let it take a structured message (emailed to the owner) and end on voicemail.
  • A self-serve tier in front of a queue: the receptionist answers FAQs from the knowledge base and only transfers / escalates the calls a human needs.
  • A specific sub-flow: drop it after a gatherDigits menu so one menu option ("press 4 to talk to our assistant") reaches the AI while the others route normally.

Configuration

Hands the caller to an AI voice receptionist — a natural, speech-to-speech agent that greets the caller, answers questions from its knowledge base, runs tools (look up a contact, take a message, check hours, send an SMS), and decides how the call should end. Build the receptionist (persona, tools, knowledge base, business hours, voice + model) under AI receptionist, then point this node at it. The node waits for the whole conversation, then branches on how it ended: Transfer, Agent handoff, Call flow, Voicemail, Completed, or Error. An escalation to a queue or an on-call rotation arrives on Transfer, with the kind in {{vars.agentTransferKind}}. Wire the Agent-handoff outcome into a second AI receptionist node for a warm AI→AI transfer on the same call (the summary is carried automatically). Override the greeting/instructions/voice/model here only when one flow needs to differ from the saved receptionist.

FieldLabelTypeRequiredDefaultNotes
agentIdReceptionisttextOptional""The published AI receptionist to run, by id. Manage receptionists under AI receptionist. Accepts a template, so a node on the Agent-handoff outcome can run whichever receptionist the previous one chose ({{vars.agentTransferTarget}}). Leave blank only when overriding everything inline below (instructions + voice + model) for a one-off agent.
greetingGreeting overridetextareaOptionalThanks for calling Acme — how can I help today?Optional. The first line the receptionist speaks. Overrides the saved receptionist's greeting for this flow only. Supports {{vars.x}} / {{caller.x}} / {{config.x}} templates.
instructionsInstructions overridetextareaOptionalYou are the receptionist for Acme Plumbing. Be warm and concise. Book callbacks, never quote prices.Optional. Replaces the saved receptionist's system prompt for this flow only. Kept as a stable, cacheable prefix — caller-specific data is injected by the runtime, not interpolated here. Leave blank to use the receptionist as configured.
voiceVoice overridetextOptionalPuckOptional. Overrides the voice from the receptionist. Pick a voice id from the model picker in the receptionist builder (the available voices depend on the provider/model).
modelModel overridellmModelOptionalOptional. Overrides the speech-to-speech model. Leave blank to use the receptionist (and the org default provider/model). Set the vendor below if you override this. Pick from the dropdown or type any model id.
vendorProvider overrideselectOptional""Options: ``, google, openai, openai_live. Optional. The speech-to-speech provider for the model override above. Only set this alongside a Model override.
silenceMsEnd-of-turn silence (ms)numberOptional500How long the caller can pause before the receptionist treats them as done speaking and replies. Higher tolerates mid-sentence pauses; lower is snappier. Default 500ms.
maxSessionSecsMax session (seconds)numberOptional600Hard cap on the AI conversation in seconds. When it elapses the session ends and the flow follows the Completed outcome. Backstop for runaway calls. Clamped to 30–3600. Default 600 (10 minutes).

Outcomes

Routing is only the six terminal outcomes — there is no plain "next" edge. Each outcome is a canonical case on vars.agentOutcome, so you can also branch on {{vars.agentOutcome}} in a downstream node. Wire every outcome you care about; an unwired outcome simply ends the flow.

OutcomeFires whenTypical wiring
Transfer (transfer)The receptionist chose to hand off to a human — an extension, a number, a call queue, or the on-call rotation. The chosen target is in vars.agentTransferTarget and what it IS in vars.agentTransferKind. An escalation arrives here: escalate_to_human ends the session as a transfer, so there is no separate Escalate outcome. Branch on {{vars.agentTransferKind}} when a queue needs different handling from one person.A Dial/Transfer node using {{vars.agentTransferTarget}}, or a requestAgent; or the system-transfer sub-flow, which reads target + kind and needs no branch.
Agent handoff (agent)The receptionist called transfer_to_agent — a WARM handoff to another AI receptionist on the same call leg (no new carrier call). The target receptionist id is in vars.agentTransferTarget; the model-authored summary carries over automatically.A short say bridge line ("Connecting you now.") → the target AI receptionist node. With multiple allow-listed targets, branch on {{vars.agentTransferTarget}} first.
Call flow (flow)The receptionist called run_flow — it handed the caller into one of your own published call flows on the same leg (a self-service menu, an automated line). The flow id it picked, resolved out of the tool’s pinned allow-list, is in vars.agentTransferTarget; vars.agentTransferKind is flow. The caller does not come back.A runFlow node whose Flow field is {{vars.agentTransferTarget}}. Never system-transfer — that would try to ring the flow id as an extension.
Voicemail (voicemail)The receptionist took (or routed to) a message. With the default structured take_message, the owner is emailed; route to a goodbye + hangup.A goodbye sayhangup, or a record node for a spoken voicemail.
Completed (completed)The conversation finished normally — caller satisfied, end_call, caller hangup, or the maxSessionSecs cap elapsed.hangup, or a wrap-up side-chain.
Error (error)The session failed — provider error, missing key, an unrecoverable tool failure, or the agent-handoff loop cap (3 per call) was breached.A safety net: a human-transfer or a "sorry, please hold" + enqueue.

vars.agentTranscript holds the running conversation transcript for logging or post-processing downstream.

Warm agent→agent handoff (the agent outcome)

Enable the Transfer to another assistant tool on receptionist A and pin its allow-list of target receptionists (the model picks a target by LABEL, never a free-form id). When it fires, the runtime carries three things into receptionist B's session on the same leg:

  • the model-authored summary (who's calling, what they need, what was collected) — injected at session start so B acknowledges the transfer instead of re-greeting from zero and never re-asks captured facts;
  • the caller's verification state (the ANI/KBA/OTP ladder level carries — same caller, same call; each tool's own gate still applies);
  • a handoff row in the call's AI-conversation log (the CDR card shows the baton pass inline).

Handoffs are capped at 3 per call — a ping-pong loop routes the error outcome instead. An agent outcome with no wired handle is a misconfiguration: the runtime error-logs it and ends the call gracefully (it never silently dials). Directly-bound receptionists (no custom flow) cannot complete the handoff in v1 — the target node must be authored on a flow.

Examples

Self-serve receptionist, then transfer or take a message

The receptionist answers from its knowledge base. If the caller asks for a person it transfers; if it takes a message it ends on voicemail; otherwise it wraps up.

{
  "id": "reception-ai",
  "type": "aiAgent",
  "config": {
    "agentId": "{{config.agentId}}",
    "greeting": "Thanks for calling Acme — I'm the front desk assistant. How can I help?",
    "silenceMs": 500,
    "maxSessionSecs": 600
  },
  "on": {
    "outcome-transfer": "dial-target",
    "outcome-voicemail": "goodbye",
    "outcome-completed": "goodbye",
    "outcome-error": "human-fallback"
  }
}

The matching transfer leg reads the target the receptionist picked:

{
  "id": "dial-target",
  "type": "dial",
  "config": { "to": "{{vars.agentTransferTarget}}", "timeoutSecs": 30 },
  "on": { "call.bridged": "end", "call.hangup": "goodbye" }
}

Warm handoff to a second receptionist (support → sales)

The support receptionist's transfer_to_agent allow-list names the sales receptionist; the flow wires the Agent-handoff outcome through a bridge line into a second aiAgent node — one call, one leg, summary carried over:

[
  {
    "id": "support-ai",
    "type": "aiAgent",
    "config": {
      "agentId": "<support-agent-id>",
      "cases": [
        { "path": "vars.agentOutcome", "op": "==", "value": "agent", "nodeId": "bridge" }
      ]
    },
    "on": { "outcome-completed": "end" }
  },
  {
    "id": "bridge",
    "type": "say",
    "config": { "prompt": { "kind": "text", "text": "Connecting you now." } },
    "on": { "__next": "sales-ai" }
  },
  {
    "id": "sales-ai",
    "type": "aiAgent",
    "config": { "agentId": "<sales-agent-id>" },
    "on": { "outcome-completed": "end" }
  }
]

Gotchas

  • It auto-answers if needed. The realtime session needs media, so the node answers the caller leg first when the call isn't already answered — the same thing gather/record do. You don't need a separate Answer node before it.
  • No "next" edge — wire the outcomes. Unlike say/gather, this node never takes a plain forward transition. Route at least completed (and ideally error) so the call doesn't dead-end when the conversation finishes.
  • The overrides are for one-off divergence, not primary config. Persona, tools, knowledge base, and business hours live on the receptionist entity (agentId). The inline instructions/voice/model fields override the saved receptionist for this flow only; leave them blank to inherit.
  • maxSessionSecs is a hard backstop. When it elapses the session ends and the flow follows the Completed outcome — it fires regardless of what the model is doing. Clamped to 30–3600 seconds. Gemini sessions with contextWindowCompression (always active) are duration-unlimited per Google's Live API docs (verified 2026-07-08); the per-WebSocket-connection limit (~10 min) is bridged transparently by session resumption.
  • Transfer target comes from the tool, not the node. The receptionist decides where to transfer at conversation time; the destination lands in vars.agentTransferTarget. Reference that in the downstream Dial/Transfer — don't hard-code a number on the node and expect the AI to use it.
  • Overrides render once, at entry. {{vars.x}} in greeting / instructions resolves when the node is entered; mutating those vars mid-conversation has no effect on the already-open session.
  • A recording-consent announcement may play before the session opens. When the org enables the consent announcement (Storage → Settings), it plays once per call before the AI session starts — the session transcribes every caller turn, which counts as capture. The org's consent audio URL plays if set; otherwise a default line is spoken ("This call may be recorded for quality purposes"). Disabled orgs (the default) get zero added verbs. The customer owns consent-law compliance.