<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Niels Swimburger.NET 🍔</title>
    <description>The latest articles on DEV Community by Niels Swimburger.NET 🍔 (@swimburger).</description>
    <link>https://dev.to/swimburger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F127864%2F0df207d5-d974-439c-83e9-c758aee3e3d8.jpg</url>
      <title>DEV Community: Niels Swimburger.NET 🍔</title>
      <link>https://dev.to/swimburger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swimburger"/>
    <language>en</language>
    <item>
      <title>How to generate TwiML using Strings in C#</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-generate-twiml-using-strings-in-c-5afi</link>
      <guid>https://dev.to/twilio/how-to-generate-twiml-using-strings-in-c-5afi</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/generate-twiml-using-strings-in-csharp" rel="noopener noreferrer"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the decades, C# has added different ways to create a string, each with their own benefit. In this tutorial, you'll learn how to generate TwiML using the different C# string features with an &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0" rel="noopener noreferrer"&gt;ASP.NET Core Minimal API&lt;/a&gt; and compare it to the object oriented way of generating TwiML.&lt;/p&gt;

&lt;p&gt;But first, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Here's what you will need to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0" rel="noopener noreferrer"&gt;.NET 6 SDK&lt;/a&gt;, or &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0" rel="noopener noreferrer"&gt;.NET 7 SDK to use Raw String Literals&lt;/a&gt; (later versions will work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE. I recommend &lt;a href="https://www.jetbrains.com/rider/" rel="noopener noreferrer"&gt;JetBrains Rider&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/vs/preview/" rel="noopener noreferrer"&gt;Visual Studio 2022 Preview&lt;/a&gt; (will also work in the non-preview Visual Studio in the future), or &lt;a href="https://code.visualstudio.com/Download" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp" rel="noopener noreferrer"&gt;the C# plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A free Twilio account (&lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up with Twilio for free&lt;/a&gt; and get trial credit)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console" rel="noopener noreferrer"&gt;Twilio Phone Number&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;ngrok CLI&lt;/a&gt;, and optionally, a free &lt;a href="https://dashboard.ngrok.com/signup" rel="noopener noreferrer"&gt;ngrok account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experience with ASP.NET Core and Minimal APIs is not required but recommended. Here's an article that brings you up to speed on &lt;a href="https://www.twilio.com/blog/sms-voice-dotnet-6-minimal-api" rel="noopener noreferrer"&gt;how to integrate ASP.NET Core Minimal APIs with Twilio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals" rel="noopener noreferrer"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals/issues" rel="noopener noreferrer"&gt;submit an issue&lt;/a&gt;, if you run into problems.&lt;/p&gt;

&lt;p&gt;Before creating your application, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks and TwiML
&lt;/h2&gt;

&lt;p&gt;Using Twilio, you can build programmatic text message and voice call applications. Twilio can do a lot of things like playing audio, gathering input, and recording a call. But Twilio doesn't know how &lt;strong&gt;you&lt;/strong&gt; want to respond to voice calls and text messages. That's why when Twilio receives a call or text message, Twilio will send an HTTP request to your application asking for instructions.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook" rel="noopener noreferrer"&gt;webhook&lt;/a&gt; is a user-defined HTTP callback. When an event happens in a service, your application is notified of that event using an HTTP request.&lt;/p&gt;

&lt;p&gt;Twilio uses webhooks heavily throughout all its products. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngu4vf8gdedwmu8gc9x3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngu4vf8gdedwmu8gc9x3.png" alt="Phone texts " width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml" rel="noopener noreferrer"&gt;TwiML&lt;/a&gt; ( &lt;strong&gt;Twi&lt;/strong&gt; lio &lt;strong&gt;M&lt;/strong&gt; arkup &lt;strong&gt;L&lt;/strong&gt; anguage) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML" rel="noopener noreferrer"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Messaging here&lt;/a&gt; and &lt;a href="https://www.twilio.com/docs/voice/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Voice here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ASP.NET Core Minimal API to handle voice calls
&lt;/h2&gt;

&lt;p&gt;First, let's create an ASP.NET Core Minimal API to handle voice calls without using Raw String Literals.&lt;/p&gt;

&lt;p&gt;Open a terminal and create a new ASP.NET Core Minimal API project using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; TwimlStrings 
&lt;span class="nb"&gt;cd &lt;/span&gt;TwimlStrings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will create the project in a folder named &lt;em&gt;TwimlStrings&lt;/em&gt; and navigate into the folder.&lt;/p&gt;

&lt;p&gt;Open the project in your editor, and update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following C# code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;?xml version=\"1.0\" encoding=\"utf-8\"?&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;Response&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Gather action=\"/answer\" method=\"GET\" input=\"speech\"&amp;gt; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;/Gather&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;?xml version=\"1.0\" encoding=\"utf-8\"?&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;Response&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;" &amp;lt;Say&amp;gt;You said: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;/Say&amp;gt;"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
&lt;span class="s"&gt;"&amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your Twilio Phone Number receives a call, Twilio will send an HTTP GET request to the &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint, which will set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" rel="noopener noreferrer"&gt;content-type&lt;/a&gt; response header to &lt;code&gt;application/xml&lt;/code&gt;, and return the following TwiML in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&amp;lt;/Gather&amp;gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not very readable, so here's the formatted version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives the TwiML, it'll ask the caller "What does the fox say?" and listen for a speech response. When the caller responds, Twilio will send an HTTP request, this time to the &lt;em&gt;/answer&lt;/em&gt; endpoint, with the transcription of what the caller said. If the caller doesn't respond, Twilio will say "Ring-ding-ding-ding-dingeringeding!" to the caller.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;/answer&lt;/em&gt; endpoint will retrieve the transcription from the query string parameter &lt;code&gt;SpeechResult&lt;/code&gt; by binding it to the &lt;code&gt;speechResult&lt;/code&gt; parameter. The &lt;code&gt;speechResult&lt;/code&gt; is used to construct some more TwiML, but it is first escaped using &lt;code&gt;SecurityElement.Escape&lt;/code&gt; to ensure no XML is in the variable. The &lt;em&gt;/answer&lt;/em&gt; endpoint will generate the following TwiML when the caller says "ring ding ding":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;You said: ring ding ding&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives this TwiML, Twilio will convert "You said: ring ding ding" to speech and stream the audio to the caller.&lt;/p&gt;

&lt;p&gt;In case you're not familiar with the song I am referencing in this app, &lt;a href="https://www.youtube.com/watch?v=jofNR_WkoCE" rel="noopener noreferrer"&gt;"What does the fox say?" originates from this famous song&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start your .NET application using your editor or using the terminal using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before integrating Twilio, open a browser and navigate to your web application URL with the suffix &lt;em&gt;/what-does-the-fox-say&lt;/em&gt;, to verify the TwiML that is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the application publicly accessible
&lt;/h2&gt;

&lt;p&gt;For Twilio to be able to send HTTP requests to your local web server, the server needs to be publicly accessible. ngrok is a free and secure tunneling service that can make your local web server public.&lt;/p&gt;

&lt;p&gt;To start ngrok, run the following ngrok command in a separate terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="o"&gt;[&lt;/span&gt;YOUR_ASPNET_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_ASPNET_URL]&lt;/code&gt; with the localhost URL from your .NET application. If you're using an HTTPS localhost URL, you'll need to &lt;a href="https://ngrok.com/docs/secure-tunnels#tunnel-authtokens" rel="noopener noreferrer"&gt;authenticate ngrok&lt;/a&gt;. The ngrok command will display an HTTPS &lt;strong&gt;Forwarding URL&lt;/strong&gt; that makes your local web server public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feilej3w5xo41lmpcuy0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feilej3w5xo41lmpcuy0v.png" alt="ngrok http command output showing information about the secure tunnel, most importantly the public forwarding URLs" width="500" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your Twilio Phone Number
&lt;/h2&gt;

&lt;p&gt;Now it's time to update your Twilio Phone Number to send HTTP requests to your &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint via the ngrok Forwarding URL. The URL should look something like &lt;strong&gt;&lt;em&gt;&lt;a href="https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say" rel="noopener noreferrer"&gt;https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1" rel="noopener noreferrer"&gt;the Active Phone Numbers section&lt;/a&gt; in the Twilio Console and click on your Twilio Phone Number.&lt;br&gt;&lt;br&gt;
This will take you to the configuration for the phone number. Find the &lt;strong&gt;Voice &amp;amp; Fax&lt;/strong&gt; section and under the " &lt;strong&gt;A CALL COMES IN&lt;/strong&gt;" label, set the dropdown to " &lt;strong&gt;Webhook&lt;/strong&gt;". In the text field next to it, enter your ngrok forwarding URL with &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; appended to it. Select “ &lt;strong&gt;HTTP GET&lt;/strong&gt; ” in the last dropdown. Finally, click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n30cb1cxb0fpvvwkn3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n30cb1cxb0fpvvwkn3o.png" alt="Voice &amp;amp; Fax section in the Twilio Phone Number configuration page. Under the " width="500" height="205"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Test that the application works
&lt;/h2&gt;

&lt;p&gt;To test out your application, call your Twilio Phone Number. You should hear the question "What does the fox say?" which you can respond to, wait 5 seconds, and then your response will be read back to you.&lt;/p&gt;

&lt;p&gt;After you've done that, stop your .NET application using your editor, or if you're using the terminal, press &lt;code&gt;ctrl + c&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Use Verbatim Strings
&lt;/h3&gt;

&lt;p&gt;The first way you can improve your code is by using &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim" rel="noopener noreferrer"&gt;Verbatim Strings&lt;/a&gt;. To turn your string into a Verbatim String, prefix your string with the "at" &lt;code&gt;@&lt;/code&gt; symbol.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;@"The fox said: ""I like using verbatim strings,
because it supports multi-line text,
you don't need to escape forward slashes (/), and backslashes (\),
and you can easily escape double quotes by doubling the double quotes""."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the Verbatim String above says, you don't need to escape forward slashes or backslashes, however you still need to escape double quotes. However, instead of using a backslash to escape the double quotes, you place two double quotes and the resulting character will be a single double quote.&lt;/p&gt;

&lt;p&gt;Applying this knowledge to your TwiML &lt;em&gt;Program.cs&lt;/em&gt; file, you will have the following endpoint code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Gather action=""/answer"" method=""GET"" input=""speech""&amp;gt; 
 &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;
 &amp;lt;/Gather&amp;gt;
 &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Say&amp;gt;You said: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;@"&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be formatted better, but still be indented oddly because the tabs used to nicely indent the code are also included in the string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using Verbatim Strings, you are able to use multi-line text so you don't have to concatenate the string for every line of TwiML. You may think that this will also improve performance as you aren't creating as many strings and then concatenating them. But the compiler will optimize your code so that there's no difference in the end result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use String Interpolation
&lt;/h3&gt;

&lt;p&gt;By using Verbatim Strings, you were able to decrease the amount of opening and closing double quotes to construct your TwiML string which makes the code a lot easier to read. However, you still had to use string concatenation to embed the &lt;code&gt;speechResult&lt;/code&gt; into your TwiML string for the &lt;em&gt;/answer&lt;/em&gt; endpoint.&lt;/p&gt;

&lt;p&gt;To embed variables in your string, you can use &lt;code&gt;String.Format&lt;/code&gt; or, much better, String Interpolation. First, start your string with the dollar &lt;code&gt;$&lt;/code&gt; sign then use the curly brackets &lt;code&gt;{}&lt;/code&gt; to embed C# expressions.&lt;/p&gt;

&lt;p&gt;For example, the following code will write "What does the fox say?" to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fox"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"What does the &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; say?"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applying this knowledge to the &lt;em&gt;/answer&lt;/em&gt; endpoint, you can further improve the code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;$@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Response&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Say&amp;gt;You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  C# 11 and .NET 7 only: Use Raw String Literals
&lt;/h3&gt;

&lt;p&gt;C# 11 is still in preview, but you can try it out today using the .NET 7 previews. Make sure you have .NET 7 installed and are using .NET 7 in your project. Then, open your project file at &lt;em&gt;TwimlStrings.csproj&lt;/em&gt; and add the &lt;code&gt;&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt; to the first &lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; node. This will enable the C# 11 preview features.&lt;/p&gt;

&lt;p&gt;Your &lt;em&gt;TwimlStrings.csproj&lt;/em&gt; in the project folder, should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;preview&lt;span class="nt"&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have enabled C# 11, let's talk about the new &lt;a href="https://devblogs.microsoft.com/dotnet/csharp-11-preview-updates/" rel="noopener noreferrer"&gt;Raw String Literals&lt;/a&gt;. To use Raw String Literals, you start and end your string with at least 3 double quotes. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;because&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="nf"&gt;slashes&lt;/span&gt; &lt;span class="p"&gt;(/),&lt;/span&gt; &lt;span class="nf"&gt;backslashes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"),
&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;better&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;having&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;indentation&lt;/span&gt; &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;span class="s"&gt;"""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax highlighting may be off in the next snippets and snippet above because the blog doesn't have C# 11 highlighting yet.&lt;/p&gt;

&lt;p&gt;As with Verbatim Strings, you don't have to escape forward slashes and backslashes, but, as opposed to Verbatim Strings, in Raw String Literals you also don't have to escape your double quotes, more or less.&lt;/p&gt;

&lt;p&gt;You can use double quotes as long as the number of subsequent double quotes is less than the amount of double quotes you started and ended your string with. This is clearer with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 3 start and end double quotes, so 1 and 2 subsequent double quotes is allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This "&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;""" 
&lt;/span&gt;
&lt;span class="c1"&gt;// 3 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This """&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;
&lt;span class="c1"&gt;// 4 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;""""&lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="s"&gt;""" is allowed.""""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lines one and two are valid Raw String Literals because the number of subsequent double quotes is lower than the number of double quotes used to start and end the Raw String Literal.&lt;/p&gt;

&lt;p&gt;Raw String Literals also makes it easier to format your strings. Take the following method that generates a Verbatim String as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateHtml&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;@"
 &amp;lt;p&amp;gt;
 This is a &amp;lt;b&amp;gt;multi-line Verbatim String&amp;lt;/b&amp;gt; to generate some HTML
 &amp;lt;/p&amp;gt;
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
 This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Verbatim String&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; to generate some HTML
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the newlines and the tab indentation? I formatted the Verbatim String for the sake of readability, but as a result I also got the newlines and tabs in it which I do not want.&lt;/p&gt;

&lt;p&gt;Let's take a look at the Raw String Literal version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""";
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Raw String Literal&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unnecessary newlines and tabs are gone. The way Raw String Literals indent your strings depends on where you place the starting and ending double quotes, and also how you indent the content of your string. It's a little funky and hard to explain all the variations, so I recommend experimenting with it and reading &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#raw-string-literals" rel="noopener noreferrer"&gt;the Microsoft documentation on Raw String Literals&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Using this knowledge, you can apply Raw String Literals to your endpoints like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;fox&lt;/span&gt; &lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Ring&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dingeringeding&lt;/span&gt;&lt;span class="p"&gt;!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""",
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;said&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""" ,
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the resulting TwiML will be readable and not contain unnecessary newlines and tabs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other ways to generate TwiML
&lt;/h3&gt;

&lt;p&gt;There are many other ways you could generate TwiML. Since TwiML is XML, you can use the APIs from the &lt;code&gt;System.Xml&lt;/code&gt; and &lt;code&gt;System.Xml.Linq&lt;/code&gt; namespace. Twilio also has a helper library for .NET that lets you generate TwiML in an object-oriented way, and the helper library for ASP.NET helps you return the TwiML in the HTTP response.&lt;/p&gt;

&lt;p&gt;In the terminal where you ran your project, run the following commands to add the &lt;a href="https://www.nuget.org/packages/Twilio" rel="noopener noreferrer"&gt;Twilio package&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/Twilio" rel="noopener noreferrer"&gt;Twilio.AspNet.Core package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Twilio
dotnet add package Twilio.AspNet.Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML.Voice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speech&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Relative&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What does the fox say?"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ring-ding-ding-ding-dingeringeding!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be the same as before. Notice, however, that you don't need to use the &lt;code&gt;SecurityElement.Escape&lt;/code&gt; anymore to escape the user input, because the Twilio helper library will XML encode it for you. Under the hood, the helper library uses the APIs from &lt;code&gt;System.Linq.Xml&lt;/code&gt; to generate the XML string which will also do the formatting for you.&lt;/p&gt;

&lt;p&gt;Now, you may be wondering, why would you use strings when you can use the Twilio helper library, or maybe you're wondering why you would use the helper library when you can use strings.&lt;/p&gt;

&lt;p&gt;The advantages of creating TwiML using strings, is that you don't need any dependencies and it's more memory efficient &amp;amp; performant. However, it is easier to make a mistake in your TwiML and there will be no compilation errors to prevent you from doing that. You also have to XML escape user input yourself to protect yourself from XML injection.&lt;/p&gt;

&lt;p&gt;The advantages of using the Twilio helper library are that you are using fully typed objects and methods that provide &lt;a href="https://en.wikipedia.org/wiki/Intelligent_code_completion" rel="noopener noreferrer"&gt;IntelliSense&lt;/a&gt; in IDEs. If you make a typo, your project will not build and you will receive a compiler error telling you where your typo is. However, you do depend on the Twilio helper library and its dependencies, and you have to create a bunch of objects that are ultimately serialized to an XML string which is less memory efficient and slower.&lt;/p&gt;

&lt;p&gt;They both have their own advantages and disadvantages, but you can mix and match based on your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You learned how Twilio uses webhooks and TwiML to give you control over how to respond to a call or text message. You then learned how to generate TwiML using different string features in C#, such as Verbatim Strings, String Interpolation, and C# 11's new Raw String Literal feature. You then looked at the difference between generating TwiML using strings and generating TwiML using the Twilio helper library.&lt;/p&gt;

&lt;p&gt;Here are a couple more resources to further your learning on Minimal APIs and Twilio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/forward-voicemails-with-transcript-to-your-email-using-csharp-and-aspnetcore" rel="noopener noreferrer"&gt;Forward Voicemails with Transcript to your Email using C# and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore" rel="noopener noreferrer"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/test-aspnetcore-minimal-apis" rel="noopener noreferrer"&gt;How to test ASP.NET Core Minimal APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can't wait to see what you build. Let us know!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Use Raw String Literals to generate TwiML in C# 11</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 15 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/use-raw-string-literals-to-generate-twiml-in-c-11-3pmp</link>
      <guid>https://dev.to/twilio/use-raw-string-literals-to-generate-twiml-in-c-11-3pmp</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/use-raw-string-literals-to-generate-twiml-in-csharp11"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Over the decades, C# added different ways to create a string, each with their own benefit, and C# 11 is adding another way:  &lt;strong&gt;Raw String Literals&lt;/strong&gt;. There are many use cases for Raw String Literals, and one of those use cases is to generate TwiML, but more on that later. In this tutorial, you'll learn how to generate TwiML with Raw String Literals with an ASP.NET Core Minimal API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Here's what you will need to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0"&gt;.NET 7 SDK&lt;/a&gt; (later versions will work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/vs/preview/"&gt;Visual Studio 2022 Preview&lt;/a&gt; (C# 11 is only supported in preview now, but will work in non-preview in the future.), or &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A free Twilio account (&lt;a href="https://www.twilio.com/try-twilio"&gt;sign up with Twilio for free&lt;/a&gt; and get trial credit)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;Twilio Phone Number&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://ngrok.com/download"&gt;ngrok CLI&lt;/a&gt;, and optionally, a free &lt;a href="https://dashboard.ngrok.com/signup"&gt;ngrok account&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experience with ASP.NET Core and Minimal APIs is not required but recommended. Here's an article that brings you up to speed on &lt;a href="https://www.twilio.com/blog/sms-voice-dotnet-6-minimal-api"&gt;how to integrate ASP.NET Core Minimal APIs with Twilio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/TwimlRawStringLiterals/issues"&gt;submit an issue&lt;/a&gt;, if you run into problems.&lt;/p&gt;

&lt;p&gt;Before creating your application, let's get you up to speed on how Twilio uses webhooks and TwiML to respond to text messages and voice calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks and TwiML
&lt;/h2&gt;

&lt;p&gt;Using Twilio, you can build programmatic text message and voice call applications. Twilio can do a lot of things like playing audio, gathering input, and recording a call, but Twilio doesn't know what &lt;strong&gt;you&lt;/strong&gt; want to do in response to voice calls and text messages. That's why when Twilio receives a call or text message, Twilio will send an HTTP request to your application asking for instructions.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook"&gt;webhook&lt;/a&gt; is a user-defined HTTP callback. When an event happens in a service, your application is notified of that event using an HTTP request.&lt;/p&gt;

&lt;p&gt;Twilio uses webhooks heavily throughout all its products. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dXfDdO_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/vQXGk8VFTcIBytBwOohc9SbQH1vKWzeKNmzR3Q4Qgu26TR.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dXfDdO_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/vQXGk8VFTcIBytBwOohc9SbQH1vKWzeKNmzR3Q4Qgu26TR.width-500.png" alt='Phone texts "Ahoy!" to a Twilio Phone Number, Twilio sends the SMS details (from and to phone number and the body of the message) via HTTP to your web application, then your application responds with TwiML instructions instructing to respond with "Hi!". Twilio receives the instructions and sends "Hi!" back to the original sender.' width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml"&gt;TwiML&lt;/a&gt; ( &lt;strong&gt;Twi&lt;/strong&gt; lio &lt;strong&gt;M&lt;/strong&gt; arkup &lt;strong&gt;L&lt;/strong&gt; anguage) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml"&gt;TwiML for Programmable Messaging here&lt;/a&gt; and &lt;a href="https://www.twilio.com/docs/voice/twiml"&gt;TwiML for Programmable Voice here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that you know what webhooks and TwiML are, let's create a voice call application with ASP.NET Core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an ASP.NET Core Minimal API to handle voice calls
&lt;/h2&gt;

&lt;p&gt;First, let's create an ASP.NET Core Minimal API to handle voice calls without using Raw String Literals.&lt;/p&gt;

&lt;p&gt;Open a terminal and create a new ASP.NET Core Minimal API project using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; TwimlRawStringLiterals 
&lt;span class="nb"&gt;cd &lt;/span&gt;TwimlRawStringLiterals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands will create the project in a folder named &lt;em&gt;TwimlRawStringLiterals&lt;/em&gt; and navigate into the folder.&lt;/p&gt;

&lt;p&gt;Open the project in your editor, and update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following C# code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;
 &amp;lt;Response&amp;gt;
 &amp;lt;Gather action=""/answer"" method=""GET"" input=""speech""&amp;gt; 
 &amp;lt;Say&amp;gt;What does the fox say?&amp;lt;/Say&amp;gt;
 &amp;lt;/Gather&amp;gt;
 &amp;lt;Say&amp;gt;Ring-ding-ding-ding-dingeringeding!&amp;lt;/Say&amp;gt;
 &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s"&gt;$@"&amp;lt;?xml version=""1.0"" encoding=""utf-8""?&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Response&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;Say&amp;gt;You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;/Response&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your Twilio Phone Number receives a call, Twilio will send an HTTP GET request to the &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint, which will set the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type"&gt;content-type&lt;/a&gt; response header to &lt;code&gt;application/xml&lt;/code&gt;, and return the following TwiML in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Gather&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;What does the fox say?&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Gather&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;Ring-ding-ding-ding-dingeringeding!&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note of how the &lt;code&gt;&amp;lt;Response&amp;gt;&lt;/code&gt; node and everything in it is indented with a tab too much.&lt;/p&gt;

&lt;p&gt;When Twilio receives the TwiML, it'll ask the caller "What does the fox say?" and listen for a speech response. When the caller responds, Twilio will send an HTTP request, this time to the &lt;em&gt;/answer&lt;/em&gt; endpoint, with the transcription of what the caller said. If the caller doesn't respond, Twilio will say "Ring-ding-ding-ding-dingeringeding!" to the caller.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;/answer&lt;/em&gt; endpoint will retrieve the transcription from the query string parameter &lt;code&gt;SpeechResult&lt;/code&gt; by binding it to the &lt;code&gt;speechResult&lt;/code&gt; parameter. The &lt;code&gt;speechResult&lt;/code&gt; is used to construct some more TwiML, but it is first escaped using &lt;code&gt;SecurityElement.Escape&lt;/code&gt; to ensure no XML is in the variable. The &lt;em&gt;/answer&lt;/em&gt; endpoint will generate the following TwiML when the caller says "ring ding ding":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Say&amp;gt;&lt;/span&gt;You said: ring ding ding&lt;span class="nt"&gt;&amp;lt;/Say&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Twilio receives this TwiML, Twilio will convert "You said: ring ding ding" to speech and stream the audio to the caller.&lt;/p&gt;

&lt;p&gt;In case you're not familiar with the song I am referencing in this app, &lt;a href="https://www.youtube.com/watch?v=jofNR_WkoCE"&gt;"What does the fox say?" originates from this famous song&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Start your .NET application using your editor or using the terminal using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before integrating Twilio, open a browser and navigate to your web application URL with the suffix &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; to verify the TwiML that is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the application publicly accessible
&lt;/h2&gt;

&lt;p&gt;For Twilio to be able to send HTTP requests to your local web server, the server needs to become publicly accessible. ngrok is a free secure tunneling service that can make your local web servers public.&lt;/p&gt;

&lt;p&gt;To start ngrok, run the following ngrok command in a separate terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="o"&gt;[&lt;/span&gt;YOUR_ASPNET_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_ASPNET_URL]&lt;/code&gt; with the localhost URL from your .NET application. If you're using an HTTPS localhost URL, you'll need to &lt;a href="https://ngrok.com/docs/secure-tunnels#tunnel-authtokens"&gt;authenticate ngrok&lt;/a&gt;. The ngrok command will display an HTTPS &lt;strong&gt;Forwarding URL&lt;/strong&gt; that makes your local web server public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hWStHNyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/apEiL_JFTXcwkjo0vLBlJd7XZjiujP63mGVlhdM-EZ7fNq.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hWStHNyb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/apEiL_JFTXcwkjo0vLBlJd7XZjiujP63mGVlhdM-EZ7fNq.width-500.png" alt="ngrok http command output showing information about the secure tunnel, most importantly the public forwarding URLs" width="500" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your Twilio Phone Number
&lt;/h2&gt;

&lt;p&gt;Now it's time to update your Twilio Phone Number to send HTTP requests to your &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; endpoint via the ngrok Forwarding URL. The URL should look something like _ &lt;strong&gt;&lt;a href="https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say"&gt;https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say&lt;/a&gt;&lt;/strong&gt; _.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1"&gt;Active Phone Numbers section&lt;/a&gt; in the Twilio Console and click on your Twilio Phone Number.&lt;br&gt;&lt;br&gt;
This will take you to the configuration for the phone number. Find the &lt;strong&gt;Voice &amp;amp; Fax&lt;/strong&gt; section and under the " &lt;strong&gt;A CALL COMES IN&lt;/strong&gt;" label, set the dropdown to " &lt;strong&gt;Webhook&lt;/strong&gt;". In the text field next to it, enter your ngrok forwarding URL with &lt;em&gt;/what-does-the-fox-say&lt;/em&gt; appended to it. Select “ &lt;strong&gt;HTTP GET&lt;/strong&gt; ” in the last dropdown. Finally, click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ1AuUPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/rcWjPSxeiAd8YuogrtDL7nBYmVsw1VBYSKHKI-wwCilS4x.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gQ1AuUPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://assets.cdn.prod.twilio.com/images/rcWjPSxeiAd8YuogrtDL7nBYmVsw1VBYSKHKI-wwCilS4x.width-500.png" alt='Voice &amp;amp; Fax section in the Twilio Phone Number configuration page. Under the "A CALL COMES IN" label, a dropdown is set to "Webhook", the text field next to it is configured with "https://cd2f8809cbd0.ngrok.io/what-does-the-fox-say", and the dropdown next to that is set to "HTTP GET".' width="500" height="205"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Test that the application works
&lt;/h2&gt;

&lt;p&gt;To test out your application, call your Twilio Phone Number. You should hear the question "What does the fox say?" which you can respond to, wait 5 seconds, and then your response will be read back to you.&lt;/p&gt;

&lt;p&gt;After you've done that, stop your application using your editor, or if you're using the terminal, press &lt;code&gt;ctrl + c&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  C# 11 and .NET 7 only: Use Raw String Literals
&lt;/h3&gt;

&lt;p&gt;C# 11 is still in preview, but you can try it out today using the .NET 7 previews. Make sure you have .NET 7 installed and are using .NET 7 in your project. Then, open your project file at &lt;em&gt;TwimlRawStringLiterals.csproj&lt;/em&gt; and add the &lt;code&gt;&amp;lt;LangVersion&amp;gt;preview&amp;lt;/LangVersion&amp;gt;&lt;/code&gt; to the first &lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; node. This will enable the C# 11 preview features.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;TwimlRawStringLiterals.csproj&lt;/em&gt; file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;preview&lt;span class="nt"&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have enabled C# 11, let's talk about the new &lt;a href="https://devblogs.microsoft.com/dotnet/csharp-11-preview-updates/"&gt;Raw String Literals&lt;/a&gt;. To use Raw String Literals, you start and end your string with at least 3 double quotes. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="n"&gt;I&lt;/span&gt; &lt;span class="n"&gt;like&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;because&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;escape&lt;/span&gt; &lt;span class="n"&gt;forward&lt;/span&gt; &lt;span class="nf"&gt;slashes&lt;/span&gt; &lt;span class="p"&gt;(/),&lt;/span&gt; &lt;span class="nf"&gt;backslashes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"),
&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;better&lt;/span&gt; &lt;span class="n"&gt;without&lt;/span&gt; &lt;span class="n"&gt;having&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;indentation&lt;/span&gt; &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; 
&lt;span class="s"&gt;"""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax highlighting may be off in the next snippets and snippet above because the blog doesn't have C# 11 highlighting yet.&lt;/p&gt;

