2026-09-05 The first real calls have been placed: five ended normally, six failed. Here is what broke.

Telephony

A switch, not a webhook.

Numbers, trunks, a dial plan, queues, operators and outbound campaigns — the parts of a phone system an agent has to live inside, rather than replace.

§01 Carriers

Nine carrier ids, and your own trunk as one of them.

A phone number belongs to a telephony account in your organization. You can search a carrier catalogue and buy a number, or import one you already own, and either way it becomes an object you can route: to an assistant, to an IVR, to a queue, to an extension.

Bring-your-own SIP is a first-class carrier rather than a downgrade path. If your numbers are already on a trunk you like, the trunk is the account and nothing else about the product changes.

Adapters exist for all nine ids, but they are not equally complete. One carrier has placed real calls so far: Twilio, eleven of them on 5 September. The rest are proven against contract tests.[1]

Carrier ids 9

  • twilio
  • telnyx
  • didww
  • plivo
  • zadarma
  • didlogic
  • chime
  • byo_sip
  • custom_sip

Number capabilities 4

  • voice
  • sms
  • mms
  • fax
§02 SIP
Trunk configuration surface

direction

  • inbound
  • outbound
  • both

transport

  • udp
  • tcp
  • tls

authentication

  • credentials
  • ip
  • both

media encryption

  • disabled
  • optional
  • required

These are the enum values, not a summary of them. Nothing here has been tested against a live carrier trunk in this build.[2]

Real SIP, with the settings that actually differ between carriers.

Trunks are configured the way a telephony engineer expects: direction, transport, authentication by credentials or by IP or both, media encryption policy, DTMF mode, and what to do with the leading plus that half the carriers in Europe disagree about.

Extensions are real extensions with registerable devices, and a device credential can be rotated on its own without disturbing the extension it belongs to.

A scheduled OPTIONS probe watches every trunk, so a trunk that stopped answering is a status in the console rather than a mystery discovered by a customer at nine in the morning.

§03 Dial plan

The assistant is one destination among several.

The moment a voice agent is useful, someone asks it to be reachable at the number that already rings the office, out of hours, behind the same greeting, with the same escalation to a human. That is a dial plan, and it needs to exist.

Each object below is a real record with its own screens and API routes, and any of them can send a call to an assistant or receive one from it.

Ring strategies 4

  • simultaneous
  • sequential
  • round_robin
  • random
The objects that make up a dial plan.
inbound routes What happens when a number rings. One destination, changeable without touching the number itself.
IVRs Menu trees with their own draft and published states, a validation step that refuses a menu with an unreachable branch, and a publish action.
extensions Internal numbers with registerable SIP devices, per-device credentials and individual credential rotation.
ring groups Several destinations rung together or in turn, with the strategies listed on the left.
time conditions Office hours, holidays and out-of-hours routing as a record rather than as a branch inside a prompt.
voicemail boxes Boxes with recorded messages, states and retrieval, for the calls that arrive when nothing is answering.
conference rooms Rooms that several legs can join, which is also what a supervisor barging into a call is standing in.
outbound routes Which trunk a call leaves on, and what caller id it leaves with.
§04 Contact centre

Where the agent runs out of road.

An agent that cannot hand over is a demo. The contact centre is the part of the product that catches the call the assistant should not finish: a queue, a set of operators with skills and states, and a supervisor who can hear what is happening.

Operator state is reconstructed from an immutable event ledger rather than stored as a mutable status column, so the answer to how long somebody was actually available is a query rather than a guess.

Queue routing strategies 9

  • fifo
  • longest_idle
  • round_robin
  • least_calls
  • skill_based
  • priority
  • vip
  • language_match
  • custom
Contact centre objects and behaviours.
queues Nine routing strategies, maximum wait, overflow and abandonment, all recorded as events on the call rather than inferred afterwards.
operators People with a state, a presence heartbeat, skills, statistics and a wrap-up period. A crashed browser does not leave someone stuck as available.
skills Attributes a queue can route on, which is what skill-based and language-match routing use.
dispositions The outcome an operator records at the end, which is what makes a campaign report mean anything.
live calls Every call in progress across the organization, with its state and its stage.
wallboard The same data sized to be read from four metres away, for the screen on the wall.
supervisor Listen, whisper and barge as API routes as well as buttons. Whisper reaches the operator only; barge joins both sides.
dispatch A dispatch loop runs every ten seconds, because an offer nobody answers has to time out and a caller past their maximum wait has to be released.
§05 Outbound

Campaigns, and 21 reasons not to dial.

A campaign is a contact list, a pacing rule, a window and an assistant. The dispatcher runs every minute and is built so that two dispatch runs overlapping cannot produce two calls to the same contact.

The interesting part of an outbound dialler is not the dialling. It is everything that stops it: calling hours, opt-outs, suppression lists, consent, country policy, attempt caps, capacity, budget and credit.

Each of those is a named skip reason recorded against the contact, so a contact that was never called has an answer to why, and a compliance question has an answer that is not a log grep.

Skip reasons 21

  • outside_calling_hours
  • outside_calling_day
  • outside_campaign_window
  • suppressed
  • platform_suppressed
  • do_not_call
  • opted_out
  • blocked_country
  • not_allowed_country
  • consent_missing
  • legal_basis_missing
  • holiday
  • max_attempts_reached
  • invalid_number
  • policy_denied
  • no_capacity
  • insufficient_credit
  • budget_exhausted
  • pacing_limit
  • carrier_rate_limited
  • campaign_stopped

Read from the skip-reason enum when this page renders. If a reason is added, it appears here.[3]

§06 Recording

Recordings you can actually use afterwards.

A recording exists to be listened to under pressure, usually by someone who needs one particular sentence from one particular side of the conversation.

Recording behaviour and storage.
modes Mono, stereo or dual-channel. Dual-channel keeps caller and assistant on separate, time-aligned channels so they can be separated afterwards; a stereo mix is spatial and cannot be.
consent Announcing and recording consent are events on the call, so proving the announcement was made is reading the ledger rather than trusting a setting.
waveform Generated for the player, so scrubbing to the moment somebody raised their voice does not mean listening to nine minutes first.
destination The platform bucket, or your own S3, R2, Azure Blob or S3-compatible bucket. Delivered as a signed URL with a short expiry.
retention A daily job applies the retention policy, and recording can be paused and resumed mid-call.

Notes

  1. 1 Carrier ids: app/Enums/TelephonyProviderId.php. Number capabilities: app/Enums/PhoneNumberCapability.php. Both read at render time.
  2. 2 Trunk settings: the Sip* enums in app/Enums. The OPTIONS probe is the scheduled callagent:sip:options-probe command in bootstrap/app.php.
  3. 3 Skip reasons: app/Enums/CampaignSkipReason.php. Queue strategies: app/Enums/QueueStrategy.php. Ring strategies: app/Enums/RingStrategy.php.

A trunk is a conversation, not a form

Connecting a SIP trunk, porting a range or putting this in front of a PBX you already run needs somebody to look at what you have. That is half an hour and it is free.