Skip to main content

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

EventDescription
SWITCH_EVENT_CHANNEL_CREATEA new channel has been created (call leg allocated)
SWITCH_EVENT_CHANNEL_ORIGINATEA channel has been originated (outbound call started)
SWITCH_EVENT_CHANNEL_PROGRESSA channel has started ringing (180 Ringing)
SWITCH_EVENT_CHANNEL_PROGRESS_MEDIAA channel has started early media (183 Session Progress)
SWITCH_EVENT_CHANNEL_ANSWERA channel has been answered
SWITCH_EVENT_CHANNEL_BRIDGEA channel has bridged to another channel
SWITCH_EVENT_CHANNEL_UNBRIDGEA channel has unbridged from another channel
SWITCH_EVENT_CHANNEL_HOLDA channel has been placed on hold
SWITCH_EVENT_CHANNEL_UNHOLDA channel has been taken off hold
SWITCH_EVENT_CHANNEL_HANGUPA channel has been hung up
SWITCH_EVENT_CHANNEL_HANGUP_COMPLETEHangup processing is complete
SWITCH_EVENT_CHANNEL_DESTROYA channel has been destroyed (memory freed)
SWITCH_EVENT_CHANNEL_STATEA channel has changed state
SWITCH_EVENT_CHANNEL_CALLSTATEA channel’s call state has changed
SWITCH_EVENT_CHANNEL_OUTGOINGA channel has been created as an outbound leg
SWITCH_EVENT_CHANNEL_PARKA channel has been parked
SWITCH_EVENT_CHANNEL_UNPARKA channel has been unparked
SWITCH_EVENT_CHANNEL_UUIDA channel’s UUID has changed

Application and API Events

EventDescription
SWITCH_EVENT_CHANNEL_EXECUTEA channel has started executing an application
SWITCH_EVENT_CHANNEL_EXECUTE_COMPLETEA channel has finished executing an application
SWITCH_EVENT_CHANNEL_APPLICATIONA channel has called an event from an application
SWITCH_EVENT_APIAn API command has been executed
SWITCH_EVENT_BACKGROUND_JOBA background API job has completed

Media and Signaling Events

EventDescription
SWITCH_EVENT_DTMFA DTMF digit was detected or sent
SWITCH_EVENT_RECORD_STARTRecording started on a channel
SWITCH_EVENT_RECORD_STOPRecording stopped on a channel
SWITCH_EVENT_PLAYBACK_STARTAudio playback started
SWITCH_EVENT_PLAYBACK_STOPAudio playback stopped
SWITCH_EVENT_DETECTED_SPEECHSpeech detection fired
SWITCH_EVENT_DETECTED_TONETone detection fired
SWITCH_EVENT_MEDIA_BUG_STARTA media bug was attached to a session
SWITCH_EVENT_MEDIA_BUG_STOPA media bug was removed from a session
SWITCH_EVENT_CODECA codec change occurred
SWITCH_EVENT_RECV_RTCP_MESSAGEAn RTCP message was received
SWITCH_EVENT_SEND_RTCP_MESSAGEAn RTCP message was sent

Presence and Messaging Events

EventDescription
SWITCH_EVENT_PRESENCE_INPresence information received (endpoint is available)
SWITCH_EVENT_PRESENCE_OUTPresence information received (endpoint is unavailable)
SWITCH_EVENT_PRESENCE_PROBEA presence probe was received
SWITCH_EVENT_MESSAGE_WAITINGA message waiting indicator changed
SWITCH_EVENT_MESSAGEA basic instant message
SWITCH_EVENT_NOTIFYA SIP NOTIFY was sent or received

System Events

EventDescription
SWITCH_EVENT_HEARTBEATPeriodic system-alive pulse (default every 20 seconds)
SWITCH_EVENT_SESSION_HEARTBEATPer-session periodic pulse
SWITCH_EVENT_STARTUPFreeSWITCH has started
SWITCH_EVENT_SHUTDOWNFreeSWITCH is shutting down
SWITCH_EVENT_SHUTDOWN_REQUESTEDA shutdown has been requested
SWITCH_EVENT_MODULE_LOADA module was loaded
SWITCH_EVENT_MODULE_UNLOADA module was unloaded
SWITCH_EVENT_RELOADXMLThe XML registry was reloaded
SWITCH_EVENT_LOGA log line was emitted
SWITCH_EVENT_TRAPAn error trap was triggered
SWITCH_EVENT_FAILUREA failure occurred that may affect normal operation
SWITCH_EVENT_CUSTOMA 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.
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.

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.

Build docs developers (and LLMs) love