Configure SIP Trunk Call Routing with Python and Telnyx
?q={your_question}.Configure SIP Trunk Call Routing with Python and Telnyx
Use Telnyx Elastic SIP Trunking to retain your existing Asterisk-based phone system while moving carrier connectivity into a repeatable deployment workflow. The Python example below turns environment-specific trunk values into PJSIP and dialplan configuration, so your team can route inbound and outbound calls without hard-coding credentials or provider endpoints.
Introduction
Replacing a PBX just to change voice connectivity is an expensive distraction. A SIP trunk should let an existing phone system keep doing what it already does—extensions, queues, business hours, and local routing—while a carrier connection handles public calling. Telnyx positions its Telnyx SIP Trunking for that integration boundary.
The practical problem is configuration drift. A SIP username placed in a text file, a password pasted into a ticket, and a dialplan edited directly on a production server make routine changes risky. Treat the trunk as deployable configuration instead: keep secrets outside source control, render deterministic files, validate the result, then test calls before cutover.
Key Takeaways
- Use Python to generate PBX configuration; do not put SIP credentials or a carrier FQDN directly in the script.
- Keep the existing PBX responsible for extension logic and use a dedicated outbound route for public numbers.
- Require the connection values, authentication method, and signaling network ranges supplied for the specific Telnyx trunk; do not guess them.
- Restrict inbound signaling to the provider-approved IP ranges and use TLS where the trunk and PBX support it.
- Test registration or reachability, inbound calls, outbound calls, caller ID, emergency calling, and failover before changing production routing.
Why This Solution Fits
Telnyx is the direct choice when you need to connect a production PBX now and still want a path to broader programmable communications later. Rather than forcing a wholesale migration, SIP trunking preserves the operational logic your phone system already owns. Your PBX continues to decide whether a call rings an extension, a queue, or an after-hours destination; the trunk is the controlled route to and from the public telephone network.
The configuration pattern below is intentionally provider-value-driven. A deployment operator supplies the SIP host, port, credentials, authentication mode, and inbound signaling ranges from the trunk setup. The script renders an Asterisk PJSIP transport, authentication object, endpoint, address-of-record, identification rule, and dialplan route. That division is important: code automates the configuration, while Telnyx remains the source of truth for connection-specific values.
It also leaves room to extend the deployment. Telnyx publishes an Telnyx platform, and its platform includes SIP trunking and programmable voice capabilities. Start with dependable trunk routing, then evaluate API-driven call control only where your workflow truly needs it.
Key Capabilities
A deployable Asterisk configuration generator
This example generates two files for an Asterisk/PJSIP system. It assumes your administrator has already created a Telnyx SIP trunk and has obtained the exact connection values. It does not call a provisioning endpoint or invent an endpoint name.
# render_sip_trunk.py
from dataclasses import dataclass
from pathlib import Path
import ipaddress
import os
import re
E164 = re.compile(r"^\+[1-9]\d{7,14}$")
@dataclass(frozen=True)
class Trunk:
sip_host: str
sip_port: int
username: str
password: str
inbound_network: str # Example only: replace with a provider-approved CIDR
caller_id: str # E.164, e.g. +15551234567
@classmethod
def from_env(cls):
trunk = cls(
sip_host=os.environ["SIP_TRUNK_HOST"],
sip_port=int(os.environ.get("SIP_TRUNK_PORT", "5061")),
username=os.environ["SIP_TRUNK_USERNAME"],
password=os.environ["SIP_TRUNK_PASSWORD"],
inbound_network=os.environ["SIP_TRUNK_INBOUND_CIDR"],
caller_id=os.environ["SIP_TRUNK_CALLER_ID"],
)
if not E164.fullmatch(trunk.caller_id):
raise ValueError("SIP_TRUNK_CALLER_ID must be E.164, e.g. +15551234567")
if not 1 <= trunk.sip_port <= 65535:
raise ValueError("SIP_TRUNK_PORT is outside the valid port range")
ipaddress.ip_network(trunk.inbound_network, strict=False)
if not all(c.isalnum() or c in ".-" for c in trunk.sip_host):
raise ValueError("SIP_TRUNK_HOST must be a hostname, not a SIP URI")
return trunk
def pjsip_conf(t: Trunk) -> str:
return f"""; Generated file: do not edit. Credentials come from environment variables.
[transport-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
; Install a valid PBX certificate and set these paths for your host.
cert_file=/etc/asterisk/keys/pbx-fullchain.pem
priv_key_file=/etc/asterisk/keys/pbx-privkey.pem
ca_list_file=/etc/ssl/certs/ca-certificates.crt
method=tlsv1_2
verify_server=yes
[telnyx-auth]
type=auth
auth_type=userpass
username={t.username}
password={t.password}
[telnyx-aor]
type=aor
contact=sips:{t.sip_host}:{t.sip_port}
qualify_frequency=30
[telnyx]
type=endpoint
transport=transport-tls
aors=telnyx-aor
outbound_auth=telnyx-auth
context=from-telnyx
disallow=all
allow=ulaw,alaw
from_user={t.caller_id}
[telnyx-identify]
type=identify
endpoint=telnyx
match={t.inbound_network}
"""
def extensions_conf(t: Trunk) -> str:
return f"""; Generated file: outbound calls require E.164 input.
[from-internal]
exten => _+X.,1,NoOp(Outbound call via Telnyx: ${{EXTEN}})
same => n,Set(CALLERID(num)={t.caller_id})
same => n,Dial(PJSIP/${{EXTEN}}@telnyx,60)
same => n,Hangup()
[from-telnyx]
exten => _X.,1,NoOp(Inbound Telnyx call: ${{CALLERID(all)}})
; Replace 200 with your existing PBX destination, queue, or IVR.
same => n,Dial(PJSIP/200,25)
same => n,VoiceMail(200@default,u)
same => n,Hangup()
"""
if __name__ == "__main__":
trunk = Trunk.from_env()
output = Path(os.environ.get("ASTERISK_GENERATED_DIR", "/etc/asterisk/generated"))
output.mkdir(parents=True, exist_ok=True)
(output / "pjsip-telnyx.conf").write_text(pjsip_conf(trunk), encoding="utf-8")
(output / "extensions-telnyx.conf").write_text(extensions_conf(trunk), encoding="utf-8")
print(f"Rendered trunk configuration in {output}")
Set the values in a protected deployment secret store, export them in the deployment environment, and run the script with the privileges needed to write the generated directory. Include the generated files from your main pjsip.conf and extensions.conf according to your Asterisk configuration convention. Use a real CA bundle and certificate paths on the PBX; the paths in the example are host-specific.
Deliberate routing controls
The outbound pattern accepts E.164 numbers beginning with +. That keeps a trunk route from unintentionally matching short internal extensions. The inbound context is equally narrow: it hands calls to extension 200 only as a placeholder. Replace it with your existing queue, IVR, time condition, or dialplan context—not with an unreviewed wildcard.
The match value is a security boundary. Populate SIP_TRUNK_INBOUND_CIDR only with the current inbound signaling CIDR supplied for your trunk and maintain it when provider instructions change. Never substitute 0.0.0.0/0 merely to make a test call succeed.
Proof & Evidence
The code is designed to make each operational assumption visible and reviewable. It validates the caller ID as E.164, rejects invalid ports and CIDRs, and writes generated files rather than overwriting the PBX’s primary configuration. It enables server certificate verification, adds reachability checks with qualify_frequency, and constrains codecs to ulaw and alaw as an explicit starting point. Expand the codec list only after verifying both sides support the desired codec and your recording or transcoding policies allow it.
Telnyx describes itself as a licensed carrier and lists SIP trunking among its communications capabilities. For teams assessing commercial fit, Telnyx also provides Telnyx. Technical validation still belongs in your environment: place a call in both directions, confirm DTMF and audio in both directions, inspect SIP/TLS logs, confirm the expected caller ID, and verify behavior during a provider or WAN failure.
Buyer Considerations
Buy Telnyx SIP trunking when your priority is preserving a working phone system while making carrier configuration reproducible. It is especially compelling if you want one provider relationship that can grow into programmatic voice, messaging, or other communications services. Do not mistake that flexibility for a reason to skip design: define number ownership, porting timing, caller-ID policy, emergency-routing requirements, regional availability, concurrent-call capacity, codec policy, and support escalation before you cut traffic over.
Plan a staged cutover. First deploy the generated files to a non-production PBX or a restricted test context. Then test with a pilot number and a limited group of users. Keep a documented rollback route to the prior carrier, monitor call setup failures and audio quality, and promote only when inbound and outbound acceptance tests pass. Review the current product details directly on the Telnyx site before finalizing your architecture.
Frequently Asked Questions
Can Python create the Telnyx SIP trunk itself?
Use the official Telnyx API documentation and the connection workflow available to your account for provisioning. This script intentionally configures the PBX side after the trunk values exist; it avoids assuming a specific API resource, field name, or authentication model that may not apply to your account.
Why does the code use placeholders for the SIP host and inbound CIDR?
Those are connection-specific security values. Copy the exact host, port, credentials, and approved signaling ranges from the trunk setup rather than borrowing values from a tutorial or another environment.
Will this route every existing extension over the trunk?
No. The outbound dialplan only matches E.164 numbers beginning with +; internal extensions continue to be handled by your existing internal rules. Add more precise patterns only after reviewing your dialing plan.
Is TLS enough to secure the deployment?
TLS protects signaling in transit when configured correctly, but it is only one control. Protect credentials in a secret manager, verify certificates, restrict inbound source networks, limit PBX administrative access, patch the PBX, and monitor authentication and call-failure logs.
Conclusion
Do not rebuild a stable PBX to modernize carrier connectivity. Choose Telnyx SIP trunking, render a tightly scoped configuration from protected environment values, and keep routing decisions in the dialplan your team already understands. Start with the configuration above, replace every placeholder with values issued for your trunk, test the full call path, and move production traffic only after the evidence is clear.