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

# Telephony Platforms

> Connect Ultravox with telephony platforms for AI-powered voice calls.

Ultravox Realtime integrates with major telephony providers to enable AI-powered voice applications that work with regular
phone numbers. Your AI agents can make outbound calls and answer inbound calls through traditional phone networks.

<Note>
  <b>SIP is separate</b>

  This page describes integrations with telephony providers like Twilio, Telnyx, Plivo, and Exotel.
  If you want to connect Ultravox to your existing phone system using SIP, see our [SIP Guide](./sip).
</Note>

## Native Integrations

Ultravox provides native integrations for the following. Each has their own unique call [`medium`](/api-reference/schema/call-definition#schema-medium) that must be used when creating calls.

| Provider   | Call Medium    | Streaming API                                                                                  | Import Credentials? | HD audio | Out of Band DTMF? | Cold transfer?\* |
| ---------- | -------------- | ---------------------------------------------------------------------------------------------- | ------------------- | -------- | ----------------- | ---------------- |
| **Twilio** | `"twilio": {}` | [Media Streams](https://www.twilio.com/docs/voice/media-streams)                               | ✅                   | ❌        | ❌                 | ✅                |
| **Telnyx** | `"telnyx": {}` | [Media Streaming](https://developers.telnyx.com/docs/voice/programmable-voice/media-streaming) | ✅                   | ✅        | ✅                 | ❌                |
| **Plivo**  | `"plivo": {}`  | [AudioStream](https://www.plivo.com/docs/voice/xml/the-stream-element/)                        | ✅                   | ✅        | ✅                 | ❌                |
| **Exotel** | `"exotel": {}` | [Voice Streaming](https://developer.exotel.com/api/product-voice-version-3)                    | ❌                   | ❌        | ❌                 | ❌                |

\* Transfers are possible with any integration, but the built-in tool is limited to particular providers. See the [transfers page](/telephony/call-transfers) for details.

## Providing Telephony Credentials

If you are using Twilio, Telnyx, or Plivo, you can import your credentials to unlock new capabilities:

* **Simplified Incoming Call Handling** → Configure your provider to send webhooks directly to Ultravox for [incoming calls](#simplified-incoming-call-handling). No need to run your own server.
* **Simplified Outbound Calling** → Allow Ultravox to directly create and connect [outbound calls](#simplified-outbound-calling).
* **Outbound Call Scheduler** → [Schedule batches of outbound calls](/telephony/outbound-call-scheduler) and let us manage the call concurrency and retry logic.
* **Out of Band DTMF** → Importing credentials allows Ultravox to use [out-of-band DTMF](/telephony/ivr-flows#sending-dtmf-tones).

You can import your credentials in the Ultravox console or via API.
For the APIs, see [Twilio](/api-reference/telephony/twilio-post), [Telnyx](/api-reference/telephony/telnyx-post), or [Plivo](/api-reference/telephony/plivo-post).

## Incoming Calls

### Simplified Incoming Call Handling

Once you've [added your credentials](#providing-telephony-credentials), just set your telephony provider's webhook callback
to `https://app.ultravox.ai/api/agents/{agent_id}/telephony_xml` to have incoming phone calls automatically create and connect
to Ultravox calls.

Note that agents need to be allowlisted to receive incoming calls. If you did not add your agent when importing your credentials,
you can add it afterward in the Ultravox console or using the `PATCH` verb: [Twilio](/api-reference/telephony/twilio-patch), [Telnyx](/api-reference/telephony/telnyx-patch), or [Plivo](/api-reference/telephony/plivo-patch).

You can also allow all agents to receive calls by setting `allowAllAgents` to `true` when importing your credentials or updating them afterward.

If there are fields in your provider's webhook that you want to use as template variables in your agent, you can set up a mapping from the webhook fields to template context fields. For example, for Twilio you could add an entry like `{"From": "user.phone_number"}` to add `{"user": {"phone_number": "+15551234567"}}` to the template context.

### Custom Incoming Call Handling

Alternatively, you can use these steps when routing calls through your own application.

<Steps>
  <Step title="User Dials You">
    User dials your phone number purchased from your telephony provider.
  </Step>

  <Step title="Incoming Webhook">
    Provider routes the call to your configured webhook/application.
  </Step>

  <Step title="Create Ultravox Call">
    Your server creates an Ultravox call using the medium for your provider and gets a `joinUrl`.
  </Step>

  <Step title="Connect & Answer">
    Connect the call to your provider using the `joinUrl`. The AI agent answers and begins the conversation.
  </Step>
</Steps>

## Outbound Calls

<Note>
  <b>firstSpeakerSettings</b>

  By default, Ultravox calls assume the agent begins conversations. This is typically what you want for inbound calls, but for outbound calls consider changing your [`firstSpeakerSettings`](/api-reference/schema/call-definition#schema-first-speaker-settings) so the agent waits for the user to answer.
</Note>

### Simplified Outbound Calling

Once you've [added your credentials](#providing-telephony-credentials), you can add an `outgoing` field to your medium to have Ultravox
create and connect outbound calls for you.

```js Outgoing Example theme={null}
{
  "systemPrompt": "You are calling to remind John Doe about their appointment.",
  "firstSpeakerSettings": {
    "user": {  // We expect the user to speak first since this is outgoing
      "fallback": { // But we add a fallback in case the user waits for the agent to speak
        "delay": "10s",
        "text": "Hello, is John available to confirm an appointment?"
      }
    }
  },
  "medium": {
    "twilio": { // or "telnyx": {} or "plivo": {}
      "outgoing": {
        "to": "+15551234567",
        "from": "+15559876543",
        "additionalParams": { // Optional.
          // See your provider's docs for all options. These will be added to the call creation request to your provider.
          "statusCallback": "https://your-server.com/status"
        }
      }
    }
  }
}
```

We recommend configuring your `systemPrompt` and `firstSpeakerSettings` in an agent so you only need to specify medium when
[creating calls with the agent](/api-reference/agents/agents-calls-post).

### Custom Outbound Calling

You can also integrate Ultravox with your existing telephony workflows by creating calls manually through your provider's API.

<Steps>
  <Step title="Call Trigger">
    Your application triggers an outbound call (user action, scheduled event, etc.).
  </Step>

  <Step title="Create Ultravox Call">
    Create an Ultravox call with the medium corresponding to your provider.
  </Step>

  <Step title="Initiate Phone Call">
    Initiate the phone call using your telephony provider's API and connect it to Ultravox using the `joinUrl`. User answers and the agent engages in the conversation.
  </Step>
</Steps>

### Batch Outbound Calling

For scheduling large volumes of outbound calls, use the [Outbound Call Scheduler](/telephony/outbound-call-scheduler) which provides:

* **Automatic Concurrency Management** - No more 429 errors from hitting rate limits
* **Flexible Scheduling** - Define time windows for when calls should be made
* **Automatic capacity reservation** - Save room for high priority or incoming calls while your campaign is running
* **Batch Management** - Track progress and control execution

## Provider-Specific Integration Examples

### Twilio

#### Outbound Calls with Twilio

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call as shown above with `medium: { "twilio": {} }`, `firstSpeakerSettings: { user: {} }`, and get a `joinUrl`.
  </Step>

  <Step title="Connect Ultravox to the Twilio Phone Call">
    Use the `joinUrl` with a Twilio `<Stream>`:

    ```js theme={null}
    // Example using the twilio node library
    const call = await client.calls.create({
        twiml: `<Response>
                    <Connect>
                        <Stream url="${joinUrl}"/>
                    </Connect>
                </Response>`,
        to: phoneNumber,
        from: twilioPhoneNumber
    });
    ```
  </Step>
</Steps>

<Info>Full example code in [Outbound Quickstart →](/gettingstarted/quickstart/telephony-outbound)</Info>

#### Incoming Calls with Twilio

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call with `medium: { "twilio": {} }` and `firstSpeakerSettings` set to `{ agent: {} }`.
  </Step>

  <Step title="Connect the Inbound Twilio Call to Ultravox">
    Use the `joinUrl` with a Twilio `<Stream>`:

    ```xml theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
        <Connect>
            <Stream url="your_ultravox_join_url" />
        </Connect>
    </Response>
    ```
  </Step>
</Steps>

<Info>Full example code in [Inbound Quickstart →](/gettingstarted/quickstart/telephony-inbound)</Info>

### Telnyx

#### Outbound Calls with Telnyx

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call as shown above with `medium: { "telnyx": {} }`, `firstSpeakerSettings: { user: {} }`, and get a `joinUrl`.
  </Step>

  <Step title="Connect Ultravox to the Telnyx Phone Call">
    Use the `joinUrl` with a TeXML `<Stream>`:

    ```js theme={null}
    // Example using the telnyx node library
    const call = await telnyx.calls.create({
      connection_id: "uuid",
      to: phoneNumber,
      from: telnyxPhoneNumber,
      stream_url: joinUrl,
      stream_track: "inbound_track",
      stream_bidirectional_mode: "rtp"
      stream_codec: "L16",
      stream_bidirectional_codec: "L16",
      stream_bidirectional_sampling_rate: 16000,
      stream_bidirectional_target_legs: "opposite",
    });
    ```

    Or using TeXML:

    ```xml theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <Connect>
        <Stream url="${joinUrl}" bidirectionalMode="rtp"  codec="L16" bidirectionalCodec="L16" bidirectionalSamplingRate="16000"/>
      </Connect>
    </Response>
    ```
  </Step>
</Steps>

#### Incoming Calls with Telnyx

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call with `medium: { "telnyx": {} }` and `firstSpeakerSettings` set to `{ agent: {} }`.
  </Step>

  <Step title="Connect the Inbound Telnyx Call to Ultravox">
    Use the `joinUrl` with a TeXML `<Stream>`:

    ```xml theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
      <Connect>
        <Stream url="${joinUrl}" bidirectionalMode="rtp"  codec="L16" bidirectionalCodec="L16" bidirectionalSamplingRate="16000"/>
      </Connect>
    </Response>
    ```
  </Step>
</Steps>

<Warning>
  <b>Telnyx `codec`</b>

  <br />

  Telnyx allows setting both `codec` and `bidirectionalCodec`. The former controls user audio while the latter controls agent audio. When using with Ultravox, **these must have the same value** because Telnyx only tells us about one of them! Now that Telnyx supports HD Audio, you most likely want "L16" for both.
</Warning>

For more details, see the [Telnyx documentation](https://developers.telnyx.com/).

### Plivo

<Info>Full example code for outbound and inbound calls with Plivo on GitHub [here →](https://github.com/fixie-ai/ultravox-examples/tree/main/telephony/plivo/plivo-phone-calls-ts)</Info>

#### Outbound Calls with Plivo

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call as shown above with `medium: { "plivo": {} }`, `firstSpeakerSettings: { user: {} }`, and get a `joinUrl`.
  </Step>

  <Step title="Connect Ultravox to the Plivo Phone Call">
    Use the `joinUrl` with AudioStream:

    ```js theme={null}
    // Example using the plivo node library
    // This assumes our server exposes an endpoint at `answerUrl`
    const call = await plivo.calls.create({
      to: phoneNumber,
      from: plivoPhoneNumber,
      answer_url: answerUrl, // URL that returns the XML below
      answer_method: "GET"
    });
    ```

    The answer URL should return:

    ```xml theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
        <Stream keepCallAlive="true"
                contentType="audio/x-l16;rate=16000"
                bidirectional="true">
            ${joinUrl}
        </Stream>
    </Response>
    ```

    Note: For best audio quality, we recommend `audio/x-l16;rate=16000`. However, any contentType supported by Plivo will work with Ultravox.
  </Step>
</Steps>

#### Incoming Calls with Plivo

<Steps>
  <Step title="Create an Ultravox Call">
    Create a new call with `medium: { "plivo": {} }` and `firstSpeakerSettings` set to `{ agent: {} }`.
  </Step>

  <Step title="Connect the Inbound Twilio Call to Ultravox">
    Use the `joinUrl` with AudioStream:

    ```xml theme={null}
    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
        <Stream keepCallAlive="true"
                contentType="audio/x-l16;rate=16000"
                bidirectional="true">
            ${joinUrl}
        </Stream>
    </Response>
    ```
  </Step>
</Steps>

For more details, see the [Plivo documentation](https://www.plivo.com/docs/).

## Learn More

* Learn how to handle [IVR trees using DTMF](/telephony/ivr-flows).
* Set up your agent to [transfer calls to a human](/telephony/call-transfers).
