# Telnyx Setup for Voice Receptionist

This guide explains how to connect a Telnyx phone number to this clinic receptionist project.

## Architecture

```text
Caller
  -> Telnyx phone number
  -> Telnyx TeXML Application
  -> POST /telnyx/texml
  -> TeXML <Connect><Stream> to wss://.../telnyx/media
  -> this Node server bridges Telnyx audio to OpenAI Realtime
  -> Ava audio is sent back to Telnyx over the same WebSocket
```

The important point: Telnyx should call this app's TeXML webhook. The app returns TeXML that opens a bidirectional media stream to `/telnyx/media`.

## Requirements

- A Telnyx account with a voice-capable phone number.
- This Node service running and reachable from the public internet over HTTPS/WSS.
- An OpenAI API key saved in `.env` or in the admin dashboard Settings page.
- For outbound follow-up calls or SMS: Telnyx API key, account SID, TeXML application SID, and from number.

Official references:

- Telnyx TeXML quickstart: https://developers.telnyx.com/docs/voice/programmable-voice/texml-setup
- Telnyx `<Stream>` verb: https://developers.telnyx.com/docs/voice/programmable-voice/texml-verbs/stream
- Telnyx media streaming over WebSockets: https://developers.telnyx.com/docs/voice/programmable-voice/media-streaming
- Telnyx Voice API application/channel settings: https://developers.telnyx.com/docs/voice/programmable-voice/voice-api-fundamentals
- Telnyx portal app configuration: https://support.telnyx.com/en/articles/4374050-configuring-call-control-texml-applications-voice-api

## 1. Configure Environment

In `voice-receptionist/.env`:

```bash
OPENAI_API_KEY=sk-proj...
PORT=18787
CLINIC_NAME=Willow Family Clinic
PUBLIC_BASE_URL=https://app.cybersky.work/voice-receptionist
HUMAN_TRANSFER_TARGET=tel:+13125550100
SQLITE_DB_PATH=./receptionist.sqlite
ADMIN_TOKEN=change-me-to-a-long-random-token

# Optional: required for outbound follow-up calls/SMS from the dashboard
TELNYX_API_KEY=KEY...
TELNYX_ACCOUNT_SID=your_telnyx_account_sid
TELNYX_APPLICATION_SID=your_texml_application_sid
TELNYX_FROM_NUMBER=+13125550100
```

`PUBLIC_BASE_URL` must be the externally reachable base URL for this service. The code derives the WebSocket URL from it:

```text
https://app.cybersky.work/voice-receptionist
  -> wss://app.cybersky.work/voice-receptionist/telnyx/media
```

Start the service:

```bash
cd /home/tedpc/.openclaw/workspace/voice-receptionist
npm install
npm start
```

Health check:

```text
https://app.cybersky.work/voice-receptionist/health
```

Local health check:

```text
http://localhost:18787/health
```

## 2. Confirm App Endpoints

Inbound TeXML webhook:

```text
https://app.cybersky.work/voice-receptionist/telnyx/texml
```

Call progress events:

```text
https://app.cybersky.work/voice-receptionist/telnyx/call-progress
```

Stream status callback is generated by the app inside the TeXML:

```text
https://app.cybersky.work/voice-receptionist/telnyx/stream-status
```

Media WebSocket:

```text
wss://app.cybersky.work/voice-receptionist/telnyx/media
```

When `/telnyx/texml` is working, it returns XML like this:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Connect>
    <Stream
      url="wss://app.cybersky.work/voice-receptionist/telnyx/media"
      track="inbound_track"
      codec="PCMU"
      bidirectionalMode="rtp"
      bidirectionalCodec="PCMU"
      bidirectionalSamplingRate="8000"
      statusCallback="https://app.cybersky.work/voice-receptionist/telnyx/stream-status"
      statusCallbackMethod="POST">
      <Parameter name="bridge" value="openai-realtime" />
    </Stream>
  </Connect>
