Skip to main content

Chapter 6: The User Directory

The user directory is the XML-based identity store that FreeSWITCH consults for SIP authentication, call routing, caller ID presentation, and voicemail credential lookup. It organizes users into one or more domains, each containing user records composed of authentication parameters and channel variables that are stamped onto calls originating from or delivered to that user.

Directory Structure and File Layout

The directory section of the FreeSWITCH configuration tree lives under conf/directory/. The top-level entry point is conf/directory/default.xml, which wraps a <domain> element inside an <include> block. User records are stored in separate per-user XML files under conf/directory/default/ and pulled in at startup via an X-PRE-PROCESS include directive:

<X-PRE-PROCESS cmd="include" data="default/*.xml"/>

This directive causes every .xml file in conf/directory/default/ to be merged into the enclosing <users> block at configuration parse time. Each included file must be a valid XML fragment wrapped in <include> tags and containing one or more <user> elements.

Files are merged, not overridden. Adding a new file to conf/directory/default/ is sufficient to register a new user; no changes to default.xml are required. The merge order follows glob expansion, which FreeSWITCH sorts alphabetically by filename.


Domains

The Default Domain

The directory is organized around domains, matching the right-hand side of a SIP address (the part after @). The vanilla configuration defines a single domain whose name is the preprocessor variable $${domain}:

<domain name="$${domain}">
...
</domain>

The $${domain} variable is set in conf/vars.xml:

<X-PRE-PROCESS cmd="set" data="domain=$${local_ip_v4}"/>

By default, $${domain} resolves to the machine's primary IPv4 address. To use a hostname instead, change that line to assign your desired hostname, for example:

<X-PRE-PROCESS cmd="set" data="domain=pbx.example.com"/>

Multiple domain blocks can coexist within the directory tree to support multi-tenant deployments. Each domain must have a unique name attribute.

Domain-Level Params and Variables

A <domain> element may carry its own <params> and <variables> blocks. These values apply to every user in the domain and are overridden by user-level declarations of the same name.

The vanilla default.xml sets two domain-level params and several variables:

<domain name="$${domain}">
<params>
<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})}"/>
<param name="jsonrpc-allowed-methods" value="verto"/>
</params>

<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>
...
</domain>

The dial-string param at the domain level provides the default contact resolution expression used when a user/ channel is originated. It can also be placed at the user level to override per-user routing.

Domain-level params reference:

ParamPurposeValuesDefault
dial-stringDefault contact resolution expression for user/ origination within this domain. Overridden by a user-level dial-string.FreeSWITCH dial string expressionNone
jsonrpc-allowed-methodsComma-separated list of JSON-RPC method families permitted for Verto WebSocket connections in this domain.verto and othersNone
jsonrpc-allowed-event-channelsComma-separated list of event channel families a Verto client in this domain may subscribe to.Channel family namesNone

Defining a User

Each user is represented by a <user> element with a mandatory id attribute. The id value is the left-hand side of the SIP address and must be unique within its domain.

User Attributes

AttributePurposeNotes
idUnique user identifier within the domain. Corresponds to the SIP username.Required
number-aliasAlternate number string that resolves to this user record. Allows a phone number to map to an alphanumeric id.Optional
cidrAdds a <user-id>@<domain> token to any ACL whose node lists this domain (a network-list ACL), pairing the user with the given CIDR range. It does not by itself bypass digest auth; a SIP profile must reference that ACL via apply-inbound-acl/apply-register-acl for the IP-based allow to take effect.Optional

Example using cidr and number-alias:

<user id="brian" number-alias="1000" cidr="192.0.2.0/24">

A minimal user record wraps <params> and <variables> blocks inside an <include> fragment:

<include>
<user id="1000">
<params>
<param name="password" value="$${default_password}"/>
<param name="vm-password" value="1000"/>
</params>
<variables>
<variable name="toll_allow" value="domestic,international,local"/>
<variable name="accountcode" value="1000"/>
<variable name="user_context" value="default"/>
<variable name="effective_caller_id_name" value="Extension 1000"/>
<variable name="effective_caller_id_number" value="1000"/>
<variable name="outbound_caller_id_name" value="$${outbound_caller_name}"/>
<variable name="outbound_caller_id_number" value="$${outbound_caller_id}"/>
<variable name="callgroup" value="techsupport"/>
</variables>
</user>
</include>

