# TELNYX VOICE BOT SETUP SOP
## Standard Operating Procedure — All Genesis Agents

**Version**: 1.0  
**Authority**: Kinan  
**Last Updated**: 2026-02-22  
**Source**: Live session learnings + post-mortem Session 82 + TELNYX_AI_ASSISTANT_KB.md

> **READ THIS BEFORE TOUCHING ANYTHING.**  
> 3 hours were wasted and $0 revenue generated because this SOP did not exist.  
> Every step below is battle-tested. Do not improvise.

---

## OVERVIEW: What We're Building

A Telnyx AI Assistant that can:
1. **Take calls on a phone number** (inbound PSTN)
2. **Run on a website via voice widget** (WebRTC, no phone number needed)
3. **Make outbound calls** (via API)

These are three distinct delivery channels. You may need one, two, or all three.

---

## CREDENTIALS (Required Before Starting)

| Item | Value | Location |
|------|-------|----------|
| Telnyx Portal | https://portal.telnyx.com | Browser |
| Login Email | kinan@agileadapt.com | Genesis Credentials additions.txt |
| API Key | `KEY019BE7A3A2D749FCA8681CFF8448A7F0_vTMM1n77CtQxLDT2ra3P1z` | Genesis Credentials additions.txt |
| Backup Codes | `1a7381d6338b`, `1001d3cfe73d`, `23242b73cd3a`, `59d5a4f1ad14`, `6bf44e380342` | Genesis Credentials additions.txt |

> ⚠️ `8281fbfc631c` was consumed 2026-02-22. Do not use.

---

## THE PROCESS (10 Steps, ~20 Minutes)

---

### STEP 1: Log Into Telnyx Portal

Navigate to: `https://portal.telnyx.com`

**Login**: `kinan@agileadapt.com`

> ⚠️ **Cloudflare Turnstile** blocks automated login attempts.  
> Use **Collaborative Browser Handoff**: Open browser to login page → Kinan logs in → Genesis takes over the authenticated session.

Once logged in, confirm you see the dashboard with account balance visible.

---

### STEP 2: Create the AI Assistant

Navigate to: `portal.telnyx.com/#/ai/assistants`

Click **"Create Assistant"** (or **"+ New Assistant"**).

**Required fields at creation:**
- **Name**: Client-specific name, e.g. `Sarah - Bunker FNQ Receptionist`
- **Model**: `Telnyx/Qwen3-235B-A22B` ← recommended (lowest latency, no external API key needed)
  - Alternative: `google/gemini-2.5-flash` (higher quality, requires Google API key ref)

Save. You will now have an **Assistant ID** in format: `assistant-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`

**Record this ID immediately.** You will use it everywhere.

---

### STEP 3: Configure General Settings

In the **General** tab:

| Field | What to Set |
|-------|-------------|
| **Name** | Client business name + persona, e.g. "Sarah - Bunker FNQ" |
| **Instructions** | Full system prompt (see System Prompt Template below) |
| **Greeting** | Optional opening line when call connects, e.g. "Thanks for calling Bunker FNQ, this is Sarah speaking. How can I help you today?" |
| **Model** | Qwen3 or Gemini 2.5 Flash |

**System Prompt Template:**
```
You are [PERSONA_NAME], the AI receptionist for [BUSINESS_NAME].

Business: [BUSINESS_NAME]
Location: [SUBURB, STATE]
Phone: [PHONE]
Email: [EMAIL]
Service areas: [AREAS]

Services:
- [SERVICE_1]
- [SERVICE_2]
- [SERVICE_3]

Your role:
- Answer enquiries about services, pricing, and availability
- Qualify leads (what service, when, where)
- Book appointments or capture contact details for follow-up
- Handle missed call callbacks professionally

Tone: Warm, professional, naturally Australian. Never folksy or slang-heavy.

Current time: {{telnyx_current_time}}
Channel: {{telnyx_conversation_channel}}
```

> **Available dynamic variables**: `{{telnyx_current_time}}`, `{{telnyx_conversation_channel}}`, `{{telnyx_agent_target}}`, `{{telnyx_end_user_target}}`

---

### STEP 4: Configure Voice Settings

In the **Voice Settings** tab:

