Skip to main content

Introduction to SWML

SignalWire Markup Language (SWML) lets you write Relay applications using simple statements in a YAML or a JSON document. Relay applications are programs connected to the SignalWire network through a real-time WebSocket connection. They can dynamically and interactively use the services offered by SignalWire, including voice calls, video conferences, messaging and chat.

SWML also allows you to instantiate AI agents. AI Agents hold natural conversations with the caller, and perform actions intuitively based on the resources you have specified (like REST API endpoints or SWAIG functions).

On your SignalWire Dashboard, you can configure SMWL scripts to be executed in response to a phone call, through a Relay script, or when a subscriber calls a SWML resource. You can write and store SWML on the SignalWire Dashboard, but you can also serve SWML scripts directly from a web server. You can even respond with dynamically generated SWML scripts.

SWML and Call Fabric Resource Addresses

SWML is the best-supported way to use the new Call Fabric paradigm. To access a Resource, simply reference its Address using this syntax:

space_name.signalwire.com/context/address

Each Resource Address has two components:

  • Context: A identifier that indicates in which context the resource is located.
  • Name: The name is the unique identifier for the resource.

For example, the address for a Subscribers resource named Alice in the public context would be /public/Alice.

Learn more by reading our Introduction to Call Fabric or the guide to Managing Resources.

Writing SWML

SWML documents are written in YAML or its equivalent JSON. The following SWML script will answer a call, play some music, then hang up:

version: 1.0.0
sections:
main:
- answer: {}
- execute:
dest: play_music
params:
to_play: 'https://cdn.signalwire.com/swml/April_Kisses.mp3'
play_music:
- play:
url: '%{params.to_play}'

Let us walk through the components of this script.

version

Every SWML script starts with a version string. Version 1.0.0 is the latest, and the only supported version.

sections

Each SWML script is divided into sections, which are arrays of statements and methods to execute. Methods that don't expect parameters are be mentioned as strings (like answer). Methods with parameters are invoked as objects (like execute and play).

Every SWML script must have the main section, which is where execution starts. You can switch to other sections by using either the execute method or the transfer method. Sections behave like functions when you invoke them with the execute method, but when you invoke a section using transfer, control is transferred to the new section and doesn't return.

In the example above, we have two sections: main and play_music. The main section first answers the call. Then it executes the play_music section which calls the play method.

Variable substitution and expression evaluation

Anything inside a %{ } bracket is considered a JavaScript expression. This can be used to insert variables in SWML. In the example, params passed through the execute method are accessed by%{params.to_play}. If we wanted to lowercase the URL, we could have written %{params.to_play.toLowerCase()}.

AI in SWML

You can use the AI methods to very quickly create flexible AI agents.

version: 1.0.0
sections:
main:
- ai:
post_prompt_url: https://example.com/my-api
prompt:
text: |
You are Franklin's assistant, and your job is to collect messages for him over the phone.
You can reassure that Franklin will get in touch as soon as possible.
Collect the user's name and number if you do not already know it from the caller id.
Start by presenting yourself, then let the contact know that Franklin is not available, then offer to collect a message.
After collecting the message, do not wait for the user to end the conversation: say good bye and hang up the call.
post_prompt:
text: |
Summarize the message as a valid anonymous json object by filling the upper case placeholders in this template:
{ "contact_info": { "name": "CONTACT_NAME", "number": "CONTACT_PHONE" }, "message": "MESSAGE" }

In this example, we instantiate an AI agent with instructions to get messages from the caller. Once the call ends, the post_prompt text will summarize the conversation and HTTP POST it to the post_prompt_url. Check out our AI Santa guide for a complete example.

Serving SWML

From the Dashboard

tip

This use case is described in detail in the Making and Receiving Phone Calls guide.

You can write and save new SWML scripts from the "Relay/SWML" section of your Dashboard. In that section, switch to the tab named SWML Scripts. Once there, you can create a new SWML script:

SignalWire Dashboard with SWML tab open

After you save the SWML, navigate to the Phone Numbers page. Open the settings for a phone number you own (you may have to buy a new one), and configure it to handle incoming calls using the SWML script you just saved.

SignalWire Dashboard's phone number setting screen, selecting a SWML script as call handler.

From a web server

tip

This use case is described in detail in the Handling Incoming Calls from Code guide.

In the phone number settings, when you check the "Use External URL for SWML Script handler?" option, you can set a Web URL that will serve the SWML script. Every time a call comes in (or some other designated event occurs), you'll get a HTTP POST request to the URL with the following JSON parameters:

ParameterTypeDescription
callCall objectContains properties describing the call.
varsany objectContains the list of variables set in the calling SWML. Empty when invoked as a direct response to a call.
paramsany objectContains the list of params set by the calling SWML. Empty when invoked as a direct response to a call.

The following is an example JSON that you might receive as a POST request on your server when a SWML is requested.

{
"call": {
"call_id": "<CALL_UUID>",
"node_id": "<NODE_ID>",
"segment_id": "<SEGMENT_ID>",
"call_state": "created",
"direction": "inbound",
"type": "phone",
"from": "<CALLING PHONE NUMBER>",
"to": "<THE NUMBER ATTACHED TO THE SWML>",
"from_number": "<CALLING PHONE NUMBER>",
"to_number": "<NUMBER ATTACHED TO THE SWML>",
"headers": [],
"project_id": "<YOUR PROJECT ID>",
"space_id": "<YOUR SPACE ID>"
},
"vars": {}
}

The Call Object

The call object is a description of the received call. It will have the following properties:

ParameterTypeDescription
call_idstringA unique identifier for the call.
node_idstringA unique identifier for the node handling the call.
segment_idstringA unique identifier for the segment.
call_statestringThe current state of the call.
directionstringThe direction of this call.
Possible values: inbound, outbound
typestringThe type of call.
Possible values: sip, phone
fromstringThe number/URI that initiated this call.
tostringThe number/URI of the destination of this call.
headersobject[]The headers associated with this call.
headers.namestringThe name of the header.
headers.valuestringThe value of the header.
project_idstringThe Project ID this call belongs to.
space_idstringThe Space ID this call belongs to.

The vars object and the params object will be empty for a new call. If you're executing a remote SWML script using the execute or transfer methods, the vars parameter has a list of the variables declared in the script so far. And the params object has the list of parameters explicitly set by the execute or transfer methods.

You can also reference the properties of call and params objects during the script execution using the variable subtitution bracket like so:

version: 1.0.0
sections:
main:
- play:
url: 'say:%{call.from}'

Further, consider the following SWML script:

# hosted on https://example.com/swml.yaml
version: 1.0.0
sections:
main:
- play:
url: '%{params.file}'
- return: 1

It references params.file in it's play method. If this SWML was invoked as a response to a phone call, it would cause an error as the params object is empty. But if it was hosted on a server and called with the execute or the transfer method, the params object is passed into the SWML.

The SWML above can be invoked as follows:

version: 1.0.0
sections:
main:
execute:
dest: https://example.com/swml.yaml
params:
file: https://cdn.signalwire.com/swml/audio.mp3

From a Relay application

You can also execute SWML from a Relay application. The following is an snippet using the RealTime API.

const { Voice } = require("@signalwire/realtime-api");
const script = `
version: 1.0.0
sections:
main:
- answer: {}
- execute:
dest: play_music
params:
to_play: 'https://cdn.signalwire.com/swml/April_Kisses.mp3'
play_music:
- play:
url: '%{params.to_play}'
`;

const client = new Voice.Client({
project: "<your project token>",
token: "<your project API key>",
topics: ["swml"],
});

client.on("call.received", async (call) => {
try {
await client.execute({
method: "calling.transfer",
params: {
node_id: call.nodeId,
call_id: call.callId,
dest: script,
},
});
} catch (error) {}
});

In this snippet, we are registering an event for every time a call is received to any phone number in your project with the topic "swml". You can set the topics a number is subscribed to from the phone number settings page in the SignalWire Dashboard. Every time a call is received, the SWML script is executed using the client.execute method.