Skip to main content

Chapter 26: HTTP Integration: XML Curl and HTTAPI

FreeSWITCH provides two distinct HTTP integration points. mod_xml_curl replaces the static XML file lookup for one or more configuration sections with an HTTP request to a backend service, enabling centralized, database-driven configuration. mod_httapi is a dialplan application that hands live call control to an HTTP backend: for each step of the call, FreeSWITCH posts current call state to a URL and the backend returns an XML document telling FreeSWITCH what to do next. The two mechanisms are independent and complementary.

Part 1: XML Curl

How XML Curl Works

FreeSWITCH uses an XML registry as its runtime data store. When FreeSWITCH needs to look up a dialplan, a directory entry, or a module configuration, it queries the registry for the appropriate section. By default, the registry is served from static files on disk.

mod_xml_curl registers a fetch handler for one or more sections. When FreeSWITCH queries a bound section, mod_xml_curl makes an HTTP request to a configured URL, passing the lookup context as POST fields. The backend must return a well-formed XML document for that section. FreeSWITCH parses the response and uses it in place of the static file it would otherwise read.

The binding is exclusive for the bound sections: once a section is bound to a URL, that URL is the sole source for that section. Static files for bound sections are not consulted.


Enabling the Module

Add mod_xml_curl to autoload_configs/modules.conf.xml if it is not already present:

<load module="mod_xml_curl"/>

When mod_xml_curl must serve configuration for modules that load at startup, it must load before those modules. See Loading mod_xml_curl Early for the early-load mechanism.


Configuration File

mod_xml_curl reads autoload_configs/xml_curl.conf.xml. The root element is a <configuration> element containing a single <bindings> child. Each <binding> element defines one HTTP gateway.

<configuration name="xml_curl.conf" description="cURL XML Gateway">
<bindings>
<binding name="my-backend">
<param name="gateway-url" value="https://example.com/freeswitch/xml" bindings="directory"/>
<param name="gateway-credentials" value="fsuser:secret"/>
<param name="auth-scheme" value="basic"/>
<param name="timeout" value="10"/>
</binding>
</bindings>
</configuration>

Multiple <binding> elements are permitted. Each binding is independent and may serve different sections.


Bindings

The gateway-url Parameter and the bindings Attribute

The gateway-url parameter sets the HTTP endpoint for the binding. Its bindings XML attribute (not a child <param>) declares which sections this binding handles:

<param name="gateway-url" value="https://example.com/freeswitch/xml" bindings="directory"/>

The bindings attribute accepts one or more pipe-delimited section names:

<param name="gateway-url" value="https://example.com/freeswitch/xml" bindings="directory|dialplan"/>

If the bindings attribute is omitted, the binding handles all sections.

The value may also use the file: scheme to read from a local file path instead of making an HTTP request:

<param name="gateway-url" value="file:/etc/freeswitch/static-directory.xml" bindings="directory"/>

When a file: URL is used, mod_xml_curl reads the named file directly. The gateway-credentials, method, timeout, and all TLS parameters are ignored for file-scheme URLs.

Bindable Section Names

The following section names are recognized by switch_xml_parse_section_string and may be used in the bindings attribute:

Section NameWhat It Covers
configModule configuration (autoload_configs/*.conf.xml)
directoryUser directory and SIP authentication
dialplanDialplan contexts and extensions
languagesPhrase macro language definitions
chatplanChat routing plans
channelsChannel variable lookups

Binding Parameter Reference

All parameters are child <param> elements of a <binding> element.

ParameterPurposeAccepted ValuesDefault
gateway-urlEndpoint to fetch XML from. Carries a bindings XML attribute to declare which sections this binding serves. Also accepts a file: scheme path to read from a local file.HTTP or HTTPS URL, or file:/path/to/file. Variable substitution in the URL is supported when use-dynamic-url is enabled.None (required)
gateway-credentialsHTTP authentication credentials.username:password stringNone
auth-schemeAuthentication scheme used when gateway-credentials is set. Prefix with = to use the specified scheme exclusively rather than allowing libcurl to negotiate.basic, digest, NTLM, GSS-NEGOTIATE, anybasic
methodHTTP method for the request. When absent or set to post, the fields are sent as a POST body. When set to any other value, the fields are appended to the URL as a query string and the request is issued with that method name.post, or any HTTP method stringAbsent (POST behavior)
disable-100-continueControls whether the Expect: 100-continue header is suppressed. Set to false to send the header; omitting this parameter or setting it to true suppresses it.true, falsetrue (header suppressed)
timeoutMaximum seconds to wait for a response from the backend. Zero means no timeout.Non-negative integer0 (no timeout)
enable-cacert-checkEnables libcurl CA root certificate verification. Only meaningful for HTTPS URLs.true, falsefalse
enable-ssl-verifyhostVerifies that the server hostname matches the certificate's CN or SAN.true, falsefalse
ssl-cert-pathPath to the PEM-format client public certificate for mutual TLS. Use with ssl-key-path.Absolute file pathNone
ssl-key-pathPath to the PEM-format client private key for mutual TLS. Use with ssl-cert-path.Absolute file pathNone
ssl-key-passwordPassphrase for the private key specified in ssl-key-path.StringNone
ssl-versionForces a specific TLS/SSL protocol version.SSLv3, TLSv1libcurl auto-negotiates
ssl-cacert-filePath to a PEM-format CA certificate used to verify the peer. Requires enable-cacert-check to be true.Absolute file pathNone
cookie-filePath to a file for storing and sending HTTP cookies across requests.Absolute file pathNone
bind-localSource IP address or interface to use for outbound requests.IP address stringSystem default
use-dynamic-urlEnables FreeSWITCH channel variable expansion in the gateway-url value at request time. When enabled, the five core request fields (hostname, section, tag_name, key_name, key_value) are added to the variable set available for expansion.true, falsefalse
enable-post-varRestricts which POST fields are sent. Each instance names one variable to include. Without any enable-post-var parameters, all available fields are sent.Variable name stringAll fields sent
response-max-bytesMaximum response body size in bytes that FreeSWITCH will accept. Responses exceeding this limit are discarded.Non-negative integer1048576 (1 MB)

The HTTP Request

mod_xml_curl uses libcurl to contact the configured gateway-url. By default the request is HTTP POST with Content-Type: application/x-www-form-urlencoded. The User-Agent header is freeswitch-xml/1.0.

The request body (or query string, when using non-POST method) contains the following fields, plus any additional FreeSWITCH channel or event variables in scope at the time of the lookup:

FieldDescription
hostnameThe FreeSWITCH switch name (the switch_name value from switch.conf.xml, or the system hostname if not configured)
sectionThe section being requested (e.g., directory, dialplan)
tag_nameThe XML tag being searched (e.g., domain, context)
key_nameThe attribute name used for the lookup (e.g., name)
key_valueThe attribute value being looked up (e.g., the domain name or context name)

When enable-post-var parameters are present, only the named variables and the five core fields above are included.

When method is set to any value other than post, all fields are appended to the URL as a query string and the request is issued using that method.

FreeSWITCH follows up to 10 HTTP redirects.


The HTTP Response

Found Response

The backend must return HTTP 200 with a body that is a valid FreeSWITCH XML section document. The document must begin with a <document> root element containing a <section> element whose name attribute matches the requested section.

Example response for a directory lookup:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
<section name="directory">
<domain name="example.com">
<users>
<user id="1000">
<params>
<param name="password" value="correcthorsebatterystaple"/>
</params>
<variables>
<variable name="toll_allow" value="domestic"/>
</variables>
</user>
</users>
</domain>
</section>
</document>

Any HTTP status code other than 200 causes FreeSWITCH to discard the response and log an error.

Not-Found Response

When the backend cannot find the requested entry, it must return HTTP 200 with a not-found result document. FreeSWITCH checks for this structure and treats it as a miss, allowing the lookup to fall through:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
<section name="result">
<result status="not found"/>
</section>
</document>

Returning an empty body or a non-200 status code is not equivalent to a not-found result and will produce log errors.


Loading mod_xml_curl Early

mod_xml_curl loads in the normal module load sequence defined by autoload_configs/modules.conf.xml. If mod_xml_curl is bound to the config section and another module that loads at startup reads its configuration from that section, a race condition occurs: the module loads before mod_xml_curl is ready to serve its configuration.

To load mod_xml_curl before any other modules, add it to autoload_configs/pre_load_modules.conf.xml:

<configuration name="pre_load_modules.conf" description="Modules">
<modules>
<load module="mod_xml_curl"/>
</modules>
</configuration>

Modules listed in pre_load_modules.conf.xml are loaded before the main module list is processed. Do not duplicate the entry in modules.conf.xml; list mod_xml_curl in only one file.


Behavior with Static Files

A binding to a section completely replaces static-file lookup for that section. FreeSWITCH does not fall back to disk files when mod_xml_curl returns a valid not-found document. The backend is the sole authority for every lookup in a bound section.

Sections not listed in any binding's bindings attribute continue to be served from static files as normal.


Debug Mode

mod_xml_curl exposes an API command to retain temporary response files on disk for inspection instead of deleting them after each fetch:

freeswitch> xml_curl debug_on
freeswitch> xml_curl debug_off

When debug mode is on, each response is written to a .tmp.xml file in the FreeSWITCH temp directory and the path is printed to the console log. Use debug_off to resume normal cleanup.


Part 2: HTTAPI

HTTAPI Overview

mod_httapi is a dialplan application. Instead of statically configuring call flow in the dialplan, the operator points a call at the httapi application with a URL. For each step of the call, FreeSWITCH POSTs the current session state to that URL and expects an XML document in response. The document contains a <work> element whose child elements are verbs that instruct FreeSWITCH what to do next: play a file, collect digits, transfer the call, hang up, and so on. When the verb completes, FreeSWITCH POSTs again, creating a request-response loop until the call ends.

This is distinct from XML Curl. XML Curl serves static configuration and directory data to FreeSWITCH at lookup time; it is not involved in live call control. HTTAPI drives call control step by step over HTTP during a live session.

mod_httapi is included in the default FreeSWITCH build and is loaded automatically when present in autoload_configs/modules.conf.xml.


Enabling mod_httapi

Confirm that mod_httapi is listed in autoload_configs/modules.conf.xml:

<load module="mod_httapi"/>

The module reads its configuration from autoload_configs/httapi.conf.xml on load.


httapi.conf.xml

The configuration file has three top-level sections under the <configuration> root: <settings> for global parameters, <profiles> containing one or more <profile> elements, and within each profile: a <params> block for HTTP connection settings, a <permissions> block controlling what the backend is allowed to do, and optional <conference> and <dial> blocks for defaults used by those verbs.

<configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
<settings>
<param name="debug" value="false"/>
<param name="file-cache-ttl" value="300"/>
<param name="file-not-found-expires" value="300"/>
</settings>
<profiles>
<profile name="default">
<conference>
<param name="default-profile" value="default"/>
</conference>
<dial>
<param name="context" value="default"/>
<param name="dialplan" value="XML"/>
</dial>
<permissions>
<permission name="set-params" value="true"/>
<permission name="execute-apps" value="true">
<application-list default="deny">
<application name="info"/>
<application name="hangup"/>
</application-list>
</permission>
<permission name="dial" value="true"/>
<permission name="conference" value="true"/>
</permissions>
<params>
<param name="gateway-url" value="http://example.com/httapi"/>
</params>
</profile>
</profiles>
</configuration>

Global Settings

These parameters appear in the <settings> element and apply to all profiles.

ParameterPurposeAccepted ValuesDefault
debugPrint the full XML response to the console on every request.true, falsetrue (in sample config; use false in production)
file-cache-ttlSeconds before re-checking the HTTP server to see whether a remotely fetched audio file has changed.Non-negative integer300
abs-file-cache-ttlAbsolute maximum seconds a cached file is kept regardless of server headers.Non-negative integer0 (disabled)
file-not-found-expiresSeconds to cache a 404 response for a remote audio file before retrying. Set to -1 to cache indefinitely.Integer300

Profile Params Reference

Parameters inside a profile's <params> block configure the HTTP connection used when contacting the backend for call control. All are optional except gateway-url.

ParameterPurposeAccepted ValuesDefault
gateway-urlURL that FreeSWITCH POSTs call state to. Can be overridden per-call by passing a URL as application data.HTTP or HTTPS URLNone (required)
gateway-credentialsHTTP authentication credentials.username:password stringNone
auth-schemeAuthentication scheme when gateway-credentials is set. Prefix with = to force the exact scheme rather than allowing negotiation.basic, digest, NTLM, GSS-NEGOTIATE, anybasic
methodHTTP method. When absent or set to post, fields are sent as a POST body. Any other value causes parameters to be appended as a query string using that method.post, or any HTTP method stringPOST
disable-100-continueSuppresses the Expect: 100-continue header when true.true, falsetrue (header suppressed)
timeoutMaximum seconds to wait for the backend response.Non-negative integer0 (no timeout)
connect-timeoutMaximum seconds to wait when establishing the TCP connection.Non-negative integer0 (no timeout)
enable-cacert-checkEnables libcurl CA certificate verification for HTTPS.true, falsefalse
enable-ssl-verifyhostVerifies that the server hostname matches the TLS certificate.true, falsefalse
ssl-cert-pathPath to the PEM client public certificate for mutual TLS.Absolute file pathNone
ssl-key-pathPath to the PEM client private key for mutual TLS.Absolute file pathNone
ssl-key-passwordPassphrase for the private key in ssl-key-path.StringNone
ssl-versionForces a specific TLS protocol version.SSLv3, TLSv1libcurl auto-negotiates
ssl-cacert-filePEM CA certificate to verify the peer. Requires enable-cacert-check true.Absolute file pathNone
cookie-filePath to a file for storing and sending HTTP cookies across requests.Absolute file pathNone
bind-localSource IP address for outbound HTTP requests.IP address stringSystem default
enable-post-varRestricts which POST fields are sent. Each instance names one variable to include. Without any enable-post-var params, all available fields are sent.Variable name stringAll fields sent
user-agentHTTP User-Agent header value.Stringmod_httapi/1.0

Permissions Reference

Permissions are child <permission> elements of the <permissions> block inside a profile. They control what actions the HTTP backend is allowed to request. Setting name="all" value="true" enables everything; name="none" value="true" disables everything. Individual permissions override all/none when listed after them.

PermissionDefaultWhat it allows
set-paramstrueBackend can set named parameters in the <params> section of a response, which are then included in subsequent POSTs.
set-varsfalseBackend can set FreeSWITCH channel variables via a <variables> section in the response. Supports an optional <variable-list> child to allowlist or denylist specific variable names.
get-varsfalseBackend can request channel variable values using the <getVariable> verb. Supports an optional <variable-list> child.
extended-datafalseFull channel event data (caller profile and all channel variables) is included in every POST, not just on the first request.
execute-appsfalse (vanilla httapi.conf.xml sets it true)Backend can execute FreeSWITCH dialplan applications using the <execute> verb. Requires an <application-list> child element to define which applications are allowed or denied; the default attribute on <application-list> sets the list's default policy (allow or deny).
expand-vars-in-tag-bodyfalseFreeSWITCH expands channel variable references found in verb tag body text before executing the verb. Supports <variable-list> and <api-list> children to restrict which variables and API functions can be expanded.
dialtrueBackend can use the <dial> verb to transfer the call or bridge to a destination.
dial-set-contextfalseBackend can override the dialplan context used by <dial>. Enabling this implicitly enables dial.
dial-set-dialplanfalseBackend can override the dialplan module used by <dial>. Enabling this implicitly enables dial.
dial-set-cid-namefalseBackend can override the caller ID name via the caller-id-name attribute of <dial>. Enabling this implicitly enables dial.
dial-set-cid-numberfalseBackend can override the caller ID number via the caller-id-number attribute of <dial>. Enabling this implicitly enables dial.
dial-full-originatefalseBackend can supply a full originate string (with /) in the <dial> verb body, causing FreeSWITCH to bridge the call directly rather than performing a session transfer. Enabling this implicitly enables dial.
conferencetrueBackend can use the <conference> verb to drop the call into a conference room.
conference-set-profilefalseBackend can override the conference profile via the profile attribute of <conference>. Enabling this implicitly enables conference.

The httapi Dialplan Application

Application Syntax

The httapi application is invoked from the dialplan like any other application:

<action application="httapi" data="http://example.com/ivr/start"/>

The data field accepts:

  • A plain URL: FreeSWITCH uses the default profile and contacts that URL.
  • A URL with inline parameters in curly-brace notation: {param=value,param2=value2}http://example.com/ivr/start. The key httapi_profile selects which profile to use.
  • No data: FreeSWITCH uses the default profile's gateway-url.

The profile can also be selected by setting the channel variable httapi_profile before invoking the application.

Fields Posted to the Backend

FreeSWITCH sends an HTTP POST with Content-Type: application/x-www-form-urlencoded to the configured URL. The following fields are always present:

FieldDescription
session_idThe FreeSWITCH session UUID for this call.
hostnameThe FreeSWITCH switch name.
urlThe current gateway URL being requested.
exitingSet to true on the final POST made when the call ends (sent during the reporting state handler). Absent on all other requests.

When a call is first handled, caller profile fields are added under the Caller- prefix (for example, Caller-Caller-ID-Name, Caller-Destination-Number). When extended-data is enabled, all channel variables are included on the first request, and optionally on every subsequent request if full_channel_data_on_every_req is set in the backend's <params> response.

Parameters set by the backend in a <params> response block (see Section 13.3) are carried forward and re-POSTed on every subsequent request in the session, making them available as persistent state across the request-response loop.

When enable-post-var entries are configured in the profile, only the named variables (plus the core fields above) are included.

Response Document Structure

The backend must return HTTP 200 with Content-Type: text/xml. The document envelope is:

<?xml version="1.0" encoding="UTF-8"?>
<document type="xml/freeswitch-httapi">
<params>
<!-- persistent key-value pairs re-posted on every subsequent request -->
<mykey>myvalue</mykey>
</params>
<variables>
<!-- channel variables to set (requires set-vars permission) -->
<some_variable>value</some_variable>
</variables>
<work>
<!-- one or more verb elements executed in order -->
<playback file="http://example.com/audio/welcome.wav" loops="1"/>
<hangup/>
</work>
</document>

The <params> and <variables> sections are optional. The <work> section is required; if it is absent or empty, FreeSWITCH logs a warning and ends the loop.

Verbs inside <work> are processed in document order. Processing stops when a verb returns a non-success status (for example, <hangup> or <break>), or when the channel is no longer active.

Most verbs accept an action attribute that redirects subsequent requests to a different URL for that step, and a temp-action attribute that redirects only the immediately following request.

Response Verb Reference

VerbEffectKey Attributes
<playback>Plays an audio file or prompt to the caller. Supports digit collection via <bind> child elements.file (URL or path), loops, name (param name for collected input), action (URL for next step), error-file, digit-timeout, input-timeout, terminators, asr-engine, asr-grammar
<say>Speaks text using the FreeSWITCH say engine (text-to-speech via language modules).language, type, method, text (or tag body), gender, plus digit-collection attributes
<speak>Speaks text using a TTS engine directly.engine, voice, text (or tag body), plus digit-collection attributes
<pause>Waits silently for the specified duration.milliseconds
<record>Records caller audio to a file or URL, then submits it to the backend.file (destination filename or HTTP URL), name (param name), action, beep-file, error-file, digit-timeout, terminators, <bind> children
<recordCall>Records the entire call (both legs) to a file; the recording runs in the background until the call ends.name, limit (seconds), action (URL to notify when recording completes)
<execute>Executes a FreeSWITCH dialplan application. Requires execute-apps permission and the application must pass the <application-list> policy.application, data (or tag body)
<dial>Transfers the call to a dialplan destination or bridges it. Requires dial permission. The context and dialplan attributes require dial-set-context/dial-set-dialplan. Full originate strings require dial-full-originate.context, dialplan, caller-id-name, caller-id-number; destination in tag body
<conference>Drops the call into a conference room. Requires conference permission.profile (requires conference-set-profile), pin, flags; room name in tag body
<sms>Sends an SMS message from the caller's number.to (destination number); message text in tag body
<answer>Answers the call (transitions channel to answered state).is-conference (true sets the conference flag)
<preAnswer>Pre-answers the call (early media).None
<ringReady>Sends ring indication without answering.None
<hangup>Hangs up the call and ends the HTTAPI loop.cause (FreeSWITCH hangup cause string, or tag body); defaults to NORMAL_CLEARING
<getVariable>Retrieves a channel variable value and adds it to the params POSTed on the next request. Requires get-vars permission.name, permanent (true persists across the session; false sends only on the next request)
<log>Writes a message to the FreeSWITCH console log.level (FreeSWITCH log level string), clean (true omits location prefix)
<voicemail>Accesses the voicemail application for the specified user.profile, domain, id, check (boolean), auth-only (boolean)
<continue>No-op. Continues to the next verb or re-requests the current URL.None
<break>Stops processing the current <work> block without hanging up.None

Worked Example

The following dialplan extension routes an inbound call to the HTTAPI application using the default profile:

<extension name="httapi-ivr">
<condition field="destination_number" expression="^5000$">
<action application="httapi" data="http://example.com/ivr/welcome"/>
</condition>
</extension>

The backend at http://example.com/ivr/welcome receives the POST and returns:

<?xml version="1.0" encoding="UTF-8"?>
<document type="xml/freeswitch-httapi">
<params>
<step>1</step>
</params>
<work>
<answer/>
<playback file="http://example.com/audio/welcome.wav" name="choice" action="http://example.com/ivr/menu" input-timeout="5000" digit-timeout="2000">
<bind action="http://example.com/ivr/sales">1</bind>
<bind action="http://example.com/ivr/support">2</bind>
<bind action="http://example.com/ivr/operator">0</bind>
</playback>
</work>
</document>

FreeSWITCH answers the call, plays welcome.wav, and waits for a digit. When the caller presses 1, FreeSWITCH POSTs to http://example.com/ivr/sales with the session state including step=1 from the <params> block and the collected digit. If no digit is collected within the input timeout, FreeSWITCH POSTs to the action URL (http://example.com/ivr/menu). The <params> value step=1 is carried forward on all subsequent requests in the session.