Skip to main content
You built and talked to your agent in the browser in the Quickstart. Now give it a phone number. Nothing about the agent changes - same entrypoint, same LLMAgent brain, same AgentRunner against Unpod’s hosted speech service. The only new work is provisioning: telling Unpod which phone number routes to your agent.

What you’ll add

  • A Speech Pipe - the configuration entity that binds a voice profile, recording settings, and the agent_id that points at your runner.
  • A phone number - synced from a trunk and attached to that pipe.
You reference a voice profile from the read-only catalog when you create the pipe; you do not create one.

Provision the number

Provisioning uses the Management API - the REST half of the SDK, reached through AsyncClient. It reads UNPOD_API_KEY and derives its REST endpoint from UNPOD_BASE_URL (https://<host>/platform) - both set in the Quickstart. 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 pipe, sync numbers from your trunks, and attach the first available number:
The agent_id on the pipe ("browser-playground") is the same string your AgentRunner registers under in the Quickstart. They must match exactly, or inbound calls never reach your runner. See IDs You’ll Meet.

You need a trunk first

numbers.sync() pulls numbers from your trunks - the SIP connections that carry calls between a telephony provider and Unpod. If numbers.list(status="available") comes back empty, you have no trunk yet. Register one with client.trunks.create(...) before running the setup above. You configure a trunk once; after that you work with numbers, not SIP. See Trunks for both trunk types, or the Setup Checklist for the full production path.

Answer an inbound call

This is the agent from the Quickstart, unchanged. The same entrypoint and the same AgentRunner registered against wss://<UNPOD_BASE_URL>, using your real UNPOD_API_KEY:
Start the runner:
The runner connects to the orchestrator and waits. Now call the number you attached. Unpod recognises the number, looks up its pipe, sees the pipe’s agent_id, and dispatches the call to your waiting runner. Your agent answers and speaks - the same brain you heard in the browser, now on the phone.
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.

Make an outbound call (optional)

Inbound is one direction. To have your agent place a call, use calls.create with the pipe 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.
You can dispatch by agent instead of by pipe: pass agent_id= to calls.create and omit pipe_id. Unpod resolves a pipe bound to that agent_id server-side (agent_id wins if you pass both). to_number is always required.

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.