Some carriers do not hand you an inbound DID as plain SIP. Instead of sending an 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:
  • piopiy
  • vobiz
  • plivo
  • twilio
Whichever dialect you choose, the document’s job is the same: connect the inbound call to the SIP user registered for this trunk. The dialect only decides the wire format the carrier expects to read. If you paste a 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

  1. Select the dialect for your carrier.
  2. 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.)
  3. In the carrier console, set it as the answer URL / application URL / inbound webhook for the DID you want to land on this trunk.
The console hint says it plainly: paste into the carrier console (Piopiy answer URL, vobiz/Plivo application, Twilio webhook) to route inbound calls to this trunk. Because the URL is derived from the trunk rather than stored, you can re-open the trunk and copy it again at any time. There is no “reveal secret” step and no one-time display.

How the URL resolves to a trunk and its SIP user

admin.carrierAnswerUrl takes { orgId, trunkId, dialect } and:
  1. Reads the trunk’s config from the trunks_data hash in Redis.
  2. Rejects the request with NOT_FOUND when there is no such trunk, and with INTERNAL_SERVER_ERROR when the stored config cannot be parsed.
  3. Compares the config’s tenant_id against orgId. A mismatch is a FORBIDDEN — you cannot mint an answer URL for another tenant’s trunk.
  4. Returns { path, url }, where the URL is the hosted answer endpoint plus the derived path for that dialect and trunk.
The key inside the path is derived, not stored. Nothing is persisted at mint time, and minting the URL twice produces the same value. The same pattern is used for the WhatsApp webhook URL, where the derived key also doubles as the verify token. When the carrier fetches the URL, the returned document connects the call to the SIP user registered for that trunk — i.e. the SIP username you configured on the trunk, with the password held only in sealed Redis.

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 platform sip_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 SIP 407 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_data hash, which is what admin.carrierAnswerUrl later reads.
  • sip_password and api_bearer_token are never hydrated back into the form. Blank means “keep what is stored” (keepTrunkSecrets); typing a value rotates it. The browser sends them as null on the mirror row, which also clears any pre-existing plaintext there.
  • Saving pushes the trunk id onto the SIP reload queue so mod_sip picks 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.