In outbound mode, the roles are reversed: FreeSWITCH is the client and your application is the server. When an incoming (or originated) call matches a dialplan extension that uses theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/signalwire/freeswitch/llms.txt
Use this file to discover all available pages before exploring further.
socket application, FreeSWITCH opens a TCP connection to your server and hands over control of that call leg. Your server then drives the call — answering, playing audio, collecting DTMF, recording, transferring, or hanging up — using the same ESLconnection API used in inbound mode, but scoped entirely to the single call that was connected.
Outbound mode is ideal for per-call IVR logic, voicemail, call recording, and any scenario where each call needs independent, sequential application control without a global event subscription.
How It Works
Call arrives in the dialplan
FreeSWITCH processes the call and reaches a dialplan extension containing the
socket application.FreeSWITCH connects to your server
FreeSWITCH opens a TCP connection to the host and port specified in the
socket application’s data attribute.FreeSWITCH sends a connect event
FreeSWITCH sends a
Content-Type: command/reply with a full CHANNEL_DATA event body describing the call. Your server reads this via getInfo().Your server controls the call
Your server uses
ESLconnection(socket_fd) to wrap the accepted socket, then calls execute() to run dialplan applications — answer, playback, play_and_get_digits, hangup, etc.Dialplan Configuration
Add asocket action to any dialplan extension. The data attribute specifies your server’s address, port, and optional flags.
data value format is: <host>:<port> [async] [full]
| Flag | Description |
|---|---|
async | Execute socket commands asynchronously. The call continues processing while your server handles events. Without async, each execute() call blocks until the application completes before the next command is sent. |
full | Send full event headers to your server (equivalent to myevents on an inbound connection). Without full, only minimal event data is delivered. |
data="127.0.0.1:8084 async full" is the most common form.
Without the
async flag, execute() calls are serialised: FreeSWITCH will not process the next command until the current application finishes. This makes synchronous mode simpler to reason about for linear IVR flows but means your server blocks between steps. With async, you receive events for each application completion and must track state yourself.Writing an Outbound Server
Your server is a standard TCP server. For each accepted connection, create anESLconnection from the raw socket file descriptor, call getInfo() to retrieve the initial session data, then issue execute() calls to control the call.
Python outbound server
The following example is adapted fromlibs/esl/python3/server.py:
Perl outbound server
Adapted fromlibs/esl/perl/server.pl:
Session Commands in Outbound Mode
Usecon.execute(app, args, uuid) to run any FreeSWITCH dialplan application on the connected call. The uuid can be retrieved from getInfo().getHeader("unique-id").
Common dialplan applications for outbound mode
| Application | Key arguments | Description |
|---|---|---|
answer | — | Answer the call |
playback | <path> | Play an audio file |
play_and_get_digits | <min> <max> <tries> <timeout> <term> <prompt> <invalid> <var> <regexp> | Collect DTMF with prompts |
record | <path> [max_sec] [silence_threshold] [silence_seconds] | Record the call audio |
bridge | <dial-string> | Bridge this leg to another destination |
transfer | <dest> [<dialplan>] [<context>] | Transfer the call to the dialplan |
sleep | <ms> | Pause execution for the given number of milliseconds |
hangup | [<cause>] | Hang up the call |
speak | <engine>|<voice>|<text> | Text-to-speech playback |
set | <variable>=<value> | Set a channel variable |
Event Handling in Outbound Mode
By default the outbound socket delivers only basic call control events. Usemyevents to receive all events for this call, and linger to keep the connection open after the call hangs up (useful for post-call processing).
libs/esl/ruby/server3.rb demonstrates this pattern with divert_events on to capture DTMF events during playback: