Skip to main content
The Quickstart placed a call with a Prompt brain and deployed nothing. This page is the Runner path: your own long-lived Python process at the text boundary - for DB reads mid-call, your own framework, or logic a prompt cannot carry. The deal is unchanged: your AgentRunner receives a transcribed turn as a string and returns a string. No frames, no codecs, no SIP - the speech pipeline and the carrier stay on Unpod’s side.

Prerequisites

  • Python 3.12+ and pip install unpod
  • UNPOD_API_KEY (sk_...) in the environment - the runner’s bearer, from unpod.ai/api-keys
  • A number in your account. Provision one from the dashboard under Dev Platform → Numbers. No carrier account, no SIP trunk - see Numbers.
Already own a number elsewhere? You can bring it over a trunk instead. That is the only path that needs carrier credentials.

Step 1 - Provision the number

Provisioning uses the Management API - the REST half of the SDK, reached through AsyncClient. For REST auth it accepts either the org-scoped UNPOD_PLATFORM_TOKEN (with UNPOD_ORG_HANDLE) or UNPOD_API_KEY; this page uses UNPOD_API_KEY, since the runner needs it anyway. The REST endpoint derives from UNPOD_BASE_URL (default api.unpod.aihttps://<host>/platform). Provisioning is the same agents.voice.create() call as the Quickstart - the only difference is brain=Runner(): your process answers the turns instead of a platform-run prompt. If you need one-off overrides in code, pass base_url= to AsyncClient or AgentRunner; those arguments win over .env for that process only. Run this once to pick a voice profile, create the agent, and attach a free number from your account:
What it does:
  1. voice_profiles.list() - pick a voice from the read-only catalog.
  2. agents.voice.create() - create the agent, its brain, and its first voice.
  3. numbers.list() - find a number in your account with nothing attached.
  4. numbers.attach() - route that number to the agent_id. It takes agent_id, not a pipe id.
If step 3 finds nothing, provision a number from the dashboard first.
BYON numbers arrive over a trunk you register once, then sync into your account:
Full setup: Trunks.
The agent’s agent_id ("my-runner-agent") is the same string your AgentRunner registers under in Step 2. They must match exactly, or inbound calls never reach your runner. See IDs You’ll Meet.

Step 2 - Answer an inbound call

One entrypoint, one AgentRunner. The brain here is SuperDialog’s LLMAgent; any adapter works - Anthropic, OpenAI, LangChain, an HTTP endpoint, or a playbook (see Bring your agent) - and nothing about the call path changes:
Start the runner:
The runner connects to the orchestrator and waits. Now call the number you attached. Unpod recognises the number, resolves the agent_id it is attached to, and dispatches the call to your waiting runner. Your agent answers and speaks - your own process at the text boundary, live on a phone call.
The runner does not need to restart when you provision the number. Run setup.py once, then leave the runner up; it serves every inbound call until you stop it.

If the runner will not connect

Three close codes cover almost every failure:
  • 4001 on the control socket - the orchestrator refused the credential. Do not retry: a reconnect loop on a bad key is indistinguishable from a network outage.
  • 4003 on the bridge - the pairing failed: an unknown call_id, an expired pairing (it expires ~20s after dispatch), or a bad call token.
  • 4009 - that call already has a live bridge socket.

Step 3 - Make an outbound call (optional)

Inbound is one direction. To have your agent place a call, use calls.create with the agent_id and the destination number:
calls.create enqueues the call and returns immediately with status="pending". Unpod dials out, then dispatches the answered call to the same running AgentRunner - your entrypoint handles outbound exactly as it handles inbound.
calls.create also still accepts pipe_id= for older code. agent_id wins if you pass both, and to_number is always required. Outbound dispatch may also need from_number= depending on your numbers - see Outbound calls.

Next steps

Production setup

The full path: trunks, numbers, recording, and deployment.

Outbound calls

Campaigns, dynamic instructions, and per-call data.

Use your own agent

Plug in LangChain, an HTTP endpoint, or any brain you already have.