Skip to content

Custom Applications Module Documentation

15 min readUpdated: Sep 26, 2026
View as Markdown
  1. Module Overview (Technical)
  2. Module Overview (Commercial/Business)
  3. Module Overview (End User/Administrator)
  4. User Roles & Key Capabilities
  5. Configuration Fields Reference
  6. Call Flow / Logic Explanation
  7. Common Scenarios & Examples
  8. Model Context Protocol (MCP) AI Integration
  9. Limitations & Important Notes
  10. Troubleshooting Tips
  11. Glossary

Custom Applications allow administrators to create programmable call logic using Lua scripts or XML dialplan entries. When routed to via IVR menus, inbound routes, or time conditions, FreeSWITCH executes the custom code before optionally routing to a final destination.

Type Description Use Case
Lua Script Programmatic logic with Lua code Complex logic, API calls, database queries
XML Dialplan Declarative FreeSWITCH XML Simple routing, variable setting
┌─────────────────────────────────────────────────────────────────┐
│ Custom Applications Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ User dials: *99 (trigger number) │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Custom Application Lookup │ │
│ │ SELECT * FROM custom_applications │ │
│ │ WHERE trigger_number = '*99' AND enabled = true │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Execute Script │ │
│ │ │ │
│ │ LUA SCRIPT: │ │
│ │ ├─ Load script from file system │ │
│ │ ├─ Execute Lua code (session:answer(), play, etc.) │ │
│ │ └─ Access variables: caller_id, CUSTOM_APP_UUID, etc. │ │
│ │ │ │
│ │ XML DIALPLAN: │ │
│ │ ├─ Parse XML content │ │
│ │ └─ Execute as inline dialplan │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Final Destination (if not skipped) │ │
│ │ │ │
│ │ ├─ extension → Transfer to extension │ │
│ │ ├─ queue → Send to call queue │ │
│ │ ├─ ivr → Send to IVR menu │ │
│ │ ├─ conference → Join conference │ │
│ │ ├─ voicemail → Send to voicemail │ │
│ │ ├─ announcement→ Play announcement │ │
│ │ ├─ external → Dial external number │ │
│ │ └─ hangup → End call │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Scripts are stored as files in Telephony Server:

/usr/share/freeswitch/scripts/custom_apps/[uuid].lua
/usr/share/freeswitch/scripts/custom_apps/[uuid].xml
Variable Description
CUSTOM_APP_DOMAIN The domain/tenant the call is in
CUSTOM_APP_UUID Unique ID of this application
caller_id_number Caller’s phone number
caller_id_name Caller’s name
destination_number Number that was dialed

Custom Applications provide extensibility without core modifications:

Without Custom Apps With Custom Apps
Hardcoded logic only Dynamic programmable logic
Developer required Admin can create via UI
System restart needed Hot-deployed scripts
No customization Unlimited flexibility
  1. Custom IVR Logic

    • Complex branching based on caller ID
    • Database lookups for personalized greetings
    • API integrations with external systems
  2. Pre-Call Announcements

    • “This call may be recorded”
    • Holiday hour announcements
    • Emergency notifications
  3. Call Routing Logic

    • Time-based routing with custom logic
    • VIP caller detection
    • Geographic routing
  4. Integration Points

    • CRM lookups before connecting
    • Ticket creation on call start
    • SMS notifications
Feature Benefit
Lua Scripting Full programming language for complex logic
XML Dialplan Simple declarative routing
Final Destination Route anywhere after script completes
Skip Destination Script handles everything (hangup internally)
Hot Deploy Changes apply immediately

3. Module Overview (End User/Administrator)

Section titled “3. Module Overview (End User/Administrator)”
  • Create custom applications with Lua or XML code
  • Assign trigger numbers (extension or feature code)
  • Define final destinations for post-script routing
  • Enable/disable applications without deleting
  1. Navigate to PBX → Applications → Custom Applications in the main sidebar.
  2. The list view displays all configured programmable applications, including Name, Type (LUA / XML), Final Destination, Status (Enabled / Disabled), and quick actions to edit or delete.
  3. Click the + Add button in the top-right toolbar to define a new Custom Application.
  4. Click any existing application row or its edit icon to modify its script logic, destination, or settings.

