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

# LiveKit Integration

> Connect Plivo SIP trunking with LiveKit for real-time voice AI applications using the Plivo API or Console

Connect Plivo SIP trunking to LiveKit to enable your voice AI applications to make and receive phone calls through Plivo's global voice network. You can configure Plivo using the [API](/docs/sip-trunking/api/overview) or the [Console](https://cx.plivo.com/); each step in this guide includes both options.

***

## Prerequisites

| Requirement               | Description                                                                                                                                                             |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Plivo Account**         | [Sign up](https://cx.plivo.com/signup) with SIP trunking enabled                                                                                                        |
| **Phone Number**          | [Purchase a voice-enabled number](https://cx.plivo.com/phone-numbers)<br />**India:** Requires KYC verification. See [Rent India Numbers](/docs/numbers/rent-india-numbers). |
| **LiveKit Cloud Project** | [Create a project](https://cloud.livekit.io)                                                                                                                            |

If you're using the API, you also need your Plivo **Auth ID** and **Auth Token**, available on the [Plivo Console home page](https://cx.plivo.com/home). The Plivo API uses HTTP Basic authentication. Export your credentials as environment variables to use with the code samples in this guide:

```shell theme={null}
export PLIVO_AUTH_ID="<your_auth_id>"
export PLIVO_AUTH_TOKEN="<your_auth_token>"
```

<Tip>
  **Optimize for latency:** Select the LiveKit region closest to your call traffic. This minimizes audio delay and improves conversation flow.
</Tip>

<Warning>
  **India regional requirement:** If your calls are made from a Plivo India phone number, or you're dialing numbers in India, you must enable [region pinning](https://docs.livekit.io/telephony/features/region-pinning/) for your LiveKit project. This is a regulatory requirement; calls will fail to connect otherwise. See [Calling in India](/docs/voice-agents/sip-trunking/deploy/calling-in-india).
</Warning>

***

## Part 1: Receive incoming calls

Route calls from your Plivo phone number to LiveKit.

<Steps>
  <Step title="Create an inbound trunk in Plivo" titleSize="h3">
    Create an inbound trunk in Plivo, setting your LiveKit [SIP endpoint](https://docs.livekit.io/telephony/start/sip-trunk-setup/#sip-endpoint) as the primary URI.

    <Tabs>
      <Tab title="API">
        First, create an [origination URI](/docs/sip-trunking/api/origination-uris) that points to your LiveKit SIP endpoint. Include `;transport=tcp` in the URI:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Zentrunk/URI/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -H 'Accept: application/json' \
          -d '{
            "name": "LiveKit SIP endpoint",
            "uri": "<livekit_sip_host>;transport=tcp"
          }'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Zentrunk/URI/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              name: 'LiveKit SIP endpoint',
              uri: '<livekit_sip_host>;transport=tcp',
            }),
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]

          response = requests.post(
              f"https://api.plivo.com/v1/Account/{auth_id}/Zentrunk/URI/",
              auth=(auth_id, auth_token),
              json={
                  "name": "LiveKit SIP endpoint",
                  "uri": "<livekit_sip_host>;transport=tcp",
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        Copy the `uri_uuid` from the response for the next step:

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "message": "Origination URI created successfully",
          "uri_uuid": "<uri_uuid>"
        }
        ```

        Next, create an [inbound trunk](/docs/sip-trunking/api/trunks) using the origination URI as the primary URI:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Zentrunk/Trunk/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -H 'Accept: application/json' \
          -d '{
            "name": "My LiveKit inbound trunk",
            "trunk_direction": "inbound",
            "primary_uri_uuid": "<uri_uuid>"
          }'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Zentrunk/Trunk/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              name: 'My LiveKit inbound trunk',
              trunk_direction: 'inbound',
              primary_uri_uuid: '<uri_uuid>',
            }),
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]

          response = requests.post(
              f"https://api.plivo.com/v1/Account/{auth_id}/Zentrunk/Trunk/",
              auth=(auth_id, auth_token),
              json={
                  "name": "My LiveKit inbound trunk",
                  "trunk_direction": "inbound",
                  "primary_uri_uuid": "<uri_uuid>",
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        Copy the `trunk_id` from the response. You use it to connect your phone number in the next step:

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "message": "Trunk created successfully.",
          "trunk_id": "<trunk_id>"
        }
        ```
      </Tab>

      <Tab title="Console">
        1. Sign in to the [Plivo Console](https://cx.plivo.com/).
        2. Navigate to **SIP Trunking** → [**Inbound Trunks**](https://cx.plivo.com/sip-trunking/inbound).
        3. Select **Create Trunk** and provide a descriptive name for your trunk.
        4. For **Primary URI**, select **Add New URI** and enter your LiveKit [SIP endpoint](https://docs.livekit.io/telephony/start/sip-trunk-setup/#sip-endpoint). Include `;transport=tcp` in the URI. For example, `vjnxecm0tjk.sip.livekit.cloud;transport=tcp`.
        5. For **Link Numbers**, select your phone number from the dropdown menu. Or connect your phone number in the next step.
        6. Select **Create Trunk**.
      </Tab>
    </Tabs>

    <Info>
      **Secure trunking:** If you're setting up [secure trunking](https://docs.livekit.io/telephony/features/secure-trunking/), use `;transport=tls` instead of `;transport=tcp`.
    </Info>

    <Info>
      **Region-based endpoints:** To restrict calls to a specific region, replace your global LiveKit SIP endpoint with a [region-based endpoint](https://docs.livekit.io/telephony/features/region-pinning/).
    </Info>
  </Step>

  <Step title="Connect your phone number" titleSize="h3">
    Connect your Plivo phone number to the inbound trunk. You can skip this step if you connected your phone number when you created the inbound trunk.

    <Tabs>
      <Tab title="API">
        Assign the inbound trunk to your phone number using the [update a number](/docs/numbers/account-phone-numbers#update-an-account-phone-number) endpoint. Set the `app_id` field to the **trunk ID** of your inbound trunk. Use your phone number in E.164 format without the leading `+`. For example, `15105550100`:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Number/15105550100/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -H 'Accept: application/json' \
          -d '{
            "app_id": "<trunk_id>"
          }'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');
          const phoneNumber = '15105550100';

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Number/${phoneNumber}/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              app_id: '<trunk_id>',
            }),
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]
          phone_number = "15105550100"

          response = requests.post(
              f"https://api.plivo.com/v1/Account/{auth_id}/Number/{phone_number}/",
              auth=(auth_id, auth_token),
              json={
                  "app_id": "<trunk_id>",
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        A successful response returns HTTP `202`:

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "message": "changed"
        }
        ```

        To list your purchased numbers, run the following command:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Number/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Accept: application/json'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Number/`, {
            headers: { 'Authorization': authHeader },
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]

          response = requests.get(
              f"https://api.plivo.com/v1/Account/{auth_id}/Number/",
              auth=(auth_id, auth_token),
          )
          print(response.json())
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Console">
        1. Navigate to **Phone Numbers** → [**Purchased Numbers**](https://cx.plivo.com/phone-numbers/list).
        2. Select the phone number to connect to the trunk.
        3. For **Application Type**, select **SIP Trunk**.
        4. For **Trunk**, select the trunk you created in the previous step.
        5. Select **Save changes**.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure LiveKit to accept calls" titleSize="h3">
    Set up an [inbound trunk](https://docs.livekit.io/telephony/accepting-calls/inbound-trunk/) and [dispatch rule](https://docs.livekit.io/telephony/accepting-calls/dispatch-rule/) in LiveKit to accept calls to your Plivo phone number.
  </Step>

  <Step title="Test incoming calls" titleSize="h3">
    Start your LiveKit agent and call your Plivo phone number. Your agent should answer the call. If you don't have an agent, see the [Voice AI quickstart](https://docs.livekit.io/agents/start/voice-ai/) to create one.
  </Step>
</Steps>

***

## Part 2: Make outgoing calls

Enable LiveKit to make outbound calls through Plivo.

<Steps>
  <Step title="Create an outbound trunk in Plivo" titleSize="h3">
    Set up an outbound trunk with username and password authentication in Plivo.

    <Tabs>
      <Tab title="API">
        First, create a [credential](/docs/sip-trunking/api/credentials) with a username and strong password for outbound call authentication:

        * **Username:** 5 to 20 characters, alphanumeric only.
        * **Password:** 5 to 20 characters, using only alphanumeric characters and the special characters `~!@#$%^&*()_+`, with at least one special character.
        * Use the same username and password when you configure your LiveKit outbound trunk.

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Zentrunk/Credential/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -H 'Accept: application/json' \
          -d '{
            "name": "LiveKit outbound credential",
            "username": "<username>",
            "password": "<password>"
          }'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Zentrunk/Credential/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              name: 'LiveKit outbound credential',
              username: '<username>',
              password: '<password>',
            }),
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]

          response = requests.post(
              f"https://api.plivo.com/v1/Account/{auth_id}/Zentrunk/Credential/",
              auth=(auth_id, auth_token),
              json={
                  "name": "LiveKit outbound credential",
                  "username": "<username>",
                  "password": "<password>",
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        Copy the `credential_uuid` from the response for the next step:

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "message": "Credential created successfully",
          "credential_uuid": "<credential_uuid>"
        }
        ```

        Next, create an [outbound trunk](/docs/sip-trunking/api/trunks) using the credential:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Zentrunk/Trunk/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -H 'Accept: application/json' \
          -d '{
            "name": "My LiveKit outbound trunk",
            "trunk_direction": "outbound",
            "credential_uuid": "<credential_uuid>",
            "secure": true
          }'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Zentrunk/Trunk/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              name: 'My LiveKit outbound trunk',
              trunk_direction: 'outbound',
              credential_uuid: '<credential_uuid>',
              secure: true,
            }),
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]

          response = requests.post(
              f"https://api.plivo.com/v1/Account/{auth_id}/Zentrunk/Trunk/",
              auth=(auth_id, auth_token),
              json={
                  "name": "My LiveKit outbound trunk",
                  "trunk_direction": "outbound",
                  "credential_uuid": "<credential_uuid>",
                  "secure": True,
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        Copy the `trunk_id` from the response:

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "message": "Trunk created successfully.",
          "trunk_id": "<trunk_id>"
        }
        ```

        Finally, retrieve the trunk to get your **Termination SIP Domain**. It's returned in the `trunk_domain` field. For example, `21784177241578.zt.plivo.com`:

        <CodeGroup>
          ```shell cURL theme={null}
          curl "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Zentrunk/Trunk/<trunk_id>/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Accept: application/json'
          ```

          ```javascript Node theme={null}
          const authId = process.env.PLIVO_AUTH_ID;
          const authToken = process.env.PLIVO_AUTH_TOKEN;
          const authHeader = 'Basic ' + Buffer.from(`${authId}:${authToken}`).toString('base64');
          const trunkId = '<trunk_id>';

          const response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Zentrunk/Trunk/${trunkId}/`, {
            headers: { 'Authorization': authHeader },
          });
          console.log(await response.json());
          ```

          ```python Python theme={null}
          import os
          import requests

          auth_id = os.environ["PLIVO_AUTH_ID"]
          auth_token = os.environ["PLIVO_AUTH_TOKEN"]
          trunk_id = "<trunk_id>"

          response = requests.get(
              f"https://api.plivo.com/v1/Account/{auth_id}/Zentrunk/Trunk/{trunk_id}/",
              auth=(auth_id, auth_token),
          )
          print(response.json())
          ```
        </CodeGroup>

        ```json theme={null}
        {
          "api_id": "<api_id>",
          "object": {
            "trunk_id": "<trunk_id>",
            "name": "My LiveKit outbound trunk",
            "trunk_status": "enabled",
            "secure": true,
            "trunk_domain": "<trunk_id>.zt.plivo.com",
            "trunk_direction": "outbound",
            "ipacl_uuid": null,
            "credential_uuid": "<credential_uuid>",
            "primary_uri_uuid": null,
            "fallback_uri_uuid": null
          }
        }
        ```

        Copy the **Termination SIP Domain** (`trunk_domain`) for the next step.
      </Tab>

      <Tab title="Console">
        1. Sign in to the [Plivo Console](https://cx.plivo.com/).
        2. Navigate to **SIP Trunking** → [**Outbound Trunks**](https://cx.plivo.com/sip-trunking/outbound).
        3. Select **Create Trunk** and provide a descriptive name for your trunk.
        4. In the **Trunk Authentication** section → **Credential**, select **Create new credential**.
        5. Add a credential name, and a username and strong password for outbound call authentication, then select **Create credential**.
           * **Username:** 5 to 20 characters, alphanumeric only.
           * **Password:** 5 to 20 characters, using only alphanumeric characters and the special characters `~!@#$%^&*()_+`, with at least one special character.
           * Use the same username and password when you configure your LiveKit outbound trunk.
        6. For **Authentication**, select the credential you created in the previous step.
        7. Enable **Secure Trunking**.
        8. Select **Create Trunk** to complete your outbound trunk configuration.

        Copy the **Termination SIP Domain** for the next step.
      </Tab>
    </Tabs>

    <Info>
      **Secure trunking:** If you enable secure trunking in Plivo (`"secure": true`), you must also enable secure trunking in LiveKit. To learn more, see [Secure trunking](https://docs.livekit.io/telephony/features/secure-trunking/).
    </Info>
  </Step>

  <Step title="Configure LiveKit to make outbound calls" titleSize="h3">
    Create an [outbound trunk](https://docs.livekit.io/telephony/making-calls/outbound-trunk/) in LiveKit using the **Termination SIP Domain**, **username**, and **password** from the previous step.
  </Step>

  <Step title="Place an outbound call" titleSize="h3">
    Test your configuration by placing an outbound call with LiveKit using the `CreateSIPParticipant` API. To learn more, see [Creating a SIP participant](https://docs.livekit.io/telephony/making-calls/outbound-calls/#creating-a-sip-participant).
  </Step>
</Steps>

***

## Troubleshooting

If a call fails to connect, check the following common issues:

| Issue                 | Solution                                                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Call doesn't connect  | Verify your SIP URI includes `;transport=tcp`. Verify your Plivo phone number is associated with the correct trunk.      |
| Authentication errors | Verify credentials match exactly in both Plivo and LiveKit.                                                              |
| India calls failing   | Ensure [region pinning](https://docs.livekit.io/telephony/features/region-pinning/) is enabled for your LiveKit project. |

**Debug logs:**

* Inbound issues: First check the [Plivo logs](https://cx.plivo.com/logs/sip-trunking), then check the [call logs](https://cloud.livekit.io/projects/p_/telephony) in your LiveKit Cloud dashboard.
* Outbound issues: First check the [call logs](https://cloud.livekit.io/projects/p_/telephony) in your LiveKit Cloud dashboard, then check the [Plivo logs](https://cx.plivo.com/logs/sip-trunking).

For error codes, see [Plivo hangup codes](/docs/voice-agents/sip-trunking/troubleshooting/hangup-codes).

***

## Related

<CardGroup cols={2}>
  <Card title="SIP Trunking API overview" href="/docs/sip-trunking/api/overview">
    Plivo SIP Trunking API reference.
  </Card>

  <Card title="SIP Trunking overview" href="/docs/sip-trunking">
    Plivo SIP trunking documentation.
  </Card>

  <Card title="LiveKit SIP documentation" href="https://docs.livekit.io/sip/">
    Complete LiveKit SIP configuration.
  </Card>

  <Card title="LiveKit Plivo guide" href="https://docs.livekit.io/telephony/start/providers/plivo/">
    LiveKit's official Plivo integration guide.
  </Card>
</CardGroup>
