> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unpod.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Provisioning checklist

> One-time resource provisioning before your first call.

Before your first call routes to your agent, complete these four steps once.

## 1. Pick a Voice Profile

```python theme={null}
import asyncio
from unpod import AsyncClient

async def list_profiles():
    client = AsyncClient()
    profiles = await client.voice_profiles.list()
    for p in profiles:
        print(p.profile_id, p.name)

asyncio.run(list_profiles())
```

Copy a `profile_id` (`vp_...`) for step 2. See
[Voice Profiles](/speech-stack/voice-profiles) for filtering and fields.

## 2. Create an Agent

```python theme={null}
import asyncio
from unpod import AsyncClient, Runner

async def create_agent():
    client = AsyncClient()
    voice = await client.agents.voice.create(
        "my-agent",                        # agent_id - MUST match AgentRunner(agent_id=...)
        brain=Runner(),                    # this checklist runs its own worker
        name="My Agent",
        voice_profile="vp_en_female_hd",   # a profile_id from step 1
    )
    print(voice.agent_id, voice.voice_profile_name)
    return voice

asyncio.run(create_agent())
```

<Warning>
  **`agent_id` is a string you choose, and it must match everywhere.**

  The `agent_id` you pass here must exactly match `AgentRunner(agent_id=...)` in
  step 4. Mismatching them is the most common first-run failure - the call arrives
  and no runner claims it.
</Warning>

Other options (`greeting`, `recording`, `max_call_duration_s`, `max_concurrent`)
and the other three brain sources are in [Agents](/speech-stack/agents).

## 3. Attach a Phone Number

Numbers attach to an **`agent_id`**, not to a pipe id.

```python theme={null}
import asyncio
from unpod import AsyncClient

async def assign_number(agent_id: str) -> None:
    client = AsyncClient()
    numbers = await client.numbers.list(status="active")
    free = [n for n in numbers if n.pipe_id is None]
    if not free:
        print("No free numbers. Provision one in the Unpod dashboard first.")
        return
    await client.numbers.attach(free[0].number_id, agent_id)
    print(f"Attached {free[0].number}")

asyncio.run(assign_number("my-agent"))
```

<Note>
  `client.numbers.attach()` used to take `pipe_id=`. It takes `agent_id` now -
  supervoice resolves the pipe from the agent. `client.agents.numbers.attach(agent_id, number)`
  does the same thing keyed by the E.164 number instead of the id.
</Note>

No numbers yet, or bringing your own carrier? See
[Phone Numbers](/speech-stack/numbers) and [Trunks](/speech-stack/numbers#trunks).

## 4. Start Your Runner

```python theme={null}
from unpod import AgentRunner

AgentRunner(
    entrypoint=entrypoint,
    agent_id="my-agent",   # must match the agent_id from step 2
).start()
```

## Full Setup Script

```python theme={null}
import asyncio
from unpod import AsyncClient, AgentRunner, CallContext, Runner
from unpod.adapters.langchain import LangChainAdapter

async def setup():
    client = AsyncClient()

    # 1. Pick a voice profile
    profiles = await client.voice_profiles.list()
    profile = profiles[0]

    # 2. Create the agent
    voice = await client.agents.voice.create(
        "my-agent",
        brain=Runner(),
        name="My Agent",
        voice_profile=profile.profile_id,
    )

    # 3. Attach the first free number
    free = [n for n in await client.numbers.list() if n.pipe_id is None]
    if free:
        await client.numbers.attach(free[0].number_id, "my-agent")

    print(f"Agent created: {voice.agent_id}")
    print(f"Number: {free[0].number if free else 'none assigned'}")

asyncio.run(setup())
```

Then start your runner in a separate process:

```python theme={null}
async def entrypoint(ctx: CallContext) -> None:
    ctx.session.dialog_machine = LangChainAdapter(your_chain)
    await ctx.session.run()

AgentRunner(entrypoint=entrypoint, agent_id="my-agent").start()
```

## Environment Variables

| Variable                 | Required | Description                                                                                                                                                   |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `UNPOD_API_KEY`          | Yes      | Direct-mode Bearer key. **Required by the `AgentRunner`** (orchestrator connection); also the management-client fallback when `UNPOD_PLATFORM_TOKEN` is unset |
| `UNPOD_PLATFORM_TOKEN`   | No       | Backend-core DRF token - enables management **proxy mode** (preferred for the `AsyncClient`/`Client`). Falls back to `UNPOD_API_KEY` if unset                 |
| `UNPOD_ORG_HANDLE`       | No       | Org handle sent as the `Org-Handle` header alongside `UNPOD_PLATFORM_TOKEN` (required for org-scoped / telephony endpoints)                                   |
| `UNPOD_BASE_URL`         | No       | Single shared endpoint. REST derives `https://<host>/platform` and the runner derives `wss://<host>`                                                          |
| `UNPOD_SERVICE_BASE_URL` | No       | Management REST override only. Takes precedence over `UNPOD_BASE_URL` when set                                                                                |
| `UNPOD_ORCHESTRATOR_URL` | No       | Runner WebSocket override only. Takes precedence over `UNPOD_BASE_URL` when set                                                                               |

### Which URL setting to use

* `UNPOD_BASE_URL` - management API and runner share one host.
* `UNPOD_SERVICE_BASE_URL` - management REST override only.
* `UNPOD_ORCHESTRATOR_URL` - runner WebSocket override only.
* `base_url=` / `orchestrator_base_url=` in code - one script or test overrides `.env`.

Production values and the `serve`-transport extras: [Deploy](/speech-stack/deploy).