Custom Applications List View

┌─────────────────────────────────────────────────────────────────┐
│ Creating a Custom Application │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: Basic Information (General Settings Tab) │
│ ├─ Name: "Recording Disclaimer" │
│ ├─ Trigger Number: *99 │
│ ├─ Description: "Plays recording notice before transfer" │
│ └─ Application Type: Lua Script │
│ │
│ Step 2: Script Content (Script Content Tab) │
│ ├─ Write Lua code: │
│ │ session:answer() │
│ │ session:sleep(500) │
│ │ session:streamFile("ivr/recording_disclaimer.wav") │
│ │ -- Final destination handles the rest │
│ │ │
│ Step 3: Final Destination │
│ ├─ Skip Final Destination: ✗ (transfer after script) │
│ ├─ Destination Type: Queue │
│ └─ Destination Value: support_queue │
│ │
│ Step 4: Enable and Save │
│ └─ Enabled: ✓ │
│ │
│ Result: Dial *99 → Plays disclaimer → Transfers to queue │
│ │
└─────────────────────────────────────────────────────────────────┘

[!TIP] Start Simple: Begin with basic Lua scripts before attempting complex integrations.

[!TIP] Test Thoroughly: Always test custom applications in a development environment first.

[!CAUTION] Syntax Errors: Invalid Lua/XML will cause the application to fail. Check Telephony Server logs for errors.


Role Key Capabilities Best Practice / Limitations
Super Admin Deploy, audit, and debug custom Lua and XML applications across tenants; inspect sandboxed execution logs and runtime safety. Verify script paths and avoid infinite loops; ensure protected pcall execution in all custom logic.
PBX Administrator Create and edit custom applications, select engine type (lua or xml), author inline code, and configure post-execution destination handoffs. Always test script syntax using Monaco editor and test with test calls before routing production traffic.
Branch Manager View existing custom applications and check execution status for branch call flows. Consult PBX Administrator or Super Admin to request custom logic or API webhook integration changes.
Agent / Extension User Interact seamlessly with interactive custom applications (e.g. automated surveys, balance checkers, recording disclaimers). Caller experience is completely transparent; execution occurs natively in FreeSWITCH.

Custom Applications Configuration Form

Field Description User-Friendly Tooltip Example Required
Name * Unique identifier for the custom application Enter application name VIP Account Balance Check Yes
Description Optional note explaining the script purpose Enter description (optional) Interactive Lua balance verification and CRM sync No
Application Type Engine type executing the code Select execution environment (Lua Script or XML Dialplan) Lua Script Yes
Enabled Master switch to activate or deactivate the application Enable or disable this application Yes / No (toggle) Yes
Field Description User-Friendly Tooltip Example Required
Skip Final Destination Whether to stop execution after the script completes If enabled, the application will not route to a final destination Yes / No (toggle) No
Final Destination Target destination module and specific value Select where to route callers after the script successfully executes Module: Extensions, Destination: 1001 Only if Skip Final Destination is No
Field Description Notes
Script Content Lua code or XML dialplan Monaco editor with syntax highlighting
Field Description Options
Skip Final Destination Don’t route after script On = script handles hangup
Destination Type Where to route Extension, Queue, IVR, Conference, Voicemail, Announcement, External, Hangup, Time Condition, Call Flow, Ring Group
Destination Value Target identifier Depends on type
Type Value Format Example
Extension Extension number 1001
Queue Queue name support_queue
Ring Group Ring group ID sales_group
IVR IVR name main_menu
Voicemail Extension number 1001
Announcement Announcement ID holiday_hours
Conference Conference extension 8000
Hangup (none) Ends call
External Phone number +15551234567
Time Condition Time condition ID business_hours
Call Flow Call flow ID main_flow