| Field | Recommended Value |
|-------|------------------|
| **TTS Provider** | Telnyx |
| **Voice** | `Telnyx.NaturalHD.eucalyptus` (Australian female, natural) |
| **STT Provider** | Telnyx (Whisper) |
| **Voice Speed** | 1.0 (default) |
| **wait_seconds** | 0.3 (snappy responses) |
| **on_punctuation_seconds** | 0.1 (minimal delay after sentence ends) |
| **on_no_punctuation_seconds** | 1.5 (wait if speaker mid-sentence) |
| **on_number_seconds** | 1.0 (don't cut off while reading a number) |

> For tradie/noisy environments: consider `silenceDurationMs: 500` in SDK config.

---

### STEP 5: ⚠️ ENABLE TELEPHONY (CRITICAL — Do This First)

> **THIS IS THE STEP THAT COST 3 HOURS WHEN MISSED.**  
> The widget will NOT work. Phone calls will NOT work. NOTHING works until this is done.

In the **Telephony** tab:

1. You will see a banner: **"Telephony is not enabled for this assistant, do you want to enable it now?"**
2. Click **"Enable"** (or "Yes")
3. The tab will now show configuration options

**Then set:**

| Toggle | Required Setting |
|--------|-----------------|
| **Enable Telephony** | ✅ ON |
| **Supports Unauthenticated Web Calls** | ✅ ON ← required for website widget |
| **Time Limit (secs)** | 1800 (30 min default, adjust as needed) |

> ⚠️ **API PATCH CANNOT ENABLE THE PARENT TELEPHONY MODULE.**  
> You MUST toggle this in the portal UI. The API can configure sub-settings, but cannot turn on the parent feature. This is a Telnyx platform limitation.

**Verify via API after saving:**
```bash
node -e "
const https = require('https');
const options = {
  hostname: 'api.telnyx.com',
  path: '/v2/ai/assistants/ASSISTANT_ID_HERE',
  method: 'GET',
  headers: { 'Authorization': 'Bearer KEY019BE7A3A2D749FCA8681CFF8448A7F0_vTMM1n77CtQxLDT2ra3P1z' }
};
https.request(options, r => {
  let d = '';
  r.on('data', c => d += c);
  r.on('end', () => {
    const j = JSON.parse(d);
    const t = j.data.telephony_settings;
    console.log('telephony_enabled:', t?.enabled);
    console.log('unauthenticated_web_calls:', t?.supports_unauthenticated_web_calls);
  });
}).end();
"
```
Expected output:
```
telephony_enabled: true
unauthenticated_web_calls: true
```

---

### STEP 6: Assign a Phone Number (For Inbound Calls Only)

> **SKIP THIS STEP if only building a website widget.** The widget uses WebRTC and needs no phone number.  
> **REQUIRED if:** Client needs an inbound phone number for calls, MCTB, or outbound campaigns.

**6a. Buy a number (if in stock):**
1. Go to `portal.telnyx.com/#/numbers/buy-numbers`
2. Set Country = Australia +61
3. Set Search By = City/Region
4. Type the city name (e.g. "Cairns") → select from dropdown
5. Set Type = Local (or Toll-Free)
6. Click **Search Numbers**
7. If numbers appear → add to cart → purchase
8. If no numbers → see Step 6b

**6b. Request a number (if not in stock):**
1. On the "no results" page → click **"Request Number"**
2. Fill the dialog:
   - Country: Australia
   - Type: Local
   - Features: ✅ Voice (check this box)
   - Area Code: e.g. `07` for Queensland/Cairns
   - Locality: City name
   - Quantity: How many needed (get 3 for buffer)
   - Click **Submit Request**
3. Record the Request ID shown in confirmation dialog
4. Telnyx fulfils within 1–5 business days
5. Watch for compliance email — Australian numbers may require address documentation

**6c. Assign number to assistant:**
1. Go back to the assistant → **Telephony** tab
2. Find **"Assign Phone Number"** field
3. Select the purchased/provisioned number
4. Save

---

### STEP 7: Embed the Website Widget

> **USE THE OFFICIAL TELNYX WIDGET. DO NOT BUILD CUSTOM WEBRTC CODE.**  
> The official `<telnyx-ai-agent>` tag handles all ICE, STUN, TURN, audio mounting, and error recovery internally. Custom code adds zero value and 100% failure surface.

**Basic embed (2 lines):**
```html
<telnyx-ai-agent agent-id="YOUR_ASSISTANT_ID_HERE"></telnyx-ai-agent>
<script async src="https://unpkg.com/@telnyx/ai-agent-widget@next"></script>
```

**Branded embed (with customisation):**
```html
<style>
  /* Hide "Powered by Telnyx" footer — non-negotiable for white-label */
  telnyx-ai-agent::part(powered-by) { display: none !important; }
</style>

<telnyx-ai-agent 
  agent-id="YOUR_ASSISTANT_ID_HERE"
></telnyx-ai-agent>
<script async src="https://unpkg.com/@telnyx/ai-agent-widget@next"></script>
```

**Widget appearance settings** (configure in portal Widget tab, NOT in HTML):

| Field | Example Value |
|-------|--------------|
| Button Label | "Speak with Sarah" |
| Thinking Label | "Sarah is thinking..." |
| Interrupt Label | "Speak to interrupt Sarah" |
| Logo Icon URL | Client favicon URL |
| Theme | Light or Dark (match client site) |
| Visualizer Preset | Round Bars |
| Visualizer Color | Verdant (or match brand) |

**To configure Widget tab in portal:**
1. AI Assistants → select assistant → **Widget** tab
2. Fill all appearance fields
3. For MUI autocomplete dropdowns (Theme, Visualizer, etc.): click the **"Open"** button next to the field, then click the option — do NOT use `selectOption` (it fails on MUI)
4. Click **Save** — confirm "Updated AI Assistant successfully"

---

### STEP 8: Branding & Obfuscation

**To hide the Telnyx stack from clients/competitors:**

1. **Logo**: Set Logo Icon URL to client's favicon — replaces Telnyx triangle logo ✅
2. **Powered by footer**: Add CSS shadow DOM override to the page:
   ```css
   telnyx-ai-agent::part(powered-by) { display: none !important; }
   ```
3. **Script source**: The `unpkg.com/@telnyx/ai-agent-widget` URL is visible in page source. To hide: proxy the script through our own CDN/domain (optional, lower priority)
4. **Button text**: Set all text fields to persona name (e.g. "Sarah") — no Telnyx branding in UI

> **Branding summary**: Most clients never look at page source. The CSS override handles the only visible Telnyx branding. This is sufficient for white-label ReceptionistAI deployments.

---

### STEP 9: Add Tools & Webhooks (Optional but Recommended)

In the **Tools & Integrations** tab:

**Lead Capture Webhook:**
```json
{
  "name": "capture_lead",
  "url": "https://your-n8n-or-ghl-webhook-url.com",
  "method": "POST",
  "parameters": {
    "name": "string - caller name",
    "phone": "string - caller phone",
    "service": "string - service requested",
    "notes": "string - any other details"
  },
  "async": true
}
```

**Built-in tools to enable:**
- ✅ **Hangup** — lets the AI end the call gracefully
- ✅ **Agent Handoff** — transfer to human or another AI assistant

**Agent Handoff modes:**
- **Unified**: Same voice, seamless (caller doesn't notice transfer) — use for routing
- **Distinct**: Different voice, announced transfer — use for department transfers

---

### STEP 10: Test Before Shipping

**Test checklist:**

```
□ API verification: telephony_enabled: true + supports_unauthenticated_web_calls: true
□ Widget loads on target page (no console errors)
□ Widget button shows correct persona text (not "Telnyx Agent")
□ "Powered by Telnyx" footer is hidden
□ Voice call connects when widget button is clicked
□ AI responds to a test question correctly
□ AI persona matches client brief (professional, not folksy)
□ If phone number assigned: test inbound call to the number
□ If webhook configured: test lead capture fires on call end
□ Screenshot/record proof of working state
```

---

## DELIVERY CHANNELS QUICK REFERENCE

| Channel | What's Needed | Phone Number? | Telephony Toggle? |
|---------|--------------|--------------|-------------------|
| **Website Widget** | Widget embed code | ❌ No | ✅ Yes + Unauthenticated Web Calls |
| **Inbound Phone** | Assigned phone number | ✅ Yes | ✅ Yes |
| **Outbound Calls** | TeXML App + API call | ✅ Yes (From number) | ✅ Yes |
| **GHL MCTB** | Phone number in GHL | ✅ Yes | ✅ Yes |

---

## OUTBOUND CALLS VIA API

Once TeXML App is configured, trigger outbound calls like this:

```javascript
const response = await fetch(
  `https://api.telnyx.com/v2/texml/ai_calls/${TEXML_APP_ID}`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer KEY019BE7A3A2D749FCA8681CFF8448A7F0_vTMM1n77CtQxLDT2ra3P1z`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      From: '+61XXXXXXXXXX',  // Our Telnyx number
      To: '+61XXXXXXXXXX',    // Lead's number
      AIAssistantId: 'assistant-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
    })
  }
);
```

---

## PORTAL NAVIGATION CHEATSHEET

| What You Need | Direct URL |
|--------------|-----------|
| AI Assistants list | `portal.telnyx.com/#/ai/assistants` |
| Buy Numbers | `portal.telnyx.com/#/numbers/buy-numbers` |
| My Numbers | `portal.telnyx.com/#/numbers/my-numbers` |
| API Keys | `portal.telnyx.com/#/app/api-keys` |
| Connections | `portal.telnyx.com/#/app/connections` |
| Advanced Orders | `portal.telnyx.com/#/numbers/advanced-orders` |
| AI Insights | `portal.telnyx.com/#/ai/insights` |
| MCP Servers | `portal.telnyx.com/#/ai/mcp-servers` |

