SIP_SETUP.md¶
Overview¶
- Self-hosted backend using Asterisk and a SIP trunk with T.38 fax.
- Send-only. You don’t need to accept inbound faxes to send.
- Full control, no per-fax cloud charges (you still pay your trunk provider).
- Requires some networking setup; this guide assumes minimal prior knowledge.
What Is SIP? (Crash Course)¶
- SIP (Session Initiation Protocol): signaling protocol to set up calls over the internet.
- SIP Trunk: your account/connection to a carrier that places calls to the PSTN.
- DID (Direct Inward Dialing): a phone number you can buy from a SIP provider. For sending only, a DID is optional but recommended so your caller ID is valid.
- T.38: a protocol for fax over IP using UDPTL; more reliable than voice codecs for fax.
- UDPTL: the transport for T.38; you must open/forward a port range for it.
- AMI (Asterisk Manager Interface): how the API tells Asterisk to start a fax call.
- E.164: the international phone number format (e.g.,
+15551234567). Use E.164 for destinations and caller IDs when possible.
Requirements¶
- A SIP trunk that supports T.38 over UDPTL
- A public/static IP or NAT configured to forward required ports (below)
- Docker and Docker Compose
Networking¶
- SIP signaling:
5060/tcp+udp - AMI (Manager):
5038/tcp(internal only; do not expose publicly) - T.38 UDPTL media:
4000–4999/udp - If behind NAT, forward 5060 (tcp+udp) and 4000–4999/udp to the Asterisk host. Keep 5038 internal.
Tips: - Many home routers call this “port forwarding” or “virtual servers”. - If your provider supports registration, the trunk will stay up behind NAT; still forward UDPTL.
Why port forwarding is needed (simple analogy): - Think of your router like an office front desk. Internet calls arrive at the front desk but don’t know which room (device) to go to. - Port forwarding is the instruction to the front desk: “When fax data comes for room 4000–4999 (UDPTL), send it to the Asterisk machine.” - Without it, the fax data can’t reach your server, and calls fail or time out. - If you use a cloud VM instead of your home network, you don’t need a home router—just open those ports in the VM’s firewall.
How to set up port forwarding on a typical home router: 1) Find your router brand/model (sticker on the device) and log into its admin page (often 192.168.0.1 or 192.168.1.1). 2) Reserve a fixed LAN IP for the machine running Asterisk (DHCP reservation), e.g., 192.168.1.50. 3) Create port forward rules: - UDP 4000–4999 → 192.168.1.50 - UDP 5060 and TCP 5060 → 192.168.1.50 4) Save and reboot if required. 5) On your server firewall, also allow those ports.
If you’re on CGNAT (carrier-grade NAT) or can’t port forward: - Use a cloud VM (e.g., small Linux instance) with a public IP and open the same ports there. - Or choose the Phaxio cloud backend instead of SIP.
Configure Environment¶
Edit .env:
FAX_BACKEND=sip
# AMI (used by API to originate fax calls)
ASTERISK_AMI_HOST=asterisk
ASTERISK_AMI_PORT=5038
ASTERISK_AMI_USERNAME=api
ASTERISK_AMI_PASSWORD=change_me_safe
# SIP trunk (from your provider)
SIP_USERNAME=your_username
SIP_PASSWORD=your_password
SIP_SERVER=sip.provider.example
SIP_FROM_USER=+15551234567
SIP_FROM_DOMAIN=sip.provider.example
# Presentation
FAX_LOCAL_STATION_ID=+15551234567
FAX_HEADER=Your Org Name
Start Services¶
docker compose up -d --build
8080, Asterisk on 5060/udp, 5060/tcp, AMI on 5038, UDPTL 4000–4999/udp.
How It Works¶
- API converts input file to PDF, then to fax-optimized TIFF (Ghostscript).
- API creates a job and originates a Local channel via AMI.
- Asterisk dials your SIP trunk; on answer, executes
SendFAX()in T.38. - Asterisk emits
UserEvent(FaxResult, ...); API updates job status.
Logs & Debugging¶
- API:
docker compose logs -f api - Asterisk:
docker compose logs -f asterisk - Inside Asterisk shell:
docker exec -it <asterisk_container> asterisk -rvvv - Check module load:
module show like fax - Call flow: watch for
SendFAXandFaxResultevents
Common Pitfalls¶
- T.38 disabled at provider → enable UDPTL and verify
udptl.confrange. - NAT issues → enable
rtp_symmetric,force_rport, correctmatchandfrom_domain. - Wrong credentials → check
pjsip.confgenerated from templates (envsubst instart.sh). - Ghostscript missing → API warns and stubs conversion; install for production.
- CGNAT / no port forwarding → use a cloud VM or Phaxio backend.
Test Send¶
curl -X POST http://localhost:8080/fax \
-H "X-API-Key: your_secure_api_key" \
-F to=+15551234567 \
-F file=@./example.pdf
- Open Admin Console → Send Fax
- Enter a valid E.164 number; attach a small PDF
- Watch Jobs for status; see Asterisk logs if calls fail
Choosing a SIP Provider (T.38)¶
Pick one of these two (beginner-friendly):
1) Telnyx — developer‑friendly, good for small deployments - Self‑serve signup, clear portal - T.38 support documented; confirm for your region and use case - HIPAA/BAA: contact Telnyx to sign a BAA before handling PHI - Links: Telnyx Voice/Numbers pricing, HIPAA resources - https://telnyx.com/
2) Flowroute — flexible SIP trunking for small scale - Self‑serve signup, good DID inventory - T.38 support documented; confirm with support for your route - HIPAA/BAA: contact Flowroute to discuss BAA before handling PHI - Link: https://flowroute.com/pricing-details/
Notes: - Always ask your provider to confirm T.38 support and sign a BAA if you’ll transmit PHI. - Typical US costs (ballpark): local DID ~$0.5–$2/mo; outbound ~$0.005–$0.02/min. Verify current pricing pages.
TLS Signaling & VPN Examples (Advanced)¶
PJSIP TLS Transport (example)¶
[transport-tls]
type=transport
protocol=tls
bind=0.0.0.0:5061
method=tlsv1_2
local_net=10.0.0.0/8
cert_file=/etc/asterisk/keys/asterisk.pem
priv_key_file=/etc/asterisk/keys/asterisk.key
ca_list_file=/etc/asterisk/keys/ca.crt
external_media_address=<public_ip>
external_signaling_address=<public_ip>
Then reference transport=transport-tls in your trunk endpoint/registration if your provider supports TLS.
Site‑to‑Site VPN (WireGuard sketch)¶
- Provision a WireGuard tunnel between your Asterisk host and the SIP provider’s VPN endpoint.
- Route provider IP ranges through the WG interface; restrict firewall to permit SIP/T.38 only via the tunnel.
- Example (Asterisk side
/etc/wireguard/wg0.conf):[Interface] PrivateKey = <your_private_key> Address = 10.7.0.2/32 [Peer] PublicKey = <provider_public_key> Endpoint = <provider_vpn_host>:51820 AllowedIPs = <provider_sip_subnets> PersistentKeepalive = 25
Restart Asterisk with external_* addresses set to the tunnel’s public IP if required.
Understanding the Asterisk Configuration¶
asterisk/etc/asterisk/templates/pjsip.conf.templateis rendered from your.envat container start.asterisk/etc/asterisk/templates/manager.conf.templateuses${ASTERISK_AMI_USERNAME}as the user section and${ASTERISK_AMI_PASSWORD}for the secret. Ensure these match your API env so AMI auth succeeds.- The
faxoutdialplan inextensions.confusesSendFAX()with T.38 and emitsUserEvent(FaxResult)on completion. - The API listens for that event via AMI to update job status.
Minimal Telephony Glossary¶
- SIP: signaling protocol for VoIP calls.
- SIP Trunk: your carrier connection for inbound/outbound PSTN calls.
- DID: a phone number; optional for send-only but helpful for caller ID.
- T.38: fax-over-IP protocol (preferred over G.711 for reliable faxing).
- UDPTL: transport used by T.38; requires UDP port range open.
- AMI: Asterisk Manager Interface; API uses it to originate calls and receive events.