User Params

<params> hold authentication credentials and per-user configuration that FreeSWITCH reads during registration and call setup. These are not automatically stamped onto call channel variables.

User Variables

<variables> hold channel variables that FreeSWITCH sets on every call leg associated with this user. The variables are available to the dialplan, CDR, and other subsystems throughout the life of the call.


Parameter Reference

Common User Params

ParamPurposeValuesDefault
passwordSIP digest authentication password. Used by mod_sofia to verify the user's credentials via MD5 digest.Any stringNone (required for digest auth)
a1-hashPre-computed MD5 hash of user:domain:password (HA1). Accepted in place of password to avoid storing plaintext credentials.32-character hex stringNone
allow-empty-passwordWhen true, allows a user with no password or a1-hash to authenticate. Disabled by default; enabling it is a security risk.true, falsetrue (core default; set false on the Sofia profile to deny)
auth-aclNamed ACL that the registering IP must pass in addition to digest authentication. If the IP fails the ACL, registration is denied regardless of credentials.ACL list name defined in conf/autoload_configs/acl.conf.xmlNone
sip-forbid-registerWhen true, REGISTER requests from this user are unconditionally rejected with 403.true, falsefalse
dial-stringPer-user override for the contact resolution expression used when originating to user/id@domain. Overrides the domain-level dial-string.FreeSWITCH dial string expressionInherited from domain
mwi-accountAlternate user@domain address whose message-waiting indicator state is reported to this user's UA during registration. Useful when a user's SIP username differs from their voicemail mailbox identity.user@domain stringDefaults to the registering user and domain
reverse-auth-userUsername FreeSWITCH presents when authenticating outbound SIP requests (INVITEs) sent to this user's UA. Used when the remote UA challenges FreeSWITCH.SIP username stringNone
reverse-auth-passPassword FreeSWITCH presents when authenticating outbound SIP requests sent to this user's UA. Used together with reverse-auth-user.Any stringNone
user-agent-filterRegular expression applied to the User-Agent header of incoming REGISTER requests. Registration is denied if the header does not match.POSIX regex stringNone (no filtering)
max-registrations-per-extensionMaximum number of simultaneous registrations allowed for this user. Additional REGISTER attempts beyond this limit are rejected.IntegerUnlimited
jsonrpc-allowed-methodsPer-user override of the JSON-RPC method families permitted over a Verto WebSocket connection.verto and othersInherited from domain
http-allowed-apiComma-separated list of HTTP API endpoints this user may access.API namesNone

Voicemail Params

All vm-* params are read from the user's <params> block by mod_voicemail. They are not channel variables and are not stamped onto call legs.

