# Ultravox

Connect Ultravox agents to Indian phone numbers over a Vonyx SIP trunk.

Ultravox agents can place calls through your Vonyx trunk and answer calls to your Vonyx numbers. Both directions use plain SIP: Ultravox dials `43.204.9.41` for outbound calls, and Vonyx forwards inbound calls to your Ultravox SIP domain.

## Before you start

- A Vonyx [SIP trunk](/docs/console/sip-trunks) with a [number](/docs/console/numbers) attached
- The trunk's username and password, from the console
- An Ultravox account with SIP enabled. Ultravox charges extra for SIP minutes, and SIP must be enabled for your account.
- An Ultravox API key (sent in the `X-API-Key` header)
- An Ultravox agent. Copy its agent ID.

## Outbound calls

<Steps>
<Step>

### Build the SIP addresses

Ultravox takes SIP URIs, not bare phone numbers. Point `to` at the Vonyx server and set `from` to your Vonyx number.

| Field | Value |
| --- | --- |
| `to` | `sip:91XXXXXXXXXX@43.204.9.41:5060` (the number you're calling) |
| `from` | `sip:91XXXXXXXXXX@43.204.9.41` (your Vonyx number) |
| `username` | `your_trunk_username` |
| `password` | `your_trunk_password` |

UDP is the default. To use TCP, add `;transport=tcp` to `to`, for example `sip:91XXXXXXXXXX@43.204.9.41:5060;transport=tcp`. Don't use `;transport=tls`, because Vonyx doesn't support TLS.

</Step>
<Step>

### Create an agent call

Call the Create Agent Call endpoint with a `medium.sip.outgoing` block. Ultravox sends the INVITE as soon as the call is created, so the response has no `joinUrl`.

```bash title="outbound.sh"
curl -X POST https://api.ultravox.ai/api/agents/YOUR_AGENT_ID/calls \
  -H "X-API-Key: $ULTRAVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "medium": {
      "sip": {
        "outgoing": {
          "to": "sip:91XXXXXXXXXX@43.204.9.41:5060",
          "from": "sip:91XXXXXXXXXX@43.204.9.41",
          "username": "your_trunk_username",
          "password": "your_trunk_password"
        }
      }
    }
  }'
```

The phone rings, and the agent talks once the call is answered.

</Step>
</Steps>

## Inbound calls

Ultravox accepts an incoming INVITE only when it comes from an allowed IP address and its To address matches one of your agents. You set both in your account's SIP configuration.

<Steps>
<Step>

### Find your Ultravox SIP domain

```bash
curl https://api.ultravox.ai/api/sip \
  -H "X-API-Key: $ULTRAVOX_API_KEY"
```

The response's `domain` field is your account's SIP domain. It also returns your current `allowedCidrRanges` and `allowedAgents`.

</Step>
<Step>

### Allow Vonyx and route the number to your agent

Add Vonyx's server as an allowed range, and add an agent allowance whose `toUserPattern` matches your Vonyx number. The pattern is a regular expression matched against the user part of the INVITE's To address. Without a pattern, Ultravox only matches `agent_YOUR_AGENT_ID`, and Vonyx can't send that user part.

```bash
curl -X PATCH https://api.ultravox.ai/api/sip \
  -H "X-API-Key: $ULTRAVOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "allowedCidrRanges": ["43.204.9.41/32"],
    "allowedAgents": [
      { "agentId": "YOUR_AGENT_ID", "toUserPattern": "^91XXXXXXXXXX$" }
    ]
  }'
```

Include any ranges and allowances you already have, from the previous step. You can add up to 20 allowances. Ultravox checks them in order. For a single agent that answers every number, use the pattern `.*`.

</Step>
<Step>

### Point Vonyx at Ultravox

In the Vonyx console, open the trunk, turn on **Inbound calls** and set **Inbound address** to your SIP domain from the first step, with no `sip:` and no user part. See [inbound calls](/docs/console/inbound-calls).

```text
YOUR_ULTRAVOX_SIP_DOMAIN
```

</Step>
</Steps>

<Callout title="Routing more than one number">
Add one allowance per number, each with its own `agentId` and `toUserPattern`. To decide in code, set a `fallbackHandler` webhook on the SIP configuration. Ultravox calls it for INVITEs that match no allowance, and the webhook replies with `startAgentCall`, `startCall` or `reject`.
</Callout>

## Test the connection

1. Run the outbound request with your own mobile as `to`. Your phone should ring with your Vonyx number as caller ID.
2. Call your Vonyx number from another phone. The agent should answer.
3. If a call fails, wait for it to end, then get its SIP logs from Ultravox. Use `GET https://api.ultravox.ai/api/calls/YOUR_CALL_ID/sip_logs` with your `X-API-Key` header; it redirects to the log file. Then see [troubleshooting](/docs/sip/troubleshooting).

## Limitations

- DTMF and call transfer are not available on Vonyx yet. Don't give Ultravox agents the `playDtmfSounds` tool or transfer tools on Vonyx calls.
- New accounts make 1 call at a time ([raise limits](/docs/sip/limits)).
- Audio uses G.711 (PCMU or PCMA) at 8 kHz. Ultravox's HD codecs (G.722, Opus) aren't used on Vonyx calls.
- No TLS or SRTP. Use UDP or TCP.
- Ultravox bills SIP minutes on top of call minutes.

## Troubleshooting

| Problem | Fix |
| --- | --- |
| Outbound call fails right away | Check `username` and `password` match the trunk, and that `to` is a full SIP URI (`sip:91XXXXXXXXXX@43.204.9.41:5060`), not a bare number. |
| Call connects with wrong or blocked caller ID | `from` must use one of your Vonyx numbers. |
| Second call fails while one is live | You're at your channel limit. [Raise limits](/docs/sip/limits). |
| Inbound call is rejected | Check `allowedCidrRanges` contains `43.204.9.41/32` and a `toUserPattern` matches your number. Ultravox rejects calls that match no agent and have no fallback handler. |
| Inbound call never reaches Ultravox | Check the trunk's inbound address is exactly your Ultravox `domain`, and that **Inbound calls** is on. |
| One-way or no audio | Allow RTP over UDP on ports 10000–20000 from `43.204.9.41`. |
| Call fails during setup with a codec error | Vonyx only offers PCMU and PCMA. Both are on Ultravox's supported list, so check any device in between isn't rewriting the SDP. |
