Multiple teams, outbound CID by country/area code

Hi,
New to the community, thanks for being lenient.

We’re working to move away from our current provider (justcall.io) and into freepbx. Due to integrations etc. all our numbers are on a pjsip with twilio (currently single SIP). I can’t find a definitive answer for our needs, I’d like your advise on how to approach the configuration.

I have 3 separate teams: recruiting, service support and Client Success. Each Team has at least 2 outbound CIDS: one for Canada, one for US. As we grow, it is possible more US local numbers are added. In our current solution, the user simply toggles between assigned outbound CIDs in their softphone. In FreePBX, since this does not seem possible, I was thinking to automate the outgoing routing based on area codes.

I searched this topic and the suggestion seems to be to use the settings at extension level (there’s no user group level and trunk levels does not seem to fit the need?).

Is there a way to effectively lump all US or Canadian area codes in fewer lines when setting CID field in pattern matching? Or I should go the database or custom code approach I’ve seen suggested?

In terms of trunks, you suggest me to split them by team (i.e. 3 trunks) or by number (i.e. currently 6)?

Does this approach makes sense to you?

If it matters, additional details:

  • inbound these are 3 ring groups with voicemail for each going to a virtual extension.
  • I considered setting up a single extension per group and be done but it does not seem elegant and I would lose control in terms of knowing who does what in the PBX.
  • we only use softphones on both desktop and mobile, no physical phones.
  • A solution where the user must add a number to chose route is not viable for us, it will mess with our click to call, CRM, etc.
  • I looked into the CallerID commerical module, I do not mind paying for it. But it doesn’t seem to be able to do what we need (it’s a hard route this prefix to that caller id, but doesn’t flex by user or extension or group). Unless I’m misreading the documentation.

Some custom dialplan.that does this

Thank you!

  • First off, stupid question: how does it work exactly? :grinning: I place this on /etc/asterisk/extensions_custom.conf and edit as needed, but then is this associated to all extensions? How would I instruct the PBX to for example use the [outbound-el-cid] vs [outbound-random-cid]?

  • if this impacts all extensions, how to I use the user/extension (or best, user group) into this? I.e. recruiting team will have different settings than service team.

  • For our user case I assume I will mainly focus on [outbound-el-cid] because matching alone will not work (i.e. currently one number for all US). is there a way besides wildcards to set OR options while writing a single exten line? It seems that Canadian extensions can’t really be easily segregated by first area code digit etc. So I can for example say 4XX and catch them all.

  • Does the PBX goes in order when processing line 57 - 58 ? Meaning, can I also add a (example) line 59 that works as catch all and route the calls with a specific CID if all above exten lines to not have a match? Eg. exten => XXX,1,Set(CALLERID(number)=9205553333)

In it’s current form, this dialplan would apply to all outbound calls from all extensions. It would need to be modified for your use case which is fairly complex.

If each user is a member of one and only one group, you could repurpose the extension’s accountcode field to indicate which custom dialplan to run.

Lines 57-58 are where the area codes start with only 2 examples. You can list as many or as few area codes as you want in this section using the same format and you can add a general case of _XXX. The way things are written, the lines are not executed in order, they will match more specific cases first and the more general cases last.

Yes, a team member can only be part of a single team. Will something like this work then to incorporate accountcode?

[custom-variables]
exten => s,1,Set(RecruitingCA=12345678911)   ; Canadian CID for Recruiting
exten => s,n,Set(RecruitingUS=98765432111)   ; US CID for Recruiting
exten => s,n,Set(SupportCA=9999999999)      ; Canadian CID for Support
exten => s,n,Set(SupportUS=8888888888)      ; US CID for Support
exten => s,n,Set(CSCA=7777777777)          ; Canadian CID for CS
exten => s,n,Set(CSUS=6666666666)          ; US CID for CS

[macro-dialout-trunk-predial-hook]
exten => s,1,NoOp(Entering user defined outbound macro)
exten => s,n,GotoIf($["${OUTBOUND_GROUP_ID}" != ""]?dialout-group-${OUTBOUND_GROUP_ID}-predial-hook,s,1)

; Ontario Area Codes
exten => _437XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _647XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _905XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _249XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _343XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _416XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _519XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _705XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _365XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _548XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _613XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _807XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _226XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _289XXXXXXXX,1,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
;...repeat for each Ontario Area Code and Support, CS

; Prince Edward Island Area Codes
exten => _902XXXXXXX,14,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _782XXXXXXX,14,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?canadian_recruiting)
exten => _902XXXXXXX,15,GotoIf($["${ACCOUNTCODE}" = "Support"]?canadian_support)
exten => _782XXXXXXX,15,GotoIf($["${ACCOUNTCODE}" = "Support"]?canadian_support)
exten => _902XXXXXXX,16,GotoIf($["${ACCOUNTCODE}" = "CS"]?canadian_cs)
exten => _782XXXXXXX,16,GotoIf($["${ACCOUNTCODE}" = "CS"]?canadian_cs)
;...will add Quebec and Saskatchewan below

; Set Canadian CID for each team
exten => s,n(canadian_recruiting),Set(CALLERID(number)=${RecruitingCA})
exten => s,n(canadian_support),Set(CALLERID(number)=${SupportCA})
exten => s,n(canadian_cs),Set(CALLERID(number)=${CSCA})
exten => s,n,Goto(default_cid)


; Default to US CID if not a Canadian call
exten => s,n(default_cid),GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?us_recruiting) 
exten => s,n,GotoIf($["${ACCOUNTCODE}" = "Support"]?us_support)         
exten => s,n,GotoIf($["${ACCOUNTCODE}" = "CS"]?us_cs)       


; Set US CID for each team
exten => s,n(us_recruiting),Set(CALLERID(number)=${RecruitingUS})
exten => s,n(us_support),Set(CALLERID(number)=${SupportUS})
exten => s,n(us_cs),Set(CALLERID(number)=${CSUS})

Perhaps this is shorter with a canadian number flag:

[custom-variables]
exten => s,1,Set(RecruitingCA=11234567891)   ; Canadian CID for Recruiting
exten => s,n,Set(RecruitingUS=19876543211)   ; US CID for Recruiting
exten => s,n,Set(SupportCA=19999999999)      ; Canadian CID for Support
exten => s,n,Set(SupportUS=18888888888)      ; US CID for Support
exten => s,n,Set(CSCA=17777777777)          ; Canadian CID for CS
exten => s,n,Set(CSUS=16666666666)          ; US CID for CS

[macro-dialout-trunk-predial-hook]
exten => s,1,NoOp(Entering user defined outbound macro)
exten => s,n,GotoIf($["${OUTBOUND_GROUP_ID}" != ""]?dialout-group-${OUTBOUND_GROUP_ID}-predial-hook,s,1)

; Function to Check if a Number is Canadian
exten => s,n,Set(IsCanadian=0) ; Default to not Canadian
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "204"]?set_canadian:check_next_area_code) ; Manitoba
exten => s,n(check_next_area_code),GotoIf($["${OUTNUM:0:3}" = "226"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "236"]?set_canadian:check_next_area_code) ; British Columbia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "249"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "250"]?set_canadian:check_next_area_code) ; British Columbia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "289"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "306"]?set_canadian:check_next_area_code) ; Saskatchewan
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "343"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "365"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "367"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "387"]?set_canadian:check_next_area_code) ; Alberta
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "403"]?set_canadian:check_next_area_code) ; Alberta
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "416"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "418"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "431"]?set_canadian:check_next_area_code) ; Manitoba
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "437"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "438"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "450"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "506"]?set_canadian:check_next_area_code) ; New Brunswick
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "514"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "519"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "548"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "579"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "581"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "587"]?set_canadian:check_next_area_code) ; Alberta
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "604"]?set_canadian:check_next_area_code) ; British Columbia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "613"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "639"]?set_canadian:check_next_area_code) ; Saskatchewan
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "647"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "705"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "709"]?set_canadian:check_next_area_code) ; Newfoundland and Labrador
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "778"]?set_canadian:check_next_area_code) ; British Columbia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "780"]?set_canadian:check_next_area_code) ; Alberta
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "782"]?set_canadian:check_next_area_code) ; Prince Edward Island
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "807"]?set_canadian:check_next_area_code) ; Ontario
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "819"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "825"]?set_canadian:check_next_area_code) ; British Columbia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "867"]?set_canadian:check_next_area_code) ; Yukon, Northwest Territories, Nunavut
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "873"]?set_canadian:check_next_area_code) ; Quebec
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "902"]?set_canadian:check_next_area_code) ; Nova Scotia
exten => s,n,GotoIf($["${OUTNUM:0:3}" = "905"]?set_canadian:default_cid) ; Ontario 

exten => s,n(set_canadian),Set(IsCanadian=1)
exten => s,n,Goto(default_cid)

; Default CID logic based on team and IsCanadian flag
exten => s,n(default_cid),GotoIf($["${ACCOUNTCODE}" = "Recruiting" & ${IsCanadian} = 1]?canadian_recruiting)
exten => s,n,GotoIf($["${ACCOUNTCODE}" = "Recruiting"]?us_recruiting) 

exten => s,n,GotoIf($["${ACCOUNTCODE}" = "Support" & ${IsCanadian} = 1]?canadian_support)
exten => s,n,GotoIf($["${ACCOUNTCODE}" = "Support"]?us_support) 

exten => s,n,GotoIf($["${ACCOUNTCODE}" = "CS" & ${IsCanadian} = 1]?canadian_cs)
exten => s,n,GotoIf($["${ACCOUNTCODE}" = "CS"]?us_cs) 

; Set the final CallerID based on team and country
exten => s,n(canadian_recruiting),Set(CALLERID(number)=${RecruitingCA})
exten => s,n(us_recruiting),Set(CALLERID(number)=${RecruitingUS})
exten => s,n(canadian_support),Set(CALLERID(number)=${SupportCA})
exten => s,n(us_support),Set(CALLERID(number)=${SupportUS})
exten => s,n(canadian_cs),Set(CALLERID(number)=${CSCA})
exten => s,n(us_cs),Set(CALLERID(number)=${CSUS})

edit: fixed few lines of code

@lgaetz can I ask why in your example configuration you use a logic like:

exten => s,n,ExecIf($[${LEN(${OUTNUM})}=10 || ${LEN(${OUTNUM})}=11]?set(area_code=${OUTNUM:-10:3}))
exten => s,n,noop(Area Code: ${area_code})

and then:

[outbound-el-cid]
exten => 902,1,Set(CALLERID(number)=9025554444)

instead of something like this:

exten => _416XXXXXXXX,n,Set(LOCATION_COUNTRY=ON-ca) ; Ontario

I’m attempting the latter but it does not seem to work. Assuming your does ( jk :slightly_smiling_face:) I would like to learn what’s wrong with my approach. I.e. if it is just matching or in general it will never work this way.

Your system will work too, but you would need to have separate dial patterns for both 10 and 1+10 digit strings. On my system I have a mix of providers, one of which requires e164, which would would add a third dial pattern. It’s easier to isolate the NANP area code which will always be three digits and apply the rule based on that. Post your full dial plan if you want help debugging, I can’t tell much from a single line.

Thanks so much. I think I got it. As you hinted, i may have 416, 1416 and +416 being $OUTNUM so it’s not working.

Will adopt same logic and post if I need more help.

1 Like

So, this my current version. I tested it as much as I could and so far I have not broke it. Thanks for all your help @lgaetz

Any feedback to improve it, especially performance-wise, would be much appreciated. My specific deployment will only have 15-20 users with likely only 2-4 calls at the same time. I am sure there are a lot of stuff that could be done better, this is my first time working with a PBX.

edit: in anonymizing my script from my working config i also left out important configuration lines. Should be complete now.

; FreePBX Custom Dialplan Logic for Advanced Outbound CID Management
; 
; Purpose:
;   This configuration dynamically sets outbound Caller IDs (CIDs) in a FreePBX system.
;   It's designed for organizations with multiple teams, each needing different CIDs
;   based on the dialed number's location (e.g., state, province) and country.
;
; Key Features:
;   1. Team-Based CIDs: Each team (like "SALES") has its own set of CIDs.
;   2. Location-Aware: Uses area codes to set region-specific CIDs.
;   3. Country-Specific: Differentiates between US and Canadian numbers.
;   4. Alternative CIDs: Use *31 prefix for "personal" or alternative CIDs.
;   5. Fallback System: Multiple fallback levels if a specific CID isn't found.
;
; How to Use:
;   1. Set each extension's ACCOUNTCODE to its team name (e.g., "SALES").
;   2. Configure team-specific CIDs in their sections (e.g., [SALES]).
;   3. Set outbound routes in FreePBX GUI to use this system.
;   4. If you intend to use *31 logic, ensure that for each dial pattern in your outbound route you have a *31 equivalent (*31 prefix AND append)
;
; Thanks to lgaetz for the support on this thread https://community.freepbx.org/t/multiple-teams-outbound-cid-by-country-area-code/96553/4
; 
; Initial logic and area code stripping taken from lgaetz https://gist.github.com/lgaetz/2b3d67a30f86a827121b004ec2f3024a
;
; Usage: Content below goes in /etc/asterisk/extensions_custom.conf
;
; License:  GNU GPL3+

[macro-dialout-trunk-predial-hook]
exten => s,1,NoOp(Entering Outbound CID Logic)
; Safety Checks: Skip this logic for emergency or inbound calls
exten => s,n,ExecIf($["${EMERGENCYROUTE}"="YES"]?MacroExit) ; Don't mess with emergency calls
exten => s,n,ExecIf($["${DIRECTION}"="INBOUND"]?MacroExit)   ; Only handle outbound calls

; Set a global fallback CID in case all other methods fail
; This ensures we always have a valid CID to show
exten => s,n,Set(catchall_fallback_CID=<YOUR CATCHALL CID HERE>)

; Check if the caller's extension has an ACCOUNTCODE
; If it does, try to find a matching configuration section
; If not, or if no matching section exists, use the fallback CID
exten => s,n,GosubIf($[${EXISTS(${CHANNEL(accountcode)})}]?check_accountcode_exists:catchall_fallback_CID,s,1(${OUTNUM}))
exten => s,n(check_accountcode_exists),GosubIf($[${EXISTS(${CHANNEL(accountcode)},s,1)}]?${CHANNEL(accountcode)},s,1:catchall_fallback_CID,s,1(${OUTNUM}))

[<TEAM ACCOUNTCODE HERE>] ; Team 1 Configuration (e.g., [SALES])
; This section defines all the CIDs for a specific team.
; The name of this section must match an extension's ACCOUNTCODE.
exten => s,1,Set(Team_name=<TEAM ACCOUNTCODE HERE>) ; e.g., SALES
exten => s,n,NoOp(Entering Custom ${Team_name} Dialplan Logic)
; Default CIDs for this team
exten => s,n,Set(default_cid=<YOUR DEFAULT US CID HERE>)   ; Used for US calls if no better match
exten => s,n,Set(default_cid_ca=<YOUR DEFAULT CA CID HERE>) ; Used for Canadian calls if no better match

; Alternative CIDs used when *31 is dialed
exten => s,n,Set(alternative_default_cid_us=<YOUR ALTERNATIVE US CID HERE>)
exten => s,n,Set(alternative_default_cid_ca=<YOUR ALTERNATIVE CA CID HERE>)

; Location-specific CIDs, e.g., for Ontario
exten => s,n,Set(outbound_cid_ON=<YOUR ONTARIO CID HERE>)

; You can add more location-specific CIDs here:
; exten => s,n,Set(outbound_cid_BC=<YOUR BRITISH COLUMBIA CID HERE>)
; exten => s,n,Set(outbound_cid_IL=<YOUR ILLINOIS CID HERE>)

; After setting all CIDs, go to the Dialing Engine
exten => s,n,Goto(Dialing_Engine,s,1)

; End of this team configuration. Repeat for each team.

[Dialing_Engine]
; This is the core logic that determines which CID to use.
; It's separate from team configurations to avoid code duplication.
exten => s,1,NoOp(Dialing Engine begins)

; Area Code Analysis
; Stripping Area Code from $OUTNUM and setting $AREA_MATCH to contain a 3 digit area code
; IMPORTANT: this is tested for US/CAN with international dial code "1". This may not behave as intended for countries with 2 digits country code. Adapt as needed.
exten => s,n,NoOp(Entering LOCATION_COUNTRY matching logic)
; exten => s,n,NoOp($OUTNUM is ${OUTNUM}) ; uncomment for testing
exten => s,n,ExecIf($["${OUTNUM:0:1}" = "+"]?Set(AREA_MATCH=${OUTNUM:1}):Set(AREA_MATCH=${OUTNUM})) ; Check if $OUTNUM contains a +. If it does strip it while setting AREA_MATCH
exten => s,n,ExecIf($[${LEN(${AREA_MATCH})}=10 || ${LEN(${AREA_MATCH})}=11]?set(AREA_MATCH=${AREA_MATCH:-10:3})) ; Handle 1. and . case for US/CAN by setting AREA_MATCH to the area code only

; Add below all desired LOCATION-COUNTRY combinations
; Canadian Area Codes
exten => s,n,ExecIf($["${AREA_MATCH}" = "204"]?Set(LOCATION_COUNTRY=MB-ca)) ; Manitoba
exten => s,n,ExecIf($["${AREA_MATCH}" = "226"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "236"]?Set(LOCATION_COUNTRY=BC-ca)) ; British Columbia
exten => s,n,ExecIf($["${AREA_MATCH}" = "249"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "250"]?Set(LOCATION_COUNTRY=BC-ca)) ; British Columbia
exten => s,n,ExecIf($["${AREA_MATCH}" = "289"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "306"]?Set(LOCATION_COUNTRY=SK-ca)) ; Saskatchewan
exten => s,n,ExecIf($["${AREA_MATCH}" = "343"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "365"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "367"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "387"]?Set(LOCATION_COUNTRY=AB-ca)) ; Alberta
exten => s,n,ExecIf($["${AREA_MATCH}" = "403"]?Set(LOCATION_COUNTRY=AB-ca)) ; Alberta
exten => s,n,ExecIf($["${AREA_MATCH}" = "416"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "418"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "431"]?Set(LOCATION_COUNTRY=MB-ca)) ; Manitoba
exten => s,n,ExecIf($["${AREA_MATCH}" = "437"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "438"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "450"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "506"]?Set(LOCATION_COUNTRY=NB-ca)) ; New Brunswick
exten => s,n,ExecIf($["${AREA_MATCH}" = "514"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "519"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "548"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "579"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "581"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "587"]?Set(LOCATION_COUNTRY=AB-ca)) ; Alberta
exten => s,n,ExecIf($["${AREA_MATCH}" = "604"]?Set(LOCATION_COUNTRY=BC-ca)) ; British Columbia
exten => s,n,ExecIf($["${AREA_MATCH}" = "613"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "639"]?Set(LOCATION_COUNTRY=SK-ca)) ; Saskatchewan
exten => s,n,ExecIf($["${AREA_MATCH}" = "647"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario ; commented for testing
exten => s,n,ExecIf($["${AREA_MATCH}" = "705"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "709"]?Set(LOCATION_COUNTRY=NL-ca)) ; Newfoundland and Labrador
exten => s,n,ExecIf($["${AREA_MATCH}" = "778"]?Set(LOCATION_COUNTRY=BC-ca)) ; British Columbia
exten => s,n,ExecIf($["${AREA_MATCH}" = "780"]?Set(LOCATION_COUNTRY=AB-ca)) ; Alberta
exten => s,n,ExecIf($["${AREA_MATCH}" = "782"]?Set(LOCATION_COUNTRY=PE-ca)) ; Prince Edward Island
exten => s,n,ExecIf($["${AREA_MATCH}" = "807"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario
exten => s,n,ExecIf($["${AREA_MATCH}" = "819"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "825"]?Set(LOCATION_COUNTRY=BC-ca)) ; British Columbia
exten => s,n,ExecIf($["${AREA_MATCH}" = "867"]?Set(LOCATION_COUNTRY=YT-ca)) ; Yukon, Northwest Territories, Nunavut
exten => s,n,ExecIf($["${AREA_MATCH}" = "873"]?Set(LOCATION_COUNTRY=QC-ca)) ; Quebec
exten => s,n,ExecIf($["${AREA_MATCH}" = "902"]?Set(LOCATION_COUNTRY=NS-ca)) ; Nova Scotia
exten => s,n,ExecIf($["${AREA_MATCH}" = "905"]?Set(LOCATION_COUNTRY=ON-ca)) ; Ontario

; US Illinois example configuration
; exten => s,n,ExecIf($["${AREA_MATCH}" = "217"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "224"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "309"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "312"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "331"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "618"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "630"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "708"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "773"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "779"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "815"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "847"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois
; exten => s,n,ExecIf($["${AREA_MATCH}" = "872"]?Set(LOCATION_COUNTRY=IL-us)) ; Illinois

; Split LOCATION_COUNTRY into LOCATION and COUNTRY_CODE
; This makes it easier to work with in later logic
exten => s,n,Set(LOCATION=${CUT(LOCATION_COUNTRY,-,1)})   ; e.g., "ON" from "ON-ca"
exten => s,n,Set(COUNTRY_CODE=${CUT(LOCATION_COUNTRY,-,2)}) ; e.g., "ca" from "ON-ca"

; Check if *31 is pre-dialed
; This is a special code that triggers the use of alternative CIDs
exten => s,n,GotoIf($["${PREDIAL}" = "*31"]?star_31:location_match)

; *31 logic: Use alternative CIDs
exten => s,n(star_31),NoOp(*31 logic triggered)
exten => s,n,GotoIf($[${REGEX("^[0-9]+$" ${alternative_default_cid_${COUNTRY_CODE}})} = 1]?set_alternative_country_cid:check_us_cid) ; checks if an alternative_cid_<MATCHING COUNTRY> exists. If it does, call it. If it doesn't go to check_us_cid
exten => s,n(set_alternative_country_cid),Set(cid_to_use=${alternative_default_cid_${COUNTRY_CODE}})
exten => s,n,Goto(call_fun)
exten => s,n(check_us_cid),GotoIf($[${REGEX("^[0-9]+$" ${alternative_default_cid_us})} = 1]?set_alternative_us_cid:catchall_fallback_CID) ; checks if an alternative_cid_us exists. If it does, call it. If it doesn't go to [catchall_fallback_CID]
exten => s,n(set_alternative_us_cid),Set(cid_to_use=${alternative_default_cid_us})
exten => s,n,Goto(call_fun)

; Determine CID to use based on LOCATION and COUNTRY_CODE
; if a matching, valid LOCATION number exist, call it. If not go to country_code_logic
exten => s,n(location_match),NoOp(Entering LOCATION MATCHING)
exten => s,n,GotoIf($[${REGEX("^[0-9]+$" ${outbound_cid_${LOCATION}})} = 1]?location_logic:country_code_logic)
exten => s,n(location_logic),Set(cid_to_use=${outbound_cid_${LOCATION}})
exten => s,n,NoOp(LOCATION ${LOCATION} matches specific outbound_cid_${LOCATION})
exten => s,n,Goto(call_fun)

; if a matching, valid COUNTRY_CODE number exist, call it. If not go to check_default_cid
exten => s,n(country_code_logic),GotoIf($[${REGEX("^[0-9]+$" ${default_cid_${COUNTRY_CODE}})} = 1]?set_country_cid:check_default_cid)
exten => s,n(set_country_cid),Set(cid_to_use=${default_cid_${COUNTRY_CODE}})
exten => s,n,NoOp(COUNTRY_CODE ${COUNTRY_CODE} matches specific default_cid_${COUNTRY_CODE})
exten => s,n,Goto(call_fun)

; if default_cid is a valid number, call it. If not jump to [catchall_fallback_CID]
exten => s,n(check_default_cid),GotoIf($[${REGEX("^[0-9]+$" ${default_cid})} = 1]?set_default_cid:catchall_fallback_CID)
exten => s,n(set_default_cid),Set(cid_to_use=${default_cid})
exten => s,n,NoOp(default_cid ${default_cid} is a valid number. cid_to_use set accordingly)
exten => s,n,Goto(call_fun)

; Call Function
; This is where we actually set the CID and initiate the call
exten => s,n(call_fun),Set(CALLERID(num)=${cid_to_use})
exten => s,n,NoOp(Number to call is ${OUTNUM})
exten => s,n,NoOp(Outbound CID to use is ${cid_to_use})
exten => s,n,Return  ; Go back to FreePBX's main dialplan

[catchall_fallback_CID]
; This is our last resort. If all other CID selections fail,
; we use a globally defined fallback CID.
exten => s,1,noop(Entering fallback configuration)
exten => s,n,NoOp(catchall_fallback_CID is ${catchall_fallback_CID})

; Check if we have a fallback CID. If not, apologize and hang up.
; This prevents showing a wrong or invalid CID.
exten => s,n,GotoIf($["${catchall_fallback_CID}" != ""]?set_caller_id)
exten => s,n,Playback(sorry-we-cannot-complete-your-call)
exten => s,n,Hangup()

; Set the fallback CID and return to the main dialplan
exten => s,n(set_caller_id),Set(CALLERID(num)=${catchall_fallback_CID})
exten => s,n,Return


This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.