&lt;p&gt;You don't have to escape forward slashes, backslashes, and you also don't have to escape your double quotes, more or less. You can use double quotes as long as the number of subsequent double quotes is less than the amount of double quotes you started and ended your string with. This is clearer with an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 3 start and end double quotes, so 1 and 2 subsequent double quotes is allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This "&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;""" 
&lt;/span&gt;
&lt;span class="c1"&gt;// 3 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;"""This """&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt;
&lt;span class="c1"&gt;// 4 start and end double quotes, so 3 subsequent double quotes is NOT allowed&lt;/span&gt;
&lt;span class="s"&gt;""""&lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="s"&gt;""" is allowed.""""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line one and two are valid Raw String Literals because the number of subsequent double quotes is lower than the number of double quotes used to start and end the Raw String Literal.&lt;/p&gt;

&lt;p&gt;Raw String Literals also makes it easier to format your strings. Take the following method that generates a Verbatim String (&lt;code&gt;@&lt;/code&gt;) as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateHtml&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;@"
 &amp;lt;p&amp;gt;
 This is a &amp;lt;b&amp;gt;multi-line Verbatim String&amp;lt;/b&amp;gt; to generate some HTML
 &amp;lt;/p&amp;gt;
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
 This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Verbatim String&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; to generate some HTML
&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the newlines and the tab indentation? I formatted the Verbatim String for the sake of readability, but as a result I also got the newlines and tabs in it which I do not want.&lt;/p&gt;

&lt;p&gt;Let's take a look at the Raw String Literal version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;Raw&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""";
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting string now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
This is a &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;multi-line Raw String Literal&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The unnecessary newlines and tabs are gone. The way Raw String Literals indent your strings depends on where you place the starting and ending double quotes, and also how you indent the content of your string. It's a little funky and hard to explain all the variations, so I recommend experimenting with it and reading &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#raw-string-literals"&gt;the Microsoft documentation on Raw String Literals&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Using this knowledge, you can apply Raw String Literals to your endpoints like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"speech"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="n"&gt;What&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;fox&lt;/span&gt; &lt;span class="n"&gt;say&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Ring&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ding&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dingeringeding&lt;/span&gt;&lt;span class="p"&gt;!&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""",
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"""
&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="n"&gt;xml&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;You&lt;/span&gt; &lt;span class="n"&gt;said&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;SecurityElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)}&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="s"&gt;""" ,
&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"application/xml"&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the resulting TwiML will be readable and not contain unnecessary newlines and tabs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other ways to generate TwiML
&lt;/h3&gt;

&lt;p&gt;There are many other ways you could generate TwiML. Since TwiML is XML, you can use the APIs from the &lt;code&gt;System.Xml&lt;/code&gt; and &lt;code&gt;System.Xml.Linq&lt;/code&gt; namespace. Twilio also has a helper library for .NET that lets you generate TwiML in an object-oriented way, and the helper library for ASP.NET helps you return the TwiML in the HTTP response.&lt;/p&gt;

&lt;p&gt;In the terminal where you ran your project, run the following commands to add the &lt;a href="https://www.nuget.org/packages/Twilio"&gt;Twilio package&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/Twilio"&gt;Twilio.AspNet.Core package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Twilio
dotnet add package Twilio.AspNet.Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML.Voice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/what-does-the-fox-say"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gather&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InputEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speech&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UriKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Relative&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"What does the fox say?"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ring-ding-ding-ding-dingeringeding!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;VoiceResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Say&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"You said: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;speechResult&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting TwiML will be the same as before. Notice, however, that you don't need to use the &lt;code&gt;SecurityElement.Escape&lt;/code&gt; anymore to escape the user input, because the Twilio helper library will XML encode it for you.&lt;br&gt;&lt;br&gt;
Under the hood, the helper library uses the APIs from &lt;code&gt;System.Linq.Xml&lt;/code&gt; to generate the XML string which will also do the formatting for you.&lt;/p&gt;

&lt;p&gt;Now, you may be wondering, why would you use strings when you can use the Twilio helper library, or maybe you're wondering why you would use the helper library when you can use strings.&lt;/p&gt;

&lt;p&gt;The advantages of creating TwiML using strings, is that you don't need any dependencies and it's more memory efficient &amp;amp; performant. However, it is easier to make a mistake in your TwiML and there will be no compilation errors to prevent you from doing that. You also have to XML escape user input yourself to protect yourself from XML injection.&lt;/p&gt;

&lt;p&gt;The advantages of using the Twilio helper library are that you are using fully typed objects and methods that provide &lt;a href="https://en.wikipedia.org/wiki/Intelligent_code_completion"&gt;IntelliSense&lt;/a&gt; in IDEs. If you make a typo, your project will not build and you will receive a compiler error telling you where your typo is. However, you do depend on the Twilio helper library and its dependencies, and you have to create a bunch of objects that are ultimately serialized to an XML string which is less memory efficient and slower.&lt;/p&gt;

&lt;p&gt;They both have their own advantages and disadvantages, but you can mix and match based on your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You learned how Twilio uses webhooks and TwiML to give you control over how to respond to a call or text message. You then learned how to generate TwiML instructions using C# 11's new Raw String Literal feature, and compared it to generating TwiML with the Twilio helper library.&lt;/p&gt;

&lt;p&gt;Here are a couple more resources to further your learning on Minimal APIs and Twilio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/forward-voicemails-with-transcript-to-your-email-using-csharp-and-aspnetcore"&gt;Forward Voicemails with Transcript to your Email using C# and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/test-aspnetcore-minimal-apis"&gt;How to test ASP.NET Core Minimal APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can't wait to see what you build. Let us know!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Use Visual Studio dev tunnels to handle Twilio Webhooks</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 08 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/use-visual-studio-dev-tunnels-to-handle-twilio-webhooks-58dc</link>
      <guid>https://dev.to/twilio/use-visual-studio-dev-tunnels-to-handle-twilio-webhooks-58dc</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/use-visual-studio-port-tunneling-with-twilio-webhooks" rel="noopener noreferrer"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio recently introduced a new feature called  &lt;strong&gt;dev tunnels&lt;/strong&gt;. By using dev tunnels, Visual Studio will create a new public URL (tunnel URL) for you, and HTTP requests sent to the tunnel URL will be forwarded to your ASP.NET Core project running on localhost.&lt;/p&gt;

&lt;p&gt;Dev tunnels has a lot of use cases. You could use this to easily test your web application on other devices like mobiles phone and tablets. You could also use this to make your application temporarily, publicly available for doing interactive demos and inviting your audience to participate.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use dev tunnels with Twilio
&lt;/h2&gt;

&lt;p&gt;The most exciting use case for Twilio is that you can use dev tunnels to test webhooks. &lt;a href="https://www.twilio.com/docs/glossary/what-is-a-webhook" rel="noopener noreferrer"&gt;What are webhooks again&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;" &lt;strong&gt;Webhooks are user-defined HTTP callbacks.&lt;/strong&gt; They are triggered by some event in a web application and can facilitate integrating different applications or third-party APIs, like Twilio."&lt;/p&gt;

&lt;p&gt;At Twilio, we heavily use webhooks throughout all of our products. You can use webhooks to respond to text messages or to manage voice calls. Here's a diagram of what it looks like when there's an incoming text message to your Twilio Phone Number and your application is handling the messaging webhook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83dtzwfrugx6cqin6vv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83dtzwfrugx6cqin6vv.png" alt="Phone texts " width="500" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When your Twilio Phone Number receives a text message, Twilio will send an HTTP request with the message details to your application. Your application then has to respond with &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml" rel="noopener noreferrer"&gt;TwiML&lt;/a&gt; (Twilio Markup Language) to instruct Twilio how to respond. TwiML is &lt;a href="https://en.wikipedia.org/wiki/XML" rel="noopener noreferrer"&gt;XML&lt;/a&gt; with special tags defined by Twilio to provide instructions on how to respond to messages and voice calls. In the diagram depicted above, Twilio will respond with "Hi!" because the app responded with TwiML that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Hi!&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more about &lt;a href="https://www.twilio.com/docs/messaging/twiml" rel="noopener noreferrer"&gt;TwiML for Programmable Messaging here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some other types of webhooks will inform you of an event and not expect instructions as a response. However, &lt;strong&gt;webhook requests can only be made to publicly available URLs&lt;/strong&gt; , and when you're developing and testing applications, you are usually doing so locally. But with Visual Studio dev tunnels, you can quickly make your web application public, so you can develop your webhooks!&lt;/p&gt;

&lt;p&gt;So when a text message comes in, instead of Twilio sending an HTTP request directly to your application, Twilio can send it to your Visual Studio tunnel which will forward it to your application running on localhost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwilio-cms-prod.s3.amazonaws.com%2Fdocuments%2FDevTunnels.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftwilio-cms-prod.s3.amazonaws.com%2Fdocuments%2FDevTunnels.svg" alt="Diagram of the SMS webhook flow to an ASP.NET Core project tunneled via Visual Studio dev tunnels. Phone sending an SMS has arrows pointing back and forth to the Twilio logo, with arrows back and forth pointing to tunnels.api.visualstudio.com, with arrows pointing back and forth to a laptop with the Visual Studio logo on it." width="638" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to use Visual Studio to create an ASP.NET Core project that will handle the Twilio SMS webhook, use dev tunnels to make the locally running application public, and then configure a Twilio Phone Number to send webhook requests to the public tunnel URL whenever a text message comes in so you can respond to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You'll need the following things in this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Windows machine (there is no information about support for Visual Studio for Mac yet.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://visualstudio.microsoft.com/vs/preview/" rel="noopener noreferrer"&gt;Visual Studio 2022 Preview&lt;/a&gt;, with the ASP.NET and web development workload installed&lt;/li&gt;
&lt;li&gt;In Visual Studio, you need to enable the dev tunnels preview feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find details on how to do these steps in &lt;a href="https://devblogs.microsoft.com/visualstudio/dev-tunnels-in-visual-studio-for-asp-net-core-projects/" rel="noopener noreferrer"&gt;the dev tunnels announcement post by Microsoft&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly, you'll also need a Twilio account (trial or upgraded). &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;Try Twilio for free with promotional trial credit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/VsTunnelsSmsBot" rel="noopener noreferrer"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it as a reference if you run into any issues, or &lt;a href="https://github.com/Swimburger/VsTunnelsSmsBot/issues" rel="noopener noreferrer"&gt;submit an issue&lt;/a&gt; if you need assistance&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an empty ASP.NET Core web application with dev tunnels
&lt;/h2&gt;

&lt;p&gt;Open Visual Studio 2022 preview and click " &lt;strong&gt;Create a new project&lt;/strong&gt;", then search for the " &lt;strong&gt;ASP.NET Core Empty&lt;/strong&gt;" project template and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fritkipf7ncoa0svp9imo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fritkipf7ncoa0svp9imo.png" alt="Visual Studio " width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, give your project a meaningful name and click &lt;strong&gt;Next&lt;/strong&gt;. Then on the last screen, leave everything as is and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Visual Studio will now generate an empty ASP.NET Core application that returns "Hello World!" using Minimal APIs. Go ahead and run your project to see it in action by pressing the green play arrow with your project name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbhg5c7187xdphubk788.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbhg5c7187xdphubk788.png" alt="Visual Studio top toolbar with green play arrow next to the project name " width="500" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual Studio will run your application, open your browser, and then browse to the web application URL, which will look similar to &lt;em&gt;&lt;a href="https://localhost:5000" rel="noopener noreferrer"&gt;https://localhost:5000&lt;/a&gt;&lt;/em&gt;. However, port number 5000 will be a random port number instead. The "Hello World!" application works as expected, but is only reachable on your machine. No other devices can reach it.&lt;/p&gt;

&lt;p&gt;Back in Visual Studio, click on the debug dropdown menu, and then select the &lt;strong&gt;Dev Tunnels&lt;/strong&gt; menu item. Now, the &lt;strong&gt;Dev Tunnels&lt;/strong&gt; fly out menu shows you different options, to either select a dev tunnel (but there are none yet), to create a new tunnel, or to open the dev tunnels window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcxm2ri730m4uewzaa2w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftcxm2ri730m4uewzaa2w.png" alt="Visual Studio Debug dropdown menu containing a Dev Tunnels subitem with a fly out menu showing three menu items: None (active the tunnel), Create A Tunnel..., and Show Dev Tunnels Window." width="500" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Create A Tunnel...&lt;/strong&gt; which opens a dialog to create your tunnel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7regscivf8irgix1lss9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7regscivf8irgix1lss9.png" alt="Dialog with form to create dev tunnel. The form asks for the account to create the tunnel for, in this case a GitHub account, a name for the tunnel, the type which is set to Persistent, and the access level which is set to Public." width="800" height="867"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like the form above, select an &lt;strong&gt;Account&lt;/strong&gt; , enter a &lt;strong&gt;Name&lt;/strong&gt; for the tunnel, set the &lt;strong&gt;Tunnel Type&lt;/strong&gt; to &lt;strong&gt;Persistent&lt;/strong&gt; , and the &lt;strong&gt;Access&lt;/strong&gt; to &lt;strong&gt;Public&lt;/strong&gt;. Then click &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you set the Tunnel Type to Persistent, the URL of your dev tunnel is preserved until you delete the tunnel. When set to Temporary, the URL of the tunnel changes whenever you restart Visual Studio. The Tunnel Access has three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private&lt;/strong&gt; : Only you can access the web application by logging in with your Visual Studio account. This is great for personally testing your application on other devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizational&lt;/strong&gt; : Anyone within your organization can access the web application. You can use this to demo the application to your teammates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public&lt;/strong&gt; : Anyone can access the web application, and you don't need to log in. Make sure it is safe for you to share your application publicly! &lt;strong&gt;This is what you need to receive HTTP requests from Twilio webhooks.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like before, Visual Studio will open your web browser and browse to your web application. However, this time it will browse to the public tunnel URL, which looks something like &lt;em&gt;&lt;a href="https://0pbvlk3m-7032.use.devtunnels.ms/" rel="noopener noreferrer"&gt;https://0pbvlk3m-7032.use.devtunnels.ms/&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa6pc552s1kto8fm1d0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxa6pc552s1kto8fm1d0v.png" alt="Browser with public tunnel URL in the URL bar. The URL ends with " width="500" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can open the same URL on any other device and send it to your friends and colleagues. As long as your project is running, they will be able to use the web application that is running locally on your machine.&lt;/p&gt;

&lt;p&gt;Now that your application is publicly available, let's add some Twilio magic to it ☎️&lt;/p&gt;

&lt;h2&gt;
  
  
  Respond to SMS with ASP.NET Core
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Buy a Twilio Phone Number
&lt;/h3&gt;

&lt;p&gt;If you haven't done so already, you'll need to buy a Twilio phone number. To do that, log in to the &lt;a href="https://console.twilio.com/" rel="noopener noreferrer"&gt;Twilio Console&lt;/a&gt;, select &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/active?frameUrl=/console/phone-numbers/incoming" rel="noopener noreferrer"&gt;Phone Numbers&lt;/a&gt;, and then click on the “ &lt;strong&gt;Buy a number&lt;/strong&gt; ” button. Note that if you have a free account, you will be using your trial credit for this purchase.&lt;/p&gt;

&lt;p&gt;On the “ &lt;strong&gt;Buy a Number&lt;/strong&gt; ” page, select your country and check SMS in the “ &lt;strong&gt;Capabilities&lt;/strong&gt; ” field. If you’d like to request a number that is local to your region, you can enter your area code in the “ &lt;strong&gt;Number&lt;/strong&gt; ” field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4990lvhfck2czicjauci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4990lvhfck2czicjauci.png" alt="Buy a Twilio phone number" width="500" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “ &lt;strong&gt;Search&lt;/strong&gt; ” button to see what numbers are available, and then click “ &lt;strong&gt;Buy&lt;/strong&gt; ” for the number you like from the results. After you confirm your purchase, click the “ &lt;strong&gt;Close&lt;/strong&gt; ” button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Twilio SMS webhook in ASP.NET Core
&lt;/h3&gt;

&lt;p&gt;Since TwiML is XML, you can use any technique to produce the XML as a response to the Twilio webhook request. However, you can use the &lt;a href="https://www.twilio.com/docs/libraries/csharp-dotnet" rel="noopener noreferrer"&gt;Twilio .NET SDK&lt;/a&gt; and the &lt;a href="https://github.com/twilio-labs/twilio-aspnet" rel="noopener noreferrer"&gt;Twilio helper library for ASP.NET Core&lt;/a&gt; to make it easier.&lt;/p&gt;

&lt;p&gt;First, make sure your application isn't running anymore. You can stop your app by pressing the red square icon in the Visual Studio top toolbar, or close the browser window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fe1lphte5kl4y1tkoyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fe1lphte5kl4y1tkoyj.png" alt="Arrow pointing to the stop button which is a red square in the top toolbar." width="500" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Visual Studio, right-click on the &lt;strong&gt;Dependencies&lt;/strong&gt; node of your project, and click " &lt;strong&gt;Manage NuGet Packages&lt;/strong&gt;". This will open the NuGet Package Manager window in Visual Studio. Click on the &lt;strong&gt;Browse&lt;/strong&gt; tab and search for " &lt;strong&gt;Twilio.AspNet.Core&lt;/strong&gt;". Click on the &lt;strong&gt;Twilio.AspNet.Core&lt;/strong&gt; package by Twilio Labs, and click the &lt;strong&gt;Install&lt;/strong&gt; button to install the latest version.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpusi5xbh6n9fovxi82ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpusi5xbh6n9fovxi82ov.png" alt="NuGet Package Manager window in Visual Studio searching for " width="500" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By installing the Twilio.AspNet.Core package, you also implicitly install the Twilio NuGet package which has the Twilio SDK. You can also explicitly install the Twilio NuGet package to choose the package version yourself.&lt;/p&gt;

&lt;p&gt;Now, replace the code from &lt;em&gt;Program.cs&lt;/em&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messagingResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ahoy!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Hello World!" GET / endpoint has been replaced with the &lt;em&gt;/message&lt;/em&gt; endpoint. The &lt;em&gt;/message&lt;/em&gt; endpoint creates a &lt;code&gt;MessagingResponse&lt;/code&gt;. &lt;code&gt;messagingResponse.Message("Ahoy!")&lt;/code&gt; will create a Message TwiML verb to respond with "Ahoy!".&lt;br&gt;&lt;br&gt;
Lastly, the &lt;code&gt;Results.Extensions.TwiML&lt;/code&gt; method will create a &lt;code&gt;TwiMLResult&lt;/code&gt; object which takes care of serializing the object to XML and setting the correct content-type headers.&lt;/p&gt;

&lt;p&gt;Start your project again. Visual Studio will open the browser again, but you'll see a HTTP 404 error which is fine, this is expected. Copy the URL somewhere as you'll need it in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your SMS webhook
&lt;/h3&gt;

&lt;p&gt;Now that your web application is running and publicly available through the tunnel, you can now configure the messaging webhook on your Twilio Phone Number.&lt;/p&gt;

&lt;p&gt;Back in the Twilio Console, navigate to &lt;strong&gt;Phone Numbers &amp;gt; Manage &amp;gt; Active Numbers&lt;/strong&gt; , or &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=/console/phone-numbers/incoming?x-target-region=us1" rel="noopener noreferrer"&gt;click this link to the Active Numbers page&lt;/a&gt;, then click on your Twilio Phone Number to navigate to the configuration page. Scroll down to the &lt;strong&gt;Messaging&lt;/strong&gt; section and where it says " &lt;strong&gt;A MESSAGE COMES IN&lt;/strong&gt;", pick the " &lt;strong&gt;Webhook&lt;/strong&gt;" option in the first dropdown, paste your Visual Studio tunnel URL and add the &lt;em&gt;/message&lt;/em&gt; path, and then pick the " &lt;strong&gt;HTTP POST&lt;/strong&gt;" option from the next dropdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazz7r485h6zvygeh5clz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazz7r485h6zvygeh5clz.png" alt="Twilio Phone Number Messaging form. There" width="500" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your SMS application
&lt;/h3&gt;

&lt;p&gt;Now that everything has been set up, it's time to test out your SMS application. Pull out your personal phone, and send an SMS to your Twilio Phone Number. You should receive a response saying "Ahoy!".&lt;/p&gt;

&lt;p&gt;One thing I always do to gain a better understanding is to put a breakpoint in the application and see when the breakpoint is hit. Feel free to put a breakpoint inside the &lt;em&gt;/message&lt;/em&gt; endpoint, and play around with the application to see what you can do with Twilio Messaging.&lt;/p&gt;

