> ## Documentation Index
> Fetch the complete documentation index at: https://patter-06b046ce-feat-py-gemini-tts-stt.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Consult your agent

> Let an in-call Patter agent escalate to your own back-office agent over HTTP — the dispatch + consult pattern.

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

```ts theme={null}
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`:

```json theme={null}
{
  "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:

```json theme={null}
{ "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`

| Field              | Default            | Notes                                                                                                                                                                                                   |
| ------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`              | `undefined`        | Generic webhook endpoint. SSRF-validated at call start. **Mutually exclusive** with `openaiCompatible` — set exactly one.                                                                               |
| `openaiCompatible` | `undefined`        | Native OpenAI-compatible target (e.g. an OpenClaw agent) — no hand-written adapter. Use `openclawConsult(...)`. See [OpenClaw](#openclaw-native-target-no-adapter).                                     |
| `headers`          | `undefined`        | Sent with every POST (e.g. an `Authorization` bearer). Never logged.                                                                                                                                    |
| `timeoutMs`        | `30000`            | Per-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.                                                                                                                                                               |
| `reassurance`      | `undefined`        | Filler the agent speaks while the consult runs (Realtime mode only). The `openclawConsult` preset sets a sensible default.                                                                              |
| `allowLoopback`    | `false`            | Opt-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). |

## OpenClaw (native target — no adapter)

To consult an [OpenClaw](/integrations/openclaw) agent, skip the hand-written
adapter entirely: `openclawConsult(...)` speaks OpenClaw's OpenAI-compatible
`POST /v1/chat/completions` gateway directly.

```ts theme={null}
import { openclawConsult } from 'getpatter';

const agent = phone.agent({
  engine: ...,                        // Realtime recommended — reassurance works there
  systemPrompt: 'You are the after-hours receptionist...',
  consult: openclawConsult('receptionist'),
});
```

That one line targets `model="openclaw/receptionist"` on the local gateway,
sends the call id as both the OpenAI `user` field and the `x-openclaw-session-key`
header (one OpenClaw session per call), reads the operator-grade bearer from
`OPENCLAW_API_KEY` (never logged), auto-enables `allowLoopback` for the
co-located gateway, and attaches a "let me check" reassurance filler. Overridable
options (`openclawConsult(agent, opts)`):

| Option        | Default                                     | Notes                                                                                                                                                                                                |
| ------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent`       | — (required)                                | Agent id → `model="openclaw/<agent>"`. An already-namespaced target (`openclaw/x`, `agent:x`) passes through. Pass a **least-privileged** agent — selecting one is routing, not a security boundary. |
| `baseUrl`     | `http://127.0.0.1:18789/v1`                 | OpenClaw gateway (OpenAI-compatible base).                                                                                                                                                           |
| `apiKey`      | env `OPENCLAW_API_KEY`                      | Operator-grade bearer. Prefer the env var.                                                                                                                                                           |
| `timeoutMs`   | `30000`                                     | Phone-safe. Don't raise above 30 s on a live call.                                                                                                                                                   |
| `reassurance` | "Let me check on that for you, one moment." | Realtime-only filler.                                                                                                                                                                                |

**Enable the endpoint first.** Set
`gateway.http.endpoints.chatCompletions.enabled = true` in
`~/.openclaw/openclaw.json` and keep the gateway bound to loopback. A 404 from the
endpoint means it is disabled (the consult logs an actionable hint). See the
[OpenClaw integration guide](/integrations/openclaw) for agent scoping and
deployment.

Under the hood this is a generic `OpenAICompatibleConsult` codec (`baseUrl` +
`model` + optional `apiKey` / `apiKeyEnv` / `sessionHeader`), so the same
primitive drives any OpenAI-compatible gateway (vLLM, Ollama, Groq). The generic
`{ url }` webhook path below stays the escape hatch for custom request/response
mappings.

## 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:

```ts theme={null}
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](#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](/python-sdk/consult) for the same feature in
`getpatter` for Python.
