# Dialt + Twilio inbound bridge

> These files are the maintained public Dialt reference and are served from Dialt developer docs. They also power the private production smoke harness.

This reference app connects an inbound Twilio phone call to one Dialt session. It keeps Twilio
as the telephony provider: your service owns the phone number, webhook, deployment and application
tools; Dialt owns the voice experience.

## Run it

1. Copy `env.example` to `.env` and fill in the three required values.
2. If you downloaded the public example from Dialt developer docs, install and run with uv:

   ```sh
   uv venv
   uv pip install -r requirements.txt
   uv run uvicorn bridge:app --env-file .env --host 0.0.0.0 --port 8000
   ```

   Maintainers running this directory from the Dialt repository can instead use `uv sync --frozen`
   with the committed development lock.

3. Put the app behind a public HTTPS endpoint. `PUBLIC_BASE_URL` must be that exact external origin,
   for example `https://voice.example.com`; Twilio signatures are checked against it.
4. Set the Twilio phone number's incoming Voice webhook to `POST https://voice.example.com/voice`.

The webhook returns `<Connect><Stream>`, so this is a bidirectional stream. The bridge accepts only
Twilio-signed HTTP and WebSocket requests. It converts Twilio's 8 kHz G.711 mu-law audio to the
Dialt 16 kHz wire format and maps Twilio `mark`/`clear` events to Dialt's
`playback_stopped` contract on interruption.

## Add application tools

Edit `tool_manifest()` and `execute_tool()` in `bridge.py`. Tool calls run concurrently with the
audio/event loop, so a slow application lookup does not starve Dialt audio. Keep all service
credentials and effects in this bridge—only schemas, arguments and bounded results cross the
Dialt session.

## Optional human handoff

Set both variables below to expose the example's permission-gated `request_human_handoff` tool:

```sh
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_HUMAN_HANDOFF_URL=https://voice.example.com/handoff
```

When the caller approves a handoff, the bridge updates the active Twilio call to the configured
HTTPS URL. That customer-owned endpoint returns the TwiML for the real destination: a `<Dial>`,
Flex flow, TaskRouter workflow, queue, conference or another supported call path. The destination
is configuration, never a model argument.

This is a cold-transfer reference, not a generic contact-center implementation. The tool supplies
a reason and concise summary to `execute_tool()`, but the example deliberately does not put either
value in a URL. Persist them by `CallSid` in your application before redirecting if the receiving
agent needs context. Keep the handoff endpoint signed and authenticated according to Twilio's
webhook-security guidance.

This is an inbound reference integration, not a dialer.


## Maintainer browser smoke test

Production also runs `browser_harness.py` behind the existing team sign-in at
`/internal/twilio-smoke/`. Only the signed `/twilio-smoke/voice` and
`/twilio-smoke/media` callbacks are exposed to Twilio. The UI issues ten-minute
outgoing-only Voice SDK tokens and rate-limits starts per signed-in team account. With no
`DIALT_VOICE` override, the broker uses its global Classic default and records it as default
rather than as a user choice.

The production service reads only `/etc/converse/twilio-smoke.env`; use
`PUBLIC_BASE_URL=https://dialt.com/twilio-smoke` and configure the TwiML
App Voice URL as `https://dialt.com/twilio-smoke/voice` with HTTP POST.