&lt;p&gt;Alternatively, you can &lt;a href="https://www.twilio.com/blog/test-sms-and-phone-call-applications-with-twilio-dev-phone" rel="noopener noreferrer"&gt;test your messaging and voice applications using the Twilio Dev Phone&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Congratulations on building this SMS application! Since your application is now public, that also means anyone on the internet can reach it which comes with its own risks. To mitigate these risks, you can &lt;a href="https://www.twilio.com/docs/usage/tutorials/how-to-secure-your-csharp-aspnet-core-app-by-validating-incoming-twilio-requests" rel="noopener noreferrer"&gt;validate that the incoming requests originated from Twilio and not some bad actor&lt;/a&gt;. In this tutorial, you manually updated the webhook URL in the Twilio console, but you can also do this programmatically. Check out &lt;a href="https://www.twilio.com/blog/configure-twilio-webhooks-with-visual-studio-dev-tunnels-during-aspdotnet-core-startup" rel="noopener noreferrer"&gt;this follow-up tutorial on how to automatically configure your Twilio webhooks using Visual Studio dev tunnels&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to learn what else you can do with Twilio? Here are some more tutorials for inspiration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/send-vcard-with-mms-using-csharp-and-dotnet" rel="noopener noreferrer"&gt;How to send a vCard with MMS using C# and .NET&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/how-to-build-an-email-newsletter-application-using-asp-net-core-and-sendgrid" rel="noopener noreferrer"&gt;How to build an Email Newsletter application using ASP.NET Core and SendGrid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore" rel="noopener noreferrer"&gt;Find your U.S. Representatives and Congressional Districts with SMS and ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.twilio.com/blog/send-emails-with-csharp-handlebars-templating-and-dynamic-email-templates" rel="noopener noreferrer"&gt;Send Emails with C#, Handlebars templating, and Dynamic Email Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding" rel="noopener noreferrer"&gt;How to send SMS with C# .NET and Azure Functions using the Twilio Output&lt;/a&gt;&lt;a href="https://www.twilio.com/blog/send-sms-with-csharp-dotnet-and-azure-functions-using-the-twilio-output-binding" rel="noopener noreferrer"&gt;Binding&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Shout-out to &lt;a href="https://twitter.com/sayedihashimi" rel="noopener noreferrer"&gt;Sayed I. Hashimi&lt;/a&gt; for answering my questions and taking my feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>visualstudio</category>
    </item>
    <item>
      <title>How to create an ASP.NET Core Minimal API with Visual Basic .NET (there's no template)</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Thu, 25 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/swimburger/how-to-create-an-aspnet-core-minimal-api-with-visual-basic-net-theres-no-template-45gd</link>
      <guid>https://dev.to/swimburger/how-to-create-an-aspnet-core-minimal-api-with-visual-basic-net-theres-no-template-45gd</guid>
      <description>&lt;p&gt;Visual Basic .NET (VB) is not dead, but it's not getting the same amount of love as C# or even F#. That is instantly clear when you list the available templates that come with the .NET SDK:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CNn5b5tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://swimburger.net/media/331jvnmb/dotnet-new-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CNn5b5tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://swimburger.net/media/331jvnmb/dotnet-new-list.png" alt="List of templates that come with the .NET SDK, only 6 are for Visual Basic." width="880" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only 6 out 23 project templates have support for VB, while F# has support for 10, and C# has support for all of them.&lt;/p&gt;

&lt;p&gt;However, this does not mean you cannot recreate the C# template in VB. In this tutorial, you'll see how you can start from the VB console template, and update it to a ASP.NET Core Minimal API project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along you'll need to have the .NET 6 SDK or newer on your machine, and I recommend using a .NET editor with VB support such as &lt;a href="https://visualstudio.microsoft.com/downloads/"&gt;Visual Studio&lt;/a&gt; or &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a C# Minimal API and VB Console project
&lt;/h2&gt;

&lt;p&gt;The easiest way to convert a C# project template to VB, is to generate that C# project, generate a VB console project, and then compare the two. So let's first create a C# Minimal API project and VB Console project. Open a terminal and run the following .NET CLI commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-lang&lt;/span&gt; C# &lt;span class="nt"&gt;-o&lt;/span&gt; MinimalApiSharp
dotnet new console &lt;span class="nt"&gt;-lang&lt;/span&gt; VB &lt;span class="nt"&gt;-o&lt;/span&gt; MinimalApiVb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compare C# Minimal API and update VB Console app
&lt;/h2&gt;

&lt;p&gt;The first files you need to compare are the project file: &lt;em&gt;MinimalApiSharp/MinimalApiSharp.csproj&lt;/em&gt; and &lt;em&gt;MinimalApiVb/MinimalApiVb.vbproj&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MinimalApiSharp/MinimalApiSharp.csproj&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class="nt"&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: I'm using .NET 7, but you can apply the same technique to older and newer versions of .NET.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MinimalApiVb/MinimalApiVb.vbproj&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class="nt"&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;MinimalApiVb&lt;span class="nt"&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice a couple differences between the two project files, but the difference that matters is the SDK that is being used. The Minimal API project uses the &lt;code&gt;Microsoft.NET.Sdk.Web&lt;/code&gt; SDK which will load the necessary dependencies and build steps for running ASP.NET Core projects. In &lt;em&gt;MinimalApiVb.vbproj&lt;/em&gt;, replace &lt;code&gt;Microsoft.NET.Sdk&lt;/code&gt; with &lt;code&gt;Microsoft.NET.Sdk.Web&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MinimalApiVb/MinimalApiVb.vbproj&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class="nt"&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;RootNamespace&amp;gt;&lt;/span&gt;MinimalApiVb&lt;span class="nt"&gt;&amp;lt;/RootNamespace&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;net7.0&lt;span class="nt"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, compare the &lt;em&gt;MinimalApiSharp/Program.cs&lt;/em&gt; and &lt;em&gt;MinimalApiVb/Program.vb&lt;/em&gt; files.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MinimalApiSharp/Program.cs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;MinimalApiVb/Program.vb&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vb"&gt;&lt;code&gt;&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;

&lt;span class="k"&gt;Module&lt;/span&gt; &lt;span class="nn"&gt;Program&lt;/span&gt;
    &lt;span class="k"&gt;Sub&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nb"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Sub&lt;/span&gt;
&lt;span class="k"&gt;End&lt;/span&gt; &lt;span class="k"&gt;Module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you need to convert the C# code into VB code and place it into the &lt;code&gt;Main&lt;/code&gt; method, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vb"&gt;&lt;code&gt;&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;

&lt;span class="k"&gt;Module&lt;/span&gt; &lt;span class="nn"&gt;Program&lt;/span&gt;
    &lt;span class="k"&gt;Sub&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="ow"&gt;As&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Dim&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Function&lt;/span&gt;&lt;span class="err"&gt;()&lt;/span&gt; &lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="nf"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="s"&gt;")

        app.Run()
    End Sub
End Module
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The C# project uses &lt;code&gt;ImplicitUsings&lt;/code&gt; which means that commonly needed namespaces are already imported without explicitly having to import them with the &lt;code&gt;using&lt;/code&gt; statement. This is great for C#, but makes it a little harder for you to discover which namespaces to import in your VB application. However, with a good IDE like Visual Studio and JetBrains Rider, importing namespaces is relatively straightforward.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;Imports&lt;/code&gt; statement like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vb"&gt;&lt;code&gt;&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;
&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Builder&lt;/span&gt;
&lt;span class="k"&gt;Imports&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you've got a working Minimal API using the VB language!&lt;/p&gt;

&lt;p&gt;You can also copy and paste the &lt;em&gt;appsettings.json&lt;/em&gt; and &lt;em&gt;appsettings.Development.json&lt;/em&gt; file from the C# project into your VB project so you can configure your application using the JSON files.&lt;/p&gt;

&lt;p&gt;Now, go ahead and test your application by running &lt;code&gt;dotnet run&lt;/code&gt;. The command will print the localhost URLs where your app is hosted. Grab the URL and open it in your browser to see "Hello World!".&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While VB is still supported by Microsoft, it isn't getting the same level of support which you can see in the missing project templates. Luckily, all that is .NET can be used by all .NET languages including VB, so you can still use ASP.NET Core and Minimal APIs with the VB language.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>vb</category>
    </item>
    <item>
      <title>Find your US Representatives and Congressional Districts with SMS and ASP.NET Core</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/find-your-us-representatives-and-congressional-districts-with-sms-and-aspnet-core-2ceo</link>
      <guid>https://dev.to/twilio/find-your-us-representatives-and-congressional-districts-with-sms-and-aspnet-core-2ceo</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/find-us-representatives-congressional-districts-with-sms-aspnetcore"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When someone runs for office in the United States of America (U.S.), they have to fulfill certain "Ballot Access Requirements" to become listed on the ballot. One of those requirements could be that you need a certain number of signatures per district. So when volunteers and campaign workers go out to gather signatures, it is instrumental to know which congressional district (CD) the signee belongs to. However, knowing which congressional district you are from is not common knowledge, especially in areas where the district cuts right through counties, cities, and neighborhoods. &lt;/p&gt;

&lt;p&gt;States redraw their congressional districts every decade after getting an updated population count from the Census Bureau. Each district is represented by a single U.S. representative in the U.S. House of Representatives. However, the manner in which these districts are drawn often result in unique and odd shapes which can be confusing.  This is why constituents living just a street apart sometimes live in different districts, and as a result, vote in different house races. &lt;/p&gt;

&lt;p&gt;This blog post won't discuss why U.S. congressional district maps are drawn the way they are, however, I highly recommend you look into this topic and learn how it affects political voting power.&lt;/p&gt;

&lt;p&gt;When I gathered signatures, the first question I had to ask over and over again was "what congressional district are you from?", and 95% of the time, the answer was "I don't know". That's because in Northern Virginia, the border between the 10th and 11th congressional district cuts directly through some counties, cities, neighborhoods, and streets. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i2kt3DDg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/BbH8CY4qRX1VgWmjvjnHZ7VONqfP3_ezHIH_L1hA0yauWzJ.original.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i2kt3DDg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/BbH8CY4qRX1VgWmjvjnHZ7VONqfP3_ezHIH_L1hA0yauWzJ.original.png" alt="Map of Virginia's 11th Congressional District" width="800" height="1068"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Map of Virginia's 11th Congressional District&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The house.gov website has a &lt;a href="https://www.house.gov/representatives/find-your-representative"&gt;tool to look up your congressional district and representative&lt;/a&gt;, but unfortunately, the tool is slow and painful to use, especially when you're on the go on mobile internet.&lt;/p&gt;

&lt;p&gt;That's why instead of using web technology, I built a solution using connectivity technology that's more accessible, more reliable, and simpler. Using &lt;a href="https://www.twilio.com/docs/sms/api"&gt;Twilio SMS&lt;/a&gt;, I built a phone number that you can text your address to and you will receive a response with your congressional district and representative. Let me walk you through how you can build this SMS bot using Twilio, C# .NET, and &lt;a href="https://developers.google.com/civic-information"&gt;Google's Civic Information API&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You'll need a couple of things to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0"&gt;.NET 6 SDK&lt;/a&gt; (older or newer should work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/"&gt;Visual Studio&lt;/a&gt;, or &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://ngrok.com/download"&gt;ngrok CLI&lt;/a&gt;, and optionally, a free &lt;a href="https://dashboard.ngrok.com/signup"&gt;ngrok account&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;A Twilio account (If you &lt;a href="https://www.twilio.com/referral/OhKZOM"&gt;register for a Twilio account here&lt;/a&gt;, you'll receive $10 in Twilio credit when you upgrade to a paid account!)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;A Twilio Phone Number&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A personal phone or &lt;a href="https://www.twilio.com/blog/test-sms-and-phone-call-applications-with-twilio-dev-phone"&gt;the Twilio Dev Phone to test your bot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/docs/get-started"&gt;A free Google Cloud account&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/UsRepresentativeLookup"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it as a reference if you run into any issues, or &lt;a href="https://github.com/Swimburger/UsRepresentativeLookup/issues"&gt;submit an issue&lt;/a&gt; if you need assistance.&lt;/p&gt;
&lt;h2&gt;
  
  
  Get Started with the Google Civic Information API
&lt;/h2&gt;

&lt;p&gt;This SMS bot will use the &lt;a href="https://developers.google.com/civic-information"&gt;Google Civic Information API&lt;/a&gt; to look up the U.S. congressional districts and representatives. This API has a free tier as part of the Google Cloud Platform.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://console.cloud.google.com/"&gt;Google Cloud Console&lt;/a&gt; and &lt;a href="https://cloud.google.com/resource-manager/docs/creating-managing-projects"&gt;create a new project&lt;/a&gt;. Once the project is created, select the project using the project dropdown in the top-left navigation bar.&lt;/p&gt;

&lt;p&gt;You'll first need to enable the Civic Information API. Click on "ENABLE APIS AND SERVICES", then search for the " &lt;strong&gt;Civic Information API&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vCvknjN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/weqzRsMyJFP3ADHDxzkMX1fJiCNI4QoLUHuGS9a1xXUQvS.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vCvknjN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/weqzRsMyJFP3ADHDxzkMX1fJiCNI4QoLUHuGS9a1xXUQvS.width-500.png" alt='The "Enabled APIs &amp;amp; services" page in Google Cloud Platform. The page has a button to "ENABLE APIS AND SERVICES".' width="500" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the search result and then click &lt;strong&gt;ENABLE&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uj3XgLuq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/mnWiRUPNE2VyU-rRC5QaYlKcdIU8BIYAxETsaggVKMwqkx.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uj3XgLuq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/mnWiRUPNE2VyU-rRC5QaYlKcdIU8BIYAxETsaggVKMwqkx.width-500.png" alt='The API listing for the "Google Civic Information API" with an enable button.' width="500" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the API is enabled for your project, you'll need to create an API token to be able to authenticate when consuming the API from your .NET project. Navigate to the Credentials page, click on " &lt;strong&gt;CREATE CREDENTIALS&lt;/strong&gt;" and then on " &lt;strong&gt;API key&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k_wVB1Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/e8u-22_2WDeSs3NTxqo5CpKy-jABTmgjRaSwbVPgM_PHGk.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k_wVB1Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/e8u-22_2WDeSs3NTxqo5CpKy-jABTmgjRaSwbVPgM_PHGk.width-500.png" alt='GCP Credentials page where the user clicks on the "CREATE CREDENTIALS" button and then on the "API key" menu item.' width="500" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A modal will appear showing your API key. You will need this API key later, so copy and paste the API key somewhere safe. Next, click on the " &lt;strong&gt;Edit API key&lt;/strong&gt;" link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rrIYpCqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/q5lY2i7PdnIUZmJFESAYXGwwj4oOOW2MH8tuq0KPrnIGOg.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rrIYpCqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/q5lY2i7PdnIUZmJFESAYXGwwj4oOOW2MH8tuq0KPrnIGOg.width-500.png" alt='API key created modal with a copy button to copy the key and a link "Edit API key".' width="500" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the "​​ &lt;strong&gt;Edit API key&lt;/strong&gt;" page, you can configure the name and restrictions for your API key. This step isn't strictly required, but it is a good practice to follow the principle of least privilege, which means you should only give accounts and keys the level of access necessary to do their job.&lt;/p&gt;

&lt;p&gt;In that spirit, find the " &lt;strong&gt;API restrictions&lt;/strong&gt;" field and click on the " &lt;strong&gt;Restrict key&lt;/strong&gt;" radio button.&lt;/p&gt;

&lt;p&gt;Click on the newly appeared dropdown and select the " &lt;strong&gt;Google Civic Information API&lt;/strong&gt;" option, and then hit &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QtxF-Ehu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/E4_t56z1NRDodfjVSedz3uGRItJfM1Acljn0YmGXCx8NUC.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QtxF-Ehu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/E4_t56z1NRDodfjVSedz3uGRItJfM1Acljn0YmGXCx8NUC.width-500.png" alt='API restrictions form where the "Restrict key" radio button is enabled and the "Google Civic Information API" is selected in the dropdown.' width="500" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're all done setting up your GCP project and API key. Let's go try out the API!&lt;/p&gt;
&lt;h2&gt;
  
  
  Try out the Civic Information API
&lt;/h2&gt;

&lt;p&gt;Open a new tab in your browser and navigate to the &lt;a href="https://developers.google.com/civic-information/docs/v2/representatives/representativeInfoByAddress"&gt;Civic Information API documentation for getting representatives by address&lt;/a&gt;.The &lt;code&gt;representativeInfoByAddress&lt;/code&gt; will be the specific method that you'll be using for this project.&lt;/p&gt;

&lt;p&gt;You can pass a couple of parameters to the method to filter down the representative information, most importantly the required &lt;code&gt;address&lt;/code&gt; field. This &lt;code&gt;address&lt;/code&gt; field expects a physical mail address, however, it can be a partial address. You can pass in just the ZIP code, the city and state, or a full address including the street and number.&lt;/p&gt;

&lt;p&gt;The HTTP response will contain &lt;code&gt;divisions&lt;/code&gt;, &lt;code&gt;offices&lt;/code&gt;, and &lt;code&gt;officials&lt;/code&gt; for the given address:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;divisions&lt;/code&gt; are the political geographic areas that the address is within. Among these you would find the U.S. congressional districts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;offices&lt;/code&gt; are the political offices someone can be elected to within the returned &lt;code&gt;divisions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;officials&lt;/code&gt; are the elected officials holding the returned &lt;code&gt;offices&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, &lt;code&gt;representativeInfoByAddress&lt;/code&gt; will return all divisions, offices, and officials that represent the given address, all the way from the highest office of the President down to the local offices.&lt;br&gt;&lt;br&gt;
To filter this down, you can pass in the &lt;code&gt;levels&lt;/code&gt; and &lt;code&gt;roles&lt;/code&gt; parameters. &lt;/p&gt;

&lt;p&gt;This bot will look for U.S. representatives, so to filter down to only return representatives, you'll need to pass in &lt;code&gt;country&lt;/code&gt; into the &lt;code&gt;levels&lt;/code&gt; parameter because the U.S. representative is a national office, and you'll need to pass in &lt;code&gt;legislatorLowerBody&lt;/code&gt; to the &lt;code&gt;roles&lt;/code&gt; parameter because U.S. Congress has two houses: the Senate is the upper body and the House of Representatives is the lower body.&lt;/p&gt;

&lt;p&gt;Click the "Try it now" link at the top of the page and play around with the method.&lt;/p&gt;

&lt;p&gt;Set the &lt;code&gt;address&lt;/code&gt; field to &lt;code&gt;10600 Little Run Farm Ct, Vienna&lt;/code&gt;, the &lt;code&gt;levels&lt;/code&gt; to &lt;code&gt;country&lt;/code&gt;, and &lt;code&gt;roles&lt;/code&gt; to &lt;code&gt;legislatorLowerBody&lt;/code&gt;, and then click EXECUTE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eFULLm9U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/8pHGzoovtnsUIajKG-CaVss9k2ADDuHoJBf6PdPm_rcwWU.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eFULLm9U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/8pHGzoovtnsUIajKG-CaVss9k2ADDuHoJBf6PdPm_rcwWU.width-500.png" alt="A form to try out the representativeInfoByAddress API method. The address field has an address, the levels field is set to country, and the roles field is set to legislatorLowerBody. The API result shows the details of a single U.S. Representative." width="500" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response should have a single division, office, and official.&lt;/p&gt;

&lt;p&gt;New congressional district maps are going into effect for the November 2022 elections, so the maps and data will change soon!&lt;/p&gt;

&lt;p&gt;Now change the &lt;code&gt;address&lt;/code&gt; field to &lt;code&gt;Little Run Farm Ct, Vienna&lt;/code&gt; and click EXECUTE again.&lt;br&gt;&lt;br&gt;
This time no division, office, or official is returned. Why is that?&lt;/p&gt;

&lt;p&gt;When you look up this street and overlay the congressional district maps, you can see that the border of district 10 and 11 cuts through this street. In fact, the border follows a creek that runs between house number 10600 and 10602.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---irJ6Dgx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/jgl5iGwYPZmPklt5wPouBIBXAwnTgg2nmwuv77DgVM49ge.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---irJ6Dgx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/jgl5iGwYPZmPklt5wPouBIBXAwnTgg2nmwuv77DgVM49ge.width-500.png" alt='A map with Virginia"s Congressional District 11 overlayed. On the map, you can see two houses on the street Little Run Farm Ct are part of District 10, and the rest of the street is part of District 11.' width="500" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's why the Civic Information API cannot determine the district when you pass in the street without a house number, and as a result, &lt;code&gt;representativeInfoByAddress&lt;/code&gt; returns nothing.&lt;/p&gt;

&lt;p&gt;The house.gov representative lookup tool will claim that house number 10600 and 10602 are both in Virginia's 11th congressional district which is incorrect (for the pre-2022 maps which are current at the time of writing this). Luckily, the Civic Information API returns the correct result.&lt;/p&gt;

&lt;p&gt;This does not mean you always have to enter a full address, in fact when you put in the ZIP code 20301 without anything else, &lt;code&gt;representativeInfoByAddress&lt;/code&gt; returns the information for the 8th congressional district. That's because the entire ZIP code is located within district 8, so the API doesn't need more information to determine the district.&lt;/p&gt;

&lt;p&gt;Now that you're more familiar with the Civic Information API and specifically the &lt;code&gt;representativeInfoByAddress&lt;/code&gt; method, it's time to consume the API from .NET!&lt;/p&gt;
&lt;h2&gt;
  
  
  Build an SMS bot to look up representatives
&lt;/h2&gt;

&lt;p&gt;This project will be built on ASP.NET Core Minimal APIs. Open a shell and run the following command to create the project and navigate into the project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new web &lt;span class="nt"&gt;-o&lt;/span&gt; RepresentativeBot
&lt;span class="nb"&gt;cd &lt;/span&gt;RepresentativeBot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the project in your preferred editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieve Representatives from the Civic Information API
&lt;/h3&gt;

&lt;p&gt;You can consume the Civic Information API using an HTTP client, but Google generates .NET libraries for all of its APIs including this one, which is convenient. You can install the library by adding the &lt;a href="https://www.nuget.org/packages/Google.Apis.CivicInfo.v2"&gt;Google.Apis.CivicInfo.v2 NuGet package&lt;/a&gt;using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Google.Apis.CivicInfo.v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file &lt;em&gt;RepresentativeLookupClient.cs&lt;/em&gt; and add the following C# code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Google&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Google.Apis.CivicInfo.v2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Google.Apis.CivicInfo.v2.Data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Google.Apis.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Apis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CivicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RepresentativesResource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RepresentativeLookupClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;CivicInfoService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RepresentativeLookupClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gcpApiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CivicInfoService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BaseClientService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Initializer&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ApplicationName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"US Representative Lookup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gcpApiKey&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Representative&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetRepresentativeByAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RepresentativeInfoByAddressRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// Level = Country and Roles = LegislatorLowerBody filters down to U.S. Representatives&lt;/span&gt;
            &lt;span class="n"&gt;Levels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RepresentativeInfoByAddressRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LevelsEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RepresentativeInfoByAddressRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RolesEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LegislatorLowerBody&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;RepresentativeInfoResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GoogleApiException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BadRequest&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                                           &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Failed to parse address"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FailedToParseAddressException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// if the address did not resolve to a specific district,&lt;/span&gt;
        &lt;span class="c1"&gt;// for example, no district, or multiple districts&lt;/span&gt;
        &lt;span class="c1"&gt;// then response.Offices will be null&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Offices&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RepresentativeNotFoundException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// only one office, one division, and one official will be returned&lt;/span&gt;
        &lt;span class="c1"&gt;// the office of U.S. representative, the congressional district, and the elected official&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;office&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Offices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;division&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Divisions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;office&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DivisionId&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;official&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Officials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Representative&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;DistrictName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;division&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;RepresentativeName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;official&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Party&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;official&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Party&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;PhotoUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;official&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhotoUrl&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;RepresentativeLookupClient&lt;/code&gt; constructor accepts the GCP API key as the &lt;code&gt;gcpApiKey&lt;/code&gt; parameter, which is then used to create a new &lt;code&gt;CivicInfoService&lt;/code&gt; object that is stored into the &lt;code&gt;service&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;GetRepresentativeByAddress&lt;/code&gt; method accepts an &lt;code&gt;address&lt;/code&gt; as a parameter and returns a &lt;code&gt;Task&lt;/code&gt; that will resolve to a &lt;code&gt;Representative&lt;/code&gt; object. In this method, a &lt;code&gt;RepresentativeInfoByAddressRequest&lt;/code&gt; object is created passing in the &lt;code&gt;service&lt;/code&gt; into the constructor and the &lt;code&gt;address&lt;/code&gt; into the &lt;code&gt;Address&lt;/code&gt; property.&lt;br&gt;&lt;br&gt;
The &lt;code&gt;Levels&lt;/code&gt; and &lt;code&gt;Roles&lt;/code&gt; property are hard-coded to &lt;code&gt;Country&lt;/code&gt; and &lt;code&gt;LegislatorLowerBody&lt;/code&gt; to filter down to U.S. representatives and congressional districts.&lt;/p&gt;

&lt;p&gt;The API request is made using the &lt;code&gt;request.ExecuteAsync&lt;/code&gt; method, which is surrounded by a try/catch block to handle some of the exceptions that can be thrown. When the API fails to parse the address, a custom exception of type &lt;code&gt;FailedToParseAddressException&lt;/code&gt; is thrown. When no offices are returned, because the API couldn't determine the correct congressional district, or for some other reason, then a custom exception of &lt;code&gt;RepresentativeNotFoundException&lt;/code&gt; is thrown. These two custom exceptions will be caught later to provide user-friendly messages to the end user.&lt;/p&gt;

&lt;p&gt;However, if an office is returned, then a new &lt;code&gt;Representative&lt;/code&gt; object is created with the data from the &lt;code&gt;response&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Representative&lt;/code&gt; object only has a couple of properties, however, feel free to extend the class and retrieve more information from the &lt;code&gt;response&lt;/code&gt; object. The API retrieves a bunch of &lt;a href="https://developers.google.com/civic-information/docs/v2/representatives/representativeInfoByAddress#response"&gt;information&lt;/a&gt; like a physical address, an email address, a phone number, social media links, etc.&lt;/p&gt;

&lt;p&gt;For example, here's &lt;a href="https://www.twilio.com/blog/sms-service-contact-local-representatives-twilio-studio-sendgrid"&gt;a tutorial that uses the same API to retrieve contact information of the representatives and uses Twilio SMS and SendGrid to contact them, built on Twilio Studio and Functions&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Create 3 new files named &lt;em&gt;Representative.cs&lt;/em&gt;, &lt;em&gt;FailedToParseAddressException.cs&lt;/em&gt;, and &lt;em&gt;RepresentativeNotFoundException.cs&lt;/em&gt; and then update each file with the contents listed below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Representative.cs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Representative&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;DistrictName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;RepresentativeName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Party&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PhotoUrl&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;FailedToParseAddressException.cs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FailedToParseAddressException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;FailedToParseAddressException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;innerException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;innerException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;innerException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;RepresentativeNotFoundException.cs&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RepresentativeNotFoundException&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the &lt;code&gt;RepresentativeLookupClient&lt;/code&gt; in your future API endpoints, you'll need to add it to ASP.NET Core's Dependency Injection (DI) container. Open your &lt;em&gt;Program.cs&lt;/em&gt; file and add the highlighted lines to the existing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gcpApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GcpApiKey"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GcpApiKey is not configured."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RepresentativeLookupClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gcpApiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GCP API key you created earlier needs to be configured using the &lt;code&gt;GcpApiKey&lt;/code&gt; configuration key. Since the API key is a secret, you should avoid hard-coding it or storing it in source control.You can use environment variables or a secure vault service, or for local development, you can use the .NET Secrets Manager also known as user secrets.&lt;/p&gt;

&lt;p&gt;Run the following command to initialize user secrets in your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet user-secrets init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, configure the GCP API key as a user secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;GcpApiKey &lt;span class="s2"&gt;"[YOUR_GCP_API_KEY]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_GCP_API_KEY]&lt;/code&gt; with the API key secret you copied earlier.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;RepresentativeLookupClient&lt;/code&gt; is now ready to be used! Feel free to add some code to quickly test it out, otherwise, move to the next step where you'll integrate the client into your minimal API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create your Twilio Webhook
&lt;/h3&gt;

&lt;p&gt;There's a useful library called &lt;code&gt;Twilio.AspNet&lt;/code&gt; which will help you build Twilio webhooks.&lt;br&gt;&lt;br&gt;
Add the &lt;a href="https://www.nuget.org/packages/Twilio.AspNet.Core/"&gt;Twilio.AspNet.Core NuGet package&lt;/a&gt; to install the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Twilio.AspNet.Core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, open your &lt;em&gt;Program.cs&lt;/em&gt; file and update it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RepresentativeBot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.AspNet.Core.MinimalApi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Twilio.TwiML.Messaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;gcpApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"GcpApiKey"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GcpApiKey is not configured."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RepresentativeLookupClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gcpApiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HttpResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messagingResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessagingResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"HasBeenGreeted"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"False"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HasBeenGreeted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"True"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome to the U.S. Representative lookup bot. Respond with your address."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// TODO: remove me&lt;/span&gt;
    &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YOU HAVE BEEN GREETED ALREADY!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TwiML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new &lt;em&gt;/message&lt;/em&gt; endpoint accepts a couple of parameters that will be dependency injected.&lt;br&gt;&lt;br&gt;
The &lt;code&gt;MessagingResponse&lt;/code&gt; object will help you construct &lt;a href="https://www.twilio.com/docs/glossary/what-is-twilio-markup-language-twiml"&gt;TwiML&lt;/a&gt; to respond to an incoming message. &lt;/p&gt;

&lt;p&gt;The first message you will respond with, is an introductory message. To only introduce the bot once, a cookie is used to keep track of whether the sender has been greeted with the introduction. &lt;/p&gt;

&lt;p&gt;You can use cookies when responding to Twilio webhooks, however, the cookie will expire after 4 hours and you can only store one cookie. In this tutorial you only need to keep track of one thing using cookies, but if you need to keep track of more state you can use a session cookie instead and store state in session. Learn more about &lt;a href="https://support.twilio.com/hc/en-us/articles/223136287-How-do-Twilio-cookies-work-"&gt;the cookie limitations with Twilio webhooks here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;messagingResponse.Message&lt;/code&gt; method is used to create a TwiML &lt;code&gt;Message&lt;/code&gt; verb that will respond to the sender. The &lt;code&gt;Results.Extensions.TwiML&lt;/code&gt; method creates a &lt;code&gt;TwiMLResult&lt;/code&gt; that will serialize the &lt;code&gt;messagingResponse&lt;/code&gt; to XML and set the correct content-type header.&lt;/p&gt;

&lt;p&gt;If the sender has already been greeted, the bot responds with "YOU HAVE BEEN GREETED ALREADY!".&lt;/p&gt;

&lt;p&gt;However, this last message is a placeholder. Replace the placeholder with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadFormAsync&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ConfigureAwait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;representativeLookupClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RepresentativeLookupClient&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;representative&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;representativeLookupClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRepresentativeByAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;$"Your representative is &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;representative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RepresentativeName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;representative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Party&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
        &lt;span class="s"&gt;$", representing &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;representative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DistrictName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;."&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;representative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhotoUrl&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;representative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PhotoUrl&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FailedToParseAddressException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The address you entered is invalid."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RepresentativeNotFoundException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Your representative could not be determined. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                              &lt;span class="s"&gt;"This may be because there's no representative or multiple representatives for the given location. "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                              &lt;span class="s"&gt;"Try entering a more specific address."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"An unexpected error occurred when looking up representative"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;messagingResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"An unexpected error occurred."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the sender has been greeted, the bot will assume that whatever message is sent next will be the address to return representative information for. The message that is sent will be stored in the form encoded "Body" parameter. &lt;/p&gt;

&lt;p&gt;After getting the body of the message, the bot will request an instance of &lt;code&gt;RepresentativeLookupClient&lt;/code&gt; from the DI container, then the bot invokes the &lt;code&gt;RepresentativeLookupClient.GetRepresentativeByAddress&lt;/code&gt; method passing in the body as a parameter.&lt;br&gt;&lt;br&gt;
If no exception occurs, two messages will be added. One message will describe the district and representative, and the other will return the image of the elected official, if an image is provided.&lt;/p&gt;

&lt;p&gt;A TwiML &lt;code&gt;Message&lt;/code&gt; with &lt;code&gt;Media&lt;/code&gt; will send the media as an MMS message, not as an SMS. You can choose to create a single message with the representative information and the image which looks great!&lt;br&gt;&lt;br&gt;
However, I chose to create separate messages because of these reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An SMS is smaller and will be transmitted to the user faster, giving them the information as quickly as possible. &lt;/li&gt;
&lt;li&gt;MMS uses mobile internet which may not be turned on, or there may be no mobile internet connectivity in rural areas.&lt;/li&gt;
&lt;li&gt;MMS may not be properly configured or disabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your project is ready! Start your project using &lt;code&gt;dotnet run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To quickly test out your &lt;em&gt;/message&lt;/em&gt; endpoint, you can use the following PowerShell or Bash script in a new shell tab:&lt;/p&gt;

&lt;p&gt;PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;YOUR_LOCALHOST_URL&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;/message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Hi'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'application/x-www-form-urlencoded'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-SessionVariable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SmsBotSession&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;YOUR_LOCALHOST_URL&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;/message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'10602 Little Run Farm Ct, Vienna'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'application/x-www-form-urlencoded'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-WebSession&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$SmsBotSession&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="o"&gt;[&lt;/span&gt;YOUR_LOCALHOST_URL]/message &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Body=Hi"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cookie-jar&lt;/span&gt; SmsBotCookieJar

curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="o"&gt;[&lt;/span&gt;YOUR_LOCALHOST_URL]/message &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"Body=10602 Little Run Farm Ct, Vienna"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cookie&lt;/span&gt; SmsBotCookieJar

&lt;span class="nb"&gt;rm &lt;/span&gt;SmsBotCookieJar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_LOCALHOST_URL]&lt;/code&gt; with one of the localhost URLs printed out when running your .NET project.&lt;/p&gt;

&lt;p&gt;The first web request will be responded to with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Welcome to the U.S. representative lookup bot. Respond with your address.&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The subsequent request will be responded to with the following TwiML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Response&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;Your representative is Gerald E. "Gerry" Connolly (Democratic Party), representing Virginia's 11th congressional district.&lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Message&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Media&amp;gt;&lt;/span&gt;http://bioguide.congress.gov/bioguide/photo/C/C001078.jpg&lt;span class="nt"&gt;&amp;lt;/Media&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Message&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is working as expected, you can now configure your Twilio Phone Number to use your Minimal API endpoint as the SMS webhook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your Twilio SMS Webhook
&lt;/h3&gt;

&lt;p&gt;Start your project if it isn't running yet (&lt;code&gt;dotnet run&lt;/code&gt;) and take note of one of the localhost URLs printed out by the application. For Twilio to be able to send HTTP requests to your local web server, the server needs to become publicly accessible. ngrok is a free secure tunneling service that can make your local web servers public.&lt;/p&gt;

&lt;p&gt;Run the following ngrok command in a separate shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="o"&gt;[&lt;/span&gt;YOUR_ASPNET_URL]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;[YOUR_ASPNET_URL]&lt;/code&gt; with the localhost URL from your .NET application. If you're using an HTTPS localhost URL, you'll need to &lt;a href="https://ngrok.com/docs/secure-tunnels#tunnel-authtokens"&gt;authenticate ngrok&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The ngrok command will display an HTTPS Forwarding URL that makes your local web server public.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s3BwemZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Xcf4sbW_rAQ_fuqioIOcEj42MIkuRk20psfYf6nfesowBZ.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s3BwemZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Xcf4sbW_rAQ_fuqioIOcEj42MIkuRk20psfYf6nfesowBZ.width-500.png" alt="ngrok http command output showing information about the secure tunnel, most importantly the public forwarding URLs" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to update your Twilio Phone Number to send HTTP requests to your &lt;em&gt;/message&lt;/em&gt; endpoint via the ngrok Forwarding URL. The URL should look something like &lt;strong&gt;&lt;em&gt;&lt;a href="https://1cc74f4c9f70.ngrok.io/message"&gt;https://1cc74f4c9f70.ngrok.io/message&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/incoming?frameUrl=%2Fconsole%2Fphone-numbers%2Fincoming%3Fx-target-region%3Dus1"&gt;the Active Phone Numbers section in the Twilio Console&lt;/a&gt; and click on your Twilio Phone Number.&lt;br&gt;&lt;br&gt;
This will take you to the configuration for the phone number. Find the Messaging section and under the " &lt;strong&gt;A MESSAGE COMES IN&lt;/strong&gt;" label, set the dropdown to Webhook. In the text field next to it, enter your ngrok forwarding URL with &lt;em&gt;/message&lt;/em&gt; appended to it. Select “ &lt;strong&gt;HTTP POST&lt;/strong&gt; ” in the last dropdown. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ljd_yOUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/JA3Ixn88UiVfm7Te23s7gRxbBo_44uK-SSAOph44EmKMcu.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ljd_yOUe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/JA3Ixn88UiVfm7Te23s7gRxbBo_44uK-SSAOph44EmKMcu.width-500.png" alt='Messaging section in the Twilio Phone Number configuration page. Under the "A MESSAGE COMES IN" label, a dropdown is set to "Webhook", the text field next to it is configured to "https://eaa3c4359db8.ngrok.io/message", and the dropdown next to that is set to "HTTP POST".' width="500" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your SMS Twilio Bot
&lt;/h3&gt;

&lt;p&gt;To test your bot, you can either use a personal phone to send text messages, or you can &lt;a href="https://www.twilio.com/blog/test-sms-and-phone-call-applications-with-twilio-dev-phone"&gt;use the Twilio Dev Phone to test&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Send a text message to your Twilio Phone Number with anything as the body. You should receive the greeting you configured. Once you've been greeted, reply with any address that you want to retrieve representative and district information for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--34KRl-zt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/XUTXLzXFkI16xCVixHDGVCYGvEKh9oSGRwzjoNlEVwHCgK.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--34KRl-zt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/XUTXLzXFkI16xCVixHDGVCYGvEKh9oSGRwzjoNlEVwHCgK.width-500.png" alt='Conversation between an iPhone user and the SMS bot. The user says "Hi" and the bot responds with "Welcome to the U.S. Representative lookup bot. Respond with your address. The user sends an address and the bot responds with "Your representative is Gerald E. Connolly (Democratic Party) representing Virginia"s 11th congressional district. Then the bot sends an image of the representative.' width="500" height="933"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Future improvements
&lt;/h2&gt;

&lt;p&gt;This bot is a great start, however, you can improve this solution in a couple of ways:&lt;/p&gt;

&lt;p&gt;You can respond to the user with more information about the elected official and how to reach them. And instead of only returning U.S. representatives, you could return elected officials from every level.&lt;/p&gt;

&lt;p&gt;Currently, anyone can send HTTP requests to your webhook and pretend to be Twilio. This bot doesn't respond with any sensitive information so the risk is lower, however, it is always a good idea to &lt;a href="https://www.twilio.com/docs/usage/tutorials/how-to-secure-your-csharp-aspnet-core-app-by-validating-incoming-twilio-requests"&gt;secure your webhooks using webhook signature validation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial, you manually spun up a secure tunnel using ngrok, but you could also do this &lt;a href="https://www.twilio.com/blog/integrate-ngrok-into-aspdotnet-core-startup-and-automatically-update-your-webhook-urls"&gt;programmatically so that a tunnel is created and your Twilio SMS webhook is updated whenever you start your .NET application&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;If you made it this far, a big shout-out to you! 🎉&lt;/p&gt;

&lt;p&gt;In this tutorial, you learned how to respond to incoming text messages with Twilio, but if you’re looking to learn more, you can also &lt;a href="https://www.twilio.com/blog/2016/04/send-an-sms-message-with-c-in-30-seconds.html"&gt;initiate text messages yourself using the Twilio SDK&lt;/a&gt;, or &lt;a href="https://www.twilio.com/blog/send-emails-using-the-sendgrid-api-with-dotnetnet-6-and-csharp"&gt;send emails using Twilio SendGrid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you found this useful, let me know and share what you're working on. I can't wait to see what you build!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image credit: &lt;a href="https://www.linkedin.com/in/matthew-geason"&gt;Matthew Geason&lt;/a&gt; has kindly allowed us to use his picture of the U.S. Capitol.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>aspnet</category>
      <category>twilio</category>
    </item>
    <item>
      <title>How to Bulk Email with C# and .NET: Zero to Hero</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Tue, 02 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-bulk-email-with-c-and-net-zero-to-hero-d0i</link>
      <guid>https://dev.to/twilio/how-to-bulk-email-with-c-and-net-zero-to-hero-d0i</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/bulk-email-with-csharp-and-dotnet"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://www.twilio.com/blog/send-emails-using-the-sendgrid-api-with-dotnetnet-6-and-csharp"&gt;previous tutorial&lt;/a&gt;, I shared how you can send individual emails using the SendGrid API. This use case is perfect for &lt;a href="https://sendgrid.com/resource/what-is-transactional-email/"&gt;transactional emails&lt;/a&gt; where you send an email to a single recipient or a small number of recipients. But what if you need to send emails to a very large audience? Well, in this tutorial you'll learn how to send bulk emails using the SendGrid API and C# .NET. &lt;/p&gt;

&lt;p&gt;If you're not familiar with sending transactional emails using .NET, I advise you go through &lt;a href="https://www.twilio.com/blog/send-emails-using-the-sendgrid-api-with-dotnetnet-6-and-csharp"&gt;the previous post about sending emails with C# and SendGrid first&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Here’s what you will need to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads"&gt;Git CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0"&gt;.NET 6 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/"&gt;Visual Studio&lt;/a&gt;, or &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A Twilio SendGrid account. &lt;a href="https://signup.sendgrid.com/"&gt;Sign up for a SendGrid account here&lt;/a&gt; to send up to 100 emails per day completely free of charge.&lt;/li&gt;
&lt;li&gt;You'll need an &lt;strong&gt;Email Sender&lt;/strong&gt; configured in SendGrid to send emails from, and you'll need an &lt;strong&gt;API key&lt;/strong&gt; with permission to send emails. Both of these steps can be found in the &lt;a href="https://www.twilio.com/blog/send-emails-using-the-sendgrid-api-with-dotnetnet-6-and-csharp#configure-sendgrid"&gt;Configuring your SendGrid account to send emails section in the previous tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An email address for testing, preferably from an email service that supports &lt;a href="https://en.wikipedia.org/wiki/Email_address#Subaddressing"&gt;Subaddressing&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many email services like Gmail and Outlook allow you to put a &lt;code&gt;+&lt;/code&gt; sign and any string before the &lt;code&gt;@&lt;/code&gt; symbol like this &lt;code&gt;youremail+test123@gmail.com&lt;/code&gt;. Emails sent to this type of address will still be delivered to the original email address. This feature is called &lt;a href="https://en.wikipedia.org/wiki/Email_address#Subaddressing"&gt;Subaddressing&lt;/a&gt;. By using subaddressing, you will be able to test sending multiple emails to "different" addresses, however, email services will still prevent large amount of emails from coming through even when using subaddressing.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/BulkEmail"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/BulkEmail"&gt;submit an issue&lt;/a&gt;, if you run into problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send bulk email with C# .NET
&lt;/h2&gt;

&lt;p&gt;As is common in software development, there are many solutions to a given problem, and the best solution depends on your use case; the same goes for sending email in bulk. This tutorial will walk you through all the different ways of sending bulk email, and you'll learn which to use for your specific use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;For this tutorial, I have provided a starter project. Open a shell and run the following commands to clone the project and navigate to the project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Swimburger/BulkEmail.git &lt;span class="nt"&gt;--branch&lt;/span&gt; start
&lt;span class="nb"&gt;cd &lt;/span&gt;BulkEmail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project is a console application which currently only sends one email.&lt;br&gt;&lt;br&gt;
The console app has the following C# files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Program.cs&lt;/em&gt;: This is the starting point of the application, it will configure everything and then send the emails.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;SenderOptions.cs&lt;/em&gt;: This file has the &lt;code&gt;SenderOptions&lt;/code&gt; class which is used to store some configuration about the SendGrid Email Sender.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;SubscriberRepository.cs&lt;/em&gt;: This file has the &lt;code&gt;SubscriberRepository&lt;/code&gt; class which is responsible for returning your imaginary subscribers, represented as &lt;code&gt;Person&lt;/code&gt; objects. The &lt;code&gt;Person&lt;/code&gt; class is provided by &lt;a href="https://github.com/bchavez/Bogus"&gt;the Bogus library&lt;/a&gt; and the subscribers are fake data generated using the same library, for testing purposes. Let's pretend this class retrieves real data from a SQL database as it would look exactly the same from the outside.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to take a look at these C# files, but don't worry about them, because the only important C# file is &lt;em&gt;EmailSender.cs&lt;/em&gt;. This file has the &lt;code&gt;EmailSender&lt;/code&gt; class which you will update so that it sends bulk email.&lt;/p&gt;

&lt;p&gt;This project does need some configuration which you will store as user secrets. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;SendGridApiKey &lt;span class="o"&gt;[&lt;/span&gt;SENDGRID_API_KEY]
dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;Sender:Email &lt;span class="o"&gt;[&lt;/span&gt;SENDER_EMAIL]
dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;Sender:Name &lt;span class="s1"&gt;'[SENDER_NAME]'&lt;/span&gt;
dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;ToEmailTemplate &lt;span class="s1"&gt;'[YOUR_EMAIL_TEMPLATE]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[SENDGRID_API_KEY]&lt;/code&gt; with the API key you created in SendGrid,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[SENDER_EMAIL]&lt;/code&gt; with your Sender email address that you verified in SendGrid,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[SENDER_NAME]&lt;/code&gt; with the name you want the recipients to see,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[YOUR_EMAIL_TEMPLATE]&lt;/code&gt; with the email address you use to test. If you're using subaddressing, use this format: &lt;code&gt;youremail+{0}@gmail.com&lt;/code&gt;. &lt;code&gt;{0}&lt;/code&gt; will be replaced by the program with unique numbers. If you're not using subaddressing, use your email address without any special modifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your project should be ready. Try it out by running the project using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--SubscriberCount&lt;/span&gt; 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though you specified &lt;code&gt;5&lt;/code&gt; subscribers, you should only receive one email in your inbox. That's because the &lt;code&gt;EmailSender&lt;/code&gt; class only sends one email at the moment.&lt;/p&gt;

&lt;p&gt;Let's take a look at the &lt;em&gt;EmailSender.cs&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Encodings.Web&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;BulkEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SubscriberRepository&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ISendGridClient&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HtmlEncoder&lt;/span&gt; &lt;span class="n"&gt;htmlEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SenderOptions&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;SubscriberRepository&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ISendGridClient&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SenderOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;senderOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HtmlEncoder&lt;/span&gt; &lt;span class="n"&gt;htmlEncoder&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscriberRepository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendGridClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;htmlEncoder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;htmlEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;senderOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendToSubscribers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ahoy matey!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;friend&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email not queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;EmailSender&lt;/code&gt; receives a bunch of parameters via its constructor and stores them into fields. Some of these are unused right now but will be used later. The &lt;code&gt;SendToSubscribers&lt;/code&gt; method is responsible for sending an email to all subscribers coming from the &lt;code&gt;SubscriberRepository&lt;/code&gt;, but currently it only grabs the first subscriber and stores it in the &lt;code&gt;subscriber&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;SendToSubscribers&lt;/code&gt; method then creates a &lt;code&gt;SendGridMessage&lt;/code&gt; from your configured sender and adds the &lt;code&gt;subscriber&lt;/code&gt; as the recipient. Next, the &lt;code&gt;SendGridMessage&lt;/code&gt; is passed into the &lt;code&gt;SendEmailAsync&lt;/code&gt; method which sends the email to the SendGrid API. The SendGrid API will queue the email and return an HTTP success status code in the response if successful. Lastly, the method logs whether the email was queued or not.&lt;/p&gt;

&lt;p&gt;Currently, the program only emails one subscriber, however, the goal is to send an email to all subscribers. Let's fix that!&lt;/p&gt;

&lt;h3&gt;
  
  
  Bulk email using loops
&lt;/h3&gt;

&lt;p&gt;The first solution is to wrap the email code in a loop. In this example, you can use a &lt;code&gt;foreach&lt;/code&gt; -loop like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ahoy matey!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;friend&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email not queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace lines 34 to 47 in the &lt;em&gt;EmailSender.cs&lt;/em&gt; file with the above code and save the file. Use the following command to try it out with 5 subscribers, or however many you'd like to spam your own inbox with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--SubscriberCount&lt;/span&gt; 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the easiest and most flexible solution, however, it is also the least performant solution. &lt;/p&gt;

&lt;p&gt;You could further optimize this solution using parallelization or using &lt;code&gt;Task.WhenAll&lt;/code&gt;, however, keep in mind that you can send a maximum of 10,000 API requests per second as noted in &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/v3-mail-send-faq#are-there-limits-on-how-often-i-can-send-email-and-how-many-recipients-i-can-send-to"&gt;the v3 Mail Send API FAQ&lt;/a&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  Customize the email per recipient
&lt;/h4&gt;

&lt;p&gt;When you send emails in bulk by submitting a &lt;code&gt;SendGridMessage&lt;/code&gt; for every recipient, you have complete control over every email. To customize the subject and email body, you can use any means necessary.&lt;/p&gt;

&lt;p&gt;Here's an example that uses string interpolation to customize the subject and email body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Ahoy &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Welcome aboard &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;htmlEncoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you build HTML templates and embed user input, your application may become vulnerable to HTML injection attacks. To prevent HTML injection attacks, always encode user input. In the example above the subscriber's full name is HTML encoded using the &lt;code&gt;HtmlEncoder.Encode&lt;/code&gt; method. For more details, read &lt;a href="https://www.twilio.com/blog/prevent-email-html-injection-in-csharp-and-dotnet"&gt;&lt;em&gt;How to prevent email HTML injection in C# and .NET&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This works fine for a proof of concept, but in a real application you could use a template engine to generate your HTML. Check out this &lt;a href="https://www.twilio.com/blog/render-emails-using-razor-templating"&gt;article about rendering emails using Razor templates&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bulk email using Personalizations
&lt;/h3&gt;

&lt;p&gt;The loop solution sends a new API request for every subscriber, but there is actually a way to send many emails using a single API request.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;SendGridMessage&lt;/code&gt; class has a property called &lt;code&gt;Personalizations&lt;/code&gt; which is of type &lt;code&gt;List&amp;lt;Personalization&amp;gt;&lt;/code&gt;. Using these &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/personalizations"&gt;personalizations&lt;/a&gt;, you can customize the message for specific recipients using properties like &lt;code&gt;From&lt;/code&gt;, &lt;code&gt;Tos&lt;/code&gt;, &lt;code&gt;Ccs&lt;/code&gt;, &lt;code&gt;Bccs&lt;/code&gt;, &lt;code&gt;Subject&lt;/code&gt;, etc. However, you cannot override the &lt;code&gt;TextContent&lt;/code&gt; or &lt;code&gt;HtmlContent&lt;/code&gt; property from the &lt;code&gt;SendGridMessage&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Take a look at this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ahoy matey!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;friend&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Personalizations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Personalization&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Tos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jon@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Jon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Tos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jill@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Jill"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jane@localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Jane"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello world!"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SendGridMessage&lt;/code&gt; in the snippet above will send 2 emails because there are 2 personalizations. One email with the subject "Ahoy matey!" to jon@localhost, and 1 email with the subject "Hello world!" to jill@localhost and jane@localhost.&lt;/p&gt;

&lt;p&gt;When you add multiple email addresses to the &lt;code&gt;Tos&lt;/code&gt; property, each recipient can see and reply to all the other recipients. So Jill and Jane in the example above will be able to see and reply to each other. To avoid this you can create separate &lt;code&gt;Personalization&lt;/code&gt; objects for each recipient. In fact, when you previously used the &lt;code&gt;SendGridMessage.AddTo&lt;/code&gt; method, the method created a personalization for you.&lt;/p&gt;

&lt;p&gt;You can have up to 1,000 recipients per &lt;code&gt;SendGridMessage&lt;/code&gt;. &lt;code&gt;Tos&lt;/code&gt;, &lt;code&gt;Ccs&lt;/code&gt;, and &lt;code&gt;Bccs&lt;/code&gt; across all personalizations all add up to the 1,000 limit. The maximum number of &lt;code&gt;Personalization&lt;/code&gt; objects per &lt;code&gt;SendGridMessage&lt;/code&gt; is also 1,000.&lt;/p&gt;

&lt;p&gt;Since you can only have 1,000 recipients per message, you'll need to paginate over the subscribers. There is a &lt;code&gt;GetByPage(int pageSize, int pageIndex)&lt;/code&gt; method in the &lt;code&gt;SubscriberRepository&lt;/code&gt; to help with pagination. The following code will paginate over all subscribers by 1,000 subscribers at a time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriberCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;amountOfPages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ceiling&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;subscriberCount&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;amountOfPages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you weren't limited by 1,000 recipients per message, it would still be a good idea to paginate over the subscribers to reduce the memory usage of your application. As the number of subscribers increases, your application would run out of memory when loading all subscribers into memory at once.&lt;/p&gt;

&lt;p&gt;Now that you have your subscribers, you can create a &lt;code&gt;Personalization&lt;/code&gt; object for every one of them. Here's how you can do this using a &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq"&gt;LINQ query&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ahoy matey!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;friend&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// max 1000 Personalizations&lt;/span&gt;
    &lt;span class="n"&gt;Personalizations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Tos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After putting all the previous code snippets together, your &lt;code&gt;SendToSubscribers&lt;/code&gt; method should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendToSubscribers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscriberCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;amountOfPages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Ceiling&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;subscriberCount&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;amountOfPages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscriberRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Ahoy matey!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;friend&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

            &lt;span class="c1"&gt;// max 1000 Personalizations&lt;/span&gt;
            &lt;span class="n"&gt;Personalizations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Tos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
            &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email not queued"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can lower the &lt;code&gt;pageSize&lt;/code&gt; number to test pagination without sending 1,000 emails to your inbox.&lt;br&gt;&lt;br&gt;
For example, for testing purposes, you could set the &lt;code&gt;pageSize&lt;/code&gt; to 5 and the &lt;code&gt;SubscriberCount&lt;/code&gt; to 20:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--SubscriberCount&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Customize the email per recipient
&lt;/h4&gt;

&lt;p&gt;As I mentioned before, you can override the email subject in a &lt;code&gt;Personalization&lt;/code&gt; object, but not the &lt;code&gt;HtmlContent&lt;/code&gt; or &lt;code&gt;TextContent&lt;/code&gt;. However, you can still customize the email body per recipient using &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/substitution-tags"&gt;Substitution Tags&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, add substitution tags to your content like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;HtmlContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Welcome aboard &amp;lt;b&amp;gt;-FullName-&amp;lt;/b&amp;gt; ⚓️"&lt;/span&gt;&lt;span class="err"&gt;️&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add substitutions to your &lt;code&gt;Personalization&lt;/code&gt; objects like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// Substitutions data is max 10,000 bytes per Personalization object&lt;/span&gt;
    &lt;span class="n"&gt;Substitutions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"-FullName-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;htmlEncoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The substitution tag is decorated with dashes (-) in this example, however, you can use any type of decoration characters, as long as the substitution tags in the &lt;code&gt;HtmlContent&lt;/code&gt; and &lt;code&gt;TextContent&lt;/code&gt; match with the keys of the &lt;code&gt;Substitutions&lt;/code&gt; dictionary.&lt;/p&gt;

&lt;p&gt;You can also use these substitution tags inside of your email subject. Putting this together, you can send personalized bulk emails like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var message = new SendGridMessage
{
    From = new EmailAddress(sender.Email, sender.Name),
    Subject = "Ahoy -FirstName_Raw-!",
    HtmlContent = "Welcome aboard &amp;lt;b&amp;gt;-FullName-&amp;lt;/b&amp;gt; ⚓️"️,

    // max 1000 Personalizations
    Personalizations = subscribers.Select(s =&amp;gt; new Personalization
    {
        Tos = new List&amp;lt;EmailAddress&amp;gt; {new EmailAddress(s.Email, s.FullName)},
        // Substitutions data is max 10,000 bytes per Personalization object
        Substitutions = new Dictionary&amp;lt;string, string&amp;gt;
        {
            {"-FirstName_Raw-", s.FirstName},
            {"-FullName-", htmlEncoder.Encode(s.FullName)}
        }
    }).ToList(),
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I chose to suffix the first name substitution tag with &lt;code&gt;_Raw&lt;/code&gt; to indicate to myself and others that this variable is not HTML encoded and should not be used in HTML content, to avoid HTML injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send bulk email using Dynamic Email Templates
&lt;/h3&gt;

&lt;p&gt;Thus far, you provided the email subject and body from code, however, you can also use &lt;a href="https://sendgrid.com/solutions/email-api/dynamic-email-templates/"&gt;Dynamic Email Templates&lt;/a&gt;. You can create these templates using the SendGrid UI or API, and then instead of specifying the subject and content in your &lt;code&gt;SendGridMessage&lt;/code&gt;, you set the ID of your template to the &lt;code&gt;SendGridMessage.TemplateId&lt;/code&gt; property. Dynamic Email Templates use &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/using-handlebars"&gt;the Handlebars templating language&lt;/a&gt;, which allows you to do variable substitution, conditional rendering, looping, and more.&lt;/p&gt;

&lt;p&gt;Implementing Dynamic Email Templates is out of the scope of this tutorial, however, here is what your &lt;code&gt;SendGridMessage&lt;/code&gt; would look like to achieve the same result as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;TemplateId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"d-0a664e681ed14d76bd452637a15b20ab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Personalizations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Personalization&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Tos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
        &lt;span class="n"&gt;TemplateData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;FirstName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;FullName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of using the &lt;code&gt;Substitutions&lt;/code&gt; property on your &lt;code&gt;Personalization&lt;/code&gt;, you set the &lt;code&gt;TemplateData&lt;/code&gt; property with an object. The Dynamic Email Template will be able to access all the data from the &lt;code&gt;TemplateData&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Want an in depth guide? Read this article on how to &lt;a href="https://www.twilio.com/blog/send-emails-with-csharp-handlebars-templating-and-dynamic-email-templates"&gt;&lt;em&gt;Send Emails with C#, Handlebars templating, and Dynamic Email Templates&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since Dynamic Email Templates use the same &lt;code&gt;SendGridMessage&lt;/code&gt; and &lt;code&gt;Personalization&lt;/code&gt; classes, everything you learned about bulk email so far also applies here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send bulk email using Single Sends in SendGrid Marketing Campaigns
&lt;/h3&gt;

&lt;p&gt;SendGrid Marketing Campaigns are also out of scope for this tutorial, but for the sake of completeness, I will lightly touch on it. With SendGrid Marketing Campaigns you can create &lt;a href="https://sendgrid.com/solutions/email-marketing/signup-forms/"&gt;signup forms&lt;/a&gt;, manage contacts, organize contacts into lists and segments, create &lt;a href="https://sendgrid.com/solutions/email-marketing/email-design/"&gt;email designs&lt;/a&gt;, and more. &lt;/p&gt;

&lt;p&gt;And of course, you can send emails to those contacts stored in SendGrid.&lt;/p&gt;

&lt;p&gt;You can manage all of the previous features using the SendGrid UI or API. For example, you can &lt;a href="https://docs.sendgrid.com/ui/managing-contacts/create-and-manage-contacts"&gt;manage contacts using the SendGrid UI&lt;/a&gt; or &lt;a href="https://docs.sendgrid.com/api-reference/contacts/add-or-update-a-contact"&gt;programmatically via the API&lt;/a&gt;, and you can also send emails to your contacts, lists, and segments using the &lt;a href="https://docs.sendgrid.com/api-reference/single-sends/create-single-send"&gt;Single Send API&lt;/a&gt;. Since the contacts are all stored in SendGrid, you don't have to worry about any looping and paginating.&lt;/p&gt;

&lt;p&gt;With the &lt;a href="https://docs.sendgrid.com/api-reference/single-sends/create-single-send"&gt;Single Send API&lt;/a&gt; you can either provide the email body via the API or you can specify the ID of the email design stored in SendGrid. For either option, you cannot pass on data through the API to use in the email template. However, the templates have access to the data stored on the contacts. Once you created the Single Send, you can &lt;a href="https://docs.sendgrid.com/api-reference/single-sends/schedule-single-send"&gt;schedule the Single Send&lt;/a&gt; to be sent "now" or up to 72 hours from now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedule your bulk email
&lt;/h2&gt;

&lt;p&gt;When you're sending a lot of emails, SendGrid recommends scheduling them to be sent in the near future instead of sending them immediately. Quoting from the &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/scheduling-parameters"&gt;SendGrid docs&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;This technique allows for a more efficient way to distribute large email requests and can improve overall mail delivery time performance. This functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improves efficiency of processing and distributing large volumes of email.&lt;/li&gt;
&lt;li&gt;Reduces email pre-processing time.&lt;/li&gt;
&lt;li&gt;Enables you to time email arrival to increase open rates.&lt;/li&gt;
&lt;li&gt;Is available for free to all SendGrid customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To schedule emails you can set the &lt;code&gt;SendAt&lt;/code&gt; property on the &lt;code&gt;SendGridMessage&lt;/code&gt; or &lt;code&gt;Personalization&lt;/code&gt; object. The &lt;code&gt;SendAt&lt;/code&gt; property is a &lt;code&gt;long&lt;/code&gt; representing a Unix timestamp.&lt;/p&gt;

&lt;p&gt;To calculate this Unix timestamp, you can use the &lt;code&gt;DateTimeOffset.ToUnixTimeSeconds()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;For example, this will generate the &lt;code&gt;long&lt;/code&gt; timestamp for 5 minutes from now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToUnixTimeSeconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using &lt;code&gt;DateTime&lt;/code&gt; instead of &lt;code&gt;DateTimeOffset&lt;/code&gt;, you can implicitly and explicitly cast a &lt;code&gt;DateTime&lt;/code&gt; to a &lt;code&gt;DateTimeOffset&lt;/code&gt;, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;fiveMinutesFromNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// implicit cast&lt;/span&gt;
&lt;span class="n"&gt;fiveMinutesFromNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// explicit cast&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out &lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/datetime/converting-between-datetime-and-offset"&gt;this Microsoft doc on converting between DateTime and DateTimeOffset&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Here's the &lt;code&gt;SendToSubscribers&lt;/code&gt; method for sending the Bulk Email 5 minutes from now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task SendToSubscribers()
{
    var sendAt = DateTimeOffset.Now.AddMinutes(5).ToUnixTimeSeconds();

    var pageSize = 1_000;
    var subscriberCount = subscriberRepository.Count();
    var amountOfPages = (int) Math.Ceiling((double) subscriberCount / pageSize);

    for (var pageIndex = 0; pageIndex &amp;lt; amountOfPages; pageIndex++)
    {
        var subscribers = subscriberRepository.GetByPage(pageSize, pageIndex);

        var message = new SendGridMessage
        {
            From = new EmailAddress(sender.Email, sender.Name),
            Subject = "Ahoy -FirstName_Raw-!",
            HtmlContent = "Welcome aboard &amp;lt;b&amp;gt;-FullName-&amp;lt;/b&amp;gt; ⚓️"️,

            // max 1000 Personalizations
            Personalizations = subscribers.Select(s =&amp;gt; new Personalization
            {
                Tos = new List&amp;lt;EmailAddress&amp;gt; {new EmailAddress(s.Email, s.FullName)},
                // Substitutions data is max 10,000 bytes per Personalization object
                Substitutions = new Dictionary&amp;lt;string, string&amp;gt;
                {
                    {"-FirstName_Raw-", s.FirstName},
                    {"-FullName-", htmlEncoder.Encode(s.FullName)}
                }
            }).ToList(),

            // max 72 hours from now
            SendAt = sendAt
        };

        var response = await sendGridClient.SendEmailAsync(message);
        if (response.IsSuccessStatusCode) logger.LogInformation("Email queued");
        else logger.LogError("Email not queued");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can schedule emails to be sent up to 72 hours from now. If you need to schedule beyond that time, I recommend &lt;a href="https://www.twilio.com/blog/how-to-send-recurring-emails-in-csharp-dotnet-using-sendgrid-and-quartz-net"&gt;scheduling emails using a job scheduler like Quartz.NET&lt;/a&gt; or Hangfire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unsubscribe recipients
&lt;/h2&gt;

&lt;p&gt;When recipients mark your emails as spam, it hurts your reputation. As an email sender, you want to maintain a good reputation to maximize your email deliverability. You must also comply with anti-spam laws. Make sure to use a double opt-in process for things like newsletters, and always provide an unsubscribe link in your emails. You can add your own unsubscribe logic, or you can use &lt;a href="https://docs.sendgrid.com/ui/sending-email/global-unsubscribes"&gt;Global Unsubscribes&lt;/a&gt; or &lt;a href="https://docs.sendgrid.com/ui/sending-email/group-unsubscribes"&gt;Group Unsubscribes&lt;/a&gt; from SendGrid.&lt;/p&gt;

&lt;h2&gt;
  
  
  MailHelper
&lt;/h2&gt;

&lt;p&gt;The SendGrid SDK contains a helper class called &lt;code&gt;MailHelper&lt;/code&gt;. &lt;code&gt;MailHelper&lt;/code&gt; has convenient static methods for the most common email scenarios, including bulk email. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CreateSingleEmailToMultipleRecipients&lt;/code&gt; method lets you conveniently create a &lt;code&gt;SendGridMessage&lt;/code&gt; to send emails to many recipients. By default it will create a &lt;code&gt;Personalization&lt;/code&gt; for every recipient, but there's an overload where you can set the &lt;code&gt;showAllRecipients&lt;/code&gt; parameter to true. When this parameter is set to true, the method will create one &lt;code&gt;Personalization&lt;/code&gt; with all the recipients in it.  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;​​CreateSingleTemplateEmailToMultipleRecipients&lt;/code&gt; method will create a &lt;code&gt;SendGridMessage&lt;/code&gt; configured with a Dynamic Email Template and a &lt;code&gt;Personalization&lt;/code&gt; for every recipient.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CreateMultipleEmailsToMultipleRecipients&lt;/code&gt; method will create a &lt;code&gt;SendGridMessage&lt;/code&gt; configured with a &lt;code&gt;Personalization&lt;/code&gt; for every recipient and Substitution Tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which bulk email technique should you use?
&lt;/h2&gt;

&lt;p&gt;You just learned many different ways to send email in bulk, but which should you use?&lt;br&gt;&lt;br&gt;
Not to be cliche, but &lt;em&gt;it depends&lt;/em&gt;… Let's compare each technique:&lt;/p&gt;

&lt;p&gt;When you're using loops, you have full control over every email you send and there are no limitations.However, this is the least performant solution because you have to send an HTTP request for each email.&lt;br&gt;&lt;br&gt;
Use loops when you need to do advanced customization of the subject and email body that is not possible using Substitution Tags, or with the Handlebars templating language. This solution is ideal if you want to &lt;a href="https://www.twilio.com/blog/render-emails-using-razor-templating"&gt;generate the email body using the Razor templating language&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using Personalizations will greatly improve the performance of your application because you can send 1,000 emails per HTTP request. If you don't need to customize the email body for each recipient, you can set the content once for all emails, without the need for Substitution Tokens or Dynamic Email Templates.&lt;/p&gt;

&lt;p&gt;Personalizations with Substitution Tokens works great for doing simple email customizations where you need to substitute predefined variables for every recipient. You cannot do more complex templating like looping and conditional rendering.&lt;/p&gt;

&lt;p&gt;When you're doing Personalizations with Dynamic Email Templates, you have to manage the email templates in SendGrid which could be a benefit or a drawback depending on your needs. Dynamic Email Templates use a fully featured templating language, Handlebars, which you can use to render the simplest to the most advanced templates.&lt;/p&gt;

&lt;p&gt;If you don't want to store your contacts in your own database, you can store contacts in SendGrid and send emails to them using Single Sends. Single Sends uses the same Handlebar templating language, but can only access the data that is stored on the contact. This means you have to upload the data to your SendGrid contacts before sending the email that requires it. You can send emails to all your contacts, specific contact lists, or segments of your contacts and you can generate unsubscribe links. The unsubscribe links allow the contact to remove themselves from the contacts list. You also don't have to worry about looping or paginating over your contacts as SendGrid will do this for you. This is ideal for marketing emails and newsletters.&lt;/p&gt;

&lt;p&gt;If you're still not sure, you could go down this decision tree: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tq2R4P2S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/documents/Bulk_Mail_Decision_Tree_2.optimized.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tq2R4P2S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/documents/Bulk_Mail_Decision_Tree_2.optimized.svg" alt="How to bulk email programmatically? Store Contacts in SendGrid? If yes, use Single Sends. If no, Customize Email per Recipient? If no, Use Personalizations with static body. If yes, do you need advanced email customization? If no, use Personalizations with Substitution Tags or Dynamic Email Templates. If yes, do you want to store templates in SendGrid? If yes, use Personalizations with Dynamic Email Templates. If no, send emails in a loop." width="361" height="861"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Big congratulations if you made it this far! 🎉&lt;/p&gt;

&lt;p&gt;You learned how to send emails in bulk using a bunch of methods, each with their own advantages. However, this tutorial only covers the code side of things, but you should check out &lt;a href="https://sendgrid.com/resource/bulk-email-guide/"&gt;this guide on bulk emails to maximize the deliverability, engagement, and minimize the unsubscription rates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to see a practical example of bulk email in action, check out this &lt;a href="https://www.twilio.com/blog/how-to-build-an-email-newsletter-application-using-asp-net-core-and-sendgrid"&gt;tutorial on how to build an Email Newsletter application using ASP.NET Core and SendGrid&lt;/a&gt;. This tutorial manages the newsletter subscribers using Entity Framework Core and uses a form to upload the HTML newsletter which is then sent using Personalizations and Substitution Tokens.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>email</category>
      <category>sendgrid</category>
    </item>
    <item>
      <title>How to test SMS and Phone Call applications with the Twilio Dev Phone</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-test-sms-and-phone-call-applications-with-the-twilio-dev-phone-58ca</link>
      <guid>https://dev.to/twilio/how-to-test-sms-and-phone-call-applications-with-the-twilio-dev-phone-58ca</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/test-sms-and-phone-call-applications-with-twilio-dev-phone"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Twilio empowers you to build applications using phone numbers, text messaging, and voice calling. You can test these phone network based applications using any phone and phone number, but how do you test these applications without a phone? Maybe you're wondering, why not use your phone, which I'll get to later, but first how?!&lt;/p&gt;

&lt;h2&gt;
  
  
  Twilio Dev Phone to the rescue
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the Twilio Dev Phone
&lt;/h3&gt;

&lt;p&gt;The Twilio Dev Phone is a tool built on top of Twilio products that will let you send and receive phone calls and text messages from the browser. The &lt;a href="https://github.com/twilio-labs/dev-phone"&gt;Dev Phone is an open-source project on GitHub&lt;/a&gt; so feel free to read the source code and contribute to it. You can learn more about &lt;a href="https://www.twilio.com/docs/labs/dev-phone"&gt;the Twilio Dev Phone in the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use Twilio Dev Phone
&lt;/h3&gt;

&lt;p&gt;As promised, here are some good scenarios when you'd want to use the Twilio Dev Phone instead of your own personal phone.&lt;/p&gt;

&lt;p&gt;🌍 &lt;strong&gt;International apps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you're building applications for an international audience, you'll have a local Twilio Phone Number for each country. However, in most cases, you probably don't and you don't want to incur those international charges to your personal phone plan. So instead, you can buy a Twilio Phone Number local to the desired country and then use the Twilio Dev Phone to make and receive phone calls and SMS! &lt;/p&gt;

&lt;p&gt;You could even use this to test out &lt;a href="https://support.twilio.com/hc/en-us/articles/223181348-Alphanumeric-Sender-ID-for-Twilio-Programmable-SMS"&gt;Alpha Numeric Senders&lt;/a&gt; which may be available in other countries, but potentially not yours.&lt;/p&gt;

&lt;p&gt;📶 &lt;strong&gt;Bad connection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depending on where you live or work, you may have unreliable connectivity or no connectivity at all. Maybe your place of work is highly secured and communication is jammed, or you just lost your phone.You may not be able to use your phone in this scenario, but if you have an internet connection, you can use the Twilio Dev Phone!&lt;/p&gt;

&lt;p&gt;🚤 &lt;strong&gt;Fast and convenient&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you're building applications on your computer, you have to find your phone, unlock it, enter a phone number with some clunky UI, and then finally make or receive that call or SMS.&lt;br&gt;&lt;br&gt;
This flow can be quite disrupting. Instead of doing all that, keep the Twilio Dev Phone in one of your browser tabs and test your applications to increase your inner dev loop! &lt;/p&gt;

&lt;p&gt;There are more scenarios where Dev Phone can help. We'd love to hear what you are using it for!&lt;/p&gt;
&lt;h3&gt;
  
  
  How to test Twilio applications using the Twilio Dev Phone
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Provision Twilio SMS and Voice applications
&lt;/h4&gt;

&lt;p&gt;Before you can test a Twilio application with the Dev Phone, you’ll need to have one up and running.&lt;/p&gt;

&lt;p&gt;If you already do, you can skip this section. &lt;/p&gt;

&lt;p&gt;Alternatively, instead of deploying your own Twilio apps, you can text and call this phone number we provisioned for demo purposes: &lt;a href="https://dev.totel:+1%20903%20296%202250"&gt;+19032962250&lt;/a&gt;. You'll only be able to reach the phone number from an upgraded Twilio account, as trial accounts can only communicate with Verified Caller IDs.&lt;/p&gt;

&lt;p&gt;You can find a lot of samples on &lt;a href="https://www.twilio.com/code-exchang"&gt;Twilio CodeExchange&lt;/a&gt;, but to deploy Twilio apps in an instant, you can filter them down to samples with "&lt;a href="https://www.twilio.com/code-exchange?f=serverless"&gt;Quick Deploy&lt;/a&gt;", like the &lt;a href="https://www.twilio.com/code-exchange/basic-voice-auto-response"&gt;Voice Auto-response&lt;/a&gt; and the &lt;a href="https://www.twilio.com/code-exchange/basic-sms-auto-response"&gt;SMS Auto-response sample&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;First, make sure you have a Twilio Account and a Twilio Phone Number. If you &lt;a href="https://www.twilio.com/referral/OhKZOM"&gt;register here&lt;/a&gt;, you'll receive $10 in Twilio credit when you upgrade to a paid account! &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;Follow these instructions to buy a Twilio Phone Number&lt;/a&gt; if you haven't already. &lt;/p&gt;

&lt;p&gt;Next, open the &lt;a href="https://www.twilio.com/code-exchange/basic-voice-auto-response"&gt;Voice Auto-response sample&lt;/a&gt; in a new browser tab, select your Twilio Phone Number, and deploy the application. Now follow the same steps for the &lt;a href="https://www.twilio.com/code-exchange/basic-sms-auto-response"&gt;SMS Auto-response sample&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Congratulations, you now have two Twilio applications, one that will respond when you call or text your Twilio Phone Number! 🎉&lt;/p&gt;
&lt;h4&gt;
  
  
  Install the Twilio Dev Phone
&lt;/h4&gt;

&lt;p&gt;Before you can use the Twilio Dev Phone, you'll need to acquire another Twilio Phone Number, install Twilio CLI, and install the Twilio Dev Phone plugin.&lt;/p&gt;

&lt;p&gt;You can't buy two separate phone numbers on a trial account, so if you're using a trial account, you'll need to upgrade your account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;Buy another Twilio Phone Number&lt;/a&gt;, which will be used by the Dev Phone to make and receive calls and text messages.&lt;/p&gt;

&lt;p&gt;Next, follow these &lt;a href="https://www.twilio.com/docs/twilio-cli/quickstart"&gt;instructions to install the Twilio CLI on your machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then log in to your account using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;twilio login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, install the Twilio Dev Phone plugin using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;twilio plugins:install @twilio-labs/plugin-dev-phone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, you're ready to use the Twilio Dev Phone. Start the Dev Phone with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;twilio dev-phone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new tab in your browser should open showing the Dev Phone and your terminal output should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Hello 👋 I&lt;span class="s1"&gt;'m your dev-phone and my name is dev-phone-600487

✅ I'&lt;/span&gt;m using your profile API key.

💻 Creating a new conversation...
✅ I&lt;span class="s1"&gt;'m using the conversation CHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx from service ISxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

💻 Creating a new sync list for call history...
✅ I'&lt;/span&gt;m using the &lt;span class="nb"&gt;sync &lt;/span&gt;service ISxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

💻 Deploying a Functions Service to handle incoming calls and SMS...
✅ I&lt;span class="s1"&gt;'m using the Serverless Service ZSxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

💻 Creating a new TwiML App to allow voice calls from your browser...
✅ I'&lt;/span&gt;m using the TwiML App APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

🚀 Your &lt;span class="nb"&gt;local &lt;/span&gt;webserver is listening on port 3001
🌐 Opening http://localhost:3001/ your browser
▶️ Use ctrl-c to stop your dev-phone

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the Twilio Dev Phone is using a lot of Twilio services like &lt;a href="https://www.twilio.com/docs/conversations"&gt;Conversations&lt;/a&gt;, &lt;a href="https://www.twilio.com/sync"&gt;Sync&lt;/a&gt;, &lt;a href="https://www.twilio.com/docs/runtime/functions"&gt;Functions&lt;/a&gt; , and &lt;a href="https://support.twilio.com/hc/en-us/articles/223180928-How-Do-I-Create-a-TwiML-App-"&gt;TwiML apps&lt;/a&gt; so you can make and receive phone calls from the browser in real time.&lt;br&gt;&lt;br&gt;
All these services are created when you start the Twilio Dev Phone, and when you stop it (by pressing &lt;code&gt;ctrl + c&lt;/code&gt;), the Twilio Dev Phone will delete them again for you.&lt;/p&gt;

&lt;p&gt;Head over to the Dev Phone tab on your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KSjFAIVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/D5pLD0_goBHFbx2PbKb-oYNgEVG0-phYe56CNb0RYbaQND.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KSjFAIVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/D5pLD0_goBHFbx2PbKb-oYNgEVG0-phYe56CNb0RYbaQND.width-500.png" alt="Form with a dropdown field to select your Twilio Phone Number." width="500" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose your Twilio Phone Number and click " &lt;strong&gt;Use this phone number&lt;/strong&gt;". &lt;/p&gt;

&lt;p&gt;You will see a warning if your phone number is already configured to respond to phone calls or text messages using a webhook, Function, Flow, TwiML app, or something else. The Twilio Dev Phone will overwrite these settings if proceed with that phone number.&lt;/p&gt;

&lt;p&gt;You now have a virtual phone on your computer. 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T84c54VY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/tNr1v5EiMzIxrW3TwOHb27lAxSqZKBSrmBcqICECSi9xfI.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T84c54VY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/tNr1v5EiMzIxrW3TwOHb27lAxSqZKBSrmBcqICECSi9xfI.width-500.png" alt='The Twilio Dev Phone application. The application has a dial pad and a Destination Phone Number field to enter the phone number to text or call. There"s a call button to start a phone call. A message field and send button to send SMS. And there"s a Call History panel which lists details on past and ongoing calls.' width="500" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Send Outbound Calls and SMS using the Twilio Dev Phone
&lt;/h4&gt;

&lt;p&gt;Start testing by entering your application’s phone number in the Destination Number field.&lt;/p&gt;

&lt;p&gt;Then, enter a message into the Message field and click &lt;strong&gt;Send&lt;/strong&gt;. Your application will respond with its configured response to inbound SMS (e.g., "Hello World" in this screenshot.)&lt;/p&gt;

&lt;p&gt;Finally, to test out the voice capabilities of your application, click the Call button. You should also hear the response you’ve configured (e.g., "Hello World".)&lt;/p&gt;

&lt;p&gt;You just tested the incoming message and voice call functionality, however, don't forget that your Dev Phone can also receive inbound communications. Go ahead and give your Dev Phone Number a call or text it with your application. You can find this number in the top right corner for your convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You've learned how to set up and use the Twilio Dev Phone to test your Twilio applications. The &lt;a href="https://github.com/twilio-labs/dev-phone"&gt;Twilio Dev Phone is an open-source project on GitHub&lt;/a&gt;, so if you're curious, go and read the code, also issues and pull requests are welcome! &lt;/p&gt;

&lt;p&gt;Congratulations on making it till the end 🎉&lt;br&gt;&lt;br&gt;
We hope you find this tool helpful and we can't wait to see what you build!&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>sms</category>
      <category>phone</category>
      <category>call</category>
    </item>
    <item>
      <title>Provide default configuration to your .NET applications</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Mon, 23 May 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/provide-default-configuration-to-your-net-applications-4883</link>
      <guid>https://dev.to/twilio/provide-default-configuration-to-your-net-applications-4883</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/provide-default-configuration-to-dotnet-applications"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a previous post I shared &lt;a href="https://www.twilio.com/blog/better-configuration-csharp-dotnet-for-twilio"&gt;how you can better configure your .NET applications for Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid"&gt;SendGrid&lt;/a&gt; using the Microsoft.Extensions.Configuration APIs. In this tutorial, I'll build upon the techniques from those tutorials and you'll learn how to store default options in one section of your configuration and override them with specific options from another section. Let me clarify using an example. Applications often send different types of emails, like welcome emails, password reset emails, offer emails, etc. Depending on the type of email, you may want to send them from a different email address. For most emails, you may be using something like '&lt;a href="mailto:no-reply@yourdomain.tld"&gt;no-reply@yourdomain.tld&lt;/a&gt;', but for offer emails, the replies to the email could be of value. So instead of using the same no-reply address, you could use an email address that will be routed to a sales person and maybe automatically integrates with a Customer Relationship Management (CRM) system.&lt;/p&gt;

&lt;p&gt;In this example, you'd want to store '&lt;a href="mailto:no-reply@yourdomain.tld"&gt;no-reply@yourdomain.tld&lt;/a&gt;' as the default reply address, but for offer-emails, you want to use another email address, for example, '&lt;a href="mailto:offers@yourdomain.tld"&gt;offers@yourdomain.tld&lt;/a&gt;'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, I recommend having&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some experience with C# and .NET&lt;/li&gt;
&lt;li&gt;An OS that supports .NET (Windows/macOS/Linux)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads"&gt;Git CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0"&gt;.NET 6 SDK&lt;/a&gt; (older or newer should work too)&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/"&gt;Visual Studio&lt;/a&gt;, or &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/DotNetDefaultConfiguration"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it as a reference if you run into any issues, or &lt;a href="https://github.com/Swimburger/DotNetDefaultConfiguration/issues"&gt;submit an issue&lt;/a&gt; if you need assistance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;You can apply these concepts to any scenario, but let's continue using the email scenario throughout this tutorial. &lt;/p&gt;

&lt;p&gt;Run the following commands to clone the tutorial project to your machine using git and navigate into the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Swimburger/DotNetDefaultConfiguration.git &lt;span class="nt"&gt;--branch&lt;/span&gt; start
&lt;span class="nb"&gt;cd &lt;/span&gt;DotNetDefaultConfiguration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The git repository you cloned contains a .NET 6 console application that already contains some JSON configuration and code to load the configuration.&lt;/p&gt;

&lt;p&gt;Here's what the &lt;em&gt;appsettings.json&lt;/em&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your App Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jon@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jon Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome to our service!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thank you for signing up!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PasswordReset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your App Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jon@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jon Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reset your password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click the link below to reset your password."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"offers@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jon@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jon Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Don't miss this offer!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We have an amazing offer for you!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the &lt;em&gt;Program.cs&lt;/em&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LogEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"Logging Email Option:
    FromEmail: {0}
    FromName: {1}
    ToEmail: {2}
    ToName: {3}
    Subject: {4}
    Body: {5}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;welcomeEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;LogEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;welcomeEmail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;passwordResetEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PasswordReset"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;LogEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;passwordResetEmail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;offerEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;LogEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offerEmail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FromName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ToEmail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ToName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code builds configuration using only JSON as a source and then the GetEmailOptions local function is used to fetch the configuration and create an instance of  EmailOptions for the welcome-email, password-reset-email, and offer-email. You would probably dynamically generate the body of the emails and dynamically fetch the recipient email address in a real application, but this will work fine for the purpose of this tutorial.&lt;/p&gt;

&lt;p&gt;After each EmailOptions object is created for each email section, the configuration is logged via the LogEmailOptions local function, so you can see the result of this code.&lt;/p&gt;

&lt;p&gt;You may notice that there's a lot of redundancy in the JSON, which is why you'll introduce a defaults section in the next part of this tutorial.&lt;/p&gt;

&lt;p&gt;Run the project using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;span class="c"&gt;# Output:&lt;/span&gt;
&lt;span class="c"&gt;# Logging Email Option:&lt;/span&gt;
&lt;span class="c"&gt;# FromEmail: no-reply@yourdomain.tld&lt;/span&gt;
&lt;span class="c"&gt;# FromName: Your App Name&lt;/span&gt;
&lt;span class="c"&gt;# ToEmail: jon@doe.com&lt;/span&gt;
&lt;span class="c"&gt;# ToName: Jon Doe&lt;/span&gt;
&lt;span class="c"&gt;# Subject: Welcome to our service!&lt;/span&gt;
&lt;span class="c"&gt;# Body: Thank you for signing up!&lt;/span&gt;
&lt;span class="c"&gt;# Logging Email Option:&lt;/span&gt;
&lt;span class="c"&gt;# FromEmail: no-reply@yourdomain.tld&lt;/span&gt;
&lt;span class="c"&gt;# FromName: Your App Name&lt;/span&gt;
&lt;span class="c"&gt;# ToEmail: jon@doe.com&lt;/span&gt;
&lt;span class="c"&gt;# ToName: Jon Doe&lt;/span&gt;
&lt;span class="c"&gt;# Subject: Reset your password&lt;/span&gt;
&lt;span class="c"&gt;# Body: Click the link below to reset your password.&lt;/span&gt;
&lt;span class="c"&gt;# Logging Email Option:&lt;/span&gt;
&lt;span class="c"&gt;# FromEmail: offers@yourdomain.tld&lt;/span&gt;
&lt;span class="c"&gt;# FromName: Jane&lt;/span&gt;
&lt;span class="c"&gt;# ToEmail: jon@doe.com&lt;/span&gt;
&lt;span class="c"&gt;# ToName: Jon Doe&lt;/span&gt;
&lt;span class="c"&gt;# Subject: Don't miss this offer!&lt;/span&gt;
&lt;span class="c"&gt;# Body: We have an amazing offer for you!&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the configuration is pulled from the JSON file and then written to the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a defaults section for redundant configuration elements
&lt;/h2&gt;

&lt;p&gt;You can add a Defaults section to remove the redundant configuration elements. Open &lt;em&gt;appsettings.json&lt;/em&gt; and in front of the Welcome section, add a Defaults section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Defaults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your App Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jon@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jon Doe"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Welcome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome to our service!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thank you for signing up!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PasswordReset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reset your password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click the link below to reset your password."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"offers@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Don't miss this offer!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We have an amazing offer for you!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As shown above, also remove the redundant FromEmail, FromName, ToEmail, and ToName properties from the Welcome and PasswordReset section. However, only remove the redundant ToEmail and ToName properties from the  Offer section and leave the FormEmail and FromName properties be.&lt;/p&gt;

&lt;p&gt;Now, update the code in the &lt;em&gt;Program.cs&lt;/em&gt; file so that the configuration is retrieved from the specific email section, but if null (??), then retrieve it from the Defaults section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Defaults"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToEmail"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;?? is the &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator"&gt;null-coalescing operator&lt;/a&gt; which returns the value on the right side if the value on the left side is null. This allows you to succinctly check for null and if null, provide a fallback value.&lt;/p&gt;

&lt;p&gt;Run the project again using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the output has not changed, but now you can set configuration on specific sections and fallback on configuration from the Defaults section. &lt;/p&gt;

&lt;p&gt;This code gets the job done, but the same result could be achieved more eloquently by binding configuration sections to strongly-typed objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bind defaults and override defaults using the configuration binder
&lt;/h2&gt;

&lt;p&gt;Instead of retrieving each configuration element individually, you can use the configuration binder to bind configuration sections to strongly-typed objects. There are two methods you can use to bind configuration sections to an instance of EmailOptions: Get() and  Bind(object).&lt;/p&gt;

&lt;p&gt;To use the Get method, pass in the desired type as a generic type parameter and the Get method create an instance of that type and bind the configuration to the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the Bind method, create an instance of EmailOptions and then pass it as a parameter to the Bind method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's even more interesting is that you can use the Bind method multiple times! So, if you'd like to bind both the defaults and the email specific configuration, you could do that as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Defaults"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this technique, update the GetEmailOptions local function to create an instance of  EmailOptions, then bind the Defaults section to it, and then override the defaults by binding the specific section to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="nf"&gt;GetEmailOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Defaults"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hardcoding defaults
&lt;/h2&gt;

&lt;p&gt;In addition to pulling defaults from the configuration, you may also opt to add default values directly into your code. For example, if you're developing a library for others to consume, it would be convenient for some of the configuration elements to have default values.&lt;/p&gt;

&lt;p&gt;If you're pulling individual configuration elements, you could add default values like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;defaultsSections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe it doesn't make sense to hardcode a default value, but the configuration element is required, so you want to throw an exception if the configuration element is null. In that case, you can also use the null-coalescing operator to throw an exception inline like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSection&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"FromEmail"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Emails:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:FromEmail has to be configured"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if you're using the configuration binder, you don't need to use the null-coalescing operator, and you can instead specify the default values in the object initializer like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailsSection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Emails"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Defaults"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;emailsSection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSectionName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you could specify default values in the class definition itself like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FromEmail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"no-reply@yourdomain.tld"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;FromName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ToEmail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ToName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You've learned how to provide default configuration values through code and through a dedicated Defaults configuration section. If you haven't already, I recommend learning about more &lt;a href="https://www.twilio.com/blog/better-configuration-csharp-dotnet-for-twilio"&gt;configuration techniques in a tutorial applied to Twilio configuration&lt;/a&gt;, or applied to &lt;a href="https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid"&gt;SendGrid configuration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you found this useful, let me know and share what you're working on. I can't wait to see what you build!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Send Email and SMS from Google Forms using Zapier, SendGrid, and Twilio</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Wed, 18 May 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/send-email-and-sms-from-google-forms-using-zapier-sendgrid-and-twilio-1645</link>
      <guid>https://dev.to/twilio/send-email-and-sms-from-google-forms-using-zapier-sendgrid-and-twilio-1645</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/send-email-and-sms-from-google-forms-using-zapier-sendgrid-and-twilio"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Forms are one of the fundamental building blocks of the web. Almost all websites use forms to get feedback from users and to gather leads. Many of these forms integrate directly with a Customer Relationship Management (CRM) system, while others forward the form data directly to an email inbox.&lt;/p&gt;

&lt;p&gt;If you're building a form using a web framework, you can quickly integrate &lt;a href="https://sendgrid.com/solutions/email-api/"&gt;SendGrid's Email API&lt;/a&gt; or one of the &lt;a href="https://docs.sendgrid.com/for-developers/sending-email/libraries"&gt;SendGrid libraries&lt;/a&gt; to forward the data to an email inbox. However, sometimes there's no time to build a fully fledged form yourself, or the form's purpose is for a single short-lived campaign. In that case, you could use an off-the-shelf form product like Google Forms to get the job done quickly with minimal investment. You don't even have to sacrifice the form integrations, because with &lt;a href="https://zapier.com/"&gt;Zapier's&lt;/a&gt; workflow automation, you can integrate Google Forms with tons of products including SendGrid to send emails and Twilio Messaging to send SMS.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to forward form submission data via email and send a notification via SMS using Google Forms, Zapier, and Twilio. This is a very common scenario on the web, but with these tools you don't even need to write any code to make it happen!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dP83dK1x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/8aPk__5a4AnezzQhw9Z67jl6lE17vUxk5VNPRIy36iDeBpR.original.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dP83dK1x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/8aPk__5a4AnezzQhw9Z67jl6lE17vUxk5VNPRIy36iDeBpR.original.png" alt='A Zapier "Zap" that trigger when a Google Form is submitted, then sends an email using SendGrid, and lastly, sends an SMS using Twilio messaging.' width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zapier workflow automation with Google Forms, SendGrid, and Twilio SMS&lt;/p&gt;

&lt;p&gt;If you're looking to build a form for users to signup for your newsletter, you can use SendGrid’s &lt;a href="https://sendgrid.com/solutions/email-marketing/signup-forms/"&gt;Signup Forms&lt;/a&gt; to build and manage your email contact list for newsletters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need these things to follow along:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A free or paid SendGrid account. Sign up &lt;a href="https://signup.sendgrid.com/"&gt;here&lt;/a&gt; to send up to 100 emails per day completely free of charge.&lt;/li&gt;
&lt;li&gt;A free or paid Twilio account (If you &lt;a href="https://www.twilio.com/referral/OhKZOM"&gt;register here&lt;/a&gt;, you'll receive $10 in Twilio credit when you upgrade to a paid account!)&lt;/li&gt;
&lt;li&gt;A free Zapier account, or even better, a paid or Zapier trial account (more on that later)&lt;/li&gt;
&lt;li&gt;A Google account&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a Google Form
&lt;/h2&gt;

&lt;p&gt;You can skip this step if you already set up a Google Form. If not, head over to &lt;a href="https://forms.new/"&gt;&lt;em&gt;forms.new&lt;/em&gt;&lt;/a&gt; which will automatically create a blank new form in Google Forms for you. Give your new form a name and &lt;a href="https://support.google.com/docs/answer/2839737"&gt;use the interface to build your form&lt;/a&gt; out until you're happy with the result. &lt;/p&gt;

&lt;p&gt;For example, here's a form that will contact me, so I added a Name, Email Address, and Question field to the form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tfRey89O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/2kuWN3JL5g5XFzLWkqMvR35OqcoKPKF3_KFJjLss1nPjsT.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tfRey89O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/2kuWN3JL5g5XFzLWkqMvR35OqcoKPKF3_KFJjLss1nPjsT.width-500.png" alt='Google Form named "Contact Niels" with 3 form fields: Name, Email Address, and Question' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Responses&lt;/strong&gt; tab, click the &lt;strong&gt;3 dotted icon&lt;/strong&gt; to open the overflow menu, and click &lt;strong&gt;Select response destination&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bw_gfnmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/J0_TVNmrsoY-1e9TjcbZF6YHLR5F-J5PCICWRcgja3X3Tc.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bw_gfnmP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/J0_TVNmrsoY-1e9TjcbZF6YHLR5F-J5PCICWRcgja3X3Tc.width-500.png" alt='User clicks on the responses tab, then on the three dotted icon which opens a submenu, and the user selects the "Select response destination" option.' width="500" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A modal will appear where you can decide which Google Sheet (spreadsheet) will be used to store the form submissions in. Select the  &lt;strong&gt;Create a new spreadsheet&lt;/strong&gt;  option and, if desired, change the name of the spreadsheet and then click  &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dFPaZHdI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/6lm7Io0ccY3ITw4QFQE_72FLHF9uZYPuu9SsvwQJHm96D8.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dFPaZHdI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/6lm7Io0ccY3ITw4QFQE_72FLHF9uZYPuu9SsvwQJHm96D8.width-500.png" alt='Modal with two radio buttons: Create a new spreadsheet, and Select existing spreadsheet. The user selects the "Create a new spreadsheet" option and gives the spreadsheet the name "Contact Niels (Responses)"' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This spreadsheet will be used to store responses in rows whenever the form gets filled out and submitted.&lt;/p&gt;

&lt;p&gt;Later, you will configure Zapier to watch this spreadsheet and run your automation workflow whenever a new row appears on the spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your SendGrid account to send emails
&lt;/h2&gt;

&lt;p&gt;In SendGrid, you'll need to configure two things before you can send emails: First, you’ll need to set up &lt;strong&gt;Sender Authentication&lt;/strong&gt;. This will verify that you own the email address or domain that you will send emails from. Second, you’ll need to create a &lt;strong&gt;SendGrid API Key&lt;/strong&gt; with permission to send emails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sender Authentication
&lt;/h3&gt;

&lt;p&gt;To quickly get started, you will use &lt;strong&gt;Single Sender Verification&lt;/strong&gt; for this tutorial. This verifies that you own the email address that the application will send emails from. Single Sender Verification is great &lt;strong&gt;for testing purposes&lt;/strong&gt; , but it is not recommended for production.&lt;/p&gt;

&lt;p&gt;Twilio recommends &lt;a href="https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication/"&gt;Domain Authentication&lt;/a&gt; for production environments. An authenticated domain proves to email providers that you own the domain, and removes the "via sendgrid.net" text that inbox providers would otherwise append to your from address.&lt;/p&gt;

&lt;p&gt;To set up Single Sender Verification, click the &lt;strong&gt;Settings&lt;/strong&gt; tab in the left menu. Once the settings tab opens, click &lt;strong&gt;Sender Authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vGMJb4md--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/vclQ0x-aJkA2-aB2-4VmkYdr67TEE0WUxlJjMskCXxjeF3.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vGMJb4md--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/vclQ0x-aJkA2-aB2-4VmkYdr67TEE0WUxlJjMskCXxjeF3.width-500.png" alt='Side-navigation with a Settings group and multiple links in the group. The link "Sender Authentication" is highlighted.' width="352" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, click  &lt;strong&gt;Get Started&lt;/strong&gt;  under the  &lt;strong&gt;Single Sender Verification&lt;/strong&gt;  section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tFSS3mTM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/oqr-gzczRUGP25968jpIgpOAV0VIjzGmN_RW80qTgxeL9K.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tFSS3mTM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/oqr-gzczRUGP25968jpIgpOAV0VIjzGmN_RW80qTgxeL9K.width-500.png" alt='A button saying "Get Started" to verify an individual email address.' width="500" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This opens a form in the right-side panel. Fill out the form with your information and email address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dstFmAEg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/V71XlrV9CrIc0U5xx9Kh_S85cWXqXW03hpAXVJN3Q8X0Az.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dstFmAEg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/V71XlrV9CrIc0U5xx9Kh_S85cWXqXW03hpAXVJN3Q8X0Az.width-500.png" alt="A form to create a sender. The form asks for contact information including the email address that will be used to send emails from." width="500" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click  &lt;strong&gt;Create&lt;/strong&gt;  after filling out the form. Another panel will appear on the right, asking you to confirm your email address in your email inbox.&lt;/p&gt;

&lt;p&gt;Go to your personal email inbox, open the email from SendGrid, and click  &lt;strong&gt;Verify Single Sender&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8u3PwVX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/5E4sskPqIBXxFlFl5QArhXfk4OrNlpGD8TaV0nEgO54tk5.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8u3PwVX6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/5E4sskPqIBXxFlFl5QArhXfk4OrNlpGD8TaV0nEgO54tk5.width-500.png" alt='An email from SendGrid with a button saying "Verify Single Sender".' width="500" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your email address has been verified. You can now use it to send emails!&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a SendGrid API key to send emails
&lt;/h3&gt;

&lt;p&gt;Back on the SendGrid website, click  &lt;strong&gt;API Keys&lt;/strong&gt;  under the  &lt;strong&gt;Settings tab&lt;/strong&gt; , then click  &lt;strong&gt;Create API Key&lt;/strong&gt;  in the top right-hand corner. This opens another form in the right-side panel.&lt;/p&gt;

&lt;p&gt;Give your API Key a useful name. You can assign different permissions to the API Key. For optimal security, you should only give the minimum amount of permissions that you need.&lt;/p&gt;

&lt;p&gt;Next, click  &lt;strong&gt;Restricted Access&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RXESijNN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Ef68HmXueM6kkKsL4EJ-aJ-kROT4RCiqZn10Lnxe7Xs2c8.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RXESijNN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Ef68HmXueM6kkKsL4EJ-aJ-kROT4RCiqZn10Lnxe7Xs2c8.width-500.png" alt="A form to create an API Key. The form asks for a name for the key and what permissions to grant to the key." width="500" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the  &lt;strong&gt;Mail Send&lt;/strong&gt;  accordion item and click on it to reveal the permissions underneath. Drag the slider to the right for the  &lt;strong&gt;Mail Send&lt;/strong&gt;  permission.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hzBF_rhx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Qxl-4T9InvkVHNZRCLLze2JR1el8F1845Gw_qjuneDU5UD.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hzBF_rhx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Qxl-4T9InvkVHNZRCLLze2JR1el8F1845Gw_qjuneDU5UD.width-500.png" alt='A list of permissions that you can control using a slider bar next to each permission. The "Mail Send" accordion item is expanded to reveal the "Mail Send" permission underneath.' width="500" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll to the bottom of the form and click &lt;strong&gt;Create &amp;amp; View&lt;/strong&gt;. The API key will now be displayed on your screen. &lt;/p&gt;

&lt;p&gt;You will not be able to retrieve the API key again once you leave this screen, so make sure you copy the API Key somewhere safe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QCnnpmmH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/KEztfj7FI42kSLcqT7f_b6-7PrA73_E8nKwWOmNNoxiY_L.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QCnnpmmH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/KEztfj7FI42kSLcqT7f_b6-7PrA73_E8nKwWOmNNoxiY_L.width-500.png" alt="API key is displayed to copy and store elsewhere. Key will not be shown again." width="500" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the sender verified and the API Key created, you’ll now be able to send emails from Zapier's workflow automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Your Twilio Configuration
&lt;/h2&gt;

&lt;p&gt;To send text messages from Zapier, you'll first need to &lt;a href="https://support.twilio.com/hc/en-us/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console"&gt;buy a Twilio Phone Number&lt;/a&gt;. If you're using a trial account, the cost for the phone number will be applied to the complimentary trial credit. If you don't already have a phone number, &lt;a href="https://console.twilio.com/us1/develop/phone-numbers/manage/search?frameUrl=%2Fconsole%2Fphone-numbers%2Fsearch%3Fx-target-region%3Dus1&amp;amp;currentFrameUrl=%2Fconsole%2Fphone-numbers%2Fsearch%3FisoCountry%3DUS%26searchTerm%3D%26searchFilter%3Dleft%26searchType%3Dnumber%26x-target-region%3Dus1%26%20__override_layout__%20%3Dembed%26bifrost%3Dtrue"&gt;go and buy a phone number via the Twilio Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, you'll need some configuration elements from Twilio to send text messages from Zapier. Back in the &lt;a href="https://console.twilio.com/"&gt;Twilio Console&lt;/a&gt;, take note of your Twilio &lt;strong&gt;Account SID&lt;/strong&gt; and &lt;strong&gt;Auth Token&lt;/strong&gt; located at the bottom left of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NyyzQFvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/AZBdMRN76J8Q0YRXt8_MxpRuGfXKVhP9moSAVy5F48q8go.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NyyzQFvI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/AZBdMRN76J8Q0YRXt8_MxpRuGfXKVhP9moSAVy5F48q8go.width-500.png" alt="Account Info box holding 3 read-only fields: Account SID field, Auth Token field, and Twilio phone number field." width="500" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all these steps you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;name of the spreadsheet&lt;/strong&gt; that hosts your Google Form data&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;email address of your Verified Sender on SendGrid&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;API Key from SendGrid&lt;/strong&gt; to send emails&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Account SID and Auth Token from Twilio&lt;/strong&gt; to send SMS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fantastic! Time to create the automation with Zapier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create workflow automation with Zapier
&lt;/h2&gt;

&lt;p&gt;Head over to the &lt;a href="https://zapier.com/app/dashboard"&gt;Zapier dashboard&lt;/a&gt; and click on the &lt;strong&gt;Create Zap&lt;/strong&gt; button on the top left.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3y5Qr3Qg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/dQCr49MmkhEy_r8AyXyt96AwKnEB0d17dNkX5Hj-arVyp9.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3y5Qr3Qg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/dQCr49MmkhEy_r8AyXyt96AwKnEB0d17dNkX5Hj-arVyp9.width-500.png" alt="Big orange Create Zap button on the top left of the Zapier dashboard" width="500" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://zapier.com/help/create/basics/learn-key-concepts-in-zapier#step-1"&gt;Zapier's words&lt;/a&gt;: "A Zap is an automated workflow that connects your apps and services together". After creating the Zap, change the name of the Zap to something meaningful. This can be done by clicking on the pen icon next to &lt;strong&gt;Untitled Zap&lt;/strong&gt; in the top left corner of the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Google Forms Trigger to receive the form submission
&lt;/h3&gt;

&lt;p&gt;Every Zap needs to have a &lt;a href="https://platform.zapier.com/docs/triggers"&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/a&gt;; a trigger is an event that will start the workflow. In the &lt;strong&gt;App Event&lt;/strong&gt; search bar, enter " &lt;strong&gt;Google Forms&lt;/strong&gt;" and then click on the "Google Forms" entry. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dCIRLGem--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sqs1uhm4a6vbX5nAOQgE1nJqlMM7CEP6-YGOZOmZxWYl8D.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dCIRLGem--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sqs1uhm4a6vbX5nAOQgE1nJqlMM7CEP6-YGOZOmZxWYl8D.width-500.png" alt='The Zap Trigger to be chosen by the user. You can see different triggers listed like Twilio, Google Sheets, Twitter, and more. There"s a search bar to find the trigger you need.' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Event&lt;/strong&gt; dropdown, select the " &lt;strong&gt;New Response in Spreadsheet&lt;/strong&gt;" option, and click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Click on Sign in, authenticate with Google, and share the necessary permissions with Zapier. Once authenticated, click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, click on the Spreadsheet dropdown and select the spreadsheet that you created earlier for your Google Form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3l063k7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sONKTXNMy_ysOU9ZlHBrQYmfPBWZEh34MDTG6WtO_zza4c.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3l063k7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sONKTXNMy_ysOU9ZlHBrQYmfPBWZEh34MDTG6WtO_zza4c.width-500.png" alt='User selects the "Contact Niels (Responses)" option from the Spreadsheet dropdown for the Google Forms trigger in Zapier.' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A single spreadsheet can contain multiple worksheets, so Zapier will also prompt you to select the &lt;strong&gt;Worksheet&lt;/strong&gt; of which there is only one in this case. Select the &lt;strong&gt;Worksheet&lt;/strong&gt; and then click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You may be asked to test the trigger; if so, click Skip test since testing will be done at the end of the tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the SendGrid Action to send an email
&lt;/h3&gt;

&lt;p&gt;After the Trigger, there's already an empty action added to your Zap. Click on the action and search for the &lt;strong&gt;SendGrid&lt;/strong&gt; app. For the &lt;strong&gt;Event&lt;/strong&gt; dropdown, select " &lt;strong&gt;Send Email&lt;/strong&gt;", and then click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LKu2Tc1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Hl991rp6a5XxlxywBfT1JA-WD-J6D2ebKEnQff8c7xb_b4.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LKu2Tc1t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/Hl991rp6a5XxlxywBfT1JA-WD-J6D2ebKEnQff8c7xb_b4.width-500.png" alt='User adds SendGrid action to Zapier and selects the "Send Email" option from the "Event" dropdown.' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you have to help Zapier authenticate to your SendGrid account. Click on the &lt;strong&gt;Sign In&lt;/strong&gt; button and a window will pop up asking you for the " &lt;strong&gt;Mail API Key&lt;/strong&gt;", enter the SendGrid API Key you took note of earlier and click " &lt;strong&gt;Yes, Continue&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X-pkvc53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/N3MX-QYXXL69uuk1-t-z8B1NBwZCoJCjTqd-JPuGvldySx.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X-pkvc53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/N3MX-QYXXL69uuk1-t-z8B1NBwZCoJCjTqd-JPuGvldySx.width-500.png" alt='Zapier"s window to authenticate with SendGrid. The window has a single field "Mail API Key" asking for the SendGrid API Key with mail permissions.' width="500" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that Zapier has your SendGrid API Key, click &lt;strong&gt;Continue&lt;/strong&gt; and configure the action.&lt;/p&gt;

&lt;p&gt;As you can see in the screenshot below, I configured the fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To: The email address of the person that will review the form submission. You can use your own email address for this.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;From&lt;/strong&gt; : The Verified Sender email address that you configured earlier in SendGrid.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From Name
: A name that uniquely identifies the form. This is added using the "Insert Data …" popover that lets you use the data from a trigger or a previous action. In this case, the data is pulled from the Google Form trigger.- &lt;strong&gt;Reply To&lt;/strong&gt; : This is optional, but if your form is like mine and has an input for the user's email address, this field can be used to reply directly to the form submitter through the email instead of replying to the &lt;strong&gt;From&lt;/strong&gt; email address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subject&lt;/strong&gt; : The title that will be shown on the subject line of the email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML&lt;/strong&gt; : The HTML will be the content of your email. You can also use the &lt;strong&gt;Text&lt;/strong&gt; field if you don't want to use HTML.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the HTML I used for my email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Someone submitted the Contact Niels Form:&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Name: [FORM SUBMISSION NAME]&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Email Address: [FORM SUBMISSION EMAIL ADDRESS]&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Question: &lt;span class="nt"&gt;&amp;lt;br&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
[FORM SUBMISSION QUESTION]
&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a similar form as mine, you'll have to replace the [FORM SUBMISSION …] placeholders with the data from the Google Forms trigger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bb6nkucR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sikCwXCqCMsRtan9UAA57JKSrczZWiMvbcgthOF1osrQXk.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bb6nkucR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/sikCwXCqCMsRtan9UAA57JKSrczZWiMvbcgthOF1osrQXk.width-500.png" alt="A form to specify how to send the email from Zapier with SendGrid." width="500" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Continue&lt;/strong&gt;. The last step is to &lt;strong&gt;Test action&lt;/strong&gt; which you can skip by clicking &lt;strong&gt;Skip test&lt;/strong&gt; since you will be coming back at the end to it to test it out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Twilio Action to send an SMS
&lt;/h3&gt;

&lt;p&gt;The next action you will create is to send an SMS notification when the form has been submitted. &lt;/p&gt;

&lt;p&gt;This will require you to add a second action to your Zap, which is only possible for paid Zapier plans. If you don't have a paid Zapier plan or trial, you can only create Zaps with a single action. However, as a workaround, you could create a second Zap with the same Google Forms trigger and the Twilio action for sending SMS.&lt;/p&gt;

&lt;p&gt;Click on the plus-icon below the SendGrid action you just created. Look for "Twilio" in the App Event search box and click on the Twilio option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YWsXcNtA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/xrqNkXF9DuUY5i_wBWpTDau1YVYTpqNjwr7Qhb-wZljbTW.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YWsXcNtA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/xrqNkXF9DuUY5i_wBWpTDau1YVYTpqNjwr7Qhb-wZljbTW.width-500.png" alt='User adds another action, this time selecting "Twilio" as the "App Event".' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, select " &lt;strong&gt;Send SMS&lt;/strong&gt;" in the &lt;strong&gt;Event&lt;/strong&gt; dropdown, and click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--en9HaFqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/OLFTP-SAFpPNt5cA3PVAy13Me7qd1JnFV17QIWDHJ33nDX.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--en9HaFqu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/OLFTP-SAFpPNt5cA3PVAy13Me7qd1JnFV17QIWDHJ33nDX.width-500.png" alt='The user selected "Twilio" as the action in the Zapier flow and selected "Send SMS" for the "Event" dropdown.' width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Sign in&lt;/strong&gt; to share the Twilio credentials with Zapier. Enter the Twilio &lt;strong&gt;Account SID&lt;/strong&gt; and &lt;strong&gt;Auth Token&lt;/strong&gt; you took note of earlier and click " &lt;strong&gt;Yes, Continue&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tFw6FDYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/ljbWsfbiBeNkACbNflpu1LotIxTnn_WUc1BjTUWmI1nWK0.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tFw6FDYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/ljbWsfbiBeNkACbNflpu1LotIxTnn_WUc1BjTUWmI1nWK0.width-500.png" alt="Zapier asking the user for their Twilio Account SID and Auth Token." width="500" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in your Zap, click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Select your Twilio Phone Number from the "From Number" dropdown. Next, enter your phone number into the " &lt;strong&gt;To Number&lt;/strong&gt;" field, and enter some text into the &lt;strong&gt;Message&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c0M0WPY9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/hj4ACLKc8tIUsEIVLfhXTmvmXpjrsQ2AsfQAE9UURmm5vb.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c0M0WPY9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/hj4ACLKc8tIUsEIVLfhXTmvmXpjrsQ2AsfQAE9UURmm5vb.width-500.png" alt="A form to specify how to send the SMS from Zapier with Twilio." width="500" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Continue&lt;/strong&gt; , then &lt;strong&gt;Skip test&lt;/strong&gt; and finally &lt;strong&gt;Publish Zap&lt;/strong&gt;. You’ll be asked to confirm to turn on your Zap; click &lt;strong&gt;Publish &amp;amp; Turn On&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing your Zap
&lt;/h2&gt;

&lt;p&gt;While creating your Zap, you may have noticed that you can test the trigger and actions from Zapier's interface. Go to your trigger and under the " &lt;strong&gt;Test trigger&lt;/strong&gt;" section, click on the " &lt;strong&gt;Test trigger&lt;/strong&gt;" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bbJKN2UI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/ujK4wGFRvMWOnYFiutSp4h9xgKON2ZVLIaF748SURL2aIt.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bbJKN2UI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/ujK4wGFRvMWOnYFiutSp4h9xgKON2ZVLIaF748SURL2aIt.width-500.png" alt='Zapier lets you test the Zap by pressing the "Test trigger" button.' width="500" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If someone already submitted the form before, Zapier will grab the form submission data and use it to test your Zap. Be mindful tho, this data may be a real submission, not just your own test data. Now head over to each of your actions and test them out as well.&lt;/p&gt;

&lt;p&gt;To do an end-to-end test, go to your Google Form and submit it and wait until you get your email and text message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You just created one of the most commonly requested form flows without writing any code; well apart from some HTML that is. However, there are many more triggers and actions you could build automations with, and if you want to build and explore more communication applications, check out our no-code application builder, &lt;a href="https://www.twilio.com/studio"&gt;Twilio Studio&lt;/a&gt;. For example, learn &lt;a href="https://www.twilio.com/blog/serverless-studio-ivr-builder"&gt;how to build an Interactive Voice Response (IVR) application with Twilio Studio&lt;/a&gt; so you can take phone calls to help out customers without having a call representative.&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>sendgrid</category>
      <category>zapier</category>
      <category>googleforms</category>
    </item>
    <item>
      <title>How to better configure C# and .NET applications for SendGrid</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Thu, 12 May 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/twilio/how-to-better-configure-c-and-net-applications-for-sendgrid-25ef</link>
      <guid>https://dev.to/twilio/how-to-better-configure-c-and-net-applications-for-sendgrid-25ef</guid>
      <description>&lt;p&gt;&lt;strong&gt;This blog post was written for &lt;a href="https://www.twilio.com/"&gt;Twilio&lt;/a&gt; and &lt;a href="https://www.twilio.com/blog/better-configure-csharp-and-dotnet-apps-for-sendgrid"&gt;originally published at the Twilio blog&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are a hundred different ways to provide configuration to your applications. For almost any programming language, you can use environment variables and &lt;em&gt;.env&lt;/em&gt; files, but configuration can also be stored in other file formats like JSON, YAML, TOML, XML, INI, and the list keeps on going. However, in some scenarios configuration isn't pulled from files, but instead is pulled from Azure Key Vault, HashiCorp Vault, or a similar vault service. It is rare that your configuration will come from a single source. Luckily, there are APIs in .NET that can help you grab configuration from multiple sources and merge them together. &lt;/p&gt;

&lt;p&gt;In this tutorial, you'll start with an application that sends emails using &lt;a href="https://sendgrid.com/"&gt;Twilio SendGrid&lt;/a&gt;, where the configuration is fetched directly from the environment variables. You'll then refactor the app to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fetch configuration from multiple sources, specifically, JSON files, user-secrets, environment variables, and command-line arguments&lt;/li&gt;
&lt;li&gt;bind the configuration to strongly-typed objects&lt;/li&gt;
&lt;li&gt;inject configuration using dependency injection following the options pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need these things to follow along with this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An OS that supports .NET (Windows/macOS/Linux)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads"&gt;Git CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/6.0"&gt;.NET 6 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A code editor or IDE (I recommend &lt;a href="https://code.visualstudio.com/Download"&gt;VS Code&lt;/a&gt; with &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp"&gt;the C# plugin&lt;/a&gt;, &lt;a href="https://visualstudio.microsoft.com/"&gt;Visual Studio&lt;/a&gt;, or &lt;a href="https://www.jetbrains.com/rider/"&gt;JetBrains Rider&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A free or paid Twilio account (If you &lt;a href="https://www.twilio.com/referral/OhKZOM"&gt;register here&lt;/a&gt;, you'll receive $10 in Twilio credit when you upgrade to a paid account!)&lt;/li&gt;
&lt;li&gt;A Twilio SendGrid account. Sign up &lt;a href="https://sendgrid.com/free/?source=twilio-blog"&gt;here&lt;/a&gt; to send up to 100 emails per day completely free of charge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern"&gt;the source code for this tutorial on GitHub&lt;/a&gt;. Use it if you run into any issues, or &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/issues"&gt;submit an issue&lt;/a&gt; if you run into problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your SendGrid account to send emails
&lt;/h2&gt;

&lt;p&gt;There are two things you need to configure before you can send emails. First, you’ll need to set up &lt;strong&gt;Sender Authentication&lt;/strong&gt;. This will verify that you own the email address or domain that you will send emails from. Second, you’ll need to create a &lt;strong&gt;SendGrid API Key&lt;/strong&gt; with permission to send emails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sender Authentication
&lt;/h3&gt;

&lt;p&gt;To quickly get started, you will use &lt;strong&gt;Single Sender Verification&lt;/strong&gt; for this tutorial. This verifies that you own the email address that the application will send emails from. Single Sender Verification is great &lt;strong&gt;for testing purposes&lt;/strong&gt; , but it is not recommended for production.&lt;/p&gt;

&lt;p&gt;Twilio recommends &lt;a href="https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication/"&gt;Domain Authentication&lt;/a&gt; for production environments. An authenticated domain proves to email providers that you own the domain, and removes the "via sendgrid.net" text that inbox providers would otherwise append to your from address.&lt;/p&gt;

&lt;p&gt;To set up Single Sender Verification, click the &lt;strong&gt;Settings&lt;/strong&gt; tab in the left menu. Once the settings tab opens, click &lt;strong&gt;Sender Authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t7jhc6r8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/nPSCS5q1BytqQKT4dcVRz-BE4seRapA7j5xNz4X4rxYWc-.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t7jhc6r8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/nPSCS5q1BytqQKT4dcVRz-BE4seRapA7j5xNz4X4rxYWc-.width-500.png" alt='Side-navigation with a Settings group and multiple links in the group. The link "Sender Authentication" is highlighted.' width="352" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, click &lt;strong&gt;Get Started&lt;/strong&gt; under the &lt;strong&gt;Single Sender Verification&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L1k1QVNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/36Ob7tHuD-2qb-P2ECdPKHY9qEPLl_XfG4NoXfAs7mMt0m.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L1k1QVNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/36Ob7tHuD-2qb-P2ECdPKHY9qEPLl_XfG4NoXfAs7mMt0m.width-500.png" alt='A button saying "Get Started" to verify an individual email address.' width="500" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This opens a form in the right-side panel. Fill out the form with your information and email address.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hXEv_TNz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/IUSVGXUjozYbyqKUdaNTd3tkxjjO7zQct6155C_k54ppRQ.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hXEv_TNz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/IUSVGXUjozYbyqKUdaNTd3tkxjjO7zQct6155C_k54ppRQ.width-500.png" alt="A form to create a sender. The form asks for contact information including the email address that will be used to send emails from." width="500" height="677"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt; after filling out the form. Another panel will appear on the right, asking you to confirm your email address in your email inbox.Go to your personal email inbox, open the email from SendGrid, and click Verify Single Sender.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iwTfUvVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/iPFFYg6ptFsUG3P3iw7TAaOLTyx1UkoaMKRy75wJzFp3fv.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iwTfUvVS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/iPFFYg6ptFsUG3P3iw7TAaOLTyx1UkoaMKRy75wJzFp3fv.width-500.png" alt='An email from SendGrid with a button saying "Verify Single Sender".' width="500" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your email address has been verified. You can now use it to send emails!&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a SendGrid API Key to send emails
&lt;/h3&gt;

&lt;p&gt;Back on the SendGrid website, click &lt;strong&gt;API Keys&lt;/strong&gt; under the &lt;strong&gt;Settings tab&lt;/strong&gt; , then click &lt;strong&gt;Create API Key&lt;/strong&gt; in the top right-hand corner. This opens another form in the right-side panel. &lt;/p&gt;

&lt;p&gt;Give your API Key a meaningful name. You can assign different permissions to the API Key. For optimal security, you should only give the minimum amount of permissions that you need.&lt;/p&gt;

&lt;p&gt;Next, click Restricted Access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tTfYUHW3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/iFCCrNrpOwKqVAacydgTMnsmfKjxdwtGuHmb4v_JlGbPr1.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tTfYUHW3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/iFCCrNrpOwKqVAacydgTMnsmfKjxdwtGuHmb4v_JlGbPr1.width-500.png" alt="A form to create an API Key. The form asks for a name for the key and what permissions to grant to the key." width="500" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the &lt;strong&gt;Mail Send&lt;/strong&gt; accordion item and click on it to reveal the permissions underneath. Drag the slider to the right for the &lt;strong&gt;Mail Send&lt;/strong&gt; permission.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7ipNJTW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/M1jOW-BEJAn8NGWA6B-IyGnrs18M6hRliRTbKOhn987TYv.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z7ipNJTW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/M1jOW-BEJAn8NGWA6B-IyGnrs18M6hRliRTbKOhn987TYv.width-500.png" alt='A list of permissions that you can control using a slider bar next to each permission. The "Mail Send" accordion item is expanded to reveal the "Mail Send" permission underneath.' width="500" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll to the bottom of the form and click &lt;strong&gt;Create &amp;amp; View&lt;/strong&gt;. The API key will now be displayed on your screen. &lt;/p&gt;

&lt;p&gt;You will not be able to retrieve the API key again once you leave this screen, so make sure you copy the API Key somewhere safe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--excWg-ZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/N2zTJIaJNBJzH0HqvEPPUaVDOWrjkDqqDOYRgdH-FauuvI.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--excWg-ZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/N2zTJIaJNBJzH0HqvEPPUaVDOWrjkDqqDOYRgdH-FauuvI.width-500.png" alt="API key is displayed to copy and store elsewhere. Key will not be shown again." width="500" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the sender verified and the API Key created, you’re ready to write some code!&lt;/p&gt;

&lt;p&gt;For any of the upcoming commands and code, replace the following placeholders like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;[SENDGRID_API_KEY]&lt;/code&gt; with the API Key you just took note of&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;[FROM_EMAIL_ADDRESS]&lt;/code&gt; and &lt;code&gt;[FROM_NAME]&lt;/code&gt; with the email address you verified earlier and the name you want your recipients to see.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;[TO_EMAIL_ADDRESS]&lt;/code&gt; and &lt;code&gt;[TO_NAME]&lt;/code&gt; with the email address and name of the desired recipient of the email.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get configuration directly from environment variables
&lt;/h2&gt;

&lt;p&gt;To get the project up and running quickly on your machine, open your preferred shell, and use the following git command to clone the source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Swimburger/SendGridOptionsPattern.git &lt;span class="nt"&gt;--branch&lt;/span&gt; Step1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then navigate into the &lt;em&gt;SendGridOptionsPattern&lt;/em&gt; folder using &lt;code&gt;cd SendGridOptionsPattern&lt;/code&gt;. This project is a console application with only one C# file, &lt;em&gt;Program.cs&lt;/em&gt;, which has the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SendGridApiKey"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromEmailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FromEmailAddress"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FromName"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toEmailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ToEmailAddress"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ToName"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SendGridClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromEmailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Sending with Twilio SendGrid is Fun"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PlainTextContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"and easy to do anywhere, especially with C#"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toEmailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// A success status code means SendGrid received the email request and will process it.&lt;/span&gt;
&lt;span class="c1"&gt;// Errors can still occur when SendGrid tries to send the email. &lt;/span&gt;
&lt;span class="c1"&gt;// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity &lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Email queued successfully!"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application pulls the SendGrid API Key, as well as the email address and name for the sender and recipient from the environment variables. The code then creates a new &lt;code&gt;SendGridClient&lt;/code&gt; with the API key, creates an email message using &lt;code&gt;SendGridMessage&lt;/code&gt;, and then sends the email.&lt;/p&gt;

&lt;p&gt;The application uses the &lt;a href="https://github.com/sendgrid/sendgrid-csharp"&gt;SendGrid library for C# and .NET&lt;/a&gt; to authenticate with and send emails through the SendGrid API. The &lt;a href="https://www.nuget.org/packages/SendGrid"&gt;SendGrid NuGet package&lt;/a&gt; has been added into the project as part of the source code you cloned. You can find this dependency in the csproj-file.&lt;/p&gt;

&lt;p&gt;Before you can run this application, you'll need to configure the environment variables that the project depends on. Back in your shell, set these environment variables using the following commands. &lt;/p&gt;

&lt;p&gt;If you're using Bash or a similar shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SendGridApiKey&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;SENDGRID_API_KEY]
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FromEmailAddress&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;FROM_EMAIL_ADDRESS]
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FromName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[FROM_NAME]"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ToEmailAddress&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;TO_EMAIL_ADDRESS]
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ToName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[TO_NAME]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;SendGridApiKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[SENDGRID_API_KEY]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;FromEmailAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[FROM_EMAIL_ADDRESS]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;FromName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[FROM_NAME]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;ToEmailAddress&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TO_EMAIL_ADDRESS]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;ToName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[TO_NAME]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using CMD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"SendGridApiKey=[SENDGRID_API_KEY]"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"FromEmailAddress=[FROM_EMAIL_ADDRESS]"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"FromName=[FROM_NAME]"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"ToEmailAddress=[TO_EMAIL_ADDRESS]"&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"ToName=[TO_NAME]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the environment variables have been set, you can run the project using the following .NET CLI command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application will send an email from your verified email address to the recipient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T7jnepuz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/WG_UzQM1y5QewEJDicPNRdrirtn9MBkI-Px_GkdYszJt8_.width-500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T7jnepuz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://twilio-cms-prod.s3.amazonaws.com/images/WG_UzQM1y5QewEJDicPNRdrirtn9MBkI-Px_GkdYszJt8_.width-500.png" alt='An email with title "Sending with Twilio SendGrid is Fun" and body "and easy to do anywhere, especially with C#' width="500" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the SendGrid API responds with a successful HTTP status code, that does not necessarily mean that the email was successfully received by the recipient. It does however mean that the SendGrid API has accepted your request and will send the email. If the recipient does not receive the email, you can use the &lt;a href="https://app.sendgrid.com/email_activity"&gt;Email Activity Feed&lt;/a&gt; to find out why.&lt;/p&gt;

&lt;p&gt;Pulling the configuration from the environment variables works great, but instead of using environment variables, you could've simply hardcoded all the configuration, right?&lt;br&gt;&lt;br&gt;
Many of these configuration elements, especially the API Key, are sensitive secrets which you do not want to share with others. By pulling them from external configuration like the environment variables, you ensure that you don't accidentally check them into your public source code for everyone to see. And whenever someone else wants to run the same code, they can configure their own API Key configuration into the environment variables and run it. &lt;/p&gt;

&lt;p&gt;This is why in SendGrid samples, we will always use environment variables. Although it's an extra step compared to hard coding configuration, it is the fastest way to get you up and running with SendGrid without compromising your API Key by accident.&lt;/p&gt;

&lt;p&gt;However, in .NET, there's a better way to retrieve external configuration, using the &lt;code&gt;ConfigurationBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build your Configuration with the .NET configuration builder
&lt;/h2&gt;

&lt;p&gt;There are many ways to configure .NET applications, and typically the configuration is composed from multiple sources. ASP.NET Core originally introduced APIs to easily load configuration from a bunch of different sources using &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration#configuration-providers"&gt;configuration providers&lt;/a&gt; and merge them all together with the &lt;code&gt;ConfigurationBuilder&lt;/code&gt;. These APIs were moved into the &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Hosting"&gt;Microsoft.Extensions.Hosting NuGet package&lt;/a&gt; so it could be used in any .NET application.&lt;/p&gt;

&lt;p&gt;Not only can you use the new Configuration APIs on .NET (Core) — you can even &lt;a href="https://swimburger.net/blog/dotnet/using-configurationproviders-from-microsoft-extensions-configuration-on-dotnet-framework"&gt;use them on .NET Framework&lt;/a&gt;. .NET Framework has its own configuration APIs, but they are less intuitive and dated compared to the new Configuration APIs.&lt;/p&gt;

&lt;p&gt;Back in your shell, run the following .NET CLI commands to add NuGet packages for the configuration extensions and some of the configuration providers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.CommandLine
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration"&gt;Microsoft.Extensions.Configuration&lt;/a&gt; package contains the main APIs for building configuration and also the JSON configuration provider.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration.CommandLine"&gt;Microsoft.Extensions.Configuration.CommandLine&lt;/a&gt; package adds a configuration provider that builds configuration from command-line arguments.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration.EnvironmentVariables"&gt;Microsoft.Extensions.Configuration.EnvironmentVariables&lt;/a&gt; package adds a configuration provider that builds configuration from environment variables.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration.UserSecrets"&gt;Microsoft.Extensions.Configuration.UserSecrets&lt;/a&gt; package adds a configuration provider that builds configuration from the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets#enable-secret-storage"&gt;Secret Manager&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To start using the configuration APIs, update the &lt;em&gt;Program.cs&lt;/em&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Reflection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserSecrets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommandLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"SendGrid:ApiKey"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromEmailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:From:Email"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fromName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:From:Name"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toEmailAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:To:Email"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;toName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:To:Name"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:Subject"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Email:Body"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SendGridClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fromEmailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fromName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PlainTextContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailBody&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EmailAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toEmailAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// A success status code means SendGrid received the email request and will process it.&lt;/span&gt;
&lt;span class="c1"&gt;// Errors can still occur when SendGrid tries to send the email. &lt;/span&gt;
&lt;span class="c1"&gt;// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity &lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Email queued successfully!"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting app is the same as before but is now loading configuration from multiple sources instead of directly from the environment variables. The &lt;code&gt;ConfigurationBuilder&lt;/code&gt; is a class that uses the &lt;a href="https://en.wikipedia.org/wiki/Builder_pattern"&gt;builder pattern&lt;/a&gt; so you can chain together multiple configuration providers using the &lt;code&gt;Add…&lt;/code&gt; extension methods. When configuration elements coming from different providers have the same key, the configuration added later to the chain will overwrite the configuration added earlier in the chain. This means that the order in which you add configuration providers matters! In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User Secrets overwrites configuration from &lt;strong&gt;JSON&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables&lt;/strong&gt; overwrites configuration from &lt;strong&gt;User Secrets and JSON&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command-Line Arguments&lt;/strong&gt; overwrites configuration from &lt;strong&gt;User Secrets, JSON, and Environment Variables&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;optional&lt;/code&gt; parameter specifies whether or not the application should throw an exception if the underlying source is missing. The &lt;code&gt;reloadOnChange&lt;/code&gt; parameter specifies whether or not to update the configuration when the underlying source changes. For example, if there's an issue in your production application, but for performance reasons, the logging level is set to &lt;code&gt;Warning&lt;/code&gt; in &lt;em&gt;appsettings.json&lt;/em&gt;, you can change the logging level to &lt;code&gt;Debug&lt;/code&gt;. Once you save the file, the application begins writing logs from the &lt;code&gt;Debug&lt;/code&gt; level and up, without having to restart your application!&lt;/p&gt;

&lt;p&gt;After the configuration is built, the app will grab the configuration elements from the configuration, but notice how the configuration keys now use two different prefixes: &lt;code&gt;SendGrid:&lt;/code&gt; and &lt;code&gt;Email:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can organize your configuration into hierarchical sections by adding colons &lt;code&gt;:&lt;/code&gt; as separators. So in this case, &lt;code&gt;ApiKey&lt;/code&gt; is part of the &lt;code&gt;SendGrid&lt;/code&gt; section, and all the other configuration is part of the &lt;code&gt;Email&lt;/code&gt; section. You can also nest sections as shown in the &lt;code&gt;Email&lt;/code&gt; section. The &lt;code&gt;Email&lt;/code&gt; section contains child sections &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;To&lt;/code&gt; which hold on to the email address and name of the sender and recipient. There are a lot of different ways you could structure your configuration, but how you organize your configuration all depends on your needs.&lt;/p&gt;

&lt;p&gt;Now that the application loads configuration from multiple sources, in which source should you store configuration? It depends on the configuration, your use-case, and personal preference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use JSON for configuration that does not contain secrets or other sensitive information.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;User Secrets&lt;/strong&gt; only for local development to configure secrets and other sensitive information.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Environment Variables&lt;/strong&gt; for environment specific configuration including secrets or other sensitive information. However, User Secrets is preferred for local development. Environment Variables are a powerful source of configuration because all operating systems, container technology, and cloud infrastructure supports them.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Command-Line Arguments&lt;/strong&gt; to configure execution specific settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a new file called &lt;em&gt;appsettings.json&lt;/em&gt; and add the following JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"From"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[FROM_EMAIL_ADDRESS]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[FROM_NAME]"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ahoy from JSON!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thank you for signing up to our website. See you around!"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;appsettings.json&lt;/em&gt; file will provide the &lt;code&gt;FromEmailAddress&lt;/code&gt;, &lt;code&gt;FromName&lt;/code&gt;, &lt;code&gt;Subject&lt;/code&gt;, and &lt;code&gt;Body&lt;/code&gt; configuration, even though it would also make sense to pass this configuration in as command-line arguments.&lt;/p&gt;

&lt;p&gt;By default, in a console app, the &lt;em&gt;appsettings.json&lt;/em&gt; file will not be copied to the output of the project. As a result the configuration provider will not find the underlying source, which means the configuration in your JSON will not be loaded. To make sure the the &lt;em&gt;appsettings.json&lt;/em&gt; file is copied to the output, add a &lt;code&gt;Content&lt;/code&gt; node to the csproj-file as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Content&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt; &lt;span class="na"&gt;CopyToOutputDirectory=&lt;/span&gt;&lt;span class="s"&gt;"Always"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start using the Secret Manager, you first need to initialize it for your project using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet user-secrets init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can set the &lt;code&gt;SendGrid:ApiKey&lt;/code&gt; configuration like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet user-secrets &lt;span class="nb"&gt;set &lt;/span&gt;SendGrid:ApiKey &lt;span class="o"&gt;[&lt;/span&gt;SENDGRID_API_KEY]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's likely you would configure the API Key as an environment variable in other environments, but since you're running this locally, the Secret Manager is preferred. So, in this case, you don't need to configure any environment variables.&lt;/p&gt;

&lt;p&gt;The application now has configuration for who the email will be sent from, but not who the email will be sent to. Instead of configuring it using one of the previous sources, run the project using the .NET CLI and pass it in as a command-line argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Email &lt;span class="o"&gt;[&lt;/span&gt;TO_EMAIL_ADDRESS] &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Name &lt;span class="s2"&gt;"[TO_NAME]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like before, the recipient will receive an email, but now the subject will be "Ahoy from JSON!".&lt;/p&gt;

&lt;p&gt;Run the command again, but overwrite the &lt;code&gt;Email:Subject&lt;/code&gt; configuration using a command-line argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Email &lt;span class="o"&gt;[&lt;/span&gt;TO_EMAIL_ADDRESS] &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Name &lt;span class="s2"&gt;"[TO_NAME]"&lt;/span&gt; &lt;span class="nt"&gt;--Email&lt;/span&gt;:Subject &lt;span class="s2"&gt;"Ahoy from the CLI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the recipient will receive an email with the subject "Ahoy from the CLI".&lt;/p&gt;

&lt;p&gt;The end result may be the same, but your application is a lot more flexible now because it can be configured in many different ways. However, these are not the only configuration providers. &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration#configuration-providers"&gt;Microsoft has 5 more configuration providers&lt;/a&gt;, and if those don't meet your needs, you can also develop your own provider or use someone else's. &lt;/p&gt;

&lt;p&gt;Some operating systems and shells may not allow you to use colons. In that case you can use a double underscore &lt;code&gt;__&lt;/code&gt; as a separator, and .NET will replace it with the colon for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bind configuration to strongly-typed objects
&lt;/h2&gt;

&lt;p&gt;You just learned how to use the configuration builder to get configuration from multiple sources, but the amount of configuration can quickly get out of hand. A more manageable solution to this is to bind your configuration to strongly-typed objects.&lt;/p&gt;

&lt;p&gt;To starting binding configuration, add the &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Binder"&gt;Microsoft.Extensions.Configuration.Binder NuGet package&lt;/a&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.Configuration.Binder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;em&gt;Program.cs&lt;/em&gt; file with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Reflection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserSecrets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommandLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"SendGrid:ApiKey"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SendGridClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PlainTextContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// A success status code means SendGrid received the email request and will process it.&lt;/span&gt;
&lt;span class="c1"&gt;// Errors can still occur when SendGrid tries to send the email. &lt;/span&gt;
&lt;span class="c1"&gt;// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity &lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Email queued successfully!"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the bottom of &lt;em&gt;Program.cs&lt;/em&gt;, there is a new class which has properties with names that match your configuration. By matching the name of the properties and the keys of the configuration, you will be able to bind the configuration to instances of said class. You can grab sections from the configuration using &lt;code&gt;config.GetSection("YourSectionName")&lt;/code&gt; and then bind the section to strongly-typed objects using &lt;code&gt;.Get&amp;lt;YourClass&amp;gt;()&lt;/code&gt;. This way, the &lt;code&gt;Email&lt;/code&gt; section is bound to instances of &lt;code&gt;EmailOptions&lt;/code&gt; which is then used to send the email as before. &lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;EmailOptions&lt;/code&gt; class, the &lt;code&gt;From&lt;/code&gt; and &lt;code&gt;To&lt;/code&gt; properties are defined using the &lt;code&gt;EmailAddress&lt;/code&gt; class provided by the SendGrid library. The &lt;code&gt;EmailAddress&lt;/code&gt; class contains two properties: &lt;code&gt;Email&lt;/code&gt; and &lt;code&gt;Name&lt;/code&gt;. Because these properties names match the names of your configuration, even these nested objects will be bound.&lt;/p&gt;

&lt;p&gt;If you run the project now, you will continue to get the same result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;reloadOnChange&lt;/code&gt; is enabled on the configuration providers, and the configuration changes in the configuration source, the configuration object will be updated, but any strongly-typed object that has already been bound will not be updated accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swap configuration builder with the default host builder
&lt;/h2&gt;

&lt;p&gt;If you've used the ASP.NET templates, you may notice that the configuration has already been set up without any &lt;code&gt;ConfigurationBuilder&lt;/code&gt; code. That's because ASP.NET templates use the default &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/web-host?view=aspnetcore-6.0"&gt;Web Host&lt;/a&gt; which sets up the default configuration, logging, dependency injection, web related functionality, and more. &lt;/p&gt;

&lt;p&gt;If you want the same configuration, logging, and dependency injection from the web host builder, but not the web related functionality, you can use the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host"&gt;Generic Host&lt;/a&gt; instead of the Web Host.&lt;/p&gt;

&lt;p&gt;Add the &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.Hosting"&gt;Microsoft.Extensions.Hosting NuGet package&lt;/a&gt; using the .NET CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.Hosting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;em&gt;Program.cs&lt;/em&gt;, Replace the &lt;code&gt;using&lt;/code&gt; statements and the &lt;code&gt;ConfigurationBuilder&lt;/code&gt; code with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IHost&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of building the configuration yourself, the project now uses the defaults that come with the Generic Host. The code then retrieves the configuration from the dependency injection container (&lt;code&gt;host.Services&lt;/code&gt;), and continues using the configuration object as before.&lt;/p&gt;

&lt;p&gt;The default Generic Host will build configuration similarly to how you did it yourself, but there are some differences. You can find an &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0#default-builder-settings"&gt;overview of the Generic Host defaults in the Microsoft Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The default Generic Host builds configuration slightly differently. More specifically, your user secrets will only be loaded if the &lt;code&gt;Environment&lt;/code&gt; is configured as &lt;code&gt;Development&lt;/code&gt;. There are &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments#environments"&gt;multiple ways to configure the environment&lt;/a&gt;, but for this tutorial, pass in the &lt;code&gt;Environment&lt;/code&gt; argument when you run your project, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--Environment&lt;/span&gt; Development  &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Email &lt;span class="o"&gt;[&lt;/span&gt;TO_EMAIL_ADDRESS] &lt;span class="nt"&gt;--Email&lt;/span&gt;:To:Name &lt;span class="s2"&gt;"[TO_NAME]"&lt;/span&gt; &lt;span class="nt"&gt;--Email&lt;/span&gt;:Subject &lt;span class="s2"&gt;"Ahoy from the CLI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get configuration from dependency injection using the options pattern
&lt;/h2&gt;

&lt;p&gt;Now that you're using the Generic Host, you can start using the dependency injection (DI) that comes with it, just like you can on ASP.NET applications.&lt;/p&gt;

&lt;p&gt;Replace your existing code in &lt;em&gt;Program.cs&lt;/em&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IHost&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"SendGrid:ApiKey"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SendGridClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailSender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ISendGridClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;PlainTextContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// A success status code means SendGrid received the email request and will process it.&lt;/span&gt;
        &lt;span class="c1"&gt;// Errors can still occur when SendGrid tries to send the email. &lt;/span&gt;
        &lt;span class="c1"&gt;// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity &lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Email queued successfully!"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the &lt;code&gt;ConfigureServices&lt;/code&gt; method on the host builder and pass in a lambda to add more services to the DI container. In this lambda, the &lt;code&gt;Email&lt;/code&gt; configuration section is added as configuration using &lt;code&gt;services.Configure&amp;lt;YourOptions&amp;gt;&lt;/code&gt;. As a result, your configuration will be injected wherever DI is supported by adding a parameter of type &lt;code&gt;IOptions&amp;lt;YourOptions&amp;gt;&lt;/code&gt; to your constructor or method signature. Microsoft calls this the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#bind-hierarchical-configuration-data-using-the-options-pattern"&gt;Options pattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt;, there's also &lt;code&gt;IOptionsSnapshot&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;IOptionsMonitor&amp;lt;T&amp;gt;&lt;/code&gt;, and &lt;code&gt;IOptionsFactory&amp;lt;TOptions&amp;gt;&lt;/code&gt; which each have different behavior and use-cases. Learn more about the &lt;a href="https://docs.microsoft.com/en-us/dotnet/core/extensions/options#options-interfaces"&gt;differences between these options interfaces at Microsoft's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After configuring the options, the program will add the &lt;code&gt;EmailSender&lt;/code&gt; class to the CI container. &lt;code&gt;EmailSender&lt;/code&gt; is a new class that will be responsible for sending Emails. Now that the DI container has been built, you can retrieve the services from it. So now you have your &lt;code&gt;EmailOptions&lt;/code&gt; injected anywhere that supports DI. &lt;/p&gt;

&lt;p&gt;Now, the code will retrieve an instance of &lt;code&gt;EmailSender&lt;/code&gt; from the DI container, which is used to send an email using the &lt;code&gt;​​SendEmail&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;EmailSender&lt;/code&gt; is created by the DI container, the container injects an instance of &lt;code&gt;IOptions&amp;lt;EmailOptions&amp;gt;&lt;/code&gt; into the constructor of &lt;code&gt;EmailSender&lt;/code&gt;. &lt;code&gt;EmailSender&lt;/code&gt; grabs the message options using the &lt;code&gt;.Value&lt;/code&gt; property and uses it in the &lt;code&gt;SendEmail&lt;/code&gt; method to send an email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add SendGridClient to the Dependency Injection Container
&lt;/h2&gt;

&lt;p&gt;You've made your application more flexible and configurable, but you can also further integrate SendGrid into your application. The SendGrid SDK for .NET has an extra &lt;a href="https://www.nuget.org/packages/SendGrid.Extensions.DependencyInjection/"&gt;NuGet package that will add the SendGrid client to the dependency injection container&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run the following command to add the SendGrid DI package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package SendGrid.Extensions.DependencyInjection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update the &lt;em&gt;Program.cs&lt;/em&gt; file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Hosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;SendGrid.Helpers.Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;IHost&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSendGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"SendGrid:ApiKey"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emailSender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;emailSender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailSender&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ISendGridClient&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;EmailOptions&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EmailSender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EmailOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ISendGridClient&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendGridClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SendGridMessage&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;PlainTextContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sendGridClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendEmailAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// A success status code means SendGrid received the email request and will process it.&lt;/span&gt;
        &lt;span class="c1"&gt;// Errors can still occur when SendGrid tries to send the email. &lt;/span&gt;
        &lt;span class="c1"&gt;// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity &lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsSuccessStatusCode&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Email queued successfully!"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;From&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EmailAddress&lt;/span&gt; &lt;span class="n"&gt;To&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application adds the SendGrid client to the DI container using the &lt;code&gt;services.AddSendGrid&lt;/code&gt; method.&lt;br&gt;&lt;br&gt;
To configure the options of SendGrid, another lambda is used and passed into &lt;code&gt;AddSendGrid&lt;/code&gt;. The lambda-inception can be tricky, so make sure to verify all your parentheses match the code above.&lt;/p&gt;

&lt;p&gt;Instead of constructing the &lt;code&gt;SendGridClient&lt;/code&gt; as you did before, the &lt;code&gt;ISendGridClient&lt;/code&gt; will now be injected into the constructor of the &lt;code&gt;EmailSender&lt;/code&gt; class, and then used to send an email as before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why so complicated?
&lt;/h2&gt;

&lt;p&gt;You went from approximately 12 straightforward lines of code, to approximately 26 lines of code with classes, interfaces, lambda expressions, generic types, and more.&lt;br&gt;&lt;br&gt;
Why complicate the code so much? &lt;/p&gt;

&lt;p&gt;For a small sample where you want to go from point A to point B as fast as possible, you can use environment variables and be done with it, but for real-world applications, although it requires extra setup up-front, the techniques from this tutorial will decrease the amount of code you need to write and increase the flexibility and maintainability of your solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better .NET configuration
&lt;/h2&gt;

&lt;p&gt;After following this tutorial, you have a learned how to use .NET's configuration APIs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;securely configure secrets and sensitive information&lt;/li&gt;
&lt;li&gt;retrieve configuration from multiple sources&lt;/li&gt;
&lt;li&gt;override configuration from one source with another source&lt;/li&gt;
&lt;li&gt;add configuration to the dependency inject container&lt;/li&gt;
&lt;li&gt;inject configuration into your classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Congratulations on making it to the end of a long post about configuration! 👏&lt;br&gt;&lt;br&gt;
You can start including these techniques for SendGrid, Twilio, or really any .NET applications! If you run into any problems, you can refer to the &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern"&gt;source code for this tutorial on GitHub&lt;/a&gt;, or you can submit an issue on the GitHub repository. In addition to the end result, you can find each step in a separate branch (&lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step1"&gt;Step 1&lt;/a&gt;, &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step2"&gt;2&lt;/a&gt;, &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step3"&gt;3&lt;/a&gt;, &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step4"&gt;4&lt;/a&gt;, &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step5"&gt;5&lt;/a&gt;, and &lt;a href="https://github.com/Swimburger/SendGridOptionsPattern/tree/Step6"&gt;6&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Let me know what you're working on. I can't wait to see what you build!&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>sendgrid</category>
    </item>
    <item>
      <title>How to get the full public URL of ASP.NET Core</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Mon, 25 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/swimburger/how-to-get-the-full-public-url-of-aspnet-core-8f3</link>
      <guid>https://dev.to/swimburger/how-to-get-the-full-public-url-of-aspnet-core-8f3</guid>
      <description>&lt;p&gt;URLs are what makes the web the web. In ASP.NET Core Controllers, Razor Pages, and Razor views, there's a &lt;code&gt;Request&lt;/code&gt; property which gives you access to the &lt;code&gt;HttpRequest&lt;/code&gt; instance. The &lt;code&gt;HttpRequest&lt;/code&gt; object gives you access to scheme, host (which is the domain and port), path, query string, body content, and headers of the incoming HTTP request.&lt;/p&gt;

&lt;p&gt;If you need to know what the full URL is of your web application, you can put the URL together by grabbing the &lt;code&gt;Scheme&lt;/code&gt;, &lt;code&gt;Host&lt;/code&gt;, &lt;code&gt;PathBase&lt;/code&gt;, &lt;code&gt;Path&lt;/code&gt;, and &lt;code&gt;QueryString&lt;/code&gt; and combining it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathBase&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryString&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe you're only interested in the URL without the path and query string, in which case you can omit the &lt;code&gt;PathBase&lt;/code&gt;, &lt;code&gt;Path&lt;/code&gt;, and &lt;code&gt;QueryString&lt;/code&gt; parameter from the string interpolation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to generate URLs for your application, I suggest you use the &lt;code&gt;UrlHelper&lt;/code&gt; as explained in this blog post about generating absolute URLs.&lt;/p&gt;

&lt;p&gt;If you're using Minimal APIs, there's no &lt;code&gt;Request&lt;/code&gt; property accessible to you, but you can accept an instance of &lt;code&gt;HttpRequest&lt;/code&gt; as a parameter to your endpoint callback like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello-world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathBase&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryString&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What if there's no HTTP request?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If there is no HTTP request that you're handling in your code, like when you're doing some background processing using a &lt;code&gt;BackgroundWorker&lt;/code&gt;, you'll need to get the scheme and host some other way. You could store your desired scheme and host into your configuration, or combine them together in a single property and store it into your configuration. Once configured, extract it from configuration and use it to build your absolute URL.&lt;/p&gt;

&lt;p&gt;Be mindful of the fact that web application can be requested from multiple URLs depending on the network and infrastructure configuration. So even though you could simply store the full public URL in configuration, that may not be the only URL that users use.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How to generate absolute URLs in ASP.NET Core</title>
      <dc:creator>Niels Swimburger.NET 🍔</dc:creator>
      <pubDate>Tue, 19 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/swimburger/how-to-generate-absolute-urls-in-aspnet-core-md3</link>
      <guid>https://dev.to/swimburger/how-to-generate-absolute-urls-in-aspnet-core-md3</guid>
      <description>&lt;p&gt;URLs are what makes the web the web. In ASP.NET Core Controllers, Razor Pages, and Razor views, there's a &lt;code&gt;Url&lt;/code&gt; property which gives you access to the &lt;code&gt;UrlHelper&lt;/code&gt;. Using the &lt;code&gt;UrlHelper&lt;/code&gt; you can generate URLs to MVC actions, pages, and routes. Take a look at this C# expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Privacy"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of this expression will be a URL string pointing to the &lt;code&gt;Privacy&lt;/code&gt; action on the &lt;code&gt;HomeController&lt;/code&gt;: &lt;code&gt;"/Home/Privacy"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is only the absolute path, but that is a valid URL and should work fine. However, sometimes you want to get the absolute URL which includes the scheme, the hostname or domain name, and the port. For this purpose, the methods on the &lt;code&gt;UrlHelper&lt;/code&gt; have overloads to specify the &lt;code&gt;protocol&lt;/code&gt; which is the scheme, and the &lt;code&gt;host&lt;/code&gt; which is the hostname or domain name. Take a look at this C# expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Privacy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"your-website"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By specifying the protocol and host, the result is now &lt;code&gt;"https://your-website/Home/Privacy"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, if you omit the &lt;code&gt;host&lt;/code&gt; parameter but keep the &lt;code&gt;protocol&lt;/code&gt; parameter, the result will still be an absolute URL, because the &lt;code&gt;host&lt;/code&gt; parameter will default to the host used for the current HTTP request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Privacy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result will be something like &lt;code&gt;"https://localhost:7184/Home/Privacy"&lt;/code&gt;. Whatever the host and port used to make this HTTP request will be used to generate this URL.&lt;/p&gt;

&lt;p&gt;When you're handling an HTTP request in ASP.NET Core, you can also access the &lt;code&gt;Scheme&lt;/code&gt; property on the &lt;code&gt;HttpRequest&lt;/code&gt;, so you could pass the scheme as the &lt;code&gt;protocol&lt;/code&gt; parameter to generate the URL with same scheme and host as the current HTTP request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Privacy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also pass in &lt;code&gt;Request.Host&lt;/code&gt; as the &lt;code&gt;host&lt;/code&gt; parameter, but you don't need to as it already uses the host from the current HTTP request when you specify the &lt;code&gt;protocol&lt;/code&gt; without specifying the &lt;code&gt;host&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if you don't have access to the &lt;code&gt;UrlHelper&lt;/code&gt; or you need to build a URL in a way that the &lt;code&gt;UrlHelper&lt;/code&gt; doesn't support?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can also create the absolute base URL for the current HTTP request by grabbing the properties from the &lt;code&gt;HttpRequest&lt;/code&gt;. The following code builds the absolute URL for the absolute path "/your/custom/path":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathBase&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/your/custom/path"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of this expression looks like &lt;code&gt;"https://localhost:7184/your/custom/path"&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
If you need the full absolute URL including the current absolute path, you could use the following C# expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PathBase&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What if there's no HTTP request?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If there is no HTTP request that you're handling in your code, like when you're doing some background processing using a &lt;code&gt;BackgroundWorker&lt;/code&gt;, you'll need to get the scheme and host some other way. You could store your desired scheme and host into your configuration, or combine them together in a single property and store it into your configuration. Once configured, extract it from configuration and use it to build your absolute URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By default, the &lt;code&gt;UrlHelper&lt;/code&gt; methods will generate absolute paths which isn't the same as a full absolute URL because it doesn't contain the scheme, hostname, and port number. However, the &lt;code&gt;UrlHelper&lt;/code&gt; methods do contain overloads which allow you to generate the full absolute URL.&lt;/p&gt;

&lt;p&gt;You can also manually build the absolute URL using the &lt;code&gt;HttpRequest&lt;/code&gt; properties or by storing the desired scheme and hostname from configuration and then extracting it.&lt;/p&gt;

&lt;p&gt;In most cases, using an absolute path will be sufficient and it'll save some characters in your HTML, but absolute URLs are sometimes required such as for the technical SEO and social meta tags.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>aspnet</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
