; =====================================================; GLOBALS REQUIRED (globals.conf or globals_custom.conf) ; ------------------------------------------------------------- ; [globals] ; BRIDGE1_APIKEY= ; BRIDGE1_EXCHANGE=777 ; NXX prefix — used to build the outbound ANI only, ; ; NOT applied to inbound DIDs (full number passes through) ; BRIDGE2_APIKEY= ; BRIDGE2_EXCHANGE=888 ; ; ...add one pair per bridge; AstroCom SIP Bridge -- Dialplan (extensions.conf) ; /etc/asterisk/extensions.conf ; ; CALL FLOWS ; ------------------------------------------------------------- ; ; INBOUND (AstroCom -> downstream PBX via PJSIP) ; ----------------------------------------------- ; AstroCom delivers the full 7-digit number, e.g. 7771234. ; The full number is forwarded as-is to the downstream PBX as ; the SIP Request-URI / To: user (DID). The PBX is responsible ; for routing it internally however it sees fit. ; ; AstroCom --IAX2--> [bridgeN-from-astrocom] ; | ; Pass full DID (e.g. 7771234) to PBX ; | ; Dial PJSIP/7771234@bridgeN-pbx ; | ; v ; Downstream PBX ; ; OUTBOUND (downstream PBX -> AstroCom via IAX2) ; ------------------------------------------------ ; The PBX dials the full 7-digit AstroCom number. ; The bridge prepends the local exchange so the ANI is also ; 7 digits, then hits the AstroCom routing API: ; ; GET https://api.astrocom.tel/api/v1/route/// ; ; The API returns one of: ; "local" ; --> same block; route to this bridge's own SIP PBX ; "IAX2/:@:/" ; --> dial that URI verbatim ; "/4xx" ; --> rejected; play busy/congestion toward PBX ; ; Downstream PBX --SIP--> [bridgeN-from-pbx] ; | ; CURL api.astrocom.tel/api/v1/route/... ; | ; +----------+----------+ ; local IAX2 URI ; | | ; Dial PJSIP/XXXX@bridgeN-pbx Dial ${lookup} ; ; ISOLATION ; ------------------------------------------------------------- ; * Every bridge has exactly two contexts; they share nothing. ; * [default] silently drops anything with no context match. ; * Inter-bridge calls route through AstroCom automatically -- ; no extra config needed here. ; ; GLOBALS REQUIRED (globals.conf or globals_custom.conf) ; ------------------------------------------------------------- ; [globals] ; BRIDGE1_APIKEY= ; BRIDGE1_EXCHANGE=777 ; NXX prefix (3 digits) for bridge 1 ; BRIDGE2_APIKEY= ; BRIDGE2_EXCHANGE=888 ; ; ...add one pair per bridge ; ============================================================ [default] exten => _X!,1,Verbose(2,DROP: ${EXTEN} landed in [default] -- no matching context) same => n,Hangup(21) ; ============================================================ ; --- BRIDGE 1 ----------------------------------------------- ; ============================================================ ; Inbound from AstroCom -> forward full DID to PBX ; Context name == routes.auth == IAX2 section name in iax.conf [bridge1-from-astrocom] exten => _XXXXXXX,1,Verbose(2,BRIDGE1 inbound from AstroCom: ${CALLERID(all)} -> ${EXTEN}) same => n,Dial(PJSIP/${EXTEN}@bridge1-pbx,30,t) same => n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy) same => n,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?congestion) same => n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?noanswer) same => n,Hangup() same => n(busy),Busy(5) same => n(congestion),Congestion(5) same => n(noanswer),Hangup(19) ; Outbound from Bridge-1 PBX -> CURL AstroCom API -> IAX2 [bridge1-from-pbx] exten => _XXXXXXX,1,Verbose(2,BRIDGE1 outbound from PBX: ${CALLERID(all)} -> ${EXTEN}) ; Build the 7-digit ANI: exchange prefix + bare extension from CallerID same => n,Set(ANI=${BRIDGE1_EXCHANGE}${FILTER(0-9,${CALLERID(num)})}) ; Ask AstroCom where to send this call same => n,Set(lookup=${CURL(https://api.astrocom.tel/api/v1/route/${BRIDGE1_APIKEY}/${ANI}/${EXTEN})}) same => n,Verbose(2,BRIDGE1 AstroCom lookup: ${lookup}) ; "local" -- callee shares our block, route to our own SIP PBX same => n,GotoIf($["${lookup}" = "local"]?local) ; Empty or error URL -- reject same => n,GotoIf($["${lookup}" = ""]?reject) ; Valid IAX2 URI -- dial it verbatim same => n,Dial(${lookup},30,t) same => n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy) same => n,GotoIf($["${DIALSTATUS}" = "CONGESTION"]?congestion) same => n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?noanswer) same => n,Hangup() ; Local path -- strip exchange prefix, dial extension on own PBX same => n(local),Verbose(2,BRIDGE1 local call: routing ${EXTEN} to own PBX) same => n,Dial(PJSIP/${EXTEN}@bridge1-pbx,30,t) same => n,Hangup() ; Error paths same => n(reject),Verbose(2,BRIDGE1 AstroCom rejected call: ${lookup}) same => n,Congestion(5) same => n(busy),Busy(5) same => n(congestion),Congestion(5) same => n(noanswer),Hangup(19) ; ============================================================ ; --- BRIDGE 3 (copy & uncomment for each additional bridge) - ; ============================================================ ; Also add BRIDGE3_APIKEY and BRIDGE3_EXCHANGE to globals.conf. ; ;[bridge3-from-astrocom] ;exten => _X!,1,Verbose(2,BRIDGE3 inbound: ${CALLERID(all)} -> ${EXTEN}) ; same => n,Dial(PJSIP/${EXTEN}@bridge3-pbx,30,t) ; same => n,Hangup() ; ;[bridge3-from-pbx] ;exten => _X!,1,Verbose(2,BRIDGE3 outbound: ${CALLERID(all)} -> ${EXTEN}) ; same => n,Set(ANI=${BRIDGE3_EXCHANGE}${FILTER(0-9,${CALLERID(num)})}) ; same => n,Set(lookup=${CURL(https://api.astrocom.tel/api/v1/route/${BRIDGE3_APIKEY}/${ANI}/${EXTEN})}) ; same => n,GotoIf($["${lookup}" = "local"]?local) ; same => n,GotoIf($["${lookup}" = ""]?reject) ; same => n,Dial(${lookup},30,t) ; same => n,Hangup() ; same => n(local),Dial(PJSIP/${EXTEN}@bridge3-pbx,30,t) ; same => n,Hangup() ; same => n(reject),Congestion(5) ; ============================================================ ; --- OPTIONAL: Direct SIP inter-bridge shortcut ------------- ; ; Bridges can already call each other through AstroCom with ; zero extra config. If you want direct SIP routing between ; bridges (no AstroCom hop), add a higher-priority pattern for ; the other bridge's block BEFORE the _X! catch-all. ; ; Example -- Bridge-1 calling Bridge-2's 888XXXX block directly: ; ;[bridge1-from-pbx] ;exten => _888XXXX,1,Verbose(2,BRIDGE1->BRIDGE2 direct: ${EXTEN}) ; same => n,Dial(PJSIP/${EXTEN}@bridge2-pbx,30,t) ; same => n,Hangup() ; ; (The _X! rule below then handles all other numbers normally.) ; ============================================================