┌─────────────────────────────────────────────────────────────────┐
│ Custom Application Execution Flow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. User dials *99 (trigger number) │
│ │ │
│ ▼ │
│ 2. Dialplan matches custom_application route │
│ │ │
│ ▼ │
│ 3. Load application from database │
│ ├─ Verify enabled = true │
│ ├─ Get app_type (lua/xml) │
│ └─ Get script_filename │
│ │ │
│ ▼ │
│ 4. Set channel variables │
│ ├─ CUSTOM_APP_DOMAIN = [domain] │
│ ├─ CUSTOM_APP_UUID = [uuid] │
│ └─ (other call variables available) │
│ │ │
│ ▼ │
│ 5. Execute script │
│ │ │
│ ├─ LUA: │
│ │ session:execute("lua", "custom_apps/[uuid].lua") │
│ │ │
│ └─ XML: │
│ process inline dialplan from file │
│ │ │
│ ▼ │
│ 6. Script completes │
│ │ │
│ ├─ skip_final_destination = true → DONE (call ends) │
│ │ │
│ └─ skip_final_destination = false → Step 7 │
│ │ │
│ ▼ │
│ 7. Route to final destination │
│ ├─ extension → transfer("1001 XML default") │
│ ├─ queue → callcenter("support@...") │
│ ├─ ivr → ivr("main_menu") │
│ └─ etc. │
│ │
└─────────────────────────────────────────────────────────────────┘

Purpose: Play “This call may be recorded” before connecting to support.

Configuration:

Setting Value
Name Recording Disclaimer
Trigger *99
Type Lua Script
Skip Destination No
Final Destination Queue: support_queue

Script:

-- Recording Disclaimer
session:answer()
session:sleep(500)
session:streamFile("ivr/this_call_may_be_recorded.wav")
-- Final destination handles transfer to queue

Purpose: Check if caller is VIP and route accordingly.

Configuration:

Setting Value
Name VIP Router
Trigger *77
Type Lua Script
Skip Destination Yes (script handles routing)

Script:

-- VIP Caller Detection
local caller = session:getVariable("caller_id_number")
local vip_numbers = {"5551234567", "5559876543", "5551111111"}
local is_vip = false
for _, num in ipairs(vip_numbers) do
if caller:find(num) then
is_vip = true
break
end
end
session:answer()
if is_vip then
session:streamFile("ivr/vip_welcome.wav")
session:transfer("1001", "XML", "default") -- VIP extension
else
session:transfer("5000", "XML", "default") -- Regular queue
end

Purpose: Play emergency message and hang up.

Configuration:

Setting Value
Name Emergency Notice
Trigger *911
Type Lua Script
Skip Destination Yes

Script:

-- Emergency Announcement
session:answer()
session:streamFile("emergency/building_evacuation.wav")
session:hangup()

Purpose: Set a variable and transfer.

Configuration:

Setting Value
Name Department Router
Trigger *50
Type XML Dialplan
Skip Destination Yes

Script:

<extension name="custom_router">
<condition field="destination_number" expression=".*">
<action application="set" data="department=sales"/>
<action application="transfer" data="1001 XML default"/>
</condition>
</extension>

7. Model Context Protocol (MCP) AI Integration

Section titled “7. Model Context Protocol (MCP) AI Integration”

Custom Applications can be managed, generated, and diagnosed via the Model Context Protocol (MCP). AI assistants and engineers can review Lua logic, inspect dialplan XML configurations, and deploy programmable telephony workflows while respecting tenant isolation.

Tool Name Operation Description Access Level
list_custom_applications Query List all custom Lua and XML applications, script types, and destination handoffs. Read-Only
get_custom_application_status Query Retrieve full script source code, script filename, and execution settings for a specific application. Read-Only
create_custom_application Provisioning Deploy a new programmable Lua script or XML dialplan application with optional post-execution destination. Admin / Superadmin
update_custom_application Management Modify script contents, target destination, or active state of a custom application. Admin / Superadmin
delete_custom_application Deprovisioning Remove a custom application and its associated script files. Superadmin

Deploys a custom Lua or XML application.

