Preserve the orginal caller ID in call forward

We want to configure FreePBX to forward inbound calls to a PSTN number via a Twilio Elastic SIP Trunk while attempting to preserve the original caller ID (CID). Currently, Twilio replaces the original caller ID with the Twilio trunk number. We need an expert VoIP engineer to implement a solution that complies with Twilio’s requirements and maximizes preservation of original caller information in SIP headers.


Current Setup

  • PBX: FreePBX 17.x

  • Trunk: Twilio Elastic SIP Trunk

    • Verified Twilio number: +1954833XXXX
  • Original caller: Arbitrary external numbers (e.g., +12342713563)

  • Destination: External PSTN number (e.g., +1954737XXXX)

  • Goal: Forward calls from FreePBX → Twilio → PSTN, showing the correct caller information.


Problem Statement

When forwarding calls through the Twilio trunk, the displayed Caller ID on the PSTN side always shows the Twilio number (+19548338586), not the original caller number (+1234271XXXX).

Twilio reply :

1. Allowed Caller ID Numbers in Termination Calls

For outbound (termination) calls through Twilio Elastic SIP Trunking:

  • The Caller ID Number (From header) must be either a Twilio phone number (DID) on your account or a number that has been verified in the Twilio Console or via the Outgoing Caller ID API.

  • If a Caller ID Number is not specified in the SIP INVITE’s From field, Twilio will use the value from the Remote-Party-ID or P-Asserted-Identity header (if present) as the Caller ID, provided it is a Twilio-verified number.

Allowed Caller ID Numbers in Termination calls

2. Supported SIP Headers for Original Caller Information

Twilio Elastic SIP Trunks support the following headers for passing caller information:

  • From: This must always be a Twilio number or a Twilio-verified number for outbound PSTN calls.

  • P-Asserted-Identity (PAI): Twilio will forward this header to downstream carriers if present.

  • Diversion: If your call is a forwarded call, you can include a Diversion header. Twilio will forward this header to the PSTN.

  • Remote-Party-ID: This header is also supported for identifying the originator of the call, but is not always honoured by all carriers.

3. Best Practices for Compliance and Header Handling

  • Set the From header to your Twilio-verified number (+1 770 637 9587) to ensure compliance and call acceptance.

  • Place the original caller’s number in the P-Asserted-Identity or Diversion header. Twilio will attempt to pass this information downstream, but the final display depends on the terminating carrier and regulatory requirements.

  • Ensure your FreePBX is configured to populate these headers correctly.

4. Forwarding Original Caller Information to PSTN

  • Twilio will forward the PAI and Diversion headers to the PSTN where possible, but the number displayed to the called party is typically the one in the From header.

  • The original caller information may still be available in call logs or for compliance purposes.

5. Caller ID Flags and Feature Deprecation

  • Previous flags that allowed more flexibility with caller ID presentation have been deprecated to align with industry regulations and prevent spoofing.

  • There is no current flag or setting to override the requirement for a Twilio-verified number in the From header for PSTN calls.

6. Twilio Trunk Configuration

  • No additional configuration or flags are available on the Twilio side to change this behaviour. The key is to ensure your PBX is sending the correct headers as described above.

Next Steps:

  • If you can provide a SIP trace of a sample call, I can review the headers to confirm everything is being sent as expected.

  • I’m happy to arrange a call with our technical team to walk through your setup and answer any further questions. Please let me know your availability or preferred times.

Thank you for your patience and for working with us on this. If you have any additional details or questions, please share them and I’ll be glad to assist further.

I tried forward using misc destenation and below is my dial plan :

exten => _00015X.,1,NoOp(Forward to Twilio with correct headers)

same => n,Set(ORIGCID=${CALLERID(num)})

; Twilio verified outbound CLI
same => n,Set(CALLERID(num)=1954833XXXX)

; IMPORTANT: inherit into outbound channel
same => n,Set(__PJSIP_HEADER(remove,P-Asserted-Identity)=)
;same => n,Set(__PJSIP_HEADER(add,P-Asserted-Identity)=sip:[email protected])

