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)
- Sign up at twilio.com
- Purchase a phone number in the Twilio Console
- In thinnestAI dashboard, go to Settings > Phone Numbers > Add > Twilio
- Enter your Account SID and Auth Token
Option B: Vobiz (for Indian telephony)
- Get SIP credentials from your Vobiz account
- In thinnestAI, go to Settings > Phone Numbers > Add > Vobiz
- Enter your SIP trunk credentials
Option C: Exotel BYOK (for Indian telephony with an existing Exotel account)
- In thinnestAI, go to Settings > Phone Numbers > Add > Exotel BYOK
- Enter your Exotel API key, API token, and account SID
- 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 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)
- In thinnestAI, go to Settings > Phone Numbers > Add > Plivo BYOK
- Enter your Plivo main-account auth_id (starts with
MA…) and auth_token
- 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.
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.
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.
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:
- Release the number on your Plivo dashboard and re-purchase it. Plivo
resets the binding on a fresh purchase.
- 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.
Option E: Bring Your Own Number (other providers)
If you already have a number with any other SIP provider:
- Go to Settings > Phone Numbers > Add > Existing
- Enter the number in E.164 format (e.g.,
+14155551234)
- Point your provider’s webhook to:
https://api.thinnest.ai/twilio/incoming/YOUR_PHONE_ID
2. Assign to Your Agent
- Open your voice agent
- Go to Deploy > Phone
- Select your phone number
- Configure the greeting message
- 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
- Click Try Voice Call → Phone Call on the agent builder page.
- Type your number with country code (+91 or +1) and press Call my phone. Recently-used numbers are remembered for one-click retest.
- Your phone rings. Pick up and talk to the agent — same TTS, STT, prompt, and turn detection it would use in production.
- 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
{
"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
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:
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:
- Create a campaign with your voice agent
- Upload a contact list (CSV or manual)
- Set schedule, concurrency, and retry rules
- Launch — the platform calls each contact automatically
Call Status Webhook
Track call progress by providing a webhook URL:
{
"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.
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:
- Open the Exotel dashboard.
- Go to Apps → Create new App.
- Drag a Passthru applet onto the flow. Do not use the Voicebot applet — it expects a different audio protocol that LiveKit doesn’t speak.
- Paste the webhook URL (from the gear-icon modal) into the Passthru applet’s URL field. Save the App.
- Go to ExoPhones, find your number, and assign the App you just created.
- 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 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 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 |