FreePBX Reject calls without actually answering the trunk

I’m looking for a way for FreePBX to reject calls or play busy/congestion tones on my incoming Twilio trunk without actually answering the call. This is because Twilio starts charging for calls as soon as it’s answered, but if the call is never connected it doesn’t charge me. Is there a way to have FreePBX send a response code back to the trunk to the calling party’s carrier to play their own disconnect message or busy tone without connecting the call and having my PBX play the message?

This is pretty much how usage rating works. Answered calls get charged. Calls only get answered in certain scenarios on the PBX. So you need to be more specific.

One of the core FreePBX destinations is Terminate Call, which has several options, many of which don’t answer the channel such as “Hangup”

The problem with terminating the call without answering is that some providers will resend the invite, so you might get unexpected behavior. You can experiment with other options such as congestion or busy which shouldn’t answer the channel either.

1 Like

I tried setting destination as Terminate Call, even with Hangup selected the call still shows completed on my Twilio logs but only for 1 second so they are still marking that call as answered. Only way I can describe what I’m thinking of is from my cell phone if I dial a landline number from the local telephone company and that line is busy my phone just says “Calling…” while it plays the busy tones but the time counter never starts counting up. With my PBX number when I call that, as soon as the busy tones start playing the call counter starts counting up indicating my PBX actually answered the call and is playing the tones itself. I’m basically just looking for a way for the PBX to send some sort of reject response to tell the callers carrier, say Verizon in this case, hey this number is not reachable now play your busy signal or number disconnected message. Maybe this isn’t possible with my Twilio trunk but figured I’d check.

Where are you setting this destination? In the Inbound Route or another place? By default Asterisk does not answer channels unless told/needs to. Understanding at what point you are trying to terminate the call will help us understand if there is something in the path that is answering the call.

For these examples I just blacklisted a number I was testing from and then the Blacklist destination is where I chose the terminate call options.

Please confirm that your Inbound Route does not have any options that require answering the call, such as Detect Faxes, Privacy Manager or Force Answer.

If that’s not your issue, enable pjsip logger, call in from your blacklisted test number, paste the Asterisk log for the call at pastebin.com and post the link here.

I think I solved that problem. Instead of using the blacklist I just set the Inbound Route itself to a Terminate Call destination using Congestion, Busy, and Hangup. Now calls from my cell phone don’t complete and I get a recording from the Verizon lady saying my call couldn’t be completed as dialed. But another possible issue, the messages don’t seem to line up with the Termination I selected. Selecting Terminate Call, Congestion I get the call cannot be completed as dialed message but really I should be hearing a fast busy tone. Selecting Terminate Call, Busy I just get “Call Failed” on my screen when I should hear a slow busy tone. I’m assuming these are possibly due with Asterisk’s hangup cause codes being different the SIP Response Codes so it’s not sending the correct response code to the trunk?

The blacklist module answers calls when a CallerID matches in the blacklist check. That would explain why you were getting charged for answered calls.

I have been experimenting with a custom SIP Response context I found from another forum, creating a custom destination that points to this context below.
[send-sip-response-code]
exten => 410,1,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 410,n,NoOp(Sending SIP 410 Number Changed)
exten => 410,n,hangup(22)

It works perfect and the message I hear on the calling line changes depending which responses I choose as expected. However, I want to try to use early media, if that’s the correct term for this. To play my own custom “number disconnected” message like others are able to do. I tried doing something like this (below) but the PBX answers the call to play that recording and then hangs up.
[send-sip-response-code]
exten => 410,1,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 410,n,NoOp(Sending SIP 410 Number Changed)
exten => 410,n,Playback(number-not-in-service)
exten => 410,n,hangup(22)

I use Twilio as my trunk provider if that helps, I believe they should support early media because when I change my country signaling indication tones it appears the PBX is playing the ringing tones to the caller as early media without answering the call as I am able to change the country tones and it changes for the caller. Unless I’m getting that confused with something else. A number you can call to listen to an example of what I’m trying to do is 701-791-8160, you initially hear a voice readback the number and say it’s not in service, then right after that message I hear the standard Verizon error message (or whatever carrier you use to call). To me the first message is from the remote switch and then the second message is your normal carrier 404 error.

The Playback dialplan application[1] will automatically answer the channel. If you don’t want to do that, you have to explicitly pass the “noanswer” option:

exten => 410,n,Playback(number-not-in-service,noanswer)

[1] Playback - Asterisk Documentation

1 Like

Awesome, thanks for that! It’s hard to find good documentation on this stuff but that link is very helpful. After adding that ,noanswer I just heard ringback for the duration of the recording and then my carriers disconnected message. I found I had to add the Progress() at the beginning to signal SIP 183 Session Progress to be able to play early media.
Everything now works exactly as I want it to. Caller will hear my own custom disconnected message as early media followed by whatever error message their carrier plays, all without the call actually connecting and charging for the call.

Here is the full steps for my own future reference and anyone trying to do the same thing, specifically with Twilio as your trunk provider.

  1. Go to Admin > Config Edit > extensions_custom.conf and paste the following
[send-sip-response-code]
; Context to send a specific SIP response code and terminate the channel
; Create Custom Destination in FreePBX with a dial string of the format
;          send-sip-response-code,404,1
; substitute the appropriate response code in place of the 404
; Reference: https://wiki.asterisk.org/wiki/display/AST/Hangup+Cause+Mappings
;
; latest version: https://gist.github.com/lgaetz/480582a1827cc98db1ee539c249f074b
;
; License GPL/2
;
; History    2019-10-08   First attempt supports 404, 480 and 486
;            2023-04-14   added 501
;            2024-08-07   added Progress() to send SIP Response 183
;            2024-08-07   added Playback() to play custom audio file

exten => 410,1,Progress()
exten => 410,n,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 410,n,NoOp(Sending SIP 410 Number Changed)
exten => 410,n,Playback(custom/number-cannot-be-completed-as-dialed,noanswer)
exten => 410,n,hangup(22)

exten => 480,1,Progress()
exten => 480,n,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 480,n,NoOp(Sending SIP 480 Temporarily Unavailable)
exten => 480,n,Playback(custom/number-cannot-be-completed-as-dialed,noanswer)
exten => 480,n,hangup(19)

exten => 404,1,Progress()
exten => 404,1,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 404,n,NoOp(Sending SIP 404 Not Found)
exten => 404,n,Playback(custom/number-cannot-be-completed-as-dialed,noanswer)
exten => 404,n,hangup(1)

exten => 503,1,Progress()
exten => 503,1,NoOp(Entering user defined context send-sip-response-code in extensions_custom.conf)
exten => 503,n,NoOp(Sending SIP 503 Circuit Congestion)
exten => 503,n,Playback(custom/number-cannot-be-completed-as-dialed,noanswer)
exten => 503,n,hangup(34)

;   end context send-sip-response-code

This will give you 4 different variations of responses you can choose to send, you could add more if you want. Also if you upload your own custom recording, replace what I have after playback with the name of your file. I followed these tables to know what hangup code to use for what response I wanted, just change the number in the parenthesis behind “hangup” to whatever cause code you want.

  1. Create a custom destination with target as send-sip-response-code,410,1 or send-sip-response-code,404,1 or whatever response you want to use and a meaningful to you description.
  2. Then set an inbound route for the DID you want with destination as that custom destination

Sources:
SIP First Responder · GitHub
Hangup Cause Mappings - Asterisk Documentation
Hangup Cause Code Table | FreeSWITCH Documentation (signalwire.com)
Early Media and the Progress Application - Asterisk Documentation
Playback - Asterisk Documentation

1 Like

Hi @isaacgross1

I’m the author of the GitHub dialplan you used as the basis for this solution. Happy to see that it was useful for you and the changes you’ve made. One very minor little nitpick, you’ll note in the original source that the license line shows GPL/2. You’re free to do whatever you want with this code, but once you share it here (or anywhere), you’ve now published it, and the onus is on you to publish in a manor consistent with the GPL requirements. It’s obvious that I was very casual about licensing this tiny dialplan bite, but it is still appropriate to maintain the original header lines attributing the original copyright and to add an additional line(s) to the history indicating your changes.

Other than that, good job. Consider sharing the lines with the original by adding a comment with the lines indicating you agree to have it added.

@lgaetz
I have edited my above response, did I do that correctly?

I definitely want to give credit where credit is due, back to you. I’m just not familiar with GPL requirements so if you can let me know if I did that correctly or if there is more I need to do.

1 Like

Yeah, you’re good. In case the tone wasn’t clear in my post above, this is not a big deal to me. I don’t care much about ensuring to whom the credit goes. But importantly, now with the licensing details intact, anyone who wants to continue to modify your version in the future, now knows under what conditions they can do so.

1 Like

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