# Connecting a phone line, end to end

A working line is four things, not one: a number the carrier will route, an application that posts to a reachable public URL, a `channel_routes` row that says what the line is for and who may use it, and a real call proving all three. Configuration that has never carried a call is a guess. The last step is dialling the number yourself and reading both call logs side by side.

- **Status:** Available
- **Audience:** both
- **Channels:** phone
- **In the app:** #/phone-advanced, #/calls
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/docs/workflows/first-phone-setup/

## The chain, from no number to an answering line

1. Trigger — a business wants a number answered.
2. User event — someone with access to both the carrier account and the workspace begins setup.
3. Authentication and workspace resolution — credentials are stored per workspace; anything read outside that scope reads as empty.
4. Ingest — the carrier's number, application and webhook targets are recorded.
5. Canonical record — a `channel_routes` row: provider, assignment, inbound and outbound permission, hours, routing, recording policy, capabilities, limits, status.
6. Reasoning — the line's purpose selects the persona, the opening and the voice profile.
7. Knowledge, memory and rules — the greeting text, the closed-line message and the behaviour block are set for this line.
8. Autonomy and approval — outbound permission and the autonomy mode are decided here, not on the first call.
9. Action through a provider — the carrier is pointed at the answer, turn and status URLs.
10. Result — a real inbound call is placed and answered.
11. Relationship, timeline and memory — the test call produces a `Call` row like any other, which is exactly the proof wanted.
12. Audit, usage and Needs You — the carrier's own log is compared with `calls`, and line health starts reporting.

A line is not a new table. `channel_routes` already answers "whose endpoint is this and what is it for" for every channel, so the phone facts live there — including a person's browser line, which is a row with `channel='softphone'`.

## The verifications, and what each one catches

| Stage | What you see | What changes | What can fail |
|---|---|---|---|
| Credentials stored | The line shows its provider | Encrypted credentials on the workspace | Credentials read outside workspace scope look empty, and every real call is answered 403 before pickup |
| Public URL set | The line reports its webhook targets | The URLs the carrier will call | An unset public URL emits a host-less action URL: the greeting plays and the call goes silent |
| Answer URL wired | A test call is greeted | A `Call` row appears | The number is linked to the wrong application, or to none |
| Status URL understood | Nothing visible when correct | Provider status events recorded | The status URL is also the hangup URL; ending the call on *any* status once hung up on a live caller at `ringing` |
| Direction read | An outbound leg rings the person you dialled | `direction` and `owner_kind` on the call | A carrier posts to the same answer URL both ways; ignoring `Direction` greets your own agent as though they were the customer |
| Hours and purpose set | Out-of-hours callers hear the closed-line message | Hours and purpose on the route | Hours left empty, so nothing is ever out of hours |

> **Careful** Do not treat a green screen as proof. Two of the six production telephony defects were invisible in configuration and obvious within one real call. Reading the carrier's call log next to `calls` is the fastest audit there is: a completed call on their side with no row on yours is a defect, every time.

## Decisions to make before the first caller, not after

**Purpose** — `inbound`, `service`, `transactional` or `promotional`. It selects the voice profile and the behaviour block, and it is what "per campaign" means here — there is no separate campaign model.
**Voice** — Connect's own text-to-speech, rather than the carrier's two voices and sixteen European locales. Settings live on the Phone screen's Behaviour section for both audiences.
**Closed-line message** — Deterministic, in the business's own name, and deliberately without opening hours in it. It is warmed alongside the greetings so an out-of-hours caller hears a sentence rather than a dropped line.
**Outbound permission** — Separate from inbound. A line that answers is not automatically a line that may call.
**Engine** — The turn-based carrier path is the default. The realtime engine is a separate, opt-in piece of setup with its own console steps.

## What this line will not do

Two capabilities are asked of the provider rather than assumed, through `telephony.can`, and neither is enabled on the live carrier: call recording, and a completed transfer to a person. The model and the tests for both exist; the usable capability is narrower than the code, and the screen says so rather than offering a control that does nothing.

The live carrier also carries no SMS. `readiness.messaging` states that on the Phone screen instead of presenting a message thread that cannot send. If a line's purpose depends on texting a caller, that is a provider decision to make before the line is promised to anyone.

## Questions

### Why does the first test call matter so much?

Because every one of the known telephony defects here was silent. A signature check that passed, a URL that resolved, a setting that read as false — all looked correct and all produced a caller hearing nothing. A single real call, compared against the carrier's own log, catches what configuration review does not.

### Can one workspace run several numbers?

Yes. Each is its own `channel_routes` row with its own purpose, hours, permissions and voice profile, and profiles resolve by purpose and by line number before falling back to the workspace default.

### Where do the credentials live?

On the workspace, encrypted, and read only inside it. That scoping is not a detail: reading them outside the workspace is what made every real call fail with a refusal before the caller was even connected.

## Related

- [Turning on the realtime engine, end to end](https://connectbyjbrh.com/docs/workflows/realtime-engine-enablement/)
- [Inbound phone call, end to end](https://connectbyjbrh.com/docs/workflows/inbound-phone-call/)
- [The carrier callback was refused](https://connectbyjbrh.com/docs/troubleshooting/carrier-verification-failed/)
- [Nobody can reach the number](https://connectbyjbrh.com/docs/troubleshooting/phone-line-not-ringing/)
- [Phone and voice in Connect](https://connectbyjbrh.com/docs/phone/)

## What this page is based on

- AGENTS.md §9a — the phone system and its six production defects
- `backend/app/telephony.py`, `voice_carriers.py`, `voice_tts.py`
- Connect capability registry (docs-source/facts.py)
