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

# Phone Calls

> Deploy your voice agent on phone numbers for inbound and outbound calls via Twilio, Vobiz, or Exotel SIP trunking (BYOK supported).

# Phone Calls

Give your voice agent a phone number so customers can call it — or have it call them. thinnestAI handles the entire call lifecycle: answering, speaking, listening, and hanging up.

## Prerequisites

* A thinnestAI account with voice enabled
* A voice agent configured with TTS and STT settings
* A phone number from Twilio, Vobiz, or Exotel (BYOK supported for all three)

## Quick Start

### 1. Get a Phone Number

**Option A: Twilio** (recommended for most users)

1. Sign up at [twilio.com](https://www.twilio.com/try-twilio)
2. Purchase a phone number in the Twilio Console
3. In thinnestAI dashboard, go to **Settings > Phone Numbers > Add > Twilio**
4. Enter your Account SID and Auth Token

**Option B: Vobiz** (for Indian telephony)

1. Get SIP credentials from your Vobiz account
2. In thinnestAI, go to **Settings > Phone Numbers > Add > Vobiz**
3. Enter your SIP trunk credentials

**Option C: Exotel BYOK** (for Indian telephony with an existing Exotel account)

1. In thinnestAI, go to **Settings > Phone Numbers > Add > Exotel BYOK**
2. Enter your Exotel API key, API token, and account SID
3. Pick the ExoPhone you want to import → click **Import**

During import we:

* create an Exotel **vSIP trunk** so outbound calls route through LiveKit, and
* attempt to set the ExoPhone's **VoiceUrl** to our inbound webhook so inbound calls reach your agent.

The inbound auto-configure step is best-effort — see [Exotel inbound setup](#exotel-inbound-setup-byok) below for the exact webhook URL and a manual fallback if it doesn't take.

**Option D: Plivo BYOK** (for Indian or global telephony with an existing Plivo account)

1. In thinnestAI, go to **Settings > Phone Numbers > Add > Plivo BYOK**
2. Enter your Plivo **main-account** auth\_id (starts with `MA…`) and auth\_token
3. Pick the number you want to import → click **Import**

During import we set up the full Plivo Zentrunk SIP path on your account in
one shot:

* a SIP **credential** for outbound auth
* an **Origination URI** pointing at our LiveKit SIP endpoint
* a Zentrunk **outbound trunk** so your agent can place calls from the number
* a Zentrunk **inbound trunk** so incoming calls route directly to your agent
* attaching the number to the inbound trunk

There's no webhook in the loop. Calls go SIP-direct between Plivo and our
LiveKit termination, which is lower latency and fewer moving parts than the
webhook-based providers above.

<Warning>
  **Plivo Zentrunk is required.** Plivo BYOK only works on Plivo plans that
  include the Zentrunk SIP-trunking product (Pay-As-You-Go with the Zentrunk
  add-on, or higher). If your account is on the free trial or your plan
  doesn't include Zentrunk, the import is blocked with a clear error toast
  telling you to upgrade — we don't fall back to a slower webhook-based
  path. Use BYO-SIP (Option E) with a different provider if you can't
  upgrade Plivo.
</Warning>

<Info>
  **Main-account credentials only.** Use your Plivo main-account `auth_id`
  (starts with `MA…`), not a subaccount (`SA…`). Subaccount credentials can
  read your Zentrunk resources but typically can't create new ones, which
  makes Plivo BYOK import fail at the first step.
</Info>

<Warning>
  **Indian local numbers are one-way bound by Plivo's carrier policy.**
  Plivo permanently locks Indian local DIDs to whichever Zentrunk trunk
  they were first bound to. Once you import an Indian local number through
  thinnestAI, **you cannot delete-and-re-import it later** — Plivo's API
  rejects the re-bind step with
  `"INDIA - local numbers cannot be linked to Zentrunk."`

  If you accidentally delete one of these numbers, the only recovery paths
  are:

  1. **Release the number on your Plivo dashboard and re-purchase it.** Plivo
     resets the binding on a fresh purchase.
  2. **Open a Plivo support ticket** asking them to clear the binding on the
     specific number.

  thinnestAI catches this case before it costs you anything: a pre-flight
  check on the import endpoint refuses with HTTP 409 + a clear error message
  if the number is already locked, and the delete confirmation in the
  phone-numbers UI warns you before you click Delete on an Indian Plivo BYOK
  row.

  This restriction applies only to **Indian local numbers**. Toll-free /
  mobile / non-Indian Plivo numbers can be re-imported normally — last
  confirmed 2026-06-04 against +91 80 3144 8181.
</Warning>

**Option E: Bring Your Own Number (other providers)**

If you already have a number with any other SIP provider:

1. Go to **Settings > Phone Numbers > Add > Existing**
2. Enter the number in E.164 format (e.g., `+14155551234`)
3. Point your provider's webhook to:

```
https://api.thinnest.ai/twilio/incoming/YOUR_PHONE_ID
```

### 2. Assign to Your Agent

1. Open your voice agent
2. Go to **Deploy > Phone**
3. Select your phone number
4. Configure the greeting message
5. Click **Enable**

### 3. Test It

Call your number — your agent picks up immediately.

## Test from the studio (browser or phone)

Every voice agent's builder page has a **Try Voice Call** dropdown with two options:

* **Web Call** — opens a browser-based playground; you speak through your mic, the agent replies through your speakers. Best for quick iteration while editing the prompt.
* **Phone Call** — opens a modal, you enter your phone number, and the platform places a real outbound call to you. Best for verifying voice quality on the actual phone network and confirming the agent feels right end-to-end.

### How it works

1. Click **Try Voice Call → Phone Call** on the agent builder page.
2. Type your number with country code (+91 or +1) and press **Call my phone**. Recently-used numbers are remembered for one-click retest.
3. Your phone rings. Pick up and talk to the agent — same TTS, STT, prompt, and turn detection it would use in production.
4. The modal walks through `Setting up → Ringing → Connected (with a `MM:SS / 02:00` counter)` → `Ended`. You can hang up your phone or click **End Call** in the modal at any time.

### Limits

Phone test calls are **free** — the platform absorbs the per-minute telephony cost. Bounded by:

| Limit                                    | Value     | What happens at the limit                                                                    |
| ---------------------------------------- | --------- | -------------------------------------------------------------------------------------------- |
| Per user / day                           | 5 calls   | Modal shows *"You've used all 5 test calls for today. They reset at midnight UTC."*          |
| Per destination / day (across all users) | 3 calls   | *"That number has already received 3 test calls today. Try again tomorrow."* (anti-robodial) |
| Per call duration                        | 2 minutes | Modal counter shows `02:00`; call ends server-side at the cap, you don't have to do anything |

Counters reset at midnight UTC.

### Country support

| Destination   | Trial-pool number from                                                                                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| +91 (India)   | Vobiz IN                                                                                                                                                                 |
| +1 (US)       | Plivo US                                                                                                                                                                 |
| Anywhere else | Not supported — modal returns *"Test calls currently support +91 (India) and +1 (US) destinations only."* Use a BYOK number (Twilio / Vobiz / Exotel) for other regions. |

### What you can rely on

* **Same code path as production.** Pool dial reuses `dispatch_outbound_call` — the same flow campaigns and `/voice/outbound/dial` use. Whatever you hear is what your customers will hear.
* **Recorded and logged.** Every test call is a normal voice session in your activity log, tagged `call_type=studio_test` so analytics dashboards filter it out of paying-call totals.
* **No race on pool members.** When two studio test calls fire at the same instant, only one wins the trial number (Postgres `FOR UPDATE SKIP LOCKED`) — the other gets `503 pool_exhausted` and the modal shows *"All trial numbers are in use right now. Try again in a minute."*
* **No silent hang on failed dispatch.** If the SIP dial never reaches your phone, the modal surfaces a `dispatch_timeout` error after 90 seconds instead of spinning forever.

### Endpoints (for reference)

| Endpoint                                           | Purpose                                                                         |
| -------------------------------------------------- | ------------------------------------------------------------------------------- |
| `POST /voice/studio/test-call`                     | Place a test call. Body: `{ "agent_id": "...", "to_number": "+91XXXXXXXXXX" }`  |
| `GET  /voice/studio/test-call/{session_id}/status` | Poll for call state (`initiating` / `ringing` / `live` / `ended`) and duration. |
| `POST /voice/studio/test-call/{session_id}/end`    | End an in-progress test call (the modal's End Call button).                     |
| `GET  /voice/studio/quota`                         | Get the user's remaining-quota hint without placing a call.                     |

## Inbound Calls

When someone calls your number:

```
Caller dials your number
        ↓
Twilio/Vobiz routes to thinnestAI via SIP
        ↓
Voice engine picks up, plays greeting
        ↓
Caller speaks → STT → Agent processes → TTS → Caller hears response
        ↓
Loop continues until call ends
```

### Configuring Inbound Behavior

| Setting           | Description                   | Example                                   |
| ----------------- | ----------------------------- | ----------------------------------------- |
| **Greeting**      | First thing the agent says    | "Hello, thanks for calling Acme Support." |
| **System prompt** | Agent's personality and rules | "You are a friendly support agent..."     |
| **Max duration**  | Maximum call length           | 600 seconds (10 min)                      |
| **Recording**     | Record calls for review       | Enabled/disabled                          |
| **Transcription** | Save full text transcripts    | Enabled by default                        |
| **After-hours**   | Message when agent is off     | "We're closed. Please call back..."       |

### Example Agent Configuration

```json theme={null}
{
  "voice": {
    "greeting": "Hi, thanks for calling Acme. How can I help you today?",
    "tts": {
      "provider": "aero",
      "model": "aero-2",
      "voice": "maya"
    },
    "stt": {
      "provider": "deepgram",
      "model": "nova-2",
      "language": "en"
    },
    "max_duration": 600,
    "recording": true
  }
}
```

## Outbound Calls

Have your agent call customers proactively.

### Single Call via API

```bash theme={null}
curl -X POST "https://api.thinnest.ai/voice/outbound/dial" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_xyz",
    "to_number": "+14155551234",
    "from_number": "+18005551234",
    "context": {
      "customer_name": "Jane Doe",
      "appointment_date": "March 10, 2026"
    }
  }'
```

### Scheduled Calls

Schedule a call for a future time:

```bash theme={null}
curl -X POST "https://api.thinnest.ai/voice/outbound/dial" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "agent_id": "agent_xyz",
    "to_number": "+14155551234",
    "from_number": "+18005551234",
    "scheduled_at": "2026-03-10T14:00:00Z"
  }'
```

### Batch Calls (Campaigns)

For bulk outreach, use [Campaigns](/docs/campaigns):

1. Create a campaign with your voice agent
2. Upload a contact list (CSV or manual)
3. Set schedule, concurrency, and retry rules
4. Launch — the platform calls each contact automatically

### Call Status Webhook

Track call progress by providing a webhook URL:

```json theme={null}
{
  "webhook_url": "https://your-app.com/call-status",
  "to_number": "+14155551234",
  ...
}
```

You'll receive events: `initiating`, `ringing`, `in_progress`, `completed`, `failed`, `no_answer`.

## Exotel inbound setup (BYOK)

After importing an Exotel BYOK number, **inbound calls only reach your agent once an App on the Exotel side points at our webhook**. We try to set this automatically during import via Exotel's `IncomingPhoneNumbers` API — if that works, you're done. If it doesn't (your Exotel account uses the newer Flow Builder, or the SID couldn't be resolved), do the manual setup below.

### Find the webhook URL

Every imported number has a unique webhook URL of the form:

```
https://api.thinnest.ai/exotel/incoming/{phone_id}
```

Note: the inbound webhook lives at the root path (`/exotel/incoming/...`), **not** under `/voice/`. The dashboard always shows the exact URL for the row so you don't have to remember.

Open **Settings > Phone Numbers**, find the row for your Exotel number, and click the **gear icon** next to the BYOK pill. The modal shows the exact webhook URL for that number (with a copy button) plus a **Retry auto-configure** button that calls the Exotel API again.

### Retry auto-configure

The fastest path. The modal's **Retry auto-configure** button hits:

```
POST https://api.thinnest.ai/voice/exotel/byok/configure-inbound/{phone_id}
```

…which calls Exotel's API to set the ExoPhone's `VoiceUrl` to our webhook. Safe to retry. Returns either `{ "configured": true }` (you're done) or `{ "configured": false, "manual_steps": "…" }` (use the manual setup below).

### Manual setup in the Exotel dashboard

If auto-configure didn't take:

1. Open the [Exotel dashboard](https://my.exotel.com/).
2. Go to **Apps** → **Create new App**.
3. Drag a **Passthru** applet onto the flow. **Do not use the Voicebot applet** — it expects a different audio protocol that LiveKit doesn't speak.
4. Paste the webhook URL (from the gear-icon modal) into the Passthru applet's URL field. Save the App.
5. Go to **ExoPhones**, find your number, and assign the App you just created.
6. Place a test call to confirm.

### Why the Voicebot applet doesn't work

The Voicebot applet streams raw audio to a custom WebSocket endpoint using Exotel's proprietary protocol. thinnestAI routes calls through LiveKit SIP, so the inbound flow needs a **Passthru** applet that hits our HTTP webhook — we respond with ExoML `<Dial><Sip>` and Exotel forwards the call to LiveKit.

## SIP Trunking

For enterprise setups, connect your existing PBX or contact center via SIP:

```
Your PBX / Contact Center
        ↓ (SIP)
Twilio Elastic SIP Trunk / Vobiz
        ↓ (SIP)
thinnestAI Voice Engine (LiveKit)
        ↓
AI Agent handles the call
```

### Benefits

* Use existing phone numbers — no porting needed
* Keep your current PBX infrastructure
* Route some calls to AI, others to humans
* Failover between providers

See the full [SIP Integration guide](/docs/voice/sip-integration) for setup details.

## Call Analytics

Every call generates:

* **Duration** and timestamps
* **Full transcript** (caller and agent)
* **Recording** (if enabled)
* **Token usage** and cost
* **Caller phone number** (auto-captured as a lead)

View call history in **Voice > Sessions** in the dashboard.

## Pricing

| Provider   | Rate          | Unit                            |
| ---------- | ------------- | ------------------------------- |
| Twilio     | \~\$0.015/min | Per-minute (inbound + outbound) |
| Vobiz      | \~\$0.005/min | Per-minute (India)              |
| thinnestAI | Per-token     | LLM + TTS + STT usage           |

Exact rates depend on your Twilio/Vobiz plan and the TTS/STT providers you choose.

## Troubleshooting

| Issue                                                   | Solution                                                                                                                                                                                                          |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| No audio on calls                                       | Check TTS provider is configured and has valid API key                                                                                                                                                            |
| Agent doesn't pick up                                   | Verify webhook URL is correct in Twilio/Vobiz/Exotel                                                                                                                                                              |
| Exotel inbound caller hears "no voicebot flow attached" | Open the gear-icon modal for your Exotel number → **Retry auto-configure**, or follow the [Exotel inbound setup](#exotel-inbound-setup-byok) manual steps — make sure you use a **Passthru** applet, not Voicebot |
| Calls drop immediately                                  | Check max\_duration setting and provider account balance                                                                                                                                                          |
| Poor transcription                                      | Try a different STT model (nova-2 is recommended)                                                                                                                                                                 |
| High latency                                            | Switch to a faster TTS provider or a lower-latency STT                                                                                                                                                            |
| Echo on calls                                           | Check duplex audio settings in voice configuration                                                                                                                                                                |