---

## KNOWN GOTCHAS (DO NOT REPEAT THESE MISTAKES)

| # | Mistake | What Happens | The Fix |
|---|---------|-------------|---------|
| 1 | Skip telephony enable | Widget connects but audio fails silently | Enable Telephony toggle in portal UI |
| 2 | Use API PATCH to enable telephony | API returns success but it doesn't work | Portal UI only — cannot be done via API |
| 3 | Build custom WebRTC code | 3+ hours debugging, zero guarantee | Use `<telnyx-ai-agent>` official widget |
| 4 | selectOption on MUI dropdowns | "Element is not a select" error | Click "Open" button → click option from listbox |
| 5 | Automate Telnyx login | Cloudflare Turnstile blocks it | Collaborative Browser Handoff (Kinan logs in, Genesis configures) |
| 6 | Buy 1 number for a region | Region may be out of stock | Request 3 at a time as buffer |
| 7 | API verify telephony without checking portal | API shows True but feature is off | Always confirm in portal UI after API verification |

---

## CURRENT LIVE ASSISTANTS

| Assistant | ID | Client | Status |
|-----------|-----|--------|--------|
| AgileAdapt Stage 1 | `assistant-9c42d3ce-e05a-4e34-8083-c91081917637` | AgileAdapt demo | ✅ Live |
| Sarah - Bunker FNQ | `assistant-6f6f4ca2-3155-4930-95cb-27f59514af3e` | George / Bunker FNQ | ✅ Live, awaiting Cairns number |

