> ## 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.

# Dograh Integration

> Connect Plivo Audio Streaming with Dograh to power self-hosted voice AI agents

[Dograh](https://www.dograh.com/) is an open-source, self-hostable voice AI platform for building phone agents. Plivo Audio Streaming bridges a phone call to a Dograh voice agent over a WebSocket.

***

## How it works

<div className="sipflow sipflow--vertical">
  <div className="sipflow__head">
    <span>Call flow</span>

    <span className="sipflow__head-right">
      <span className="sipflow__led" aria-hidden="true" />

      Dograh
    </span>
  </div>

  <ol className="sipflow__body">
    <li>
      <div className="sipflow__node">
        <span className="sipflow__node-body">
          <span className="sipflow__title">Phone Call</span>
        </span>
      </div>

      <div className="sipflow__link sipflow__link--bi" aria-hidden="true">
        <span className="sipflow__arrow sipflow__arrow--back" />

        <span className="sipflow__rail" />

        <span className="sipflow__arrow" />
      </div>

      <div className="sipflow__node">
        <span className="sipflow__node-body">
          <span className="sipflow__title">Plivo</span>
          <span className="sipflow__sub">Audio Stream</span>
        </span>
      </div>

      <div className="sipflow__link sipflow__link--bi">
        <span className="sipflow__arrow sipflow__arrow--back" aria-hidden="true" />

        <span className="sipflow__rail" aria-hidden="true" />

        <span className="sipflow__edge-label">WebSocket</span>
        <span className="sipflow__edge-label sipflow__edge-label--return">WebSocket server</span>

        <span className="sipflow__arrow" aria-hidden="true" />
      </div>

      <div className="sipflow__node">
        <span className="sipflow__node-body">
          <span className="sipflow__title">Dograh</span>
          <span className="sipflow__sub">Voice agent</span>
        </span>
      </div>
    </li>
  </ol>
</div>

Dograh handles the Plivo setup. It creates a Plivo Application and points the Application's **Answer URL** to a Dograh endpoint, so hosting an Answer URL is not required.

The Dograh endpoint hosts the WebSocket server that streams the call's audio.

On a call, Plivo requests the Answer URL of the auto-created Application and connects to Dograh's WebSocket server, then streams the live call audio in both directions. Dograh's voice agent transcribes the caller, generates a response, and streams the reply back as speech to Plivo over the same WebSocket.

***

## Prerequisites

| Requirement       | Description                                                                                                                    |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **Plivo account** | [Sign up](https://cx.plivo.com/signup) and copy the Auth ID and Auth Token from the [Plivo console](https://cx.plivo.com/home) |
| **Phone number**  | A [voice-enabled Plivo number](https://cx.plivo.com/phone-numbers) in E.164 format                                             |
| **Dograh**        | Dograh Cloud, or a self-hosted deployment reachable over HTTPS                                                                 |

***

## Outbound call setup

For outbound calls, Dograh dials out with a caller ID from the Plivo configuration. No Plivo console changes are required.

<Steps>
  <Step title="Get the Plivo credentials" titleSize="h3">
    Sign in to the [Plivo console](https://cx.plivo.com) and copy the **Auth ID** and **Auth Token** from the dashboard.
  </Step>

  <Step title="Add a Plivo configuration in Dograh" titleSize="h3">
    <Tabs>
      <Tab title="API">
        Send the Plivo credentials to Dograh, authenticated with a Dograh API key.

        The `application_id` should be unset and Dograh creates a Plivo Application automatically.

        * `config.auth_id`, `config.auth_token` — the Plivo Auth ID and Auth Token.
        * `is_default_outbound: true` — makes this the default configuration for outbound calls.

        Refer to [Create a telephony configuration](https://docs.dograh.com/api-reference/telephony-configs/create) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          curl -X POST "https://app.dograh.com/api/v1/organizations/telephony-configs" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "name": "Plivo",
            "is_default_outbound": true,
            "config": {
              "provider": "plivo",
              "auth_id": "<plivo_auth_id>",
              "auth_token": "<plivo_auth_token>"
            }
          }'
          ```

          ```javascript Node theme={null}
          const response = await fetch(
            'https://app.dograh.com/api/v1/organizations/telephony-configs',
            {
              method: 'POST',
              headers: {
                'X-API-Key': process.env.DOGRAH_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                name: 'Plivo',
                is_default_outbound: true,
                config: {
                  provider: 'plivo',
                  auth_id: '<plivo_auth_id>',
                  auth_token: '<plivo_auth_token>',
                },
              }),
            },
          );
          console.log(await response.json());
          ```

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

          response = requests.post(
              "https://app.dograh.com/api/v1/organizations/telephony-configs",
              headers={"X-API-Key": os.environ["DOGRAH_API_KEY"]},
              json={
                  "name": "Plivo",
                  "is_default_outbound": True,
                  "config": {
                      "provider": "plivo",
                      "auth_id": "<plivo_auth_id>",
                      "auth_token": "<plivo_auth_token>",
                  },
              },
          )
          print(response.json())
          ```
        </CodeGroup>

        The response returns the configuration's ID. Use it as the `config_id` when adding the default caller ID in the next step.
      </Tab>

      <Tab title="Console">
        1. In Dograh, go to **/telephony-configurations** and select **Add configuration**.
        2. Choose **Plivo** as the provider and enter the Auth ID and Auth Token.
        3. Leave **Application ID** blank. Dograh creates a Plivo Application when the configuration is saved.
        4. Select **Save Configuration**.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Add the default caller ID" titleSize="h3">
    Add a **phone number** to the saved configuration in E.164 format, for example `+14150000001`. This is the **From number** (the caller ID) for outbound calls.

    <Tabs>
      <Tab title="API">
        Add the number to the configuration and mark it as the default caller ID, authenticated with a Dograh API key.

        Use the `config_id` returned when the configuration is created, or shown on the saved configuration in Dograh.

        * `address` — the phone number in E.164 format.
        * `is_default_caller_id: true` — makes it the default caller ID for outbound calls.

        Refer to [Add a phone number](https://docs.dograh.com/api-reference/telephony-configs/phone-numbers/create) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          curl -X POST "https://app.dograh.com/api/v1/organizations/telephony-configs/<config_id>/phone-numbers" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "address": "+14150000001",
            "is_default_caller_id": true
          }'
          ```

          ```javascript Node theme={null}
          const configId = '<config_id>';

          const response = await fetch(
            `https://app.dograh.com/api/v1/organizations/telephony-configs/${configId}/phone-numbers`,
            {
              method: 'POST',
              headers: {
                'X-API-Key': process.env.DOGRAH_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                address: '+14150000001',
                is_default_caller_id: true,
              }),
            },
          );
          console.log(await response.json());
          ```

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

          config_id = "<config_id>"

          response = requests.post(
              f"https://app.dograh.com/api/v1/organizations/telephony-configs/{config_id}/phone-numbers",
              headers={"X-API-Key": os.environ["DOGRAH_API_KEY"]},
              json={"address": "+14150000001", "is_default_caller_id": True},
          )
          print(response.json())
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Console">
        1. Open the saved configuration in Dograh.
        2. In the **Phone numbers** section, add the number in E.164 format.

        The number then serves as the default caller ID for outbound calls.
      </Tab>
    </Tabs>

    <Note>
      This number is not linked to a specific agent. Any agent can use this number as its caller ID when the call is started in Dograh.
    </Note>
  </Step>

  <Step title="Place an outbound call" titleSize="h3">
    Trigger a Dograh agent to call a destination number. The call dials from the default caller ID set above.

    <Tabs>
      <Tab title="API">
        Dograh dials the destination number and runs the agent on the call, authenticated with a Dograh API key.

        * `workflow_uuid` — in the URL path, the agent that places the call.
        * `phone_number` — the destination number in E.164 format.
        * `telephony_configuration_id` — optional, routes the call through a specific configuration instead of the default.

        Refer to [Trigger an outbound agent run](https://docs.dograh.com/api-reference/runs/trigger-workflow) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          curl -X POST "https://app.dograh.com/api/v1/public/agent/workflow/<workflow_uuid>" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "phone_number": "+14155550123"
          }'
          ```

          ```javascript Node theme={null}
          const workflowUuid = '<workflow_uuid>';

          const response = await fetch(
            `https://app.dograh.com/api/v1/public/agent/workflow/${workflowUuid}`,
            {
              method: 'POST',
              headers: {
                'X-API-Key': process.env.DOGRAH_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({ phone_number: '+14155550123' }),
            },
          );
          console.log(await response.json());
          ```

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

          workflow_uuid = "<workflow_uuid>"

          response = requests.post(
              f"https://app.dograh.com/api/v1/public/agent/workflow/{workflow_uuid}",
              headers={"X-API-Key": os.environ["DOGRAH_API_KEY"]},
              json={"phone_number": "+14155550123"},
          )
          print(response.json())
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Console">
        1. In Dograh, open the agent that should place the call.
        2. Select **Phone Call** in the top toolbar.
        3. In the dialog, choose the Plivo **Telephony configuration** and the **Caller ID (from)**, then enter the destination number in E.164 format.
        4. Select **Start Call**.
      </Tab>
    </Tabs>
  </Step>
</Steps>

<Note>
  On a **self-hosted** deployment, Dograh must be reachable at a public URL so Plivo can fetch the Answer URL and open the call's media WebSocket. See [Self-hosted setup](#self-hosted-setup) for how to set this up.
</Note>

***

## Inbound call setup

For inbound calls, an external caller dials the Plivo number. The number must route to the Plivo Application that holds Dograh's Answer URL, and it needs an inbound workflow that decides which agent answers.

<Steps>
  <Step title="Get the Plivo credentials" titleSize="h3">
    Sign in to the [Plivo console](https://cx.plivo.com) and copy the **Auth ID** and **Auth Token** from the dashboard.
  </Step>

  <Step title="Add a Plivo configuration in Dograh" titleSize="h3">
    <Tabs>
      <Tab title="API">
        Send the Plivo credentials to create the configuration and add the inbound number, authenticated with a Dograh API key.

        The `application_id` should be unset and Dograh creates a Plivo Application with the Answer URL already set.

        Refer to [Create a telephony configuration](https://docs.dograh.com/api-reference/telephony-configs/create) and [Add a phone number](https://docs.dograh.com/api-reference/telephony-configs/phone-numbers/create) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          # Create the configuration
          curl -X POST "https://app.dograh.com/api/v1/organizations/telephony-configs" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "name": "Plivo",
            "config": {
              "provider": "plivo",
              "auth_id": "<plivo_auth_id>",
              "auth_token": "<plivo_auth_token>"
            }
          }'

          # Add the inbound number, using the config_id returned above
          curl -X POST "https://app.dograh.com/api/v1/organizations/telephony-configs/<config_id>/phone-numbers" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "address": "+14150000001"
          }'
          ```

          ```javascript Node theme={null}
          const headers = {
            'X-API-Key': process.env.DOGRAH_API_KEY,
            'Content-Type': 'application/json',
          };

          // Create the configuration
          await fetch('https://app.dograh.com/api/v1/organizations/telephony-configs', {
            method: 'POST',
            headers,
            body: JSON.stringify({
              name: 'Plivo',
              config: {
                provider: 'plivo',
                auth_id: '<plivo_auth_id>',
                auth_token: '<plivo_auth_token>',
              },
            }),
          });

          // Add the inbound number, using the config_id returned above
          await fetch(
            'https://app.dograh.com/api/v1/organizations/telephony-configs/<config_id>/phone-numbers',
            {
              method: 'POST',
              headers,
              body: JSON.stringify({ address: '+14150000001' }),
            },
          );
          ```

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

          headers = {"X-API-Key": os.environ["DOGRAH_API_KEY"]}

          # Create the configuration
          requests.post(
              "https://app.dograh.com/api/v1/organizations/telephony-configs",
              headers=headers,
              json={
                  "name": "Plivo",
                  "config": {
                      "provider": "plivo",
                      "auth_id": "<plivo_auth_id>",
                      "auth_token": "<plivo_auth_token>",
                  },
              },
          )

          # Add the inbound number, using the config_id returned above
          requests.post(
              "https://app.dograh.com/api/v1/organizations/telephony-configs/<config_id>/phone-numbers",
              headers=headers,
              json={"address": "+14150000001"},
          )
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Console">
        1. In Dograh, go to **/telephony-configurations** and select **Add configuration**.
        2. Choose **Plivo** as the provider and enter the Auth ID and Auth Token.
        3. Leave **Application ID** blank. Dograh creates a Plivo Application when the configuration is saved and sets its Answer URL automatically.

           To use an existing application instead, paste its Application ID.
        4. Select **Save Configuration**.
        5. Open the saved configuration and add the inbound **phone number** in E.164 format, for example `+14150000001`.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Link the number to the Plivo Application" titleSize="h3">
    Dograh creates the Plivo Application but does not attach numbers to it. The inbound number should be manually linked to the Plivo application.

    <Tabs>
      <Tab title="API">
        Update the Plivo number to point at the Dograh Application, authenticated with the Auth ID and Auth Token over Basic auth.

        * `app_id` — the Application ID from the Dograh saved configuration.

        Refer to [Update an Account Phone Number](/docs/numbers/account-phone-numbers) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          curl -X POST "https://api.plivo.com/v1/Account/$PLIVO_AUTH_ID/Number/<number>/" \
          -u "$PLIVO_AUTH_ID:$PLIVO_AUTH_TOKEN" \
          -H 'Content-Type: application/json' \
          -d '{
            "app_id": "<dograh_application_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 response = await fetch(`https://api.plivo.com/v1/Account/${authId}/Number/<number>/`, {
            method: 'POST',
            headers: {
              'Authorization': authHeader,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              app_id: '<dograh_application_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"]

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

      <Tab title="Console">
        1. Sign in to the [Plivo console](https://cx.plivo.com/).
        2. Navigate to **Phone Numbers → Your Numbers** and open the inbound number.
        3. Set its **Application** to the `dograh-<random>` application shown on the Dograh saved configuration, then save the number.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Assign an inbound workflow" titleSize="h3">
    Each inbound number needs an inbound workflow, which is the agent that answers calls to the number.

    <Tabs>
      <Tab title="API">
        Update the phone number in Dograh to set its inbound workflow, authenticated with a Dograh API key.

        * `inbound_workflow_id` — the workflow that answers calls to the number.
        * `clear_inbound_workflow: true` — removes the workflow later. On its own, `inbound_workflow_id: null` is treated as no change.

        Refer to [Update a phone number](https://docs.dograh.com/api-reference/telephony-configs/phone-numbers/update) for all parameters.

        <CodeGroup>
          ```shell cURL theme={null}
          curl -X PUT "https://app.dograh.com/api/v1/organizations/telephony-configs/<config_id>/phone-numbers/<phone_number_id>" \
          -H "X-API-Key: $DOGRAH_API_KEY" \
          -H 'Content-Type: application/json' \
          -d '{
            "inbound_workflow_id": <workflow_id>
          }'
          ```

          ```javascript Node theme={null}
          const configId = '<config_id>';
          const phoneNumberId = '<phone_number_id>';
          const inboundWorkflowId = 123;

          const response = await fetch(
            `https://app.dograh.com/api/v1/organizations/telephony-configs/${configId}/phone-numbers/${phoneNumberId}`,
            {
              method: 'PUT',
              headers: {
                'X-API-Key': process.env.DOGRAH_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({ inbound_workflow_id: inboundWorkflowId }),
            },
          );
          console.log(await response.json());
          ```

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

          config_id = "<config_id>"
          phone_number_id = "<phone_number_id>"
          inbound_workflow_id = 123

          response = requests.put(
              f"https://app.dograh.com/api/v1/organizations/telephony-configs/{config_id}/phone-numbers/{phone_number_id}",
              headers={"X-API-Key": os.environ["DOGRAH_API_KEY"]},
              json={"inbound_workflow_id": inbound_workflow_id},
          )
          print(response.json())
          ```
        </CodeGroup>
      </Tab>

      <Tab title="Console">
        1. In Dograh, go to **/telephony-configurations** and open the saved Plivo configuration.
        2. In the **Phone numbers** section, find the inbound number and select **Edit**.
        3. Set **Inbound workflow** to the agent that should answer when that number is dialed.
        4. Save the number.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Receive an inbound call" titleSize="h3">
    Once the workflow is saved, calls to this number are answered by the assigned agent.
  </Step>
</Steps>

<Note>
  A single Plivo configuration can hold multiple phone numbers, each with its own **Inbound workflow**.
</Note>

***

## Self-hosted setup

The Plivo integration works the same on Dograh Cloud and on a self-hosted instance. The only difference is that Plivo has to reach the self-hosted instance directly, so it needs a public HTTPS address.

**Dograh handles this automatically.** Its [self-hosting guide](https://docs.dograh.com/getting-started#setting-up) starts the whole stack with a single Docker command, and this command brings up a Cloudflare tunnel that exposes the instance at a public HTTPS URL.

This default tunnel needs no Cloudflare account or token but the tunnel's subdomain changes each time it restarts. Dograh uses the URL as the base of the Answer URL that Plivo fetches, for example `https://<random>.trycloudflare.com/api/v1/telephony/inbound/run`.

For a stable hostname, run a named Cloudflare tunnel. This requires a Cloudflare account, and the same [guide](https://docs.dograh.com/getting-started#setting-up) walks through the setup.

Once Dograh is reachable at its public URL, the outbound and inbound setup is identical to Dograh Cloud.

***

## Troubleshooting

| Issue                                | Solution                                                                                                                                                                                                                                      |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication failed                | Confirm the Auth ID and Auth Token match the values in the [Plivo console](https://cx.plivo.com).                                                                                                                                             |
| Inbound call is not answered         | Confirm the number is linked to the Plivo Application configured in Dograh, the number has an inbound workflow assigned, and the Answer URL was set without a warning on save.                                                                |
| Call connects but has no audio       | The bundled Docker tunnel handles reachability automatically. On a self-hosted custom-domain deployment, confirm Dograh is reachable at its public URL per [Dograh's self-hosting guide](https://docs.dograh.com/getting-started#setting-up). |
| Webhook signature validation failing | Confirm the Auth Token in Dograh matches the Plivo console and that no proxy rewrites the inbound request.                                                                                                                                    |

***

## Related

<CardGroup cols={2}>
  <Card title="Plivo Audio Streaming" icon="audio-lines" href="/docs/voice-agents/audio-streaming/overview">
    How Plivo streams call audio over a WebSocket.
  </Card>

  <Card title="Dograh" icon="github" href="https://github.com/dograh-hq/dograh" arrow={true}>
    The open-source Dograh voice AI platform.
  </Card>

  <Card title="Dograh Plivo docs" icon="book" href="https://docs.dograh.com/integrations/telephony/plivo" arrow={true}>
    Dograh's own Plivo configuration reference.
  </Card>
</CardGroup>
