INVITE to your gateway, they fetch an HTTP URL when a call
arrives and execute the document that comes back. That document is what
tells the carrier to connect the call onto your TeleQuick trunk.
The trunk editor mints that URL for you. Open a saved trunk, find the
Carrier answer URL row under SIP endpoint, pick the dialect that
matches your carrier, and copy the value.
When a carrier needs an answer URL
Use the answer URL when the carrier is a programmable-voice platform rather than a wholesale SIP provider:
A conventional SIP trunk that sends
INVITEs straight at your
Gateway IP / SBC IP needs none of this. For those trunks, ignore
the row.
Why the row only appears on a saved trunk
The Carrier answer URL row renders only when the trunk already exists (initial.trunk_id is set). That is not cosmetic. Minting the
URL is a admin.carrierAnswerUrl query that reads the trunk’s stored
config out of Redis and fails with NOT_FOUND if there is no row for
that trunk id. There is nothing to resolve against until you have saved
the trunk once.
So the order is: fill in the SIP endpoint fields → Save → reopen the
trunk → copy the answer URL.
The four dialects and what each returns
The dialect selector to the left of the URL field changes the path, and therefore the shape of the document the carrier receives. Each carrier parses only its own format, so the dialect must match the console you are pasting into:piopiyvobizplivotwilio
twilio URL into a Plivo application (or vice versa),
the carrier fetches the URL successfully and then fails to parse the
response. Re-copy with the correct dialect selected.
Pasting it into the carrier console
- Select the dialect for your carrier.
- Click Copy. (If the clipboard is unavailable in your browser, the field is read-only but selectable — focus it and it selects the whole value.)
- In the carrier console, set it as the answer URL / application URL / inbound webhook for the DID you want to land on this trunk.
How the URL resolves to a trunk and its SIP user
admin.carrierAnswerUrl takes { orgId, trunkId, dialect } and:
- Reads the trunk’s config from the
trunks_datahash in Redis. - Rejects the request with
NOT_FOUNDwhen there is no such trunk, and withINTERNAL_SERVER_ERRORwhen the stored config cannot be parsed. - Compares the config’s
tenant_idagainstorgId. A mismatch is aFORBIDDEN— you cannot mint an answer URL for another tenant’s trunk. - Returns
{ path, url }, where the URL is the hosted answer endpoint plus the derived path for that dialect and trunk.
Inbound routing after the call lands
The answer URL gets the call onto the trunk. Where it goes from there is the trunk’s own inbound routing, rebuilt into the dialplan every time you save (rebuildDialplanRulesSafe): a vdn_id produces a vector: rule,
otherwise agent_id produces an ai_bidirectional_stream: rule. If you
leave both empty, the trunk has no inbound route and the call parks on
silence.
Choosing a SIP edge for residency
SIP edge sits in the same SIP endpoint block. It is a select populated from the platformsip_edge registry — enabled rows only,
readable by every tenant, ordered by name. Leaving it on the direct
option means the trunk talks to the carrier without an edge in the path.
Picking an edge is a data-residency decision: signalling for this trunk
traverses the selected regional edge. Selecting an edge patches the
trunk’s related address fields (edgeFieldPatch), and the hint line
beneath the grid describes the edge you chose. sip_edge_id is carried
into the sealed trunk payload, and withSipEdge merges it on save, so
an edge already attached to a stored trunk survives a re-save that does
not touch the field.
Bypassing the 407 gate with carrier source CIDRs
By default a trunk authenticates the carrier — the engine challenges inbound requests and expects credentials (the SIP407 gate). Some
carriers, including the programmable-voice platforms that use answer
URLs, will not answer a challenge. For those, authorise the carrier by
source address instead.
Allowed source CIDRs (acl_allow_cidrs) is a comma-separated list
of IPs or CIDR blocks, e.g. 203.0.113.0/24. Traffic arriving from a
listed source is accepted on this trunk without the credential
challenge. The form validates every entry and reports the first bad one
by name; an empty list is written as null, meaning “no bypass — the
gate applies”.
On save, withCarrierAllowCidrs merges the list into the sealed trunk
payload alongside the SIP edge, so the carrier’s own published ranges can
be maintained without being clobbered by an unrelated edit.
Scope the list to the exact ranges your carrier publishes. Every address
you list can originate calls on this trunk without presenting
credentials.
Saving, secrets, and hot reload
The trunk form is a dual write and the answer URL depends on both halves landing:- The non-secret Postgres mirror row is written server-side first by
admin.addTrunk/admin.updateTrunk(org-scoped and audited). If the mirror write fails, nothing goes live in Redis alone. - The full config — including secrets — is then sealed under the org KEK
and written to the
trunks_datahash, which is whatadmin.carrierAnswerUrllater reads. sip_passwordandapi_bearer_tokenare never hydrated back into the form. Blank means “keep what is stored” (keepTrunkSecrets); typing a value rotates it. The browser sends them asnullon the mirror row, which also clears any pre-existing plaintext there.- Saving pushes the trunk id onto the SIP reload queue so
mod_sippicks up the change without an engine bounce, and rebuilds the dialplan rules.
admin.addTrunk upserts by (org_id, trunk_id), so re-saving an
existing trunk id — for example after a bulk import — updates the
existing row rather than creating a duplicate, and the answer URL for
that trunk id stays the same.