Trunks
Carrier connections. A trunk is yours (shared with tenants) or a tenant’s own. A tenant’s own trunk is never selectable by another tenant. That is enforced in carrier selection, not merely hidden in the interface — otherwise it would be unmetered calling on somebody else’s carrier account, on their bill.No trunk credential is ever returned by the API. They are stored encrypted and
the panel has no way to display one.
A trunk’s own page
Click a trunk’s name. It answers, in the order the questions get asked:- Health — whether it is registered, whether its configuration reached the switch, and whether anything is authorised to call in on it.
- Channels now — how many are up, against the total and outbound caps.
- Who may call in — the address list, editable there.
- Usage — what it carried, spend by organisation, and why calls ended.
- Its calls, an unanswered-only view, and recent warnings that mention it.
Carrier said is the carrier’s own answer to each call —
403,503,486— which Ended (this side’s cause) does not carry. A red one is a refusal somebody has to fix.
Offers to organisations
Organisation admins choose their own outgoing routes, by destination, from offers you make on Outbound routing → Offers to organisations. An offer is a name organisations see (“Standard”, “Premium”) for one trunk at one service level — Standard = Voxbeam · Gold — offered to every organisation or just one. Organisations see the name and your description, never the carrier, its prefixes or its dial strings; their choices become outbound rules for their own organisation only, and they cannot name a trunk directly. An offer an organisation is using cannot be withdrawn until those rules are removed or changed.Connect, disconnect, verify
Three buttons at the top of a trunk’s page:- Verify runs every check that says whether this carrier can carry a call right now, and names the one that fails: in service, configuration on the switch, the carrier’s host resolving, the gateway as the switch reports it (registered — or loaded to answer the carrier’s challenge), a SIP OPTIONS probe from the server, inbound addresses, and the last 24 hours of calls. No answer to OPTIONS is a warning, not a failure: many carriers ignore it.
- Disconnect takes the carrier out of service at once: no new call uses it, its gateway comes off the switch and its addresses stop being accepted, so its inbound calls are refused too. Calls already up stay up.
- Connect puts it back. On a registering trunk that is already in service, Register now asks the switch to register immediately instead of at its next retry — what you want right after the carrier says “fixed, try again”.
A carrier that challenges calls
Some IP-authenticated carriers whitelist your address and challenge each call for a username and password (Voxbeam’s setup guide shows a gateway withregister=false). Put the credentials under Carrier challenges outbound
calls on the trunk: the switch then carries a non-registering gateway for it
and answers the challenge. The caller’s number stays in the From header, as it
was.
Who may call in
Inbound calls are accepted from listed addresses and refused from everything else. The generated list is default-deny, so a trunk with no ACL hosts accepts nothing at all — while looking enabled and healthy everywhere else. Carriers may send to port 5080 or 5060; both work, and a listed address is not challenged on either. A trunk that belongs to one organisation delivers only that organisation’s numbers; a shared trunk delivers numbers of any organisation you run. A call to a number owned elsewhere is refused, and the log says “the number belongs to an organisation this trunk does not serve” — not “unallocated”, because the number exists.Channel limits
Channel limit counts calls in both directions — it is the number your carrier contracted. Outbound channel limit caps the outgoing share of it. Without one, a dialling burst can fill the carrier and leave nothing for customers ringing in, with every screen looking healthy. Twenty against a total of thirty keeps ten channels a burst cannot take. Leave it blank and the total is the only limit.A call refused by either is congestion, and the log names which cap was
reached — “the carrier is full” and “your own outbound reservation is full”
need different fixes.
Codecs
A trunk has two codec lists, and the second one is optional.- Codecs for outgoing calls is what FireTone offers the carrier when it dials out — and, unless you set the second list, what a call arriving on this trunk is held to as well.
- Codecs for incoming calls is what this carrier may offer on calls it sends you. Leave every codec unticked to accept the same list as above.
- Tick what the carrier speaks. Each codec shows what it costs the switch to convert and who can speak it; no browser speaks G.729, iLBC or G.726.
- Order the ticked ones. Each call uses the first codec in this order that the other side also speaks, so the order is yours to choose: G.729 first saves bandwidth, G.711 first saves CPU. A newly ticked codec joins the end.
- Nothing ticked means the default, G.711 A-law then µ-law — and the editor says so rather than leaving a blank.
GET /trunks?codec=G729.
Asking the carrier. A list is what you believe the carrier accepts. Test
codecs, beside Verify on the trunk’s page, asks it: one call attempt per
codec, offering that codec alone, hung up the moment the carrier rings and
before anyone answers, so nothing is billed. A codec the carrier will not take
comes back refused (488), which is the same INCOMPATIBLE_DESTINATION a
customer’s call would have died of. Give it your own mobile: it rings once per
codec for up to twenty seconds. A destination that answers without ringing
first is hung up within a second, which some carriers bill as a one-second
call. The API is POST /trunks/{id}/codec-test, one codec per call.
Direction
A trunk carries calls both ways unless you say otherwise, and that is what every trunk does until somebody changes it.
A declaration is enforced, not decorative — that is the point of making it. On a
carrier account an unexpected inbound path is how toll fraud arrives, so a call
on a trunk you said would not receive one is refused rather than carried.
It also stops FireTone telling you something untrue. An outgoing-only carrier
has no use for ACL hosts, so Accepts no inbound is not shown for it and
firetonectl doctor stops reporting it — a false alarm the check printed on
every run until a trunk could say what it was.
It was a text box, and what was typed went to the switch as typed — which says
nothing about a name it does not know.
ULAW,PCMA offered PCMA alone; ALAW
placed no call; PCMA@30I sent 20 ms packets, because a capital I is not a
suffix. Lists are now stored in the one spelling the switch honours. A trunk
saved before that keeps what was typed until it is next saved, and
firetonectl doctor (which verify.sh runs) names any that need it.Dial prefix
Digits this carrier wants in front of the number, added on the way out after the+ is removed. Blank for none.
Number format
How numbers are sent to this carrier — the number dialled and the caller ID alike:
National needs the carrier’s country. The dial prefix, if any, goes in front
after formatting: prefix
1831 with national gives 18310412345678.
Caller ID
What the carrier is shown, chosen for each call in this order:- the extension’s own caller ID number, when it is a real number (
1001is not;0412 345 678is, read in the extension’s country); - the organisation’s Outbound caller ID — usually its main line;
- the trunk’s Default caller ID — usually the number the carrier account is registered as.
Campaigns keep their own caller ID and are not affected by this chain.
When the carrier refuses a call
The caller hears busy for busy, and “the call cannot be completed as dialled” for anything else, instead of silence. The call’s page shows Far end — the carrier’s SIP answer and cause — and the log line names the trunk, the SIP status and the caller ID that was presented, which is usually the answer: a403 straight after a caller ID change is the carrier refusing the number.
sudo deploy/staging/verify.sh checks that every registering trunk on the box
is actually registered with its carrier, and ftctl doctor finds a registering
trunk that has lost its switch node.
Service levels and outbound rules
The whole of outbound routing — the order a call is decided in, the three
kinds of rule, what Try a number means by a carrier being “passed over” —
is on its own page: Outbound routing. What
follows is the part that belongs to a carrier: its service levels.
List them on the trunk’s page under Service levels. Then choose between
them on Outbound routing, with outbound rules: a number pattern and the
legs a matching call takes, in order.
- The most specific pattern matching the number wins —
+9192beats+91beats*— and only its legs are used. A+91rule does not quietly fall back to the*rule’s legs. - A rule for one organisation beats a shared rule for the same pattern.
- A leg’s level replaces the trunk’s dial prefix for that call. A leg with no level sends none.
- A number no rule matches goes out only through the organisation’s own
trunks, and is refused if it has none. It never goes to a shared carrier
nobody chose. A rule for
*, for all organisations or for one, is the default carrier. - A call fails over only to the legs its rule lists. A rule whose carriers are all disabled or unusable refuses the call rather than trying others.
- Legs whose trunk is disabled are skipped, and
ftctl doctorreports the rule (outbound-rule-unusable). - A leg whose carrier has a price list of its own that does not cover the number is passed over for the next leg. If no leg covers it, the call is refused and the log names the trunks.
Numbers a carrier sends without a +
A number arrives as+919216217231, 919216217231 or the national
09216217231. The first two are the same digits and match a DID without any
configuration. The third only means something if you know the country.
Switch on Accept national-format numbers, set the Country, and choose
how a parsed number is accepted:
regex is the escape hatch for a carrier whose numbering the library judges
invalid. It still parses with the country’s rules — a pattern can test a
number, it cannot convert one.
This is only ever consulted after the plain forms have failed to match, so
switching it on cannot move a call that already works. It can only rescue one
that was being refused.
Usage
Trunks → Usage compares carriers side by side: inbound and outbound counts, answered rate, minutes and spend, with a trunk selected breaking spend down by organisation. Every figure comes from the same place as the call list, so the two cannot disagree about what a call is.A trunk’s own price list
On a trunk’s page, across the full width below Who may call in and Service levels, Destinations & pricing holds what that carrier can reach and what it charges: prefix, country, destination, currency, price per minute, connect fee, billing (minimum / increment, so60/60
or 30/6) and, optionally, a service level.
It does two things, and the second is the one that surprises people:
- It prices that trunk’s calls. A row for the call’s service level beats a row for every level; within each, the longest prefix wins. So Platinum and Gold on one carrier can cost what the carrier actually charges for them.
- It decides what the trunk can route. A carrier’s deck is also the list of where it can send a call. A number the list does not cover is not sent to that trunk: the next leg of the outbound rule is tried, and if no leg’s carrier lists the number the call is refused, with a log line naming the trunks and this screen.
The table
The prices are listed in the card, paged, searched and sorted on the server — a deck is tens of thousands of rows and none of it is sent to the browser to be sorted there.- Search takes a number or a word. A number is matched from its first
digit, with or without the
+, so a dialled number pasted from a call record finds its destination (+1876…finds Jamaica;44does not find1441). A word is matched anywhere in the destination, the country or the service level. - Filters: a service level — or Prices for every level — and a country.
- Sorting: prefix (the default, which keeps a prefix beside its own per-level prices), destination, service level, and per minute — the last only on a list in a single currency. Across several it would order numbers, not money.
- A price imported ahead of its date is listed with a from date badge; the one still in force sits on the row above it.
Importing a carrier’s deck
Menu → Import… takes the carrier’s CSV more or less as it arrives. Column names are matched loosely (prefix / code / dial_code, price / rate /
cost, destination, currency, billing, service level / quality,
effective from); prefixes may be written +91, 91 or 0091; a title line
starting with # is skipped; and when there is no country column the country is
worked out from the prefix (1876 is Jamaica, not the United States).
A vendor’s rate sheet in its usual form is read as it stands:
A sheet usually arrives before its date. Import it when it arrives. The new
prices are listed as scheduled, today’s prices stay in force and stay
listed until that moment, and What would a call cost? goes on quoting
today’s price until then. A trunk whose whole list is scheduled behaves as it
did before the import — it does not start refusing numbers five days early.
A withdrawn destination stops being priced on the vendor’s date, and from
then the trunk is not sent those numbers: the next carrier in the outbound rule
takes them.
Expiry dates are not obeyed. A vendor’s “expires 3 October” means “expect
another sheet”. If the list ended its prices then, a week with no new sheet
would leave the trunk with no prices at all — and it would quietly stop being
sent calls, or fall back to the shared card, because somebody was on holiday.
Prices stay in force until a newer list replaces them.
.xlsx, .xls) is refused with the way to fix it: File →
Save As → CSV UTF-8. Read as text a workbook is a zip file, and a complaint
about missing columns would send you to rename columns that are already right.
You choose what to assume when the file does not say — currency, minimum,
increment, connect fee, service level, the date the prices take effect — and one
of two modes:
Preview first — the button says Preview until you have. It shows how many
prices would be added, changed, left alone, removed and skipped, a few examples
of old against new, any row it could not use (by line, with the reason), and any
currency you have no exchange rate for.
A file with no usable rows is refused in either mode. In Replace it would
otherwise remove every price and add none — and since a trunk with no list
quietly falls back to the shared card, the mistake would not even look like one.
A date in the future leaves today’s price in force until then.
What a file does not mention, it does not change. A sheet dated Friday says
nothing about today, so today’s prices stay until Friday — in either mode. And
in Update, correcting today’s price for a destination leaves a price already
scheduled for Friday alone. (Replace makes the file the whole list: a scheduled
price it does not carry goes.)
Clear all ends everything that has not ended, scheduled prices included.
Backup, and the same deck on another trunk
Export writes the whole list as the file the importer reads: every column, service levels by name, nothing tied to the trunk it came from. Its first line says which trunk, when, and how many prices.- Restore: import a trunk’s own export in Replace mode and the list comes
back exactly — including mid-week, when it holds today’s prices, the ones
scheduled for Friday and a withdrawal due with them. All three are in the file
(a withdrawal to come is written the way a vendor writes one: a
Deletedrow on that date), and restoring a backup over itself changes nothing. - Reuse: import it into another trunk. A service level that trunk does not have is counted and shown in the preview, then left out or loaded for every level, as you choose — never quietly dropped or widened.
Import history
Menu → Import history lists every import and every Clear all on the trunk, newest first: when, who, Update or Replace, the file’s name and size, and how many prices were added, changed, removed and left alone. Rows the importer skipped open out under the entry.- The file is kept. Download hands back exactly what was uploaded — the same bytes — which makes the history a backup of what each vendor actually sent, and the way to look again at a sheet from three months ago. Downloading needs the permission to change rates, where the list itself needs only the permission to see them: a vendor’s sheet can hold more than was imported.
- An entry means it happened. It is written together with the prices, in one step. A preview is not in the history, nor is a file that was refused, and an import that failed part-way left neither prices nor an entry.
- The newest twenty files on a trunk are kept, and a file over 16 MB once compressed is not kept at all. Either way the entry stays, saying which.
- A price added or edited by hand is not here; that is in the audit log.
What would a call cost?
Menu → Quote a number… It is priced, per service level, by the same code that prices real calls: a price and the row that matched, no price, or not in this trunk’s list — calls are not sent here.Rate cards
A rate card is a set of per-destination prices with an effective date. Cards come in two sides:- Cost — what you pay the carrier.
- Sell — what the tenant pays you.