Appearance
Callback for inbound fax
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Overview
The SignalWire Fax API allows you to have full event monitoring through the usage of status callbacks. The below example will demonstrate how to configure your SIgnalWire phone number through XML bins to receive a fax and alert your own server of fax events that are occurring in your SignalWire Space.
How To Run the Application
Configuring Your Number
In order to achieve this via XML, you will have to navigate to the cXML/LaML section on the left side of your SignalWire Dashboard and under Bins select + New to create a new LaML Bin.
The new cXML bin creation dialog
Creating a new cXML bin
We then need to enter in the XML language for receiving a fax into our LaML Bin. The below code is taken from the Receive a Fax in Compatibility XML example.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Receive action="https://YOUR_URL_EXPECTING_A_CALLBACK."/>
</Response>Enter in the above XML in a new cXML bin.
Lastly, we must associate a phone number to the webhook created via a LaML bin. To do this, we must navigate to Phone Numbers on the left side of your SIgnalWire Dashboard. Click on a number and select Edit Settings.
Note that we set ‘Accept Incoming Calls As:’ Fax, We ‘Handle Faxes Using:’ LaML Webhooks, and then paste in our LaML Bin URL.
Ensure that “Accept Incoming Calls As” is set to “Fax” when working with faxes.
Testing your Configured Fax Number
Congratulations, your phone number is now configured! It is time to test your phone number by Sending a Fax to your fax-receiving phone number.
Once your fax has been received, you should receive a payload to your actionURL that looks similar to the one below. Feel free to read up more on Request Parameters and Additional Callback Parameters for more information on the data being sent.
FaxSid: ████████████-████████████-████████████-████████████-████████████
AccountSid: ████████████-████████████-████████████-████████████-████████████
From: +████████████
To: +████████████
RemoteStationId: +████████████
FaxStatus: received
ApiVersion: v1
NumPages: 2
MediaSid: ████████████-████████████-████████████-████████████-████████████
MediaUrl: https://files.signalwire.com/████████████/████████████/████████████/████████████.pdf
Timestamp: 2022-01-13T21:54:46ZAdditional Information on Callback Authentication
When using SignalWire for Inbound Fax, the callback can be made secure. Unfortunately, there is no set list of IP addresses that SignalWire can provide as they change frequently. That being said, there are a couple of ways to work around this.
As an alternative method, callbacks support basic authentication as well, for example, https://user:pass@callback-url.com. You can also view and allow SignalWire IPs in real-time by running a specific script. However, as stated before, they change frequently, so keep your eye on them.
Wrap Up
This mini-guide walked through how you can configure your SignalWire phone number to receive incoming faxes, and also opened the door to a variety of other monitoring methods through status callbacks. Each significant event that occurs will be relayed back to your actionURL so that you can keep an eye on your traffic without having to keep an eye on your SignalWire Dashboard.
Additional Resources:
- Receive a Fax in Compatibility XML example
- Fax Request Parameters
Sign Up Here
If you would like to test this example out, create a SignalWire account and Space.
Please feel free to reach out to us on our Community Discord or create a Support ticket if you need guidance!
Common webhook errors
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Below are some examples of common errors that you might encounter when using webhooks. If you are unable to resolve your issue, you can always reach out to our Support Team and provide them with a resource SID so that they can locate the call/message record and offer assistance.
HTML retrieval error (error code 11200)
HTML Retrieval Errors happen when there is a failure to retrieve the contents of the URL in your webhook. This indicates that SignalWire tried to reach your URL but did not receive a response before the connection timed out. Our current timeouts are 2 seconds for Connect and 5 seconds for Read, and we retry twice once the connection times out.
SignalWire automatically retries HTTP retrieval requests. If it’s an action type of webhook, SignalWire won’t attempt a retry but will go to the fallback URL (on inbound calls, can specify an action URL and a fallback URL). If it’s a status callback webhook, SignalWire will retry three times, and back off slightly between each attempt. So the second one will retry very quickly, and for the third retry, the system will wait a few seconds.
If you only experienced this error temporarily only to see the same webhook work successfully later, your web server was likely temporarily unavailable or experiencing a network outage. If this issue is persisting, we recommend taking a look at your systems to verify there are no processes or queries taking too long to return.
Document parse error (error code 12100)
The SignalWire Compatibility APIs and SignalWire Compatibility XML both require all documents to be properly formatted as XML in order for SignalWire to be able to read and parse them. If you receive a document parse error, something in your code is preventing SignalWire from parsing the XML document.
If you are using the Compatibility API in any of our available SDKs, make sure that your code is returning proper XML. You can convert response to XML or for an easier method, convert response to string. For example, this is the Python SDK returning response as a string:
@app.route('/open', methods=['GET', 'POST'])
def inBusinessHours():
response = VoiceResponse()
dial = Dial(record='true')
dial.number('+12342556182')
response.append(dial)
return str(response)This results in proper XML:
<?xml version="1.0" encoding="UTF-8"?><Response><Dial record="true"><Number>+12342556182</Number></Dial></Response>You can also check the following:
- Make sure your root element is
<Response> - XML is case sensitive - make sure your elements are appropriately named.
Fatal protocol violation (error code 11251)
This error occurs when SignalWire couldn’t connect to the server the way it was intended/asked to. In most cases, the correct way to fix this is to switch to whichever protocol you did not originally use. Usually, this happens when you need to use HTTPS, but you used HTTP. However, it can happen the other way around as well. Double-check to make sure that you have spelled the protocol correctly and that the protocol you entered is supported.
Too many redirects (error code 111215)
This error can occur when the request has been redirected too many times in a row and has been flagged for potentially being in a loop. SignalWire accepts and will follow up to 3 redirects for a webhook before throwing this error. If you get this error, double-check the logs and your code to make sure you have not forgotten to create a stop condition preventing an infinite loop.
502 bad gateway (error code 11200)
If you see a 502 Bad Gateway error, this may mean that SignalWire’s internal server had trouble retrieving content from the website or was unable to access it. The request must contain a valid Content-Type. SignalWire may also have had problems resolving the DNS name to an IP address or issues with the network connection.
- Confirm that the URL that you used is not protected by any HTTP Authentication, making it impossible for SignalWire to reach it.
- Double check to make sure that your web server is up, responsive, and publicly accessible.
- Make sure your web server allows HTTP POST requests to static resources (if the URL refers to .xml or .html files)
- Check the ping times and packet loss between your web server and
https://www.signalwire.com
XML response body exceeds size limit (error code 11750)
This error indicates that the response body to SignalWire’s request is larger than 64 kB. This can be caused if the XML that you are serving is larger than 64 kB or if you are serving non-XML content in your response (e.g. error message output).
- Confirm that you are serving XML in your response to SignalWire’s request.
- Confirm that you are including the following header in your XML response:
<?xml version="1.0" encoding="UTF-8"?>. - Limit your XML response to 64 kB or less.
- If your XML includes a Play verb, check the encoding and MIME type to ensure they are supported.
- Check that your XML is formatted properly.
- Check to see if your application is throwing errors.
- If you are trying to send a
200response in a status callback, use an empty XML response<Response/>
Gathering user input
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
This article explains how to gather input from users using cXML Scripts.
In general, there are three kinds of input that you could want to gather: keypad input, text input via speech recognition, and audio recordings. In this article we are going to focus on keypad input and speech recognition.
cXML for Gathering Input
We are going to define the call handling instructions in a cXML Script hosted on SignalWire.
New Dashboard
Legacy Dashboard
To create a cXML script, navigate to the Resources tab in your SignalWire Dashboard, click + Add New, and select cXML Script.
Which Dashboard version am I using?
New Dashboard: You have a Resources tab in the left sidebar.
Legacy Dashboard: You have separate tabs for SIP, LaML, RELAY, etc.
Paste the following XML content in your new cXML Script:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather
input="speech dtmf"
action="https://example.com/my-webhook"
timeout="5"
numDigits="5"
hints="one two three four five six seven eight nine">
<Say>
Welcome! Please enter or say your account number.
</Say>
</Gather>
<Say>No input detected.</Say>
<Hangup />
</Response>We used the <Gather> verb to gather input as soon as the call starts. We set five attributes:
input, which specifies which kind of input we want to gather (in our case, both speech and DTMF keypad input);action, which specifies the URL to fetch when the input has been collected (the URL should return a new cXML document to execute);timeout, i.e., the number of seconds of silence or inaction that denote the end of caller input;numDigits, which indicates the number of digits to collect via DTMF keypad input; andhints, an optional list of words to help the speech recognition algorithm.
Within the <Gather> verb, we nested <Say> in order to play some instructions.
When input gathering completes, the script will fetch the URL specified in action and will execute it. If, instead, no input is detected within the timeout, then the following instructions keep executing: in our case, we say “no input detected” and hang up.
Reading the User Input
When fetching the Gather’s action URL, SignalWire includes some parameters such as:
From: the caller’s numberTo: the callee’s numberDigits: DTMF digits gathered from the user, if anySpeechResult: Speech input gathered from the user, if any- …more
With a custom web server, you can read these parameters and, depending on their value, emit a different cXML document to execute. You can easily test this with sites such as https://webhook.site, which allow you to view the details for incoming webhook requests.
Assigning the Script to a SignalWire Phone Number
The final step is to configure one of your SignalWire phone numbers to answer calls using the cXML Script we just created. You can do that from the “Phone Numbers” section:
Assign Resource.
Add mustache template parameters to cXML responses
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Certain incoming message information can be extracted and added to a cXML application response by using parameters. Mustache templates are parameters in curly braces, like , which output the given value of a parameter.
Mustache template variables are used to make your cXML application dynamic. Variables can be parameters of the HTTP payload for the request, or a query string variable you set in your webhook (https://yourspace.signalwire.com/laml-bins/your-xml-bin-id?YOURVARIABLE=123). Either method of Mustache variable declaration will work well in translating values into your cXML application.
Which parameters work as mustache templates?
| Type of Parameter | Default Parameter Names |
|---|---|
| General Parameters | , , (equivalent to Project ID), |
| Messaging Specific | , , (number of segments), (number of media files) |
| Voice Specific | , , , |
| Inbound Domain Apps | |
Messaging examples
In looking at how to forward an incoming message to another number, the mustache parameters and can be retrieved and stored as variables to then be utilized in forwarding the message.
Following a similar format to the above article to show all the available message parameters, a cXML application/webhook can use the below XML:
<Response>
<Message to='+1XXXXXXXXXX' from='+1XXXXXXXXXX'> Message received with the following details:
MessageSid:{{MessageSid}}
SmsSid:{{SmsSid}}
AccountSid:{{AccountSid}}
From:{{From}}
To:{{To}}
Body:{{Body}}
Number of Media:{{NumMedia}}
Media:{{Media}}
Number of Segments: {{NumSegments}}
</Message>
</Response>Voice examples
The following XML will result in all calls being forwarded to the specified number.
Notice what is declared in the third line, the parameter is being assigned as the value of the callerId property.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial callerId="{{From}}">
<Number>+19074864171</Number>
</Dial>
</Response>In this case, we are using the value so that the caller ID for the forwarded call echoes the original calling number. In this way, your calls will have the correct caller identification.
Using custom mustache templates
A custom mustache template can be created by adding the mustache variable within a cXML application/webhook URL or a SIP address.
With cXML applications or webhooks
Using the below XML in an XML Bin/Webhook, the Say verb will state the value of . We will set this value in our webhook by amending the end of the URL. We can do this by following the syntax of ?YOURVARIABLE=YOURVALUE. Assuming we want to make the value of the variable equal to Something, our XML Bin URL or Webhook URL will look similar to: https://YOURSPACE.signalwire.com/laml-bins/xxxx-xxxx-xxxx-xxxx-xxxx?YOURVARIABLE=Something
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>{{YOURVARIABLE}}</Say>
</Response>Mustache variable
Remember that after copying and pasting the XML bin URL into the phone setting options, manually add the mustache variable to the end of the URL to create and declare your custom variable.
For example, to set the variable to 123, we would enter it as ?YOURVARIABLE=123 at the end of your URL.
With domain applications
When calling a Domain Application that handles incoming calls with a LaML Webhook, you can still pass the custom mustache variable in the SIP URL, much like you would with Webhooks, by adding ?YOURVARIABLE=YOURVALUE at the end: sip:123456789@YOURSPACE-EXAMPLE.dapp.signalwire.com?YOURVARIABLE=Something.
Assuming the Domain App calls the same XML Bin/Webhook as the one we outlined above, we just need to prefix the variable name with SipHeader_X-, making the end result SipHeader_X-YOURVARIABLE.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>{{SipHeader_X-YOURVARIABLE}}</Say>
</Response>This is particularly useful in BYOC setups where you wish to send calls to SignalWire and adjust the FROM number on the fly using a custom variable:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial callerId="{{SipHeader_X-FROM_NUMBER}}">
<Number>{{SipUser}}</Number>
</Dial>
</Response>If you wished to call +1111111111 from +123456789, your SIP URL would look like this:
sip:+1111111111@YOURSPACE-EXAMPLE.dapp.signalwire.com?FROM_NUMBER=%2B123456789
URL encoding
Notice the encoded + in the FROM_NUMBER, as %2B? Since it is a reserved character, we need to encode it when passing it in the URL. To learn more about URL Encoding, please read this page.
Further references
For more information about SignalWire cXML and Mustache templates, please review the cXML overview.
For further information about the functions of mustache templates, please read the Mustache manual.
Receiving your first SMS
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
To send your first SMS, you needed a phone number, API credentials, and a REST API call.
Receiving messages works a bit differently: when using the cXML API, you need to prepare an XML file with instructions on what to do after a message is received on a given phone number.
Receiving text messages from users using cXML applications.
Using cXML
Let’s write our first cXML script. We will host this one on SignalWire, but you can use your own server if you want, which allows you to generate scripts dynamically.
1
Create a cXML Script
Navigate to the “Resources” tab from the sidebar and create a new Resource. There, select the resource type of “Script”, and pick “cXML script”.
Create a new cXML script.
What's a Resource?
Resources are the building blocks of SignalWire applications. They include AI Agents, SWML Scripts, cXML Scripts, SIP Endpoints, and more.
2
Paste the script
There, you can paste the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>Hello from SignalWire!</Message>
</Response>This script will reply with a fixed message to any incoming SMS. Alternatively, you can forward the message to a different number using this script:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message to="+1xxxxxxxxxx">
You received a message from: {{From}} to SignalWire number: {{To}}.
The message is: "{{Body}}"
</Message>
</Response>Consult the full cXML technical reference to write more complex scripts.
Copy the Request URL of the script you just created, then read the next section to configure a number to handle messages using that bin.
3
Assign a phone number
The last step is connecting a number to the bin. If you don’t have a phone number yet, make sure to buy one. You will need at least one number to receive messages.
Navigate to the “Phone Numbers” section of your Dashboard and open the settings for the number you want to configure. There, assign the cXML script you just created to handle messages.
Open the settings for the number.
4
Send a test SMS
Try sending a message to the configured phone number. If everything has gone according to plan, after a few seconds you’ll receive an automated reply.
5
Next steps
We have shown how to receive a text message and perform follow-up actions.
This example used the Compatibility API, and in particular cXML Scripts, to receive a message.
Ensure message delivery
If you are sending messages to the US from a 10DLC number, you must register your traffic with the Campaign Registry. Otherwise, the carriers will not deliver your messages. Please see Campaign Registry - Everything You Need To Know for more information.
In the Legacy Dashboard
No Resources tab? Your SignalWire Space is on the Legacy Dashboard. Expand the section below to view this guide’s Legacy instructions.
These instructions are for the Legacy Dashboard. If your SignalWire Space has been updated to the new Dashboard, see the main instructions above.
cXML Scripts in the Legacy Dashboard
Follow the above guide, with the following changes:
Create a cXML Script
Navigate to the “LaML/cXML” section of your Dashboard and create a new cXML bin with the content above.
Create a new cXML script.
Assign a phone number
Open the settings for the number you want to configure. Under “Messaging Settings”, choose to handle messages using “LaML Webhooks”. In the dropdown, select the cXML script you just created.
Which Dashboard version am I using?
New Dashboard: You have a Resources tab in the left sidebar.
Legacy Dashboard: You have separate tabs for SIP, LaML, RELAY, etc.
Sending your first SMS
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Let’s get you started by sending your first SMS. In this guide we are going to use Compatibility APIs: if you are migrating from a different provider, these APIs will likely be very similar.
Your API Credentials
To send messages, make phone calls, and programmatically control video rooms, you need API credentials. Find these in your SignalWire Space, within the “API” tab on the left sidebar. If this is the first time you are using API credentials, you will need to create an API token.
The API page.
You can find your Project ID, Space URL, and API Token from the API tab in your SignalWire Space. Make sure your token has the necessary scopes enabled.
In the “API” section you will find all the information you’ll need. In particular:
- Your Project ID (e.g.,
7b981d06-fa9e-XXXX-XXXX-XXXXXXXXXXXX) - Your Space URL (e.g.,
yourname.signalwire.com) - Your API Token (e.g.,
PTda745ebXXXXXXXXXXXXXXXXXXXXXX)
The API Token is confidential: keep it private.
Obtaining a number
Log in to your SignalWire Space. From the Phone Numbers section, you can buy a new phone number if you don’t have one already. You will need at least one number to send and receive messages.
Make sure the number you are buying supports SMS messaging, and you’re good.
You can also port an existing phone number from another provider.
Sending a message
Let’s see how to send your first message. You have a few different options: you can trigger an HTTP request yourself (using cURL or the dedicated functions in your preferred programming language), or you can use one of our SDKs. We’ll show examples for both methods.
Installation
cURLPythonPHP
> Refer to https://everything.curl.dev/install
```bash title="Node.js"
```bash
npm install @signalwire/compatibility-apiOnce your environment is set up, you can proceed with your first API call.
Sending
To send a message, you can use the Create a Message endpoint from our Compatibility APIs.
If you are sending messages to the US from a 10DLC number, you must register your traffic with the Campaign Registry. Otherwise, the carriers will not deliver your messages. Please see Campaign Registry - Everything You Need To Know for more information.
Let’s see how to send a message with a REST API call:
cURLJavaScriptPythonPHP
curl "https://$SPACE_URL/api/laml/2010-04-01/Accounts/$PROJECT_ID/Messages" \
-X POST \
-u "$PROJECT_ID:$API_TOKEN" \
--data-urlencode "From=$FROM_NUMBER" \
--data-urlencode "To=$TO_NUMBER" \
--data-urlencode 'Body=Hello'Make sure to replace all placeholders with your own values.
This example used the Compatibility API, and in particular the Compatibility REST API, to send a message.
Webhook security
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
When building an application with SignalWire services, you are very likely to use webhooks to exchange information with SignalWire. A webhook is an HTTP(S) request sent to your web application when a key event has occurred, such as an inbound call, inbound message, or a status change. This allows SignalWire to query your web application in order for instructions on what to do next. For example, you might use a webhook to handle an inbound call by reading the instructions in your webhook to play an IVR, route the customer to the right department, and connect them with an agent. You could also use a webhook as a status callback where each status change of a call or message is sent to your web application which might store some instructions for handling emergent errors.
If your application exposes sensitive data or makes changes to your data, then you may want verify that the HTTP requests to your web application are coming from SignalWire, and not a malicious third party. To support that level of security, SignalWire signs every webhook request with a digital HMAC signature.
You can find your personalized signing key in the API Credentials Space of your Dashboard. Click “Show” to display the key and a copy icon to easily copy the signature into your code. Each project in your Dashboard will have its own signing key for verification. You can reset the signing key by clicking the reset button. Please note that it takes about a minute for the new key to be active. You will also be able to see the new key before finalizing the reset request so that you can copy it to wherever it needs to be updated.
The SignalWire Space showing API credentials, including Signing Key
the API Credentials page
Verifying the webhook signature
NodeJs
In your webhook logic, you can include the validateRequest method to check the key include on the request against the value you copy from your Credentials page.
import { RestClient } from "@signalwire/compatibility-api";
app.post("/mywebhook", (req, res) => {
const valid = RestClient.validateRequest(
"XXXXXXXX", // signing key copied from your credentials page
req.headers["x-signalwire-signature"],
"https://example.ngrok.io/mywebhook",
req.body
);
});This method takes as its parameters:
- The signing key copied from your Credentials page.
- Where to find the request signing key in the request header.
- The url the request is calling.
- The included request parameters. This should virtually always be the request body.
If you are using Express for your application, you also have the option of using the middleware shortcut webhook() which doesn’t require any parameters instead of the validateRequest method.
If using a service like ngrok to test locally, it is preferred to use the validateRequest method instead of the webhook() middleware. This is because ngrok will rewrite the request headers and protocol, which will cause the signature to fail validation.
import { RestClient } from "@signalwire/compatibility-api";
app.post("/mywebhook", RestClient.webhook('Signing Key Here'), (req, res) => {
const response = new VoiceResponse();
// application logic
});Python
In your Python Application you can inclued the RequestValidator constructor to check the key include on the request against the value you copy from your Credentials page.
# import the RequestValidator constructor
<Badge type="tip" text="Fresh" />
from signalwire.request_validator import RequestValidator
# Define your endpoint
@app.route("/voice_say", methods=['POST', 'GET'])
def voice_say():
url = "https://example.ngrok.io/voice_say"
token = "xxxxxx" # signing key copied from your credentials page
signature = request.headers.get("x-signalwire-signature")
if(signature == None):
return Response(json.dumps({"message": "unauthorized"}), status=401, mimetype="application/json")
else:
validator = RequestValidator(token)
validate = validator.validate(url, request.form, signature)
if(validate):
r = VoiceResponse()
r.say("This works")
return Response(r.to_xml(), status=200, mimetype="application/xml")
else:
r = VoiceResponse()
r.say("This is wrong")
return Response(r.to_xml(), status=401, mimetype="application/xml")In the above snippet we are doing the following
- Using the
RequestValidatorconstructor while passing the signing key copied from the Credentials page. - If the request is coming from Signalwire, the validator is expecting an header
x-signalwire-signature - The URL the request is calling
- The included request parameters. This should virtually always be the request body.
For Django the Snippet might look a little different like this:
# import the RequestValidator constructor
from signalwire.request_validator import RequestValidator
@csrf_exempt
def home(request):
token = "xxxxxxxx" # signing key copied from your credentials page
validator = RequestValidator(token)
request_valid = validator.validate(
"https://example.ngrok.io/api/home/", # The url the request is calling
request.POST,
request.META.get('HTTP_X_SIGNALWIRE_SIGNATURE', '')
)
if request_valid:
# perform logic Here
else:
# send unauthorized responseIn terms of Django request.POST replaces request.form while request.META.get('HTTP_X_SIGNALWIRE_SIGNATURE) replaces request.headers.get("x-signalwire-signature")
Compatibility API Webhooks
For AI agents: a documentation index is available at the root level at /llms.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
What is a Webhook?
SignalWire APIs allow you to easily send/receive HTTP requests from/to the SignalWire servers. For example, you can easily make an HTTP request to SignalWire in order to Create a Call or Send a Message. However, if you are using SignalWire to handle inbound calls or inbound messages, you will need to use a webhook!
A webhook is an HTTP(S) request sent to your web application when a key event has occurred, such as an inbound call, inbound message, or a status change. This allows SignalWire to query your web application in order for instructions on what to do next.
For example, you might use a webhook to handle an inbound call by reading the instructions in your webhook to play an IVR, route the customer to the right department, and connect them with an agent. You could also use a webhook as a status callback where each status change of a message is sent to your web application which might store some instructions for handling emergent errors.
To learn how to use ngrok for local testing, visit ngrok.com.
Some webhooks require <Response> from your web application, such as the earlier example of the inbound call being routed through an IVR in your application. In this instance, your web application MUST send valid XML to the webhook request, EVEN if the XML response is just an empty <Response> tag. If you do not return valid XML, you will get a document parse error and inconsistent results.
Other webhooks do not require a response, such as the status callback example above. This webhook request only requires an HTTP status code of 200 OK, meaning that the request was successful.
Voice Webhooks
Inbound Voice Call
You will receive an incoming voice call webhook request when you have your webhook configured to handle inbound calls to a number. You can use a webhook that points to your web application hosted on a server or ssh tunnel, or you could also use SWML Scripts as webhooks for serverless code!
Status Callback
When one of your SignalWire phone numbers receives a call or is used to place an outgoing call, you can have asynchronous HTTP requests sent to your server that tell you about the status changes and the call details. You can do this by setting the StatusCallback parameter when placing an outbound call through the API.
SignalWire will send you a status callback when the call is completed unless directed otherwise; you can use the events initiated, ringing, answered, and completed in the StatusCallbackEvent parameter in order to get updates when each of those statuses is reached.
Recording Status Callback
If you are recording your voice calls, you may notice that the recording is not always instantly available due to the need for some audio processing to transcode it into a compressed audio format after the voice call is complete. If you would like to be notified when it’s done, you can use recording status callbacks!
There are a few different statuses your recording could have; in-progress, completed, absent, or failed. When the voice recording is complete, a request will be sent with the completed status and the audio file link within the ‘RecordingUrl’ parameter.
SMS Webhooks
Inbound Message
You will receive an incoming message webhook request when you have your webhook configured to handle inbound messages to a number. You can use a webhook that points to your web application hosted on a server or ssh tunnel, or you could also use SWML Scripts as webhooks for serverless code!
It is also possible to receive an incoming text message, and not send a reply message back to the user. To do that, you can simply use a LaML Bin with an empty response tag for handling inbound messages, as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
</Response>Status Callbacks
Like status callbacks for voice, SMS status callbacks are a way to have updates on the delivery of your messages sent directly to your server! Each time the message status changes (e.g., from queued to sending to sent to delivered), SignalWire will send an update to the URL that you have designated.
You can do this by setting the StatusCallback parameter when sending an outbound message through the API.
SignalWire can’t and won’t show a message as Delivered unless we can 100% confirm delivery to the end network by getting a Delivery Receipt (DLR) from the receiving carrier. A status of Sent means that the message left SignalWire successfully and reached our downstream peer. Once we receive the Delivery Receipt indicating that the message entered the end carrier’s network, it switches to Delivered.
If you see a message with a status of Delivered that did not reach the end handset, feel free to open a support ticket with the message SID for us to escalate the example to our carrier partners to find out why the MNOs (Verizon, AT&T, T-Mobile, etc) marked it as Delivered.
Some carriers may send delayed DLRs, and others may not send DLRs at all. Providers do not currently allow for any DLRs for MMS, so you will only ever see the status Sent.
How to Configure Your Webhook to a SignalWire Number
To access the number settings and configure the webhooks in the SignalWire Space, click on Phone Numbers and find the number to set up.
The Phone Numbers tab of a SignalWire Space showing a list of phone numbers
Purchased Phone Number List page in SignalWire Dashboard
Click on the phone number to be set up and then click to Edit Settings.
In the Edit Settings page, the number can be given a name and set up Voice/Fax & Messaging settings.
Navigate to either the Voice & Fax section or the Messaging section depending on your use case, as you will have to set different webhooks for each.
Voice vs Fax
If you set your phone number to handle incoming calls using Fax, you will NOT be able to also use it for voice. The reverse is also true! You can only use a phone number for voice OR fax, not both.
Select cXML Script for When a Call Comes In to use a cXML script.
GET and POST are two different types of HTTP methods. GET is used for requesting something from a resource, whereas POST is used to send data to a server. If you were using a webhook that pointed towards your web application for handling inbound calls, you would use a POST request. If you were using an XML Bin to <Say> (text to speech) a quick message for your customers to listen to when calling your number, you would use GET.
How to Verify Webhooks
To verify webhooks that originated from SignalWire, SignalWire signs its requests with a digital HMAC security key. You can verify that the security key matches the key documented in your Dashboard’s API Credentials with the validateRequest method:
import { RestClient } from "@signalwire/compatibility-api";
app.post("/mywebhook", (req, res) => {
const valid = RestClient.validateRequest(
"<--Signing Key copied from your credentials page-->",
req.headers["x-signalwire-signature"],
"https://example.ngrok.io/mywebhook",
req.body
);
});This method responds with a boolean value. You can continue your application’s logic in the truth case and return unauthorized for the false case.
Webhook Connection Overrides
SignalWire supports URL fragment parameters that can be added to your webhook URL to override default HTTP callback connection settings on a per-request basis. These overrides allow you to:
- Set longer timeouts to better support intermittent network issues between SignalWire and your server
- Set shorter timeouts to speed up failover to a fallback URL in case of an outage
- Control the total timeout limit for all retries
Supported URL Fragment Parameters
SignalWire allows per-request HTTP callback overrides using URL fragments:
https://your-server.com/endpoint#key=value&key2=value2Parameters
| Parameter | Valid values | Default | Notes |
|---|---|---|---|
Open Timeout (ot) | 100 - 10000 (ms) | 2000 | The timeout in milliseconds SignalWire will wait to establish its TCP connection to your web server. |
Read Timeout (rt) | 100 - 15000 (ms) | 5000 | The amount of time in milliseconds after sending your webhook an HTTP request that SignalWire will wait for the initial HTTP response packet. Also applies to the amount of time SignalWire will wait between individual packets within your HTTP response. |
Total Time (tt) | 100 - 15000 (ms) | 15000 | The total time allowed for all timeouts including retries. If not set, the maximum limit is enforced. |
Examples
Set Open Timeout to 1 second
https://example.com/foo#ot=1000Set Open + Read Timeout to 1 second each
https://example.com/foo#ot=1000&rt=1000