Skip to main content
Patter conducts the call (speech-to-text, the voice agent, text-to-speech, and the carrier). The consult tool gives that in-call agent an on-demand bridge back to your own agent — reachable over HTTP — for deeper reasoning, fresh information, or an action beyond the call. This is the dispatch + consult pattern: your orchestrator (or any HTTP endpoint you host) stays off the per-turn path. It is consulted only when the in-call agent decides it needs help, so ordinary turns keep their low latency while hard turns can reach your full reasoning stack.

When to use it

  • The caller asks something the in-call agent can’t answer from its prompt (order status, account details, a policy lookup).
  • You already run an agent (e.g. an internal assistant) and want phone calls to tap into it without putting it on every conversational turn.
If instead you want your LLM to drive every turn, that is a different (“brain-on-the-line”) mode — consult is the lighter, lower-latency default.

Quickstart

import { Patter, type ConsultConfig } from 'getpatter';

const phone = new Patter({ carrier: ... });

const agent = phone.agent({
  systemPrompt:
    "You are front-desk support. If you can't answer directly, consult your back-office agent.",
  stt: ...,            // any Patter STT (pipeline) — or use a Realtime engine
  tts: ...,
  consult: {
    url: 'https://my-orchestrator.example.com/consult',
    headers: { Authorization: `Bearer ${process.env.ORCHESTRATOR_TOKEN}` },
    timeoutMs: 30_000,
  },
});

await phone.serve(agent);
When consult is set, Patter auto-injects a consult_agent tool into the agent (Realtime and Pipeline modes). The model calls it with a single request string when it needs help.

The HTTP contract

Patter POSTs JSON to your url:
{
  "request": "When does order 4815 ship?",
  "call_id": "CA0000000000000000000000000000a001",
  "caller": "+15555550100",
  "callee": "+15555550199"
}
Your endpoint returns JSON with a reply (or response / text / result / answer / message) string, which the agent speaks:
{ "reply": "Order 4815 ships Tuesday by end of day." }
Plain text and other JSON shapes are accepted too (the raw body is spoken / serialized as a fallback).

ConsultConfig

FieldDefaultNotes
url— (required)HTTP(S) endpoint. SSRF-validated at call start (private / loopback / link-local hosts and non-HTTP schemes are rejected).
headersundefinedSent with every POST (e.g. an Authorization bearer). Never logged.
timeoutMs30000Per-consult timeout — higher than the generic webhook-tool default (10 000 ms) because a consult may run deeper reasoning.
toolName"consult_agent"The tool name the model sees.
description(sensible default)Tune to steer when the agent escalates.
allowLoopbackfalseOpt-in: permit a loopback / private / link-local url (e.g. a local back-office agent on 127.0.0.1 or an RFC1918 host). See Pointing consult at a local agent.

Pointing consult at a local agent

By default the consult URL is SSRF-validated and loopback / private / link-local targets are rejected — so http://localhost:8000/consult will not pass. When your back-office agent (or a thin adapter) runs on the same machine, set allowLoopback: true to relax that check:
const agent = phone.agent({
  systemPrompt: '...',
  consult: {
    url: 'http://localhost:8000/consult',   // local agent on loopback
    timeoutMs: 30_000,
    allowLoopback: true,
  },
});
What the flag does:
  • Scope. Relaxes the loopback (127.0.0.0/8, ::1, localhost), RFC1918 private (10/8, 172.16/12, 192.168/16), and link-local host checks for the consult URL only. The generic webhook-tool validator path — which can be reached by tool / LLM input — stays strict and is unaffected.
  • Always enforced. Non-HTTP(S) schemes (file:, javascript:, …) are rejected even with the flag on.
  • Why it is safe. The consult URL is SDK-user configuration, not caller-derived input. Cloud-metadata hostnames also become reachable when opted in — only enable the flag for a URL you control.

Behaviour & limits

  • Failure is graceful. A timeout, non-2xx response, or unreachable endpoint does not crash the turn — the agent speaks a short fallback line and carries on.
  • Security. The URL is SSRF-validated; header values are never logged. The URL is trusted SDK configuration (not caller-supplied). To point consult at a local agent, opt in with allowLoopback: true — see Pointing consult at a local agent.
  • Mode support. Injected in Realtime and Pipeline modes. ElevenLabs ConvAI is not supported — its tools live on the ElevenLabs-hosted agent, so a warning is emitted if you set consult with that provider.
See the Python version for the same feature in getpatter for Python.