Respond to SMS and Phone Calls using FastEndpoints and Twilio
Time to read: 5 minutes
Twilio's messaging and voice webhooks let you respond to messages and voice calls using TwiML. You can implement these webhooks using different frameworks in ASP.NET Core such as Minimal APIs, MVC, Web APIs, and more. While Minimal APIs are a great way to handle HTTP requests, some developers prefer alternative frameworks such as FastEndpoints.
FastEndpoints is focused on accepting JSON via requests and responding back with JSON, and thus doesn't have a built-in way to respond with TwiML (or XML). In this tutorial, you'll learn how to extend FastEndpoints to respond with TwiML.
Prerequisites
Here’s what you will need to follow along:
- .NET 7 SDK (earlier and newer versions may work too)
- A code editor or IDE (I recommend JetBrains Rider, Visual Studio, or VS Code with the C# plugin)
- A free Twilio account (sign up with Twilio for free)
- A Twilio Phone Number
- The ngrok CLI and, optionally, a free ngrok account
- Git to clone the sample project
You can find the source code for this tutorial on GitHub. Use it if you run into any issues, or submit an issue if you run into problems.
Create a new FastEndpoints project
Open your preferred shell and use the .NET CLI to create a new ASP.NET Core project and add the FastEndpoints
package.
Now, open Program.cs and add the following lines to add FastEndpoints to your app:
This will configure FastEndpoints which will use reflection to find all the endpoints it needs to wire up.
Now, create a new file named MessageEndpoint.cs and add the following code:
This creates a new endpoint that listens for HTTP POST requests at /message and replies with "Ahoy!".
Let's test it out. Start your application using dotnet run
. In the upcoming commands, replace [YOUR_LOCALHOST_URL]
with the localhost URL you can find in the output of dotnet run
.
Open another shell and run either the following cURL command or PowerShell:
You should see the response is "Ahoy!".
Respond with Messaging TwiML
You'll need some of the classes and APIs from the Twilio
and Twilio.AspNet.Core
package to respond with TwiML. Add the packages using the .NET CLI:
To implement the messaging or voice webhook, you'll need to respond with TwiML instead of plain-text. In this particular case, the messaging flavor of TwiML.
Update MessageEndpoint.cs with the following code:
Instead of accepting an EmptyRequest
, the MessageEndpoint
now accepts an SmsRequest
, a class provided by the helper library for ASP.NET. When Twilio sends an HTTP POST request to your endpoint, Twilio encodes the data as a form. This is why inside the Configure
method, the endpoint is configured to accept the application/x-www-form-urlencoded
content-type.
HandleAsync
now generates TwiML that looks like this:
Since there's no built-in way to write XML or TwiML, you could serialize messagingResponse
to a string and use the SendStringAsync
method again. Instead of doing that, I decided to get the XDocument
and save it directly to the HTTP response body for better performance. Finally, to tell FastEndpoints that the response is already handled, the ResponseStarted
is set to true
.
Stop your .NET application by pressing ctrl + c
and then run it again using dotnet run
.
To quickly test this without configuring your Twilio Phone Number, use the following cURL command or PowerShell:
Create a SendTwiML extension method
If you need to have multiple endpoints that respond with TwiML, you wouldn't want to duplicate this code over and over. So you could either create a base class and add a protected SendTwiML
method containing the reusable logic, or create an extension method. Either solution would be fine, but I opted for an extension method, just in case you're already inheriting from another base class.
Create a new file EndpointExtensions.cs and add the following code:
This extension method encapsulates the same logic for writing the TwiML to the response body.
Now back in MessageEndpoint.cs, use the extension method:
Respond with Voice TwiML
You learned how to respond with message TwiML, but to handle voice calls you need to use Voice TwiML. It's time to create another endpoint. Create a new file named VoiceEndpoint.cs, and add the following code:
Stop and start your application again, and then use the following cURL or PowerShell command to test the new endpoint:
The response should look like this:
Note how the AddSpacesBetweenCharacters
method adds spaces in between each character of the phone number. By formatting the response like this, the text-to-speech translation will spell each individual number out instead of saying it as one big number.
Configure the webhooks on your Twilio Phone Number
To see how this would behave in your Twilio application, you'll need to configure the message and voice webhook on your Twilio Phone Number. But before you can do that, you'll need to make your locally running project accessible to the internet. You can quickly do this using ngrok which creates a secure tunnel to the internet for you.
Leave your .NET application running, and run the following command in a separate shell:
ngrok will print the Forwarding URL you'll need to publicly access your local application.
Now, go to the Twilio Console in your browser, use the left navigation to navigate to Phone Numbers > Manage > Active Numbers, and then select the Twilio Phone Number you want to test with.
On the phone number configuration page, locate the "A CALL COMES IN" section. Underneath that, set the first dropdown to Webhook, the text box to the ngrok Forwarding URL, adding on the /voice path. Then, set the second dropdown to "HTTP POST". Follow the same steps at the "A MESSAGE COMES IN" section, but use the /message path instead, and then click Save.
Now call and text the Twilio Phone Number. When calling, you should hear "Ahoy plus one two three four five …!". When texting, you should receive a response like "Ahoy +1234567890!".
Securing your webhooks
Now that your web application is publicly accessible, it's also accessible to malicious actors. You can use the RequestValidationHelper
from the Twilio.AspNet.Core
package to validate that the incoming HTTP request originates from Twilio. To do this in a reusable fashion, you can create a pre-processor.
Create a new file ValidateTwilioRequestProcessor.cs and add the following code:
This code will respond with a 403 Forbidden
response if the request did not originate from Twilio. , Otherwise, the endpoint will handle the request.
This code uses the TwilioRequestValidationOptions
which still needs to be configured.
Add the builder.Services.AddTwilioRequestValidation()
method to the Program.cs file:
This will load the TwilioRequestValidationOptions
from the .NET configuration.
Next, update the appsettings.json file:
Replace [NGROK_FORWARDING_URL]
with the Forwarding URL given by ngrok.
Since the Auth Token is a secret that you should not share, you should avoid hard-coding it such as putting it in your appsettings.json file, or any other way it could end up in your source control history. Instead, use the Secrets Manager aka user-secrets, environment variables, or a vault service.
Run the following commands to initialize user-secrets:
Then grab the Auth Token from your Twilio Account and set it using this command:
Lastly, you need to add the pre-processor inside the Configure
method of your endpoints:
MessageEndpoint.cs:
VoiceEndpoint.cs:
Now that everything is configured, restart your application and try sending the same HTTP requests using cURL or PowerShell as before. The response should now be 403 Forbidden
. However, when you text or call your Twilio Phone Number, everything should continue to work.
Next steps
You learned how to extend FastEndpoints to respond with TwiML and how to secure your FastEndpoints so only Twilio can send it HTTP requests.
Here are a couple more resources to further your learning on ASP.NET Core and Twilio:
- Forward Voicemails with Transcript to your Email using C# and ASP.NET Core
- Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core
- Respond to Twilio Webhooks using Azure Functions
We can't wait to see what you build. Let us know!
Niels Swimberghe is a Belgian American software engineer and technical content creator at Twilio. Get in touch with Niels on Twitter @RealSwimburger and follow Niels’ personal blog on .NET, Azure, and web development at swimburger.net.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.