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.

The FreeSWITCH directory is the authoritative source of user accounts. It stores SIP authentication credentials, voicemail PINs, caller ID presentation values, and per-user variables that control call routing. When mod_sofia receives a SIP REGISTER or a challenged INVITE, it queries the directory to look up the authenticating user’s password. When the dialplan uses a user/ dial string (e.g., bridge data="user/1000@domain.com"), it also queries the directory to resolve the user’s registered contact URI.

Directory Structure

The directory section of the configuration uses a domain-centric hierarchy: one or more <domain> elements, each containing groups of users. The vanilla configuration defines a single domain whose name is set to $${domain} (the machine’s local IP by default).
directory/
├── default.xml             # Domain definition, groups, domain-level variables
└── default/
    ├── 1000.xml            # Individual user files
    ├── 1001.xml
    ├── …
    └── 1019.xml

directory/default.xml

<include>
  <!-- The domain name — right-hand side of the SIP address (user@domain) -->
  <domain name="$${domain}">

    <params>
      <!-- Dial-string used when reaching a user via "user/id@domain" -->
      <param name="dial-string"
        value="{^^:sip_invite_domain=${dialed_domain}:presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(*/${dialed_user}@${dialed_domain})},${verto_contact(${dialed_user}@${dialed_domain})}"/>
      <!-- Required for Verto (WebRTC) clients -->
      <param name="jsonrpc-allowed-methods" value="verto"/>
    </params>

    <!-- Variables applied to every user in this domain unless overridden per-user -->
    <variables>
      <variable name="record_stereo"                value="true"/>
      <variable name="default_gateway"              value="$${default_provider}"/>
      <variable name="default_areacode"             value="$${default_areacode}"/>
      <variable name="transfer_fallback_extension"  value="operator"/>
    </variables>

    <groups>
      <group name="default">
        <users>
          <!-- Individual user files are included here -->
          <X-PRE-PROCESS cmd="include" data="default/*.xml"/>
        </users>
      </group>

      <group name="sales">
        <users>
          <!-- "pointer" means: look up the full user record in the default group -->
          <user id="1000" type="pointer"/>
          <user id="1001" type="pointer"/>
          <user id="1002" type="pointer"/>
          <user id="1003" type="pointer"/>
          <user id="1004" type="pointer"/>
        </users>
      </group>

      <group name="support">
        <users>
          <user id="1010" type="pointer"/>
          <user id="1011" type="pointer"/>
          <user id="1012" type="pointer"/>
          <user id="1013" type="pointer"/>
        </users>
      </group>
    </groups>

  </domain>
</include>

User Entry

Each user is stored in its own file inside directory/default/. The file for extension 1000 illustrates the canonical structure:
<!-- directory/default/1000.xml -->
<include>
  <user id="1000">
    <params>
      <!-- SIP registration password -->
      <param name="password"    value="$${default_password}"/>
      <!-- Voicemail PIN -->
      <param name="vm-password" value="1000"/>
    </params>

    <variables>
      <!-- Outbound call permissions -->
      <variable name="toll_allow"                  value="domestic,international,local"/>

      <!-- Accounting / billing code -->
      <variable name="accountcode"                 value="1000"/>

      <!-- Dialplan context this user is placed into after authentication -->
      <variable name="user_context"                value="default"/>

      <!-- Caller ID presented to called parties for internal calls -->
      <variable name="effective_caller_id_name"    value="Extension 1000"/>
      <variable name="effective_caller_id_number"  value="1000"/>

      <!-- Caller ID presented on external (PSTN) calls -->
      <variable name="outbound_caller_id_name"     value="$${outbound_caller_name}"/>
      <variable name="outbound_caller_id_number"   value="$${outbound_caller_id}"/>

      <!-- Group used by call pickup (*8) -->
      <variable name="callgroup"                   value="techsupport"/>
    </variables>
  </user>
</include>
The vanilla configuration ships with 20 demo user accounts — extensions 1000 through 1019 — all using the same $${default_password} (set to 1234 in vars.xml). Each user’s voicemail PIN (vm-password) defaults to that user’s own extension number (e.g., 1000 for user 1000). You must change these credentials before exposing FreeSWITCH to any network. Edit vars.xml, change the value of default_password, then run reloadxml from the fs_cli console.

Authentication

mod_sofia uses HTTP Digest authentication (RFC 3261 §22) to verify SIP REGISTER and INVITE requests. When a challenge is issued, the user agent must supply credentials that FreeSWITCH validates against the directory. FreeSWITCH supports two password storage formats:

Plaintext password

The password param stores the password in cleartext. FreeSWITCH computes the MD5 A1 hash on-the-fly during authentication.
<param name="password" value="mySecretPass"/>

Pre-hashed (a1-hash)

Store a pre-computed MD5 hash of user:domain:password for slightly better at-rest security.
<param name="a1-hash" value="3b6c6f5e..."/>
Generate it with:
echo -n "1000:$DOMAIN:password" | md5sum
The force-register-domain setting in the internal SIP profile pins all registrations to $${domain}, so users register with the IP address (or hostname) configured in vars.xml rather than whatever domain they send in their SIP headers.

Voicemail

Voicemail configuration is split between the directory (per-user settings) and autoload_configs/voicemail.conf.xml (global storage paths and behaviour). The directory controls two voicemail attributes per user:
Parameter / VariableLocationDescription
vm-password<params>The numeric PIN the user dials to retrieve messages.
voicemail_enabled<variables>Set to false to disable voicemail for a specific user. Defaults to enabled.
<params>
  <param name="vm-password" value="1000"/>
</params>
<variables>
  <!-- Uncomment to disable voicemail for this user -->
  <!-- <variable name="voicemail_enabled" value="false"/> -->
</variables>
When the dialplan bridges to a user and the call is unanswered (after call_timeout seconds), continue_on_fail=true allows execution to fall through to:
<action application="bridge"
  data="loopback/app=voicemail:default ${domain_name} ${dialed_extension}"/>
This drops the caller into the voicemail application for extension ${dialed_extension} in the default voicemail profile.

Call Permissions

The toll_allow variable is a comma-separated list of permission tokens checked by the dialplan before routing outbound calls. The default dialplan checks this variable on extensions in the Local_Extension and outbound routing sections.
ValueMeaning
localAllows calls to local extensions and internal numbers
domesticAllows domestic long-distance calls
internationalAllows international dialing (01+country code)
<!-- Grant all permissions (default for demo users) -->
<variable name="toll_allow" value="domestic,international,local"/>

<!-- Restrict to local calls only -->
<variable name="toll_allow" value="local"/>
In the dialplan you can enforce this with a condition:
<extension name="domestic">
  <condition field="${toll_allow}" expression="domestic">
    <action application="bridge" data="sofia/external/1${destination_number}@$${default_provider}"/>
  </condition>
</extension>

Multiple Domains

To serve multiple SIP domains from a single FreeSWITCH instance, create one <domain> block per domain, each in its own file under directory/. The domain name attribute must match the SIP domain that users register with.
<!-- directory/example.com.xml -->
<include>
  <domain name="example.com">
    <params>
      <param name="dial-string" value="..."/>
    </params>
    <groups>
      <group name="default">
        <users>
          <X-PRE-PROCESS cmd="include" data="example.com/*.xml"/>
        </users>
      </group>
    </groups>
  </domain>
</include>
When running multiple domains, remove the force-register-domain and force-register-db-domain parameters from your SIP profile (or set them to empty strings) so that FreeSWITCH uses the domain from the SIP packet rather than overriding it.

Directory Reload

After adding or modifying user files, reload the configuration without restarting FreeSWITCH:
# From fs_cli
reloadxml

# Or from the OS shell
fs_cli -x "reloadxml"
reloadxml re-reads and re-compiles the entire XML configuration tree, including the directory. Active calls are not affected. New registrations and authentication attempts will immediately use the updated directory. To verify a specific user record was loaded correctly:
# Show all data FreeSWITCH has for user 1000 in the default domain
xml_locate directory domain name $${domain} user id 1000

Build docs developers (and LLMs) love