{
"name": "create_custom_application",
"description": "Create a new custom programmable application executing Lua code or XML dialplan in Telephony Server.",
"inputSchema": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "Application name (e.g. 'CRM Customer Balance Lookup')." },
"appType": { "type": "string", "enum": ["lua", "xml"], "description": "Script execution runtime: 'lua' or 'xml'." },
"scriptContent": { "type": "string", "description": "The raw Lua script code or XML dialplan snippet." },
"finalDestinationModule": { "type": "string", "description": "Optional destination module after script ends (e.g. 'extension', 'queue', 'ivr')." },
"finalDestinationValue": { "type": "string", "description": "Destination target ID or extension." },
"skipFinalDestination": { "type": "boolean", "description": "Whether the script handles call completion without PBX handoff." },
"description": { "type": "string", "description": "Optional notes or documentation." }
},
"required": ["name", "appType", "scriptContent"]
}
}
  1. Domain Scoping: Custom applications execute within the tenant domain context. Global Telephony Server system commands that escape the tenant environment are restricted by the script sandbox.
  2. Auto Dialplan Reload: Once created or updated, changes automatically trigger a background reloadxml in Telephony Server so that modifications become live without restarting telephony services.

Example 1: Creating a Custom Lua API Lookup Application

Section titled “Example 1: Creating a Custom Lua API Lookup Application”

Admin Prompt:
“Create a Lua custom application named ‘Check Account Status’ that queries the external billing API and transfers to extension 1001 if active.”

AI Tool Execution:

{
"tool": "create_custom_application",
"arguments": {
"name": "Check Account Status",
"appType": "lua",
"scriptContent": "local caller_id = session:getVariable('caller_id_number')\nfreeswitch.consoleLog('INFO', 'Verifying caller: ' .. tostring(caller_id))\n-- Custom HTTP lookup logic here\nsession:execute('playback', 'ivr/ivr-please_wait.wav')",
"finalDestinationModule": "extension",
"finalDestinationValue": "1001",
"skipFinalDestination": false
}
}

MCP Response:

{
"success": true,
"data": {
"id": 5,
"name": "Check Account Status",
"appType": "lua",
"message": "Custom Application created and loaded successfully."
}
}

[!WARNING] Syntax Errors: Invalid Lua or XML will cause the application to fail silently. Always check logs.

[!WARNING] No Debugging UI: Script debugging must be done via Telephony Server console/logs.

[!IMPORTANT] Script Permissions: Scripts run with Telephony Server privileges. Be careful with file operations.

  1. Keep Scripts Simple: Complex logic is harder to debug
  2. Use Logging: Add freeswitch.consoleLog("INFO", message) for debugging
  3. Handle Errors: Wrap critical code in pcall() for error handling
  4. Test Offline: Test Lua scripts in a Lua interpreter first
  5. Document Code: Add comments explaining what the script does

[!CAUTION] Code Injection: Custom applications run arbitrary code. Restrict who can create them.

[!CAUTION] External API Calls: Be careful with API calls in scripts—they can slow down call setup.


Symptom Possible Cause Solution
Application not triggered Trigger number conflict Check for duplicate extensions
Script error Lua syntax error Check Telephony Server console logs
No audio session:answer() missing Ensure call is answered first
Final destination ignored skip_final_destination = true Set to false if routing needed
Variables undefined Wrong variable name Check available variables

Enable Lua debugging:

Terminal window
fs_cli -x "console loglevel debug"

Watch for custom app execution:

Terminal window
fs_cli -x "log 7" | grep -i custom_app

List custom applications:

SELECT name, trigger_number, app_type, enabled,
final_destination_module, final_destination_value
FROM public.custom_applications
WHERE domain_id = [domain_id]
ORDER BY trigger_number;

Check script file:

Terminal window
ls -la /usr/share/freeswitch/scripts/custom_apps/[uuid].lua

Term Definition
Custom Application User-defined script that executes when a trigger number is dialed
Lua Lightweight programming language used for Telephony Server scripting
XML Dialplan Telephony Server’s declarative routing configuration format
Trigger Number The extension or feature code that activates the application
Final Destination Where the call is routed after the script completes
Skip Destination Option to not route after script (script handles hangup)
Hot Deploy Changes take effect immediately without restart
session Lua object representing the active call/channel
Channel Variable Named values attached to a call for data passing

Documentation last updated: January 2026