same => n,Set(__PJSIP_HEADER(remove,Remote-Party-ID)=)
;same => n,Set(__PJSIP_HEADER(add,Remote-Party-ID)=sip:+${ORIGCID}<@sip-usa-05.rinXXXXX.com>;party=calling;screen=yes;privacy=off)

same => n,Dial(PJSIP/+${EXTEN:5}@1954833XXXX-infra-test-1500)

Before we were have feature from Twilio called any caller id flag, then Twilio cancel it.

The header setting is a side effect. What is inherited is the value, which in your case is an empty string, and then only if the left hand side is a variable, not a functon. To stand any chance of working, this needs to be run on the outbound channel, after the header has been set by FreePBX.

However, I would suggest that any mechanism that results in a naive callee seeing a number that has not been proved to belong to you is likely to be considered illegal, unless you are classified as a public network operator, as the whole point of the restriction is to ensure that the people only see caller IDs that you have the authority to use. I’d suggest that any service that allows pass through of arbitrary numbers. from someone who is not a trusted network operator, is either going to have to give them an impaired attestation level, which is likely to result in the recipient learning to reject the call, or is likely to be discontinued at short notice.

Thanks david,do you have idea how we do forward the call from PBX to cell phone with preserve the orginal caller id,we are using Twilio sip trunk

You override the From header in the trunk definition.

You do this with the right caller ID handling rules and you let Asterisk handle the headers, and don’t try to explicitly set ones that Asterisk normally manages.

So the result is still likely not to be what you want.

what we shoud do from freePBX end, use misc destination and custom dial plan ?:slight_smile:

[from-internal]
exten => _00015X.,1,NoOp(Forward to Twilio with correct headers)

same => n,Set(ORIGCID=${CALLERID(num)})

; Twilio verified outbound CLI
same => n,Set(CALLERID(num)=19548338586)

; IMPORTANT: inherit into outbound channel
same => n,Set(__PJSIP_HEADER(remove,P-Asserted-Identity)=)
;same => n,Set(__PJSIP_HEADER(add,P-Asserted-Identity)=sip:[email protected])

same => n,Set(__PJSIP_HEADER(remove,Remote-Party-ID)=)
;same => n,Set(__PJSIP_HEADER(add,Remote-Party-ID)=sip:[email protected];party=calling;screen=yes;privacy=off)

same => n,Dial(PJSIP/+${EXTEN:5}@19548338586-infra-test-1500)

or use follow me ?

I ran into this same problem a couple of years ago myself, also using Twilio. I ended up just using the asterisk function “Transfer()”. So I added a custom dialplan like the following:
[external-transfer]
exten => s,1,Answer()
same => n,Wait(1)
same => n,Transfer(PJSIP/+15551231234@[sip domain].pstn.umatilla.twilio.com:5061)

You must enable the setting under the trunk settings within Twilio to allow transfers. Then I create a custom destination that points to that dial plan and point whatever I need to transfer the call. Doing this shows the callee caller ID when transferred in this way.

in this way you can still have recording file for the trasnfer call or once you transfer you will lose recording ?

Transfer() does a direct transfer/handoff from the FreePBX, so it no longer maintains call control, ergo, call recording or statistics cease once the transfer takes place.

unfortunately this is issue also cause we need to have call recording ,do you have any idea how we can achieve that ,we dont want to forward/transfer from Twilio side ,we need from PBX side .
before one year twilio was giving us any caller id flag so we can forward as well,but now they cancel it and they said to keep the orginal caller id you need to send it via PAI

They say that it probably won’t work, even then.

However, the default behaviour of Asterisk is to pass through the caller ID, and if you enable sending PAI, it will do so in PAI. FreePBX has lots of options for how caller ID is handled, but I’m sure it can be set to pass through. If you override From user, in the trunk definition, the overriding value will replace any caller ID, in just the From header.

Twilio dropped that “any caller ID” flag ages ago and left everyone scrambling for alternatives. The PAI approach is your best bet on paper, but whether the terminating carrier actually passes it through to the called party is a whole other question — most just display the From header. The Asterisk Transfer() workaround is clever, though losing call recording is a real tradeoff if you need that for compliance.