Start with the request and the header owner
Record the exact provider or peer requirement, SIP method, direction, header name, value syntax, destination and whether the value is constant or call-specific. Preserve a sanitized working request when one exists. A field required on an outbound INVITE is not automatically required on REGISTER, OPTIONS, an in-dialog request or the response to an inbound call.
Map the caller channel, every Local-channel pair and the final outbound PJSIP channel. A dialplan variable visible on the caller is not proof that a SIP header exists on the callee, and a header read from the inbound request is not automatically copied to a new outbound request.
Use PJSIP_HEADER on the outbound channel
Asterisk documents PJSIP_HEADER() as reading headers from an inbound PJSIP channel and adding, updating or removing headers on an outbound PJSIP channel. In an ordinary dialplan context it operates on the caller, which may be the wrong channel. The documented pattern uses the lowercase b pre-dial option so the function runs on each newly created callee before the request is sent.
The lowercase b handler is different from uppercase B: B runs on the caller before callee channels are created, while b runs on each callee after creation and before dialing. Every pre-dial handler is a Gosub and must end with Return(). Keep it short and do not move the channel into another application from the handler.
[outbound-header]
exten => add,1,Set(PJSIP_HEADER(add,X-Example-Route)=${ARG1})
same => n,Return()
[from-example]
exten => _X.,1,Dial(PJSIP/${EXTEN}@example-trunk,30,b(outbound-header^add^1(route-a)))Choose inheritance only on supported versions
PJSIP_INHERITABLE_HEADER() is a newer path for writing a header on a PJSIP or non-PJSIP calling channel so an outbound PJSIP child inherits it. The current documentation lists it from Asterisk 20.19.0, 22.9.0 and 23.3.0. Confirm the installed branch and maintenance release before using it; its presence in current documentation does not add it to older 18, 20 or 22 builds.
Inherited headers and headers added with PJSIP_HEADER() are separate stores. A header added by one function can only be updated or removed by that same function. Inherited headers can be adjusted in a callee pre-dial handler, but mixing both paths for the same name can create duplicates and confusing rollback.
Keep request, parameter and response APIs distinct
PJSIP_HEADERS() enumerates header names from an inbound INVITE; it does not list the future outbound request or return values. PJSIP_HEADER_PARAM() gets or sets URI or header parameters and currently supports only the From header. A custom header value and a From URI parameter are therefore different edits.
PJSIP_RESPONSE_HEADER() reads headers from the 200 response on an outbound PJSIP channel and belongs in the documented pre-connect path. It is evidence about a response, not a way to add the request header. Preserve method, direction, channel and message phase whenever recording a function result.
Treat forwarding as policy, not blind copying
Do not copy every X- header or a trusted-network identity header from an inbound request to an outbound trunk. Define an allow-list, validate the source and value, remove hop-specific or customer-private data, and generate authoritative identity only at the component that owns it. Header names can be repeated, and add always creates another instance; use update only when the named instance already exists.
Caller identity, privacy, emergency location, STIR/SHAKEN, diversion and billing headers can have legal, trust and routing consequences. Follow the provider's current specification and local policy. A syntactically emitted header does not prove the peer accepted or acted on it.
Verify the wire request and rollback
Use a short authorized protocol capture and correlate the outbound request by timestamp, Call-ID, destination and dialplan branch. Confirm the exact header count, spelling, value, channel and method, then preserve the peer's response and the call outcome separately. Test a path that should carry the header and an unrelated path that should not.
On FreePBX-managed systems, use supported custom dialplan or module extension points and retain Apply Config ownership; do not edit generated dialplan. Remove temporary logging, protect captured identities and tokens, and restore the previous dialplan if another route gains, loses or duplicates a header. No PBX, channel, header, peer or call was tested for this guide.
Do not replace party-state controls with static headers
Connected-line and redirecting information can change at answer, forwarding or transfer. The party-identity guide maps those updates to PJSIP trust, send, Diversion and History-Info controls rather than treating a pre-Dial custom header as the whole identity model.
Select the Gosub handler that owns the outbound channel
Use the Gosub channel-ownership guide before setting a PJSIP header. B() on the caller, b() on each callee and U() after answer run at different lifecycle points and do not prove the same channel owns the header.
Parse header URI data on the channel that owns it
The PJSIP_PARSE_URI workflow keeps the complete header, its channel and message direction beside every extracted user, host or parameter. Parsing does not prove outbound-header placement or peer acceptance.
Read response headers after answer, not in the request handler
The dedicated PJSIP response-header guide distinguishes uppercase U() after answer from lowercase b() before dialing and preserves the selected response field instance.
Use the parameter function when the From container is already correct
The PJSIP_HEADER_PARAM guide distinguishes a narrow From URI or header parameter from adding or replacing an entire outbound header.
Keep inheritable headers separate from ordinary outbound headers
The inheritable-header guide explains why values added by PJSIP_INHERITABLE_HEADER() and PJSIP_HEADER() require their matching update and remove functions.
Do not use an inbound inventory as outbound proof
PJSIP_HEADERS() lists names from the INVITE available to the current inbound channel. The dedicated inventory guide keeps that evidence separate from callee pre-dial writes and wire verification.
Sources & applicability
Primary references for the technical details above. Operational examples and planning checklists are VoIP.info editorial guidance.
- Asterisk PJSIP_HEADER dialplan function ↗
- Asterisk PJSIP_INHERITABLE_HEADER dialplan function ↗
- Asterisk PJSIP_HEADERS dialplan function ↗
- Asterisk PJSIP_HEADER_PARAM dialplan function ↗
- Asterisk PJSIP_RESPONSE_HEADER dialplan function ↗
- Asterisk pre-dial handlers ↗
- Asterisk party-ID manipulation ↗
- Asterisk party-ID interception routines ↗
- Asterisk 22 CONNECTEDLINE function ↗
- Asterisk 22 REDIRECTING function ↗
- Asterisk 22 res_pjsip endpoint options ↗
- Asterisk PJSIP_ENDPOINT function ↗
- Asterisk Dial application ↗
- Asterisk Queue application ↗
- Asterisk NewConnectedLine AMI event ↗
- Asterisk dialplan subroutines overview ↗
- Asterisk Gosub guide ↗
- Asterisk 22 Gosub application reference ↗
- Asterisk 22 Return application reference ↗
- Asterisk STACK_PEEK function reference ↗
- Asterisk pre-bridge handlers ↗
- Asterisk 22 Dial application reference ↗
- Asterisk PJSIP_PARSE_URI function ↗
- Asterisk PJSIP_PARSE_URI_FROM function ↗
- Asterisk FILTER function ↗
- Asterisk important security considerations ↗
- RFC 3261: SIP Session Initiation Protocol ↗
- Asterisk PJSIP_RESPONSE_HEADERS dialplan function ↗
- Asterisk res_pjsip header-function source ↗
- Asterisk CHANNEL dialplan function ↗
- RFC 3261: SIP ↗
- Asterisk 20 PJSIP_INHERITABLE_HEADER reference ↗
- Asterisk 22 PJSIP_INHERITABLE_HEADER reference ↗
- Asterisk 23 PJSIP_INHERITABLE_HEADER reference ↗
Examples require adaptation to your topology. No live PBX or hardware testing is claimed.