### Cairns Number Request
- **Request ID**: `c6c5e6a4-286d-4df7-ae1f-a17e2e44a7a3`
- **Qty**: 3 local numbers, area code 07, Cairns
- **Status**: Pending (submitted 2026-02-22)
- **Action on receipt**: Assign one number to George's assistant in Telephony tab

---

## AUSTRALIAN VOICE PERSONA RULES (NON-NEGOTIABLE)

Per GLOBAL_GENESIS_RULES Rule 13:

**NEVER USE:**
- "G'day" / "G'day mate"
- "Too easy", "Righto", "Sweet as"
- "No worries mate"
- Any pub/tradie-at-the-bar tone

**ALWAYS USE:**
- Warm, clear, professional
- Naturally Australian without performing it
- Like a real receptionist at a well-run business
- Language any client would trust with their money

---

## RELATED FILES

| File | Location |
|------|----------|
| Telnyx AI Assistant KB | `.gemini/knowledge/TELNYX_AI_ASSISTANT_KB.md` |
| Dual Track Architecture | `.gemini/knowledge/TELNYX_DUAL_TRACK_ARCHITECTURE.md` |
| Post-mortem Session 82 | `Research reports/POSTMORTEM_WEBRTC_VOICE_WIDGET_2026-02-22.md` |
| Demo widget HTML | `Sunaiva/talking-widget/demo-v2.html` |
| George's GHL setup | `.gemini/knowledge/GHL_SETUP_GEORGE_DEMO.md` |

---

*This SOP is the single source of truth for all Genesis agents building Telnyx voice bots.*  
*When in doubt: read this first. Update this when new learnings emerge.*
