SIP Headers
SIP (Session Initiation Protocol) headers contain metadata about VoIP calls. Manipulating these headers enables advanced features like custom caller ID assertion, call tracking, provider compatibility, and integration with external systems. Control which headers your PBX sends to providers and how it processes headers from providers.
Understanding SIP Headers
What are SIP Headers?
SIP headers are key-value pairs in SIP messages that carry information about calls:
Basic SIP Message Structure:
INVITE sip:[email protected] SIP/2.0
Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK-1234
From: "John Smith" <sip:[email protected]>;tag=abc123
To: <sip:[email protected]>
Call-ID: [email protected]
CSeq: 1 INVITE
Contact: <sip:[email protected]:5060>
Max-Forwards: 70
User-Agent: TheVoĉo-PBX/1.0
Allow: INVITE, ACK, CANCEL, BYE, OPTIONS, REFER
Content-Type: application/sdp
Content-Length: 300
[SDP Body]
Key Header Types:
Required Headers (RFC 3261):
- From: Originator of request
- To: Intended recipient
- Call-ID: Unique call identifier
- CSeq: Command sequence number
- Via: Path taken by request
- Contact: Direct URI for reaching sender
Caller ID Headers:
- P-Asserted-Identity: Trusted caller ID from provider
- Remote-Party-ID: Legacy caller ID header
- Privacy: Privacy preferences (hide caller ID)
Custom Headers:
- X-Custom-*: User-defined headers for tracking, routing, etc.
Inbound Header Processing
P-Asserted-Identity (PAI) Header
What is PAI? RFC 3325 header for trusted caller ID. Providers use PAI to assert the authenticated identity of the caller.
Format:
P-Asserted-Identity: "John Smith" <sip:[email protected]>
P-Asserted-Identity: <tel:+15551234567>Why Use PAI?
- Trusted: Only network operators can set (not end users)
- Accurate: Contains verified caller ID
- Privacy: Separate from From header (can show different to callee)
- STIR/SHAKEN: Used for call authentication
Configuration:
Read PAI for Caller ID:
Setting: Use P-Asserted-Identity for inbound caller ID
Priority: PAI > Remote-Party-ID > From header
Behavior: If PAI present, use it; else fallback to other headersExtract Number from PAI:
PAI Header: "John Smith" <sip:[email protected]>
Extract: +15551234567
Use As: Inbound caller ID
Display: "John Smith" +15551234567Multiple PAI Headers:
SIP allows multiple PAI headers (rare):
P-Asserted-Identity: <tel:+15551234567>
P-Asserted-Identity: <sip:[email protected]>
Configuration: Use first PAI (phone number)
Or: Parse both, prefer tel: URIPAI with Privacy:
Headers:
P-Asserted-Identity: <tel:+15551234567>
Privacy: id
Behavior:
Provider: Asserts real caller ID in PAI
Privacy: User requested caller ID hidden
Result: PBX sees real ID, displays "Anonymous" to users (if honoring privacy)Troubleshooting PAI:
- Enable SIP debug, capture actual PAI value
- Check if provider sends PAI (not all do)
- Verify PAI parsing extracts number correctly
- Test with various PAI formats
Modern Standard
P-Asserted-Identity is the modern standard for carrier-provided caller ID. Always prefer PAI over Remote-Party-ID when available.
Remote-Party-ID (RPID) Header
What is RPID? Legacy header for caller ID information. Pre-dates PAI but still widely used.
Format:
Remote-Party-ID: "John Smith" <sip:[email protected]>;party=calling;privacy=off;screen=yesParameters:
- party: calling (caller), called (callee)
- privacy: off, full, name, uri
- screen: yes (screened), no (unscreened)
Configuration:
Read RPID for Caller ID:
Setting: Use Remote-Party-ID for caller ID
Priority: PAI > RPID > From
Behavior: Check PAI first, then RPID, then FromParse RPID Parameters:
Header: Remote-Party-ID: "John" <tel:+15551234567>;party=calling;privacy=off
Extract:
Name: John
Number: +15551234567
Privacy: Off (show caller ID)
Result: Display both name and numberPrivacy Handling:
privacy=off: Show caller ID
privacy=full: Hide name and number
privacy=name: Show number, hide name
privacy=uri: Show name, hide numberScreen Parameter:
screen=yes: Provider verified caller ID (trusted)
screen=no: User-provided, not verified (less trusted)
Configuration:
Trust screen=yes more than screen=no
May apply different routing rules based on screeningRPID vs PAI:
Prefer PAI:
- Modern standard
- Better defined
- STIR/SHAKEN compatible
Use RPID:
- Legacy providers
- Backward compatibility
- When PAI not availableLegacy Support
Remote-Party-ID is legacy but still common. Support both PAI and RPID for maximum provider compatibility.
Diversion Header
What is Diversion? RFC 5806 header indicating call has been diverted/forwarded. Shows original destination before forwarding.
Format:
Diversion: <sip:[email protected]>;reason=unconditional;counter=1;privacy=offUse Cases:
Call Forwarding:
Original destination: +15551111111
Forwarded to: +15552222222
Headers:
To: +15552222222
Diversion: <tel:+15551111111>;reason=unconditional
PBX knows: Call was originally for +15551111111Voicemail Redirect:
Reason values:
- unconditional: Always forward
- user-busy: Forwarded because busy
- no-answer: Forwarded because no answer
- unavailable: Forwarded because unavailable
- deflection: Call deflected by user
Use: Display appropriate message to callerMultiple Diversions:
Call forwarding chain:
A → B → C
Diversion headers (multiple):
Diversion: <tel:+1555-A>;reason=unconditional;counter=1
Diversion: <tel:+1555-B>;reason=user-busy;counter=2
Shows full forwarding pathConfiguration:
Read Diversion Information:
Setting: Parse Diversion header
Extract: Original destination, reason, counter
Use: Display to user, routing decisions, loggingExample Display:
Diversion detected:
Original number: +15551111111
Reason: No answer
Current destination: You
Display: "Call forwarded from +15551111111 (no answer)"Routing Based on Diversion:
If Diversion present and reason=no-answer:
Route to: Urgent queue (may be time-sensitive)
Else:
Route to: Standard queueCounter Parameter:
counter=1: First forward
counter=2: Second forward
counter=5: Fifth forward
Use: Prevent forward loops
Configuration: Max counter=3 (reject if >3)Call History
Diversion headers provide valuable call history. Use for better routing decisions and improved caller experience.
Reading Custom Headers from Provider
What are Custom Headers? Provider-specific headers (usually X-*) with custom information for tracking, routing, or integration.
Common Custom Headers:
X-Account-Code:
Header: X-Account-Code: CUSTOMER-12345
Use: Billing, cost allocation
Processing: Extract account code, associate with callX-Original-Called-Number:
Header: X-Original-Called-Number: +15551111111
Use: Alternative to Diversion header
Processing: Extract original destinationX-Carrier-Info:
Header: X-Carrier-Info: carrier=Verizon;quality=HD
Use: Carrier identification, call quality info
Processing: Parse for reportingX-Call-Type:
Header: X-Call-Type: emergency
Use: Special call handling
Processing: Route to priority queueX-Customer-ID:
Header: X-Customer-ID: 67890
Use: Link call to customer account
Processing: CRM lookup, screen popConfiguration:
Read Specific Custom Header:
Setting: Custom Header Processing
Header Name: X-Account-Code
Action: Extract value
Store In: Call variable ${XHEADER_ACCOUNT}
Use In: Routing, CDR, reportingMultiple Custom Headers:
Read Headers:
X-Account-Code → ${ACCOUNT_CODE}
X-Customer-ID → ${CUSTOMER_ID}
X-Call-Priority → ${PRIORITY}
Use in routing:
If ${PRIORITY} = "high":
Route to: VIP queue
Else:
Route to: Standard queueParse Complex Header Values:
Header: X-Metadata: account=12345;region=west;type=sales
Parse:
account: 12345
region: west
type: sales
Use: Multiple routing criteria from single headerHeader-Based Authentication:
Header: X-API-Key: abc123xyz789
Validation: Check against known API keys
If valid: Allow call
If invalid: Reject call
Use: Secure API-initiated callsIntegration Example (CRM):
Inbound call with:
X-Customer-ID: 67890
Processing:
1. Extract customer ID
2. Webhook to CRM: GET /customer/67890
3. CRM returns: Name, account status, history
4. Display to agent
5. Route based on customer tier
Result: Context-aware call handlingSecurity
Don't trust custom headers from untrusted sources. Validate and sanitize all header values before use.
Outbound Header Manipulation
Set P-Asserted-Identity on Outbound
Why Set PAI?
- Assert authenticated caller ID to provider
- Enable STIR/SHAKEN call authentication
- Trusted caller ID display
- Provider-required header
Configuration:
Basic PAI Setup:
Setting: Send P-Asserted-Identity
Format: <tel:${CALLERID(num)}>
Result: P-Asserted-Identity: <tel:+15551234567>PAI with Display Name:
Setting: Include display name
Format: "${CALLERID(name)}" <tel:${CALLERID(num)}>
Result: P-Asserted-Identity: "John Smith" <tel:+15551234567>Multiple PAI Headers:
Some providers accept multiple PAI:
PAI 1: <tel:+15551234567> (phone number)
PAI 2: <sip:[email protected]> (SIP URI)
Configuration: Send both if provider supportsExtension-Specific PAI:
Extension 2001: PAI = +15551111111
Extension 2002: PAI = +15552222222
Extension 2003: PAI = +15553333333
Configuration: Map extension to specific DID for PAITrunk-Specific PAI:
Trunk A: PAI = +15551234567 (main number)
Trunk B: PAI = +15559876543 (secondary number)
Configuration: Different PAI per trunkConditional PAI:
If destination is US:
PAI: +15551234567
If destination is international:
PAI: +15559999999 (international callback number)STIR/SHAKEN Attestation:
PAI with attestation level:
P-Asserted-Identity: <tel:+15551234567>;stir-attest=A
Levels:
A: Full attestation (best)
B: Partial attestation
C: Gateway attestation (minimal)
Configuration: Work with provider for attestationPrivacy with PAI:
Send both:
P-Asserted-Identity: <tel:+15551234567>
Privacy: id
Result:
- Provider knows real caller ID (PAI)
- End user sees "Anonymous" or "Private"
- Use: Executive calls, sensitive situationsProvider Requirements
Check if your provider requires PAI, accepts it, or ignores it. Some providers mandate PAI for caller ID authentication.
Set Remote-Party-ID on Outbound
Why Set RPID?
- Legacy provider compatibility
- Backup if PAI not supported
- Additional privacy controls
Configuration:
Basic RPID Setup:
Setting: Send Remote-Party-ID
Format: <tel:${CALLERID(num)}>;party=calling;privacy=off;screen=yes
Result: Remote-Party-ID: <tel:+15551234567>;party=calling;privacy=off;screen=yesRPID with Display Name:
Format: "${CALLERID(name)}" <tel:${CALLERID(num)}>;party=calling;privacy=off;screen=yes
Result: Remote-Party-ID: "John Smith" <tel:+15551234567>;party=calling;privacy=off;screen=yesPrivacy Parameter:
privacy=off: Show full caller ID
privacy=full: Hide all (anonymous)
privacy=name: Hide name, show number
privacy=uri: Hide number, show name
Configuration: Based on user preference or call typeScreen Parameter:
screen=yes: PBX verified caller ID (assert trust)
screen=no: User-provided (lower trust)
Configuration: Set screen=yes for authenticated usersParty Parameter:
party=calling: This is the caller
party=called: This is the called party (rare in RPID)
Always use: party=calling for outboundConditional RPID Privacy:
Normal calls:
privacy=off (show caller ID)
Executive extensions:
privacy=full (anonymous)
After-hours:
privacy=name (show number only, hide name)RPID for Call Forwarding:
Call forwarded from +15551111111 to +15552222222:
RPID in forwarded call:
Remote-Party-ID: <tel:+15551111111>;party=calling;privacy=off
Shows: Original caller, not forwarderCompatibility Mode:
Send both PAI and RPID:
P-Asserted-Identity: <tel:+15551234567>
Remote-Party-ID: <tel:+15551234567>;party=calling;privacy=off;screen=yes
Provider uses: Whichever it supports
Result: Maximum compatibilityDual Headers
Sending both PAI and RPID ensures compatibility with all providers. Modern providers use PAI, legacy use RPID.
Add Custom Headers to Outbound Calls
Use Cases:
Call Tracking:
Header: X-Campaign-ID: GOOGLE-SUMMER-2024
Use: Track calls by marketing campaign
Report: ROI per campaignAccount Code:
Header: X-Account-Code: DEPT-SALES
Use: Cost allocation per department
Billing: Charge back to departmentCall Priority:
Header: X-Priority: high
Use: Signal priority to provider
Provider: May route to better trunksCustomer Reference:
Header: X-Customer-ID: ${CUSTO MER_ID}
Use: Link call to CRM record
Integration: Provider sends to your webhook with customer contextAPI Key Authentication:
Header: X-API-Key: ${API_SECRET}
Use: Authenticate API-initiated calls
Provider: Validates key before connectingConfiguration:
Static Custom Header:
Setting: Add Custom Header
Name: X-Account-Code
Value: MAIN-OFFICE
Apply: All outbound callsDynamic Custom Header:
Setting: Add Custom Header
Name: X-Extension-Number
Value: ${CALLERID(num)}
Apply: All outbound calls
Result: X-Extension-Number: 2001Conditional Custom Header:
If destination matches +1555*:
Add header: X-Call-Type: local
Else:
Add header: X-Call-Type: long-distanceMultiple Custom Headers:
Header 1: X-Account-Code: SALES
Header 2: X-Department: Sales
Header 3: X-Extension: 2001
Header 4: X-Campaign: SUMMER2024
All added to outbound INVITEPer-Extension Custom Headers:
Extension 2001:
X-Department: Sales
X-Manager: John Smith
Extension 3001:
X-Department: Support
X-Manager: Jane Doe
Result: Context per extensionIntegration Example:
Outbound call with:
X-Customer-ID: 67890
X-Campaign-ID: GOOGLE-ADS-123
X-Account-Code: SALES
Provider forwards to your webhook:
POST /call-initiated
Headers:
X-Customer-ID: 67890
X-Campaign-ID: GOOGLE-ADS-123
X-Account-Code: SALES
Your system:
- Logs call against campaign
- Associates with customer
- Updates CRMHeader Limitations:
- Max header length: 256 characters (typical)
- Max custom headers: 10-20 (provider-dependent)
- Character restrictions: ASCII only, no special chars
- Some providers strip X-* headers
Provider Support
Not all providers support or forward custom headers. Test with your specific provider before relying on custom headers.
Remove/Sanitize Outbound Headers
Why Remove Headers?
Privacy:
Remove: User-Agent (reveals PBX software/version)
Remove: Server (reveals system info)
Remove: Organization (company internal info)
Reason: Don't leak system detailsSecurity:
Remove: X-Internal-Extension (exposes internal numbering)
Remove: X-Private-* (internal custom headers)
Reason: Prevent information disclosureCompatibility:
Remove: Proprietary headers provider doesn't support
Remove: Headers that confuse provider
Reason: Some providers reject calls with unknown headersConfiguration:
Remove Specific Headers:
Remove: User-Agent
Remove: Server
Remove: Organization
Remove: X-Internal-*
Result: Cleaner, more private SIP messagesWhitelist Approach:
Keep only:
- Required headers (From, To, Via, etc.)
- P-Asserted-Identity
- Remote-Party-ID
- Custom headers you intentionally add
Remove: Everything else
Result: Minimal, secure header setRemove By Pattern:
Remove: X-Internal-*
Matches: X-Internal-Extension, X-Internal-ID, X-Internal-Anything
Result: All internal headers strippedConditional Removal:
If destination is external (not internal PBX):
Remove: X-Internal-*
Remove: Organization
If destination is internal:
Keep all headers (useful for internal routing)Sanitize Header Values:
User-Agent: TheVoĉo-PBX/1.0 (internal-hostname.local)
Sanitized: TheVoĉo-PBX/1.0
Action: Remove internal hostname
Reason: Don't expose internal namesRemove Anonymous Headers:
If caller wants to be anonymous:
Remove: P-Asserted-Identity
Add: Privacy: id
From header: "Anonymous" <sip:[email protected]>
Result: Full caller ID hidingHeader Rewriting:
Original: Contact: <sip:[email protected]:5060>
Rewritten: Contact: <sip:[email protected]:5060>
Reason: Replace internal IP with public IP (NAT)Audit Outbound Headers:
Before removal:
- Capture sample outbound INVITE
- Review all headers
- Identify unnecessary/sensitive headers
- Configure removal
After removal:
- Capture again
- Verify only intended headers presentBest Practices:
- Remove User-Agent and Server (info leakage)
- Remove internal-only custom headers
- Sanitize Contact and Via (replace internal IPs)
- Keep required headers (RFC 3261)
- Keep provider-required headers (PAI, RPID)
Don't Over-Remove
Removing required headers breaks calls. Test thoroughly. Only remove genuinely unnecessary or sensitive headers.
Advanced Use Cases
Using Headers for Call Tracking
Scenario: Marketing Attribution
Setup:
Website shows dynamic numbers based on visitor source:
Google Ads: +1-555-001-GOOGLE
Facebook: +1-555-002-FACEBOOK
Organic: +1-555-003-ORGANIC
Direct: +1-555-004-DIRECTInbound Header Processing:
DID +1-555-001-GOOGLE receives call:
Add header: X-Campaign-Source: Google-Ads
Add header: X-Campaign-ID: GOOGLE-SUMMER-2024
Add header: X-Medium: PPC
DID +1-555-002-FACEBOOK receives call:
Add header: X-Campaign-Source: Facebook-Ads
Add header: X-Campaign-ID: FACEBOOK-Q4-2024
Add header: X-Medium: Social
Route all to: Sales queueCall Answered:
Agent answers, PBX sends webhook:
POST /call-answered
Headers:
X-Campaign-Source: Google-Ads
X-Campaign-ID: GOOGLE-SUMMER-2024
X-Caller-Number: +15559876543
X-Extension: 2001CRM Integration:
CRM receives webhook:
- Looks up customer by phone
- Associates call with campaign
- Updates campaign metrics
- Creates opportunity
- Screen pops agent with full contextCallback with Context:
Agent calls customer back:
Outbound call includes:
X-Campaign-ID: GOOGLE-SUMMER-2024
X-Customer-ID: 67890
X-Callback: true
Provider forwards to your analytics:
- Log callback against original campaign
- Track time-to-callback
- Measure callback effectivenessReporting:
Campaign Performance:
Google Ads (X-Campaign-Source: Google-Ads):
- Inbound Calls: 500
- Answered: 450
- Callbacks: 200
- Conversions: 75
- Revenue: $375,000
- ROI: 450%
Facebook Ads (X-Campaign-Source: Facebook-Ads):
- Inbound Calls: 300
- Answered: 270
- Callbacks: 120
- Conversions: 40
- Revenue: $200,000
- ROI: 280%
Result: Data-driven marketing decisionsFull Attribution
SIP headers enable full call attribution from initial ad click through call, callback, and conversion. Closes the marketing loop.
Automatic CRM Screen Pop
Scenario: Sales Team with CRM
Inbound Call Processing:
1. Call arrives from +15559876543
2. PBX extracts caller ID
3. PBX sends webhook to CRM:
POST /screen-pop
Headers:
X-Caller-Number: +15559876543
X-Called-Number: +15551234567
X-Extension: 2001 (if already routed)
X-Trunk-ID: Main-TrunkCRM Lookup:
CRM searches for +15559876543:
Found: John Smith
- Company: Acme Corp
- Title: VP of Sales
- Last Contact: 2024-11-15
- Opportunity: $50,000 pending
- Status: Hot lead
- Notes: Requested demo, follow up on pricingAgent Display:
Screen pop before answer:
╔════════════════════════════════╗
║ Incoming Call ║
║ ║
║ John Smith ║
║ VP of Sales, Acme Corp ║
║ ║
║ Last Contact: Nov 15 ║
║ Opportunity: $50,000 ║
║ Status: Hot Lead ║
║ ║
║ Notes: ║
║ Requested demo, follow up ║
║ on pricing ║
║ ║
║ [Answer] [Send to Voicemail] ║
╚════════════════════════════════╝Enhanced with Custom Headers:
If provider sends custom headers:
X-Customer-ID: 67890
X-Account-Tier: Premium
X-Last-Order-Date: 2024-11-01
Screen pop includes:
- Customer since: 2020
- Lifetime value: $500,000
- Last order: Nov 1, 2024 ($10,000)
- Tier: Premium (VIP treatment)Call Recording Note:
When call answered:
PBX sends:
X-CRM-Record-ID: 67890
X-Call-Purpose: Sales-Follow-Up
When call ends:
PBX sends webhook with recording URL:
POST /call-ended
Headers:
X-CRM-Record-ID: 67890
X-Recording-URL: https://pbx/recordings/12345.mp3
X-Duration: 480 (8 minutes)
CRM:
- Attaches recording to customer record
- Logs call duration
- Updates last contact dateOutbound Screen Pop:
Agent clicks "Call" in CRM:
CRM initiates call via API:
POST /click-to-call
Headers:
X-Customer-ID: 67890
X-Extension: 2001
X-Destination: +15559876543
PBX:
- Calls extension 2001
- When answered, calls customer
- Includes X-Customer-ID in outbound headers
- Enables tracking in all systemsContext is King
Screen pop with full customer context dramatically improves agent performance. Faster, more personalized service.
Multi-Tenant PBX Header Management
Scenario: Multi-Company Hosted PBX
Tenant Identification:
Company A: Calls on DIDs +1-555-1000 to +1-555-1099
Company B: Calls on DIDs +1-555-2000 to +1-555-2099
Company C: Calls on DIDs +1-555-3000 to +1-555-3099Inbound Header Processing:
Call arrives on +1-555-1050:
Add header: X-Tenant-ID: COMPANY-A
Add header: X-Tenant-Name: Acme Corp
Route to: Company A's extensions
Call arrives on +1-555-2050:
Add header: X-Tenant-ID: COMPANY-B
Add header: X-Tenant-Name: Best Co
Route to: Company B's extensionsTenant-Specific Outbound:
Company A outbound:
P-Asserted-Identity: <tel:+15551000> (Company A main number)
User-Agent: TheVoĉo-PBX/1.0 (AcmeCorp)
X-Tenant-ID: COMPANY-A
Company B outbound:
P-Asserted-Identity: <tel:+15552000> (Company B main number)
User-Agent: TheVoĉo-PBX/1.0 (BestCo)
X-Tenant-ID: COMPANY-B
Result: Proper caller ID per tenantTenant Isolation:
Security headers:
X-Tenant-ID: COMPANY-A
X-Allowed-Destinations: internal,local,mobile
X-Block-International: true
Routing logic:
If X-Tenant-ID = COMPANY-A:
If destination is international:
Block call (per tenant config)
Else:
Allow call
Result: Per-tenant call restrictionsCDR with Tenant Info:
Call Detail Record:
Caller: +15559876543
Called: +15551234567
Tenant: COMPANY-A (from X-Tenant-ID header)
Duration: 480 seconds
Cost: $0.50
Billing:
Company A charged: $0.50 for this call
Company B: Separate billingTenant-Specific Features:
Company A config:
Call Recording: All calls
Hold Music: Company A custom music
Voicemail: Company A voicemail system
Headers: X-Tenant-ID, X-Account-Code
Company B config:
Call Recording: Compliance calls only
Hold Music: Default music
Voicemail: Company B voicemail system
Headers: X-Tenant-ID, X-Department
Enforced via: Header-based routing rulesInter-Tenant Calls (if allowed):
Company A extension calls Company B extension:
Headers:
X-Source-Tenant: COMPANY-A
X-Dest-Tenant: COMPANY-B
X-Inter-Tenant-Call: true
Billing:
Both tenants charged (or free internal)
Logs:
Tracked separately for auditTenant Security
Use headers to enforce tenant isolation. Critical for multi-tenant security - prevent cross-tenant access.
Advanced Routing Based on Headers
Scenario 1: Priority Routing
Configuration:
If X-Priority header present:
X-Priority: high → VIP queue (immediate)
X-Priority: medium → Standard queue
X-Priority: low → Bulk queue (longer wait)
If no X-Priority:
Default → Standard queueApplication:
VIP customer calls:
Provider sends: X-Priority: high
Routing: Immediately to VIP queue
Wait time: < 30 seconds
Agent: Senior agent
Regular customer calls:
No X-Priority header
Routing: Standard queue
Wait time: ~2 minutes
Agent: Any available agentScenario 2: Time-Zone Routing
Headers:
Provider sends: X-Caller-Timezone: America/Los_Angeles
Or: X-Caller-Region: PSTRouting Logic:
If X-Caller-Timezone = America/Los_Angeles (PST):
Route to: West Coast office
If unavailable: East Coast office (different hours)
If X-Caller-Timezone = America/New_York (EST):
Route to: East Coast office
If unavailable: West Coast office
Result: Local office answers, or appropriate backupScenario 3: Language Routing
Headers:
X-Preferred-Language: es (Spanish)
X-Preferred-Language: en (English)
X-Preferred-Language: fr (French)Routing:
If X-Preferred-Language = es:
Route to: Spanish-speaking queue
IVR: Spanish prompts
Agents: Bilingual or Spanish-only
If X-Preferred-Language = fr:
Route to: French-speaking queue
IVR: French prompts
Default (no header or en):
Route to: English queueScenario 4: Account Type Routing
Headers:
X-Account-Type: enterprise
X-Account-Type: small-business
X-Account-Type: trialRouting:
If X-Account-Type = enterprise:
Route to: Enterprise support (24/7)
SLA: < 1 minute response
Agent: Senior certified
If X-Account-Type = small-business:
Route to: Business support (business hours)
SLA: < 5 minutes
Agent: Standard support
If X-Account-Type = trial:
Route to: Sales (convert to paid)
IVR: Upsell messaging
Agent: Sales repScenario 5: Product-Specific Routing
Headers:
X-Product-ID: PRODUCT-A
X-Product-ID: PRODUCT-B
X-Product-ID: PRODUCT-CRouting:
If X-Product-ID = PRODUCT-A:
Route to: Product A specialists
Queue: Product A support
If X-Product-ID = PRODUCT-B:
Route to: Product B specialists
Queue: Product B support
Multiple products:
X-Product-ID: PRODUCT-A,PRODUCT-B
Route to: General support (knows both)Scenario 6: Call Back Queue
Headers:
X-Call-Type: callback
X-Original-Call-Time: 2024-11-21T14:30:00Z
X-Callback-Reason: no-answerRouting:
If X-Call-Type = callback:
Priority: High (customer waiting for callback)
Route to: Agent who missed original call (if available)
Or: Next available agent with context
Display: "CALLBACK: Promised at 2:30pm"Intelligent Routing
SIP headers enable sophisticated routing logic beyond simple number-based routing. Improves customer experience and operational efficiency.
Best Practices
SIP Header Recommendations
Follow best practices for secure, compatible, and effective header manipulation.
Security
- Validate Input: Never trust inbound headers without validation
- Sanitize Values: Strip malicious content (SQL injection, XSS attempts)
- Remove Sensitive: Remove internal headers before sending to provider
- Limit Exposure: Don't send User-Agent, Server, Organization
- Whitelist Approach: Only send explicitly configured headers
Compatibility
- Test with Provider: Always test with your specific provider first
- RFC Compliance: Follow RFC 3261 for required headers
- PAI + RPID: Send both for maximum compatibility
- Fallback Headers: Have backup headers if primary rejected
- SIP Debug: Capture actual messages to verify headers sent/received
Performance
- Minimize Headers: Only add necessary custom headers
- Short Values: Keep header values concise (< 100 chars)
- Limit Quantity: < 10 custom headers per call
- Cache Lookups: Cache CRM/DB lookups referenced in headers
- Async Processing: Don't block call setup for header processing
Documentation
- Document Headers: Maintain list of all custom headers and purposes
- Integration Guide: Document header requirements for integrations
- Change Log: Track when headers added/removed/changed
- Examples: Provide sample SIP messages with headers
- Troubleshooting: Document common header-related issues
Testing
- SIP Capture: Capture actual SIP messages with sngrep/Wireshark
- Test Suite: Test calls with various header combinations
- Edge Cases: Test missing headers, malformed values, very long values
- Provider Testing: Test with provider before production
- Regression Testing: Re-test after PBX upgrades
Troubleshooting
Custom Headers Not Appearing in Outbound
Problem: Configured custom headers not in outbound INVITE
Diagnostics:
1. Verify Configuration:
Check: Settings > Trunks > [Trunk] > SIP Headers > Outbound
Expected: X-Custom-Header: value
Status: Enabled2. Capture SIP Messages:
Tool: sngrep or Wireshark
Filter: Outbound INVITE to provider
Search: X-Custom-Header
Result: Present or missing?3. Check Direction:
Configuration: Direction = Outbound
Test: Making outbound call (not inbound)
Verify: Testing correct direction4. Check Trunk:
Configuration: Applied to Trunk A
Test: Call goes through Trunk B
Problem: Headers on wrong trunk
Fix: Apply to correct trunk or all trunks5. Header Name Validation:
Valid: X-Custom-Header
Invalid: Custom Header (space not allowed)
Invalid: Custom@Header (@ not allowed)
Valid characters: Letters, numbers, hyphen6. SIP Proxy Stripping:
Your PBX: Sends X-Custom-Header
SIP Proxy: Strips unknown headers
Provider: Never receives it
Test: Capture at multiple points
Fix: Configure proxy to allow headers7. Provider Restrictions:
Some providers: Block X-* headers
Reason: Security policy
Check: Provider documentation
Alternative: Use allowed headers (Contact, From)Debug Mode
Enable SIP debug logging. Shows exactly which headers are sent in INVITE. Essential for troubleshooting.
Expected Inbound Headers Missing
Problem: Provider should send headers, but PBX doesn't see them
Diagnostics:
1. Capture Inbound INVITE:
Tool: sngrep, Wireshark, or PBX SIP debug
Capture: Inbound INVITE from provider
Search: Expected header (e.g., P-Asserted-Identity)
Result: Is it in the message?2. Provider Actually Sends It?:
Question: Does provider actually send this header?
Check: Provider documentation
Ask: Provider support
Test: Make call, capture traffic3. SIP Proxy Stripping:
Provider: Sends header
SIP Proxy: Strips it (firewall, SBC, etc.)
Your PBX: Never receives it
Test: Capture before and after proxy
Fix: Configure proxy to forward headers4. Header Parsing:
Header present: P-Asserted-Identity: <tel:+15551234567>
PBX config: Looking for "P-Asserted-Id" (typo)
Result: Not recognized
Fix: Correct header name in config5. Case Sensitivity:
Provider sends: X-Custom-Header
PBX looks for: x-custom-header
System: Case-sensitive header matching
Result: Not found
Fix: Match exact case, or make case-insensitive6. Multi-Line Headers:
Header spanning multiple lines:
X-Custom-Header: value1;
param1=foo;param2=bar
Parsing: May not handle multi-line correctly
Result: Value truncated or parsing fails
Fix: Robust parsing logic7. Encrypted SIP:
Using TLS: Can't capture with Wireshark
Need: PBX-level SIP debug logging
Enable: SIP debug in PBX
Review: Logs show decrypted headersProvider Coordination
If expecting specific headers from provider, coordinate with their support to verify they're actually sending them.
Headers Present but Values Incorrect
Problem: Headers exist but contain unexpected values
Scenario 1: Variable Not Expanded:
Configuration: X-Extension: ${EXTEN}
Expected: X-Extension: 2001
Actual: X-Extension: ${EXTEN}
Problem: Variable not expanded (literal string)
Fix: Verify variable expansion syntax for your PBXScenario 2: Empty Value:
Configuration: X-Account-Code: ${ACCOUNT_CODE}
Expected: X-Account-Code: SALES-001
Actual: X-Account-Code:
Problem: Variable ${ACCOUNT_CODE} not set
Fix: Set variable before adding headerScenario 3: Truncated Value:
Configuration: X-Long-Value: [300 character string]
Expected: Full 300 characters
Actual: First 128 characters only
Problem: Header value length limit
Fix: Shorten value or split into multiple headersScenario 4: Special Characters Escaped:
Configuration: X-Name: John's Phone
Expected: X-Name: John's Phone
Actual: X-Name: John%27s Phone
Problem: Special characters URL-encoded
Fix: Decode on receiving end, or avoid special charsScenario 5: Wrong Variable:
Configuration: X-Caller: ${CALLERID(num)}
Expected: X-Caller: +15551234567
Actual: X-Caller: 2001
Problem: Using wrong variable (extension instead of caller ID)
Fix: Use correct variable for outbound caller IDScenario 6: Dynamic Value Not Updated:
Call 1: X-Customer-ID: 67890 (correct)
Call 2: X-Customer-ID: 67890 (should be different customer)
Problem: Variable not updated between calls
Fix: Ensure variable cleared/updated per callTesting Variable Expansion:
Test call with known values:
Extension: 2001
Caller ID: +15551234567
Account Code: SALES
Configure headers:
X-Extension: ${EXTEN}
X-Caller-ID: ${CALLERID(num)}
X-Account: ${ACCOUNT_CODE}
Verify in capture:
X-Extension: 2001 ✓
X-Caller-ID: +15551234567 ✓
X-Account: SALES ✓Test Thoroughly
Always test variable expansion with actual calls. Different PBX systems use different syntax for variables.
Provider Rejects Calls Due to Headers
Problem: Calls fail when custom headers added
Scenario 1: Unknown Header Rejection:
Your PBX: Sends X-Custom-Header: value
Provider: "481 Call/Transaction Does Not Exist"
Reason: Provider strict SIP validation, rejects unknown headers
Fix: Remove custom header, or contact provider to whitelistScenario 2: Malformed Header:
Header: X-Custom: value with spaces
Provider: "400 Bad Request - Malformed Header"
Reason: Header value has syntax error
Fix: Encode spaces (%20) or removeScenario 3: Header Length Limit:
Header: X-Long-Value: [500 character value]
Provider: "513 Message Too Large"
Reason: Total message size exceeds provider limit
Fix: Shorten header values, remove unnecessary headersScenario 4: Duplicate Headers:
Your PBX:
P-Asserted-Identity: <tel:+15551234567>
P-Asserted-Identity: <sip:[email protected]>
Provider: "400 Bad Request - Duplicate PAI"
Reason: Provider doesn't accept multiple PAI headers
Fix: Send only one PAI headerScenario 5: Conflicting Headers:
Headers:
From: <sip:[email protected]>
P-Asserted-Identity: <tel:+15559999999>
Provider: "403 Forbidden - PAI Mismatch"
Reason: PAI doesn't match From, provider suspicious
Fix: Ensure PAI matches From or allowed caller IDScenario 6: Security Check Failure:
Header: X-API-Key: wrong_key
Provider: "401 Unauthorized"
Reason: Provider validates X-API-Key header
Fix: Use correct API key from providerDebugging Steps:
- Capture SIP 4xx/5xx response from provider
- Note error code and reason phrase
- Identify which header causing issue
- Test without that header
- Contact provider if unclear
Provider Whitelist Approach:
Contact provider:
- Request whitelist of allowed custom headers
- Only send headers on whitelist
- Avoid rejection
Result: Provider accepts callsProvider Coordination
Always coordinate custom headers with provider. Many providers have strict SIP validation that rejects unknown or malformed headers.