</Response>
```

## 3. Create Telnyx TeXML Application

In Telnyx Mission Control Portal:

1. Go to `Voice` -> `Programmable Voice` -> `Call Control / TeXML Applications`.
2. Create a TeXML application.
3. Set the inbound TeXML webhook URL to:

```text
https://app.cybersky.work/voice-receptionist/telnyx/texml
```

4. Use `POST` as the webhook method.
5. Set the call progress/status callback URL to:

```text
https://app.cybersky.work/voice-receptionist/telnyx/call-progress
```

6. Use `POST` as the callback method.
7. Save the application.

If the portal shows an inbound channel limit, set it to the maximum number of simultaneous inbound calls you want to allow. If you are using channel billing, the number of purchased/configured channels controls simultaneous inbound calls.

## 4. Assign a Phone Number

In Telnyx:

1. Go to your Telnyx phone numbers.
2. Open the voice-capable number you want customers to call.
3. Assign the number to the TeXML application created above.
4. Save the number settings.

Now an inbound call should hit `/telnyx/texml`, then Telnyx should open `wss://.../telnyx/media`.

## 5. Configure Dashboard Settings

Open:

```text
https://app.cybersky.work/voice-receptionist
```

Log in with `ADMIN_TOKEN`, then check `Voice Receptionist` -> `Settings`.

Set or confirm:

- OpenAI API key
- Human transfer number
- Telnyx API key
- Telnyx account SID
- Telnyx TeXML application SID
- Telnyx from number

Use `Voice Receptionist` -> `Live Monitor` during test calls. It should show live call status, caller/Ava transcript, tool calls, appointment actions, and errors.

## 6. Test Inbound Calling

1. Call the Telnyx number from a real phone.
2. In the dashboard, open `Live Monitor`.
3. Confirm:
   - A live call card appears.
   - Caller number and dialed number are shown.
   - Caller/Ava transcript appears after each turn.
   - Tool calls appear when Ava checks knowledge, appointments, messages, or transfer.
4. Check `Logs` for the stored conversation after the call.

## 7. Optional: Outbound Follow-Up Calls

The dashboard Follow Up tab can place an outbound call through Telnyx.

Required settings:

```bash
TELNYX_API_KEY=KEY...
TELNYX_ACCOUNT_SID=your_telnyx_account_sid
TELNYX_APPLICATION_SID=your_texml_application_sid
TELNYX_FROM_NUMBER=+13125550100
```

Outbound calls use Telnyx TeXML REST calls and send inline TeXML that streams audio to the same `/telnyx/media` WebSocket.

## Troubleshooting

### Telnyx says application error

Usually this means Telnyx could not fetch valid TeXML.

Check:

- `/telnyx/texml` is publicly reachable over HTTPS.
- `OPENAI_API_KEY` is set. If not, this app returns a 503 TeXML response.
- `PUBLIC_BASE_URL` is correct and includes `/voice-receptionist` if the deployed app uses that path.
- The TeXML webhook method is `POST`.

### Call connects but Ava does not speak

Check:

- Telnyx can open `wss://.../telnyx/media`.
- The public URL supports WebSocket upgrades.
- `OPENAI_API_KEY` is valid.
- Server logs do not show OpenAI Realtime errors.

### Live Monitor shows no active call

Check:

- The call is reaching `/telnyx/texml`.
- The Telnyx WebSocket is connecting to `/telnyx/media`.
- The dashboard is logged in and can load `/api/live-monitor/events`.

### Multiple inbound calls are busy

Check the Telnyx application or connection channel limit. Telnyx has an inbound channel limit setting for Voice API applications, and channel billing can also cap simultaneous inbound calls.

### Transfer to human fails

Check:

- `HUMAN_TRANSFER_TARGET` is a normalized `tel:+...` number.
- The destination number can receive calls.
- Telnyx/OpenAI call transfer permissions and call state are valid.