ParamPurposeValuesDefault
vm-passwordPIN used to access the voicemail mailbox for this user. The special value user-choose allows the caller to set their own PIN on first access. If omitted, the password param is used as the PIN.Numeric string or user-chooseFalls back to password
vm-a1-hashPre-computed MD5 hash of user:domain:vm-password. Accepted in place of vm-password to avoid storing a plaintext PIN.32-character hex stringNone
vm-enabledWhen false, voicemail is disabled for this user. Calls routed to voicemail return an invalid-extension prompt.true, falsetrue
vm-mailtoEmail address to which new voicemail messages are sent as audio attachments or notifications.Email address stringNone
vm-notify-mailtoEmail address that receives a short notification (no audio attachment) when a new voicemail arrives.Email address stringNone
vm-mailfromSender address used in the From header of voicemail email notifications.Email address stringFalls back to the profile email-from param (vanilla: ${voicemail_account}@${voicemail_domain})
vm-ccVoicemail box (in user@domain format) to which a copy of every new message is forwarded.user@domain stringNone
vm-email-all-messagesWhen true, emails are sent for every new voicemail regardless of whether the profile has email enabled globally.true, falsefalse
vm-notify-email-all-messagesWhen true, notification-only emails (no attachment) are sent for every new voicemail.true, falsefalse
vm-attach-fileWhen true, the voicemail audio file is attached to the notification email. There is no profile-level equivalent; if this param is absent, no file is attached.true, falsefalse
vm-keep-local-after-emailWhen true, the voicemail message is kept in the mailbox after it has been emailed. When false, the message is deleted from the mailbox once emailed.true, falsetrue
vm-skip-instructionsWhen true, the post-beep recording instructions are not played to the caller leaving a message.true, falsefalse
vm-disk-quotaMaximum total size in bytes of stored voicemail messages for this user. New messages are rejected when the quota is reached.Integer (bytes)Unlimited
vm-storage-dirPer-user directory path where voicemail recordings are stored, overriding the profile default.Filesystem pathFalls back to the profile storage-dir param (unset in vanilla, so $${storage_dir}/voicemail/<profile>/<domain>/<user>)
vm-domain-storage-dirDirectory path for domain-scoped voicemail storage. Deprecated storage-dir is an alias for this param.Filesystem pathFalls back to the profile storage-dir param (unset in vanilla)
vm-alternate-greet-idUser id whose greeting is played instead of this user's own greeting. Useful for shared or virtual mailboxes.User id stringNone
vm-message-extFile extension for recorded voicemail messages for this user, overriding the profile default.Extension string (e.g., mp3, wav)Falls back to the profile file-extension param (wav)
vm-operator-extensionExtension dialed when the caller presses 0 during the voicemail greeting.Extension stringNone
vm-convert-cmdShell command used to convert the recorded audio file before storage or email.Command stringFalls back to the profile convert-cmd param (unset in vanilla, so no conversion)
vm-convert-extFile extension produced by vm-convert-cmd.Extension stringFalls back to the profile convert-ext param (unset in vanilla)

Common User Variables

VariablePurposeValuesDefault
user_contextDialplan context that FreeSWITCH uses to route inbound calls from this user.Any defined context nameNone
toll_allowComma-separated list of calling class tags that this user is permitted to use. The dialplan checks this variable to authorize call types.Any string labels matching dialplan conditionsNone
accountcodeBilling or reporting identifier stamped on CDR records for calls associated with this user.Any stringNone
effective_caller_id_nameCaller ID name presented on internal calls originating from this user.Any stringNone
effective_caller_id_numberCaller ID number presented on internal calls originating from this user.E.164 or extension numberNone
outbound_caller_id_nameCaller ID name used when this user places calls through an external gateway.Any string$${outbound_caller_name} in vanilla config
outbound_caller_id_numberCaller ID number used when this user places calls through an external gateway.E.164 number$${outbound_caller_id} in vanilla config
callgroupGroup label used by the group_call API and group pickup features to aggregate users into logical call groups.Any stringNone
presence_idPresence identity published for this user in the format user@domain. Required for BLF and presence subscription.user@domain stringNone
process_cdrControls whether a CDR record is written for calls associated with this user.true, false, a_leg, b_legtrue
record_stereoWhen true, calls are recorded in stereo with the two legs on separate channels. Set at the domain level in vanilla config.true, falsefalse
default_gatewayName of the gateway used as the default outbound route for this user.Gateway name$${default_provider} in vanilla config
transfer_fallback_extensionExtension dialed when a blind transfer fails to connect.Extension stringoperator in vanilla config

Groups

Within a domain, users can be organized into named <group> elements. Groups serve two purposes: they allow users to be addressed collectively via the group_call API, and they provide a label that appears in directory API output.

The vanilla default.xml defines four groups. The default group uses a glob include to pull in all user files:

<groups>
<group name="default">
<users>
<X-PRE-PROCESS cmd="include" data="default/*.xml"/>
</users>
</group>

<group name="sales">
<users>
<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="billing">
<users>
<user id="1005" type="pointer"/>
<user id="1006" type="pointer"/>
<user id="1007" type="pointer"/>
<user id="1008" type="pointer"/>
<user id="1009" 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"/>
<user id="1014" type="pointer"/>
</users>
</group>
</groups>

