Documentation 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.
Every significant state change inside FreeSWITCH — a channel being created, a call being answered, a DTMF digit being pressed, a module loading, a registration arriving — is announced as an event. Events are structured messages with a typed identifier, a set of key-value header pairs, and an optional body. They flow through a thread-safe APR FIFO queue in the core and are delivered to all registered listeners without blocking the originating thread. This makes the event system the backbone for CDR generation, real-time monitoring, external control via ESL, and custom integrations.
Event Architecture
The event system is described in src/include/switch_event.h:
The event system uses a backend thread and an APR threadsafe FIFO queue to accept event objects from various threads and allow the backend to take control and deliver the events to registered callbacks.
Events are delivered to registered callbacks by a pool of up to 64 dispatch threads (MAX_DISPATCH_VAL = 64), ensuring that a slow consumer cannot block the entire switch. If your callback is expected to do non-trivial work, the recommended pattern is to accept the event in the callback and immediately push it into your own thread’s queue.
/* From src/include/switch_event.h — event structure */
struct switch_event {
switch_event_types_t event_id; /* the event type */
switch_priority_t priority; /* event priority */
char *owner; /* who fired it */
char *subclass_name; /* subclass for CUSTOM events */
switch_event_header_t *headers; /* linked list of key/value headers */
switch_event_header_t *last_header; /* tail pointer for O(1) append */
char *body; /* optional free-form body */
void *bind_user_data; /* data from the subclass provider */
void *event_user_data; /* data from the event sender */
unsigned long key; /* unique key */
struct switch_event *next;
int flags;
};
/* Each header is a name/value pair */
struct switch_event_header {
char *name;
char *value;
char **array; /* array space for multi-value headers */
int idx; /* array index */
unsigned long hash;
struct switch_event_header *next;
};
Event Types
FreeSWITCH defines a comprehensive set of built-in event types in the switch_event_types_t enum (from src/include/switch_types.h). Here are the most commonly used:
Channel Lifecycle Events
| Event | Description |
|---|
SWITCH_EVENT_CHANNEL_CREATE | A new channel has been created (call leg allocated) |
SWITCH_EVENT_CHANNEL_ORIGINATE | A channel has been originated (outbound call started) |
SWITCH_EVENT_CHANNEL_PROGRESS | A channel has started ringing (180 Ringing) |
SWITCH_EVENT_CHANNEL_PROGRESS_MEDIA | A channel has started early media (183 Session Progress) |
SWITCH_EVENT_CHANNEL_ANSWER | A channel has been answered |
SWITCH_EVENT_CHANNEL_BRIDGE | A channel has bridged to another channel |
SWITCH_EVENT_CHANNEL_UNBRIDGE | A channel has unbridged from another channel |
SWITCH_EVENT_CHANNEL_HOLD | A channel has been placed on hold |
SWITCH_EVENT_CHANNEL_UNHOLD | A channel has been taken off hold |
SWITCH_EVENT_CHANNEL_HANGUP | A channel has been hung up |
SWITCH_EVENT_CHANNEL_HANGUP_COMPLETE | Hangup processing is complete |
SWITCH_EVENT_CHANNEL_DESTROY | A channel has been destroyed (memory freed) |
SWITCH_EVENT_CHANNEL_STATE | A channel has changed state |
SWITCH_EVENT_CHANNEL_CALLSTATE | A channel’s call state has changed |
SWITCH_EVENT_CHANNEL_OUTGOING | A channel has been created as an outbound leg |
SWITCH_EVENT_CHANNEL_PARK | A channel has been parked |
SWITCH_EVENT_CHANNEL_UNPARK | A channel has been unparked |
SWITCH_EVENT_CHANNEL_UUID | A channel’s UUID has changed |
Application and API Events
| Event | Description |
|---|
SWITCH_EVENT_CHANNEL_EXECUTE | A channel has started executing an application |
SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETE | A channel has finished executing an application |
SWITCH_EVENT_CHANNEL_APPLICATION | A channel has called an event from an application |
SWITCH_EVENT_API | An API command has been executed |
SWITCH_EVENT_BACKGROUND_JOB | A background API job has completed |
| Event | Description |
|---|
SWITCH_EVENT_DTMF | A DTMF digit was detected or sent |
SWITCH_EVENT_RECORD_START | Recording started on a channel |
SWITCH_EVENT_RECORD_STOP | Recording stopped on a channel |
SWITCH_EVENT_PLAYBACK_START | Audio playback started |
SWITCH_EVENT_PLAYBACK_STOP | Audio playback stopped |
SWITCH_EVENT_DETECTED_SPEECH | Speech detection fired |
SWITCH_EVENT_DETECTED_TONE | Tone detection fired |
SWITCH_EVENT_MEDIA_BUG_START | A media bug was attached to a session |
SWITCH_EVENT_MEDIA_BUG_STOP | A media bug was removed from a session |
SWITCH_EVENT_CODEC | A codec change occurred |
SWITCH_EVENT_RECV_RTCP_MESSAGE | An RTCP message was received |
SWITCH_EVENT_SEND_RTCP_MESSAGE | An RTCP message was sent |
Presence and Messaging Events
| Event | Description |
|---|
SWITCH_EVENT_PRESENCE_IN | Presence information received (endpoint is available) |
SWITCH_EVENT_PRESENCE_OUT | Presence information received (endpoint is unavailable) |
SWITCH_EVENT_PRESENCE_PROBE | A presence probe was received |
SWITCH_EVENT_MESSAGE_WAITING | A message waiting indicator changed |
SWITCH_EVENT_MESSAGE | A basic instant message |
SWITCH_EVENT_NOTIFY | A SIP NOTIFY was sent or received |
System Events
| Event | Description |
|---|
SWITCH_EVENT_HEARTBEAT | Periodic system-alive pulse (default every 20 seconds) |
SWITCH_EVENT_SESSION_HEARTBEAT | Per-session periodic pulse |
SWITCH_EVENT_STARTUP | FreeSWITCH has started |
SWITCH_EVENT_SHUTDOWN | FreeSWITCH is shutting down |
SWITCH_EVENT_SHUTDOWN_REQUESTED | A shutdown has been requested |
SWITCH_EVENT_MODULE_LOAD | A module was loaded |
SWITCH_EVENT_MODULE_UNLOAD | A module was unloaded |
SWITCH_EVENT_RELOADXML | The XML registry was reloaded |
SWITCH_EVENT_LOG | A log line was emitted |
SWITCH_EVENT_TRAP | An error trap was triggered |
SWITCH_EVENT_FAILURE | A failure occurred that may affect normal operation |
SWITCH_EVENT_CUSTOM | A custom event from a module or script |
Event Structure
All events share the same envelope: a typed ID, a list of key-value headers, and an optional free-form body. Every event sent by the core or a module is fully self-describing through its headers.
Here is a sample plain-text event dump for a CHANNEL_ANSWER event as delivered by mod_event_socket:
Event-Name: CHANNEL_ANSWER
Core-UUID: e8e1b3c0-1a2b-4c3d-8e5f-6a7b8c9d0e1f
FreeSWITCH-Hostname: fs01.example.com
FreeSWITCH-Switchname: fs01
FreeSWITCH-IPv4: 192.168.1.10
FreeSWITCH-IPv6: ::1
Event-Date-Local: 2024-07-15 14:32:07
Event-Date-GMT: Mon, 15 Jul 2024 14:32:07 GMT
Event-Date-Timestamp: 1721054327093801
Event-Calling-File: switch_core_session.c
Event-Calling-Function: switch_core_session_run
Event-Calling-Line-Number: 835
Event-Sequence: 12345
Unique-ID: 3a8f1d2e-0c4b-4e7f-a19c-2b8d3e5f6a1c
Channel-State: CS_EXECUTE
Channel-Call-State: ACTIVE
Channel-State-Number: 4
Channel-Name: sofia/internal/1001@192.168.1.100
Caller-Direction: inbound
Caller-Logical-Direction: inbound
Caller-Username: 1001
Caller-Dialplan: XML
Caller-Caller-ID-Name: Alice
Caller-Caller-ID-Number: 1001
Caller-Orig-Caller-ID-Name: Alice
Caller-Orig-Caller-ID-Number: 1001
Caller-Callee-ID-Name:
Caller-Callee-ID-Number: 1002
Caller-Destination-Number: 1002
Caller-Unique-ID: 3a8f1d2e-0c4b-4e7f-a19c-2b8d3e5f6a1c
Caller-Source: mod_sofia
Caller-Context: default
Caller-Channel-Name: sofia/internal/1001@192.168.1.100
Caller-Network-Addr: 192.168.1.100
Channel-Presence-ID: 1001@192.168.1.1
Answer-State: answered
Consuming Events
FreeSWITCH provides several ways to receive events from outside the process.
ESL (mod_event_socket)
mod_json_cdr
mod_amqp
mod_erlang_event
The Event Socket Library exposes a TCP connection (default port 8021) that accepts commands and emits events. After connecting and authenticating, subscribe to events:auth ClueCon
events plain all
Subscribe to specific events only:events plain CHANNEL_ANSWER CHANNEL_HANGUP DTMF BACKGROUND_JOB
Events are delivered as plain text (as shown above) or in JSON format:events json CHANNEL_ANSWER CHANNEL_HANGUP
ESL operates in two modes:
- Inbound — your application connects to FreeSWITCH on port 8021.
- Outbound — FreeSWITCH connects to your application when a call hits the
socket dialplan application.
mod_json_cdr writes a JSON document for each completed call to disk or posts it to an HTTP endpoint. Each document contains the full channel variable set and call timing:<!-- conf/autoload_configs/json_cdr.conf.xml -->
<configuration name="json_cdr.conf" description="JSON CDR">
<settings>
<param name="url" value="http://cdr.example.com/receive"/>
<param name="log-dir" value="/var/log/freeswitch/json_cdr"/>
<param name="encode" value="true"/>
</settings>
</configuration>
mod_amqp publishes events to an AMQP broker (RabbitMQ, etc.) for distributed consumption. Configure event bindings in amqp.conf.xml to route specific event types to named exchanges.
mod_erlang_event delivers events as native Erlang terms to an Erlang/OTP node, enabling FreeSWITCH to be integrated directly into an Erlang supervision tree.
Firing Custom Events
Any module or embedded script can fire a SWITCH_EVENT_CUSTOM event with a named subclass. Custom events carry all the same header/body structure as built-in events.
From C
switch_event_t *event;
/* Reserve the subclass name (done once at module load) */
switch_event_reserve_subclass("mymodule::my_event");
/* Fire the event */
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM,
"mymodule::my_event") == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM,
"My-Header", "my-value");
switch_event_add_body(event, "Optional body text");
switch_event_fire(&event);
}
From Lua
-- Fire a custom event from a Lua dialplan script
local event = freeswitch.Event("CUSTOM", "myapp::call_tagged")
event:addHeader("Tag-Name", "vip")
event:addHeader("Unique-ID", session:get_uuid())
event:fire()
From ESL (SENDEVENT)
sendevent CUSTOM
Event-Subclass: myapp::notify
My-Custom-Header: some-value
Optional body here
Event Bindings in ESL
When connected to mod_event_socket, use the events command to subscribe:
# Subscribe to all events (verbose — use with care in production)
events plain all
# Subscribe to a specific list of events
events plain CHANNEL_CREATE CHANNEL_ANSWER CHANNEL_HANGUP_COMPLETE DTMF BACKGROUND_JOB
# Subscribe to all events in JSON format
events json all
# Subscribe to a custom event subclass
events plain CUSTOM myapp::call_tagged
To filter out noisy events after subscribing to all:
# Stop receiving a specific event type
nixevent CHANNEL_DATA
# Cancel all event subscriptions
noevents
Use nixevent to suppress high-volume events like CHANNEL_DATA and SESSION_HEARTBEAT when you only need call flow events. In a busy system, events plain all can flood a slow consumer and cause it to fall behind the dispatch queue. Subscribe only to the event types your application actually needs.
Internal Event Bindings
Modules can register C callback functions directly against the event FIFO without going through ESL:
/* Bind a C callback to CHANNEL_ANSWER events (from switch_event.h) */
SWITCH_DECLARE(switch_status_t) switch_event_bind(
const char *id,
switch_event_types_t event,
const char *subclass_name,
switch_event_callback_t callback,
void *user_data
);
/* Example callback signature */
static void my_event_handler(switch_event_t *event)
{
const char *uuid = switch_event_get_header(event, "Unique-ID");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
"Call answered: %s\n", uuid);
}
/* Register it in SWITCH_MODULE_LOAD_FUNCTION */
switch_event_bind("mod_mymodule", SWITCH_EVENT_CHANNEL_ANSWER,
SWITCH_EVENT_SUBCLASS_ANY, my_event_handler, NULL);
The id string is used to identify the binding for later unbinding with switch_event_unbind_callback(). Bindings registered this way are synchronous with the dispatch thread — keep callbacks fast and offload heavy work to a separate thread.