Preamble

If you have a requirement to add a Stir/Shaken attestation to calls leaving a FreePBX/PBXact system and you have an account with Tiltx, you can use their callshaper API to add an S/S identity header to outbound calls. There are a few third party services that will assist with generating S/S attestations for SIP calls, the Tiltx callshaper API does not rely on an SBC or proxy between the PBX and the provider.

This information is current as of 2022-05-20. Subsequent developments in FreePBX, Asterisk or in Tiltx APIs may render it obsolete. Consult reference for additional info https://help.smartcarrier.io/portal/en/kb/articles/tiltx-call-shaper-module

Prerequisites

PBX Config

Add the following code to the PBX in the file /etc/asterisk/extensions_custom.conf

[macro-dialout-trunk-predial-hook]
; history   2022-05-20 first commit
;           2022-07-08 Update for forwarded calls and check for valid API return value
exten => s,1,Noop(Entering user defined context macro-dialout-trunk-predial-hook in extensions_custom.conf)
 
; setup curlopts and curl API call with call params
exten => s,n,Set(CURLOPT(httptimeout)=10)    ; give up if we don't get a reply from API in 10s
exten => s,n,Set(CURLOPT(httpheader)=x-api-key: Tiltx-API-KEY)         ; substitute your actual Tiltx API key
exten => s,n,Set(CURLOPT(httpheader)=Content-Type: application/json)
exten => s,n,Set(response=${CURL(https://api.shaper.tiltx.com/Calls/shaper?ToNumber=${OUTNUM}&FromNumber=${CALLERID(num)})})
 
; check if we get a header back from Tiltx and skip to end if not
exten => s,n,GotoIf($["${JSON_DECODE(response,Identity)}"=""]?finish)
exten => s,n,Set(IdentityHeader=${JSON_DECODE(response,Identity)})            ; JSON_DECODE requires recent Asterisk versions 16/18/19
exten => s,n,GoSub(func-set-sipheader,s,1(Identity,${IdentityHeader}))
 
exten => s,n(finish),MacroExit()
 
; end macro-dialout-trunk-predial-hook

Substitute your Actual API key where indicated, save the file, and reload the dialplan.

Example outbound call

From the Asterisk Console, make an outbound call. If the dialplan is working correctly, there will be a section that looks like this:

-- Executing [s@macro-dialout-trunk-predial-hook:1] NoOp("PJSIP/7002-00000032", "Entering user defined context macro-dialout-trunk-predial-hook in extensions_custom.conf") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:2] Set("PJSIP/7002-00000032", "CURLOPT(httptimeout)=10") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:3] Set("PJSIP/7002-00000032", "CURLOPT(httpheader)=x-api-key: QQaUA3bSXa1ncE8vol0sV6bHIyEXSdfb2staWyQp") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:4] Set("PJSIP/7002-00000032", "CURLOPT(httpheader)=Content-Type: application/json") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:5] Set("PJSIP/7002-00000032", "response={"RequestGuid":"c19636d3-1c96-4efe-8112-45b8737fbeda","AccountGuid":"c88a4b90-86c5-4c35-8f53-7c860d3866bb","ToNumber":"<<redacted>>","FromNumber":"<<redacted>>","IPAddress":"<<redacted>>","Country":"US","IsDNC":"false","IsDIDManager":"false","IsDisconnected":"false","CallerIDToUse":null,"Identity":"long_Identity_string_shortened_for_clarity","CAID":"c19636d3-1c96-4efe-8112-45b8737fbeda","AttestationLevel":"B","SHAKEN_Server":"ca.shaken.tiltx.internal","IsInternallyFlagged":"false","RoutingNumber":"17159721948","LERGInformation":{"RequestGuid":"f188ce8b-a79b-4027-a16d-f3525c586767","NPANXX":1715972,"Block":"1","USState":"WI","AssignedTo":"BANDWIDTH.COM CLEC, LLC - WI","AssignedTo_OCN":"007F","Type":"CLEC","Ilec":"WISCONSIN BELL INC","Ilec_OCN":"9327","Lata":"350","RequestedAt":"2022-06-17T19:15:54.6658375Z","ElapsedMilliseconds":0},"DIDLERG":{"RequestGuid":"13ab720a-8258-440e-a3fe-cd7f8fe4b4c3","NPANXX":1920886,"Block":"8","USState":"WI","AssignedTo":"TDS METROCOM, INC. - WI","AssignedTo_OCN":"7804","Type":"CLEC","Ilec":"WISCONSIN BELL INC","Ilec_OCN":"9327","Lata":"350","RequestedAt":"2022-06-17T19:15:54.6776668Z","ElapsedMilliseconds":0},"DNCInformation":null,"TILTXID":"c19636d3-1c96-4efe-8112-45b8737fbeda","ServiceData":null,"RequestedAt":"2022-06-17T19:15:54.2442607Z","CompletedAt":"2022-06-17T19:15:54.8671704Z","ProcessingTime":"622.9097","ReturnCode":200,"ReturnStatus":"Attestation B assigned - Number not Found"}") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:6] Set("PJSIP/7002-00000032", "IdentityHeader=long_Identity_string_shortened_for_clarity") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:7] Gosub("PJSIP/7002-00000032", "func-set-sipheader,s,1(Identity,long_Identity_string_shortened_for_clarity)") in new stack
-- Executing [s@func-set-sipheader:1] NoOp("PJSIP/7002-00000032", "Sip Add Header function called. Adding Identity = long_Identity_string_shortened_for_clarity") in new stack
-- Executing [s@func-set-sipheader:2] Set("PJSIP/7002-00000032", "HASH(__SIPHEADERS,Identity)=long_Identity_string_shortened_for_clarity") in new stack
-- Executing [s@func-set-sipheader:3] Return("PJSIP/7002-00000032", "") in new stack
-- Executing [s@macro-dialout-trunk-predial-hook:8] MacroExit("PJSIP/7002-