A <group> element may contain its own <params> block. The dial-string and group-dial-string params inside a group's <params> override the domain-level dial-string for members of that group when group_call resolves them.

Pointer Users

A <user> element with type="pointer" does not define a user record itself. It is a reference that tells the directory lookup to continue searching for the full user record elsewhere in the domain (typically in the default group where the actual record lives). Pointer entries allow a single user to belong to multiple groups without duplicating the user record.

<user id="1000" type="pointer"/>

When FreeSWITCH resolves a pointer, it re-queries the directory for the same id without the type="pointer" constraint and uses the found record's params and variables.

Calling a Group

The group_call API generates a dial string that simultaneously or sequentially targets all members of a named group. It is called from the dialplan using the API expansion syntax:

<action application="bridge" data="${group_call(sales@$${domain})}"/>

The syntax is group_call(groupname@domain). An optional flag suffix controls the origination mode:

FlagMode
+ASimultaneous (all members bridged at once, comma-delimited)
+FSequential (members tried one at a time, pipe-delimited)
+EEnterprise origination (uses the enterprise originate delimiter)

Default mode when no flag is given is simultaneous (+A).


Directory and SIP Authentication

When a SIP UA sends a REGISTER or INVITE with digest credentials, FreeSWITCH (via mod_sofia) looks up the user in the directory using the domain from the SIP From or To header (depending on profile configuration). The lookup retrieves the password param or the a1-hash param.

If password is present, FreeSWITCH computes the HA1 digest internally as MD5(user:domain:password) and compares it against the credential in the SIP Authorization header. If a1-hash is present, FreeSWITCH uses the stored hash directly, skipping the local MD5 computation. Only one of the two should be set per user.

If auth-acl is set, the source IP of the request must pass the named ACL; failure denies registration regardless of the credential check result.

Setting sip-forbid-register to true unconditionally rejects REGISTER for that user with a 403 response, regardless of credentials.

The cidr attribute on the <user> element (see User Attributes above) does not act in the digest-auth path on its own. When an ACL is built, FreeSWITCH walks the directory and, for each <user> carrying a cidr, adds a <user-id>@<domain> token bound to that range into any network-list ACL whose node names the domain. A SIP profile then enables IP-based authentication by referencing that ACL through apply-inbound-acl or apply-register-acl; without such a reference the cidr attribute has no effect and the request is still subject to the normal digest challenge.

Which domain FreeSWITCH uses for the lookup depends on the auth-calls and related settings in the SIP profile. SIP profile configuration is covered in the chapter on SIP profiles.


Routing Inbound Calls with user_context

When a registered user places a call, the endpoint module (mod_sofia or mod_verto) reads the user_context variable from the authenticated user's directory record and uses it as the dialplan context for that call leg.

<variable name="user_context" value="default"/>

This means different users can be routed into different dialplan contexts by assigning distinct user_context values. A user with user_context set to restricted will have their calls matched against the restricted context in conf/dialplan/, while a user with user_context set to default uses the standard default context.

If user_context is absent from the user's variables and absent from the domain-level variables, the endpoint module falls back to a profile-level default context. Dialplan context structure is covered in the chapter on the dialplan.


Open-Ended Params and Variables

The <params> and <variables> blocks accept any name-value pair. Parameters not recognized by a built-in module are silently ignored during authentication and registration. Variables defined in <variables> are unconditionally stamped onto every call leg associated with the user and become available as channel variables throughout the dialplan, CDR modules, and any application that reads channel variables.

This means arbitrary data can be stored on a user record and consumed by custom dialplan logic:

<variable name="department" value="engineering"/>
<variable name="max_calls" value="5"/>

These would appear as the channel variables ${department} and ${max_calls} on any call leg for that user. (Directory <variable>s are channel variables, referenced with a single $; the $${...} form is reserved for preprocessor-global variables set in vars.xml.) There is no validation; any string is accepted.