Skip to main content


Developing with Bandwidth: C# 101

What you’ll learn

  • Install and configure your development environment in C#
  • Setup your Bandwidth Account to start using the new APIs
  • Send and receive group MMS
  • Manage inbound and outbound phone calls

Webinar Transcript

TRACY: Hey, good afternoon or good morning to wherever you are in the world right now. My name is Tracy, and I'm going to be your webinar host for today. Welcome to our Developing With Bandwidth 101 mini-series. So a few housekeeping items before we get started-- all attendees are going to be muted just to prevent any background noise. And we are going to take Q&A. On your control panel, you should see a box for questions. Feel free to ask questions throughout this demo. We're going to be a little more informal. And if you have a good question during the webinar, we'll just go ahead and ask it. For those who might want to wait until the end, we'll still have that time allotted for Q&A at the end of the webinar. If you've been keeping up with our series, the last couple of weeks we covered Node.js and Java. So if you're interested in seeing those, you can head over to and watch on demand. And you can also sign up for any of the upcoming in the series as well. But first, today we're going to be covering C# with Bandwidth technical product owner Dan Tolbert. Dan does API things at Bandwidth, and he has for quite some time. And during his time with us, he's worked closely with customers of all sizes during their integrations to Bandwidth. And he currently sits on the technology team, and he helps drive Bandwidth Developer Experience team forward. And he's really great at giving demos, and you'll probably get to see a couple of sneak peeks of his dog today. So with that, I will go ahead and turn it over to him to get it started. DAN TOLBERT: All right. Thank you, Tracy. So let's go ahead. We're not doing questions right now. I'm going to close out the presentation. And so just to give an idea of what we're going to be doing today, we're going to be going through using our C# SDK, specifically for voice and messaging. We're not going to be doing any phone number ordering through the SDKs today. We're going to be using our voice and messaging C# SDK to build an ASP.NET core version, I think, 3.1 app that is going to do a couple little things. Before we get too far, I want to show you where the source code that we're going to be running off today is and along with the tutorial that we're going to be following. So if you go to-- I guess we'll start from the documentation. We're going to go to, and you'll see this Bandwidth example's GitHub repo down at the bottom here. We're going to follow that link, and it's going to take us to this nice little put together content. Specifically, we're going to be looking at C#. So if we navigate to our C folder, and then we're going to be doing the webinar example today. So it's going to be written in .NET Core, so if you're using Visual Studio native or in that framework, things may be a bit differently or maybe a bit different. So we're going to be using .NET Core on my Mac OS. And it works a bit better for me, but overall, just should still be the same. Some of the controllers, and the way that we're interacting with the endpoints and API may be a bit different. Again, we're primarily focused on the SDK usage, not so much the framework involved. And we need a couple environment variables we'll talk to in a minute. But primarily, let's talk about the app we're going to be building today. We're going to allocate a phone number within the Bandwidth dashboard, and we're going to set that up. So if you send a text message to that phone number with the word dog, you're going to get a picture of a dog. And if you text any other phrase, anything else-- a blank space, a asdf, lorem ipsum, whatever, we're going to send back-- not with the current day time. That's a typo. We're going to send back an auto response. So you could imagine this being used for any type of just generic "I need a code" or maybe a phone number's not meant to be texted. And if someone texts that number, you could reply back, OK, please call this phone number. And then finally, we're going to be working with inbound phone calls. We're not actually going to be creating phone calls today. We're only going to be reacting to inbound phone calls where creating the phone call is pretty similar where you would specify a call back, but we'll get to that in a little bit. So let's go ahead and get started. The idea that we need today-- we do need to make sure that we have our prerequisites, so we need a Bandwidth Dashboard account. If you don't have one of those, you can sign up for at in the top right. There's a button there to get going. Let's go and just make sure. Oh, cool. I was logged out anyway. We're just going to log back into my dashboard account, and we're going to go through where everything is. And then we're going to stand up an ngrok server just to show you what that would be, This is going to allow us to expose our local host to the public internet so that Bandwidth callbacks can reach my machine right in front of me today. So let's go ahead and just dive in to the dashboard. Just go ahead and get started. Going to log in here. And once I'm logged in, I'm going to go through and do a couple of things. The first thing I want to do is go ahead and create an application for my voice callbacks and my message callbacks. So if I login, if I go over to here. I'm already logged in. I am going to create a new app and I'll just do C# webinar and we'll say this is for voice. We'll select Voice. And we need to set a callback URL. I just happen to have ngrok up and running already, so I'm going to grab this callback URL here. And note that for voice. My endpoint is going to be /bandwidthvoice. So it'll be /bandwidthvoice for me there. We're not going to worry too much about the status callback method today. Where that one in particular is meant to send an actionable event, so things that happen to the call that are more about the call, not things that would require you to take action. You can think of an action event as like an actual inbound call, where a status callback may contain something like the transcriptions to download after the call is over. So now that we have our callback URL filled out, we're just going to go ahead and create that location. We now are in application. We need to create another one and we're going to do C# webinar messaging. And callback URL. That is going to be our ngrok domain/bandwidth/messaging. And it's going to be for messaging. We're going to create that application. And now we're going to go ahead and associate those applications with our location. So let's go ahead and do that. If we go to the locations page here. A location is going to be a logical grouping of phone numbers. I just happen to have one set up here. You can see I have four phone numbers assigned to this location. And with the way that this is set up, any phone numbers I have that are associated with this location or bucketized in the location. Will inherit the settings on this location here. So I'm set up to use our HTTP voice protocol. Know the other option would be SIP voice, which we're not going to concern ourselves with today. This is for network services, more wholesale customers that want to bring their own telephony infrastructure. Where we're going to be HTTP, so we only need to bring a web server. We don't need to worry about bringing up a entire telephone stack on the back end, a switch, and all that fun stuff. And for our application we can select our C# webinar voice. And for messaging we can do the same thing down here. Our application will be C# webinar and messaging. I already have two of these already set up for myself with the environment variable saved. We will need our messaging application ID in a minute. I've already got those locked and loaded, so I don't want to go through and change the set up for here. But for yourself, this is where you would make those changes. We're going to need a couple other things just to kind of show you where everything is. We're going to head to our Account. We have our account number is going to be located here. This is going to be part of the fully qualified path in order to send message request to Bandwidth. So if we look at our usage we need the following environment variables. We're going to look for the account ID. We're going to look for an API user and an API password. That can traditionally be the login that you have when you log into Or if you would like, you can create an API only user which is what I've done here. You can see that my access methods are restricted to the API. This allows me to have more of a service account that the password is not reset. And it only has API access. So I cannot really do much in the dashboard visually. As I see here, it's only meant for my actual programming account. And that's going to be locked in my environment variables as well. And then finally, we need to get our messaging token and secret. Those are going to be tucked up under our API Credentials tab right here under Applications. So if we open that up, you can see that we can create a new token and secret pair. Specifically, these are going to disappear once I close this tab right now. This is your one chance to copy and paste these. I've already got a pair of token and secret locked in my environment variables as well. So I'm just going to delete this now, but you can see the idea here is that each one of your developers on your team would have their own token and secret pair where you could keep track of what user has what token and secret and then delete and manage those as you see fit here. I don't want to delete all of these, otherwise everyone on my account would all of a sudden lose messaging access. And then finally, for our application ID, that's going to be part of this URI here if you want. Or we have a nice little Copy button. You can copy that and paste that into wherever you may need and save as an environment variable. So to kind of go over everything we need again with the Bandwidth dashboard. We need a Bandwidth messaging application, we need a Bandwidth voice application. Of those, we absolutely need our application ID for messaging. We're going to need our account ID. It's going to be here. We need our API credentials, our token and secret for messaging. Specifically, tokens and secrets are only for messaging. We're going to need our API user or our user's login and password. And we need to order a phone number. We'll go through that right here. For phone number ordering, I'm just going to look for 919. And we can pick out any one of these numbers. Going to continue. I know that I want this under my sub account and I know I want it in this location. And I'm going to purchase that. And so at this point, this phone number is now wired up to those applications. And when I start texting or calling this number, I'll see those inbound events come in to my ngrok account that I have right here. So just for proof, if I were to launch dial pad. And I'm going to go to Make A Call, paste this number here, click that Call button, I think. There we are. We'll see soon. There we are. There's the post to the server that I have and it's doing nothing right now, so you just get busy tones. It doesn't work. We haven't wired anything up. But we can start to look at what requests came into our ngrok server. And you can see there is the inbound call event type initiate from my dial pad number to that specific number there, and you can see the call back URL that work. OK. That was a lot going on. So at this point, we should have our voice and messaging application set up. We have our messaging account ID, or our Bandwidth account ID. We have our Bandwidth tokens, we have our Bandwidth application IDs. We've got our callback set up. Let's actually run the server. So I've got a skeletal system set up here. I don't want to go through the entire dotnet command line food to get everything up and running. Essentially, if you clone this repo here, navigate to the Bandwidth webinar, or sorry, the webinar example folder, you should be able to dotnet restore and dotnet run this app based on how it is set up today. But let's actually start from the idea that we're going to be building a brand new project. If we were to run the dotnet new web. It's just an empty MVC application. We do need to add the Bandwidth SDK. That's dotnet add package Bandwidth SDK. We also need the JSON library provided by Microsoft. And then if we were to restore and run that, we'd be starting off with a very, very minimum application. Now, we do need to update our startup cs. So as you generate that app, you'll have a startup cs. It will look a little bit different than this one does here. We need to do a couple of things, specifically use endpoint routing. And we want to add these controllers. So if you look in your configure services, this will probably be empty. We need to update this right here to add the controllers to the services. And then we need to tell the app to use endpoints. So these are going to be two spots in your startup.cs file that you need to change based on this dotnet new web example. And then finally, we're actually going to be navigating to our controllers and we're going to be building that controller to handle these inbound messages and calls. So I've already got this set up today, but in order for you to follow along, basically we need to make the directory and then we need to go ahead and create a file. We're going to call it Bandwidth controller. And then we're going to set up that route for messaging and we're going to set up that route for voice. So I've got right here pulled up my Bandwidth controller. I've already got a few usings locked and loaded in here just to make things look a little bit tidy and run a little bit more smoothly. As you're working through this, I'm using vs code. It does give me nice hints and tips and tricks of what I need to do in order to import or use certain variables correctly. But first thing I need to do is go ahead and set up a new name space for my webinar example for my controllers. And then I want to mark this class here for messaging. This is going to be that inbound message path. It's going to be an API controller. I want to route that to bandwidth/controllername, so that'll be bandwidth/messaging as you saw earlier. And then I want to do the same thing for voice. However, we're not going to do voice yet, so I am going to comment out this code. Save that just for now while we're working through everything so that we don't run into any weird build errors as we're working on messaging and then we'll uncomment that as we move to voice. OK. So at this point I've already defined a class, I've got a couple of routes. I need to actually define the handler. And as I mentioned before, since we're not actually creating any outbound phone calls, we're only ever responding to events from Bandwidth's voice API. So if we go to the deb docs here, we go to Call Control for BXML, this is all driven by callbacks. So we'll get a callback for a call initiated. And then it expects your server to respond with XML, which would be generated with this right here. So Bandwidth is always initiating the request to your web server for an inbound phone call. However, an inbound message, we look at that receiving of an inbound message as life complete. There's no way to technically respond to an inbound message. You can create a new outbound message triggered by an inbound message, but there's no BXML, nice XML style markup language that lets you handle that response in a single web request. We'll be receiving the web request, closing that out, and then creating a new one back to Bandwidth to send our next message. So there's a little bit of a difference there where messaging does actually hit the API in this example, whereas voice only is responding to inbound events. So with that said, we only really need those environment variables within our messaging controller. It's not going to be used by our voice controller, so we don't need to worry about them there. Although, you could put these and extract them up a little bit higher. Wherever you need these, they're just private static variables, nothing too fancy here. And then let's go ahead and define our route. So we're going to be adding a POST request. It's going to be a consuming application, JSON. All of our callbacks our SIM is JSON. We'll take a quick look at the callbacks for an inbound incoming group and an inbound SMS. They're all going to come in as an array of objects here. And for a group message, the main difference will be the two is going to be an array with more than one object in that response. But the overall message is still an array, or a list. Whatever you want to call it. OK. So let's paste this into here. And I believe we can format this guy. I thought we could. OK, what have I done? Oh, I've already done this. That would be my issue. OK, let's actually get the meat of that. So we need to parse the call back, so we'll go ahead and grab this. Note that for this specific example here-- there we go-- I'm not actually able to accept a Bandwidth callback message list today. There's a little bit of a bug with our C# SDK in that the method or the model defined as a Bandwidth callback message contains a value called MFrom, the word MFrom, as the from value, but the actual JSON sent from Bandwidth, as you can see here, uses the word from. So it doesn't deserialize into the object very nicely if you define it as part of the function parameter. So we're just going to accept the raw string and use the stream reader function to bring in that JSON as a string right here. So we're waiting on that. And then our SDK does provide a JSON deserializer helper method that's going to take this string of JSON and deserialize it to a list of callback messages. And specifically, we only need the first or only message of that list. So today Bandwidth does send a list of messages as callbacks, however there only will ever be one message within that callback. So you can just grab the first one relatively safely. However, it could be seen as a better approach to treat every callback that you receive from Bandwidth as a list and parse them as such. So assuming there could be more than one even though there won't be will set you up for a little bit more future proofness as we move forward. OK. And as I'm going through here, I'm pulling out the callback message of the first. I only need the first element. And then finally, there's a message object within the callback message that has the from value, the text, all the information about that text that's relevant to me now, not so much this metadata right here. OK. As we move through the example, we're going to check the direction. So for the Bandwidth messaging API, there's only one URL that matters, and that's going to be the URL shoved in your application ID or your application. Both outbound messages and inbound messages are going to go to that same URL. So we're just going to trim lowercase, just normalize as best we can this message.Direction value. And then we're going to check if it's an outbound. So for a DLR, a status update about a synth message, we're just going to log that to the screen, right? Message ID has the status. Relatively straightforward. And then we're just going to get out of it. We're going to return and move on with our lives and not worry about processing anything more because we've already logged it. We're going to look at the log here as our database. This would be an opportunity to update that message resource if you were saving these to the database. Next, we're going to actually build a two array. So as you can see in the inbound group message, we have this 2 value and we have this owner are going to be the same. However, our 2 value here is actually an array. And so in order for us to respond to this group and not just the individual from, we need to do a little bit of manipulation with this array here. We need to get the owner out of it and we need to push the 2 back into this. So our response, in this regard here, would be 2, 9, 1, or 1, 2, 3, 9, 0, 3 and 9, 0, 1 and would be from that 9, 0, 2 phone number. So let's take a look at what that looks like. We're going to grab that owner there. Clean this up. We're going to grab the owner value. We're going to go ahead and create a list of strings and we're going to call it numbers. And it's going to be that array here. We're going to copy it instead of by reference. We're going to remove the owner from there, and then we're going to push that from value. So if we remember before, this m from is the reason I'm unable to put in the nice Bandwidth with callback messages as part of this function call or route as part of the parameters there and have it nicely deserialized automatically for me. We have to mainly do that for the time being while we work on fixing this m from little bug right now. So at this point, I've got a pretty good sense of what's going on. I've got an inbound message. I've got the 2 array built. I can respond, I have my response array of phone numbers. Let's actually build the message request. So at this point, we've got the same idea that regardless of what the message text was on the inbound, we're always going to be responding with a new message request. The application ID is going to be the same. That 2 list is going to be this numbers here. The from number, who we send it, what text message owner, right. Who is sending this outbound, it's going to be the receiver of this message. And then it's actually time to start looking at the text. So we're going to do a real quick check here if it's a dog. So we're going to look at the message text, we're going to drop that to lower case, remove any white space, and then check if that's equal to the word dog. And if it is, we need to add a URL. We're going to send an MMS. So we need to find a picture out in the world. I just happen to have this locked and loaded for me. It's going to be a list of strings. It's going to be a media field. And then for the text I'm just going to send a little dog emoji, and then the media is going to be equal to that list of strings right there. Otherwise, anything else you're going to send, the text is going to say hello from Bandwidth. OK. Now, let's actually build that client and send that message. Just going to paste this in right here and I'm just going to tab this out to make it look a little bit nicer. So we're going to use the bandwidthclient.builder. We're going to be sending this to the production environment. We don't actually need our voice API credentials. As I said, we're not making voice calls, but I put them in here just to kind of demonstrate that for the voice API it's going to be your traditional API user and your API password. There's no difference for the voice API just like there is for your phone numbers or login. However, for messaging, as I've iterated before, we do need that API token and that API secret that we grab from the Applications page. Then finally, once we have our client, we need to grab the message controller. So this one is hangs off the message controllers. This is going to be how we actually send that message. So once we get the message controller, we're going to await that Create Message request. It does take our account ideas a string in that message request that we generated up here and added the text and media based on the inbound text. It's important to note that I use the async method here, so my response has to be a task as part of this, right, public async task of string. It's also worth noting that this does not do any sort of air handling. I would consider this very fragile code. You more likely than not want to wrap this with a try catch. If not, even try catching this await reader.ReadToEndAsync value as well. Very, very fragile code. I would not recommend doing this in production. However, for the purposes of a webinar and a demo we can kind of move on and if it crashes, it crashes. It'll be fun to debug live, he says nervously. OK. And then altogether this is going to be the full thing here. But before we get started, let me check. Cool, no questions. And I'm going to save this file here and I'm going to run it. So I'm actually going to run through the Visual Studio code. And we just finished restoring. Cool. All right. We've got our server up and running. I'm going to pull up dial pad. And I'll say, hi. Should send. Hello, from Bandwidth. Cool. We can see that right here. Here's our message with ID and we got the DLR status there. So I'm just going to throw a break point right here with the understanding that there's going to be a good chance we're going to run into a little bit of a race condition where the Bandwidth service, when it does not receive a 200 response back from your server, it will try again and back off and try again and back off until it does receive a response. And so what's going to happen when I add a break point, we're not going to get down to this return to give that response back to Bandwidth. And so we may receive that inbound text again. But I just want to throw a break point here, actually let's go all the way down here. I do want to put a break point so we can start to look and see what those objects look like inside the C# app. So now I'll send dog this time. And we've hit our break point. We can see our numbers is a list with just the one number there. We can start to see our message request has been built up. And we can see up top here our text on the inbound call back message is dog. So we know that we've already filled our media request. And then if I were to continue through, it's going to send that message. And then for MMS we're only able-- oh, there we go. We got both of them. There's both dogs. Nice. And so for MMS, it's worthy to keep in mind that we're only able to provide DLRs at the carrier level, so really just saying, hey, the carrier has received this message, not so much that the toll free phone numbers actually give you device level delivery receipts. It's a little bit different. OK. So let's take a pause there so I can get some water. And let's keep going on and move on to voice. So at this point, we have a relatively simple app that will receive a message, text message, and respond with hello from Bandwidth or a dog. If we ask for a dog, get a dog. And we see our status updates are being published to the console log there. Obviously, you would want to probably use a real logging system in production. But let's go ahead and stop this and move on to voice. But before I do, let's take a minute for any questions while I pull up the tutorial for voice. TRACY: Don't be shy to ask anything either. DAN TOLBERT: OK, we'll keep moving on. Pop back over to here. So we're going to be creating a voice call back handler. We've already got the scaffold of our controller set up here. That's not what I wanted to do. Let's try that again. Uncomment that. And let's take a look at what we're trying to do here. So the voice API does send that in a call initiated call back whenever you have an inbound call, they're callbacks, initiate. However, with the app that we're building today, we don't really care about any of this detail except that it was to that specific endpoint that we defined in our application. So it's worth keeping in mind that we're not checking the from value here, we're not checking the event type, we're always responding with the same BXML, regardless of who called, what time, any of that. We're not really processing logic on the inbound call, we're really looking at how do we send and generate BXML using ASP.NET. OK. So we do have this voice callback method here that's going to be put into our class. As I said before, it's going to consume application JSON. It's going to be a POST request. And, write voice control or bandwidth/voice. We've got that set up. So we're going to actually be speaking a sentence and nesting that speak sentence within a gather. A gather request is a prompt to the user to please press a digit. So you can say, please enter your PIN number, please do whatever you need to do, but a gather is expecting the user to push digits on their number pad. So specifically, today we're going to be doing a really quick math game. We're just going to be asking them what is 9 plus 2. If they get it right we'll do something else, but we're going to go ahead and build up our gather, I'm sorry, our speaks sentence verb. So we're using the BXML library that provided by using bandwidth.standard.voice.bxml. Using that BXML library we're going to be generating a speaks sentence. We're going to set the voice of that to Kate, and we're going to get the sentence of said speak sentence to let's play a game, what is 9 plus 2. So we can look at our speak sentence verb here. We've got a nice little demo in C# right here to kind of go through and show you what this looks like. But we do have multiple voices. We have different locations, different genders. You can mix and match any of these, so long as they are supported. And we also do support SSML tags, so speech synthesis, I can not say that. Markup language. This allows you to add emphasis, oh, my goodness. I cannot say these words. Maybe you can add a dialect. If you wanted to say Daniel in Spanish, you could say do this with a Spanish dialect and it would be Danielle, would be an example for you if you want to use SSML within your speak sentence. Again, we're keeping this app very trivial today so we're not going to be using the SSML. But you can see how that would look in C# right here, where you just add the inline of your string. We'll parse that out and send it over to the Bandwidth API correctly. OK. So now we've got our speak sentence. We're going to be using the gather verb here. In C# you can kind of see the way that we would nest that within. So if we do look at here as an example. You can notice that the speak sentence verb is one indentation in. It's nested within the gather response. This allows the user to barge through. Essentially, they don't need to wait until the entire audio file has been done playing before they press digits. If you were to do speak sentence first and then gather without nesting that speak sentence within, there's an opportunity for a little bit of a poor user experience where they would maybe start dialing their account PIN before the gather verb is ready for them. So ideally any time you want to prompt someone to enter digits, you would want to put that speak sentence within that gather verb, and that's exactly what we've done here. Our gather has the speak sentence. We're only going to give them two digits, so we can't get this too wrong. We're not going let them put 1,000 in here, it's only going to be two digits. We're going to send the response. So when the user presses the buttons, they're going to be sent to-- the response is going to be sent back to our server to a path called bandwidth/gather. We'll have to build that in a minute. And then we're going to give them 10 seconds to press that first digit. So really trivial. Our really hard math question. What's 9 plus 2. You have 10 seconds to respond, and we're only ever going to let you get it so wrong. You can't put in 1,000, it'll only grab the first two digits that are pressed. Finally, we need to generate our response. And we're going to add that gather verb that already has a speak sentence within it to that response. We're going to parse it out to be BXML, or to a string, really. And then we're going to return that value from this controller here and move forward. So I'm going to add a little break point right here so we can look at what our BXML looks like when I call that number. So I'm going to kick this off. And keeping in mind that I won't actually probably return that BXML in time, so our call will time out and we'll just get a busy signal again. OK. So here's our BXML. Let me grab this. So you can see there's our XML. Got that hung up. We're speaking sentence. It's going to be Kate. What's 9 plus 2? Gather, response. So that is what we're going to respond to that inbound call. We'll remove our break point and we will call that number again. Keeping in mind that we don't have a response to that guy, but we can see what happens, what the ngrok shows us. Hang up that call. I mean, actually continue. We'll stop and rerun this just to make sure we're clean. Restore. There we go. All right. Let's try that again. I'll say 55. And the call is going to end there, just like we expected it to. So I'm actually going to look at our response here. So you can see here's our raw response. We sent this XML back to them. And then we received a gather event with the digits that I pressed, 55. To the endpoint bandwidth/gather. So let's actually build that route now so that we can accept this gather request, parse out the digits, and then either play a sound to celebrate your success. You did basic arithmetic, or we can sad trombone your failure if you got it wrong. Probably just got it wrong to see what would happen, not actually missing the math there. So let's go ahead and just create or copy our route path there very similar to how we've done up here. Paste that in there. We now have our gather callback, so we've got a bandwidth/gather. This is where our inbound request that we just saw right here is going to be heading. We need to go ahead and parse out that JSON like we did before for the messaging, however-- oh, we got some extra dogs coming through. That's what I said before, we will probably get that as the messages that I did not respond to trickle back in with their try agains. Anyway, so keeping in mind that we do not have the nice models for voice yet for the C# SDK. So we're just going to deserialize this object using the Newton soft JSON conversion. We're going to deserialize that to a dynamic which will allow us to access those values in this JSON payload fairly nicely using dot notation. However, again, you want to wrap this in a try catch when you're in production as those values are not guaranteed to be there because we're using a dynamic and not a built model. So let's keep going through our tutorial. We're going to grab the digits and we're going to check them and make sure that they're equal to 11. So I've got my string digits here. Note that I'm able to use that dot notation to grab those. I've got a success file. This is just going to be a WAV file that celebrates our success or our failure. I'm just going to interpret looking that if the digits are equal to 11, I want to set that string called a media to either this location here or if it's a failure, set it to that. Then we're going to build that BXML. Excuse me, and respond to the call back. So copy this right here. So we've got a play audio verb. Within that play audio verb, we're going to say that URL is equal to this media value here. So based on 11, if it's equal to 1, 1 or the string 1, 1. Success, yes. Failure, no. And then we're also going to add a hang up verb. We're going to do that to kind of keep everything nice and succinct where at the end of the call after we play that sound, we're just going to hang up that call. We don't want to leave it open, we don't want to let the Bandwidth system time out to hang up the call for us. We're going to explicitly hang that up just to reduce the total duration of the call and just be nice with the world of Bandwidth housekeeping. And then finally, we're going to generate that string from BXML using the two BXML. And we're going to return that back there. So I'm going to stop my execution here, save this, and let's run it again. May have gone too fast. A fatal error has occurred. Oh, no. Let's try rerunning that. Oh, I see what we've done. Let's pop over here. And see is dotnet running? No. OK. We should be good. Let's try that again. Address already in use. Wonderful. So let's stop this. I was too fast. I am just going to close this out entirely and open this back up. Live demos are fun. Back to my Bandwidth controller. Back to debugger. Let's try to run this guy again. Neat. I wonder, bear with me here. I'm wondering if I do dotnet run over here. Maybe that got it for us. Address already in use, interesting. I don't want to mess with that. Don't save program. We'll try once, open the terminal here and see what the difference may be. Yeah. So unfortunately, I do not know how to solve this one problem with my server already be up and running without doing any Googling here. So let me see if I can just pop in to my Task Manager Activity monitor and see if I can kill it from there somehow. Process name. Let's look for dotnet dial pad. Look at all these dotnets. Let's see if we can kill these and try again. So close this, because I'm sure it's running a version of it in the background. TRACY: You got it, Dan. I've got faith in you. DAN TOLBERT: Live debugging. Force quit. [LAUGHTER] Come on, now. There we go. All right. Let's run from here. And if this doesn't work, we'll open it up for questions. Oh, there we go. Cool. So let's call this guy, I'm going to say 11. And we get the success and the call hangs up. And if we go again, let's do 22. Get a sad trombone. So there we are. That is our application today. We can pull this up one more time to take a look at the code. Sorry about the little debugging there. Looks like my server got run and didn't shut down properly. Got that up and running. So just to kind of recap what we did today. We built a dotnet or ASP.NET application from scratch. We went through, we added the Bandwidth package. We added our Newtonsoft JSON package. We restored that project, we ran it to get a demo. We modified the startup cs to add the controllers and to map those controllers to endpoints. We built a Bandwidth controller to route for calls, inbound calls and inbound messaging. We grabbed our environment variables based on our Bandwidth authentication to make sure that we had the right variables there. We received that message, we deserialized the callback message using the API helper and not with the built in ASP.NET deserializers. We built a response array, we built the message request. We checked if the inbound text message was a dog or not. And if it was, we set a media file and an emoji, otherwise we just said hello from Bandwidth. We built that SDK builder, the client. From that client we grabbed our messaging controller. We sent that message and logged it to screen. And then all together this is what it looks like. For voice, we accepted an inbound callback. We automatically responded with a let's play a game nested within a gather request. We received those gather digits to the gather controller. We looked at those digits that we had there using the deserializer into a dynamic object. From there, we checked the digits. If the digits matched, we played back a tada.wav file. If they did not, we played back a fail.wav file and then we hung up that call. So all said and done, this is all going to be available, again, under the Bandwidth example's repo and specifically under C# and webinar example. OK. That was a lot. We covered a good bit. Hopefully you were able to follow along, sorry about the little debug at the end. But are there any questions that we have from the folks hanging out with us today? TRACY: We'll take a couple of minutes for Q&A. So definitely don't be shy or hesitant asking any questions that you might want to ask. And if we don't get any, then that's OK, too. DAN TOLBERT: I do know happenstancedly that we are working to get the m from deserializer for messaging remedied in the coming weeks. That is a byproduct of the way we generate these SDK from an open API spec file. Some languages treat the from word as a keyword, like Python. And it was not anonymized or fixed correctly for C#, right? So Python absolutely needs the m from, as from is a keyword, but for C# we don't need to do that. It was just an oversight. And continuing that thought, I know that we're getting the models for voice callbacks some time in May, where we need to work to get those defined in the open API spec correctly. Until then, you can always deserialize to a dynamic. TRACY: Cool. All right. Well, since you guys don't have any questions, I will let you know that you'll receive a link to the recording for today's webinar. So you'll have all the slides and the whole demo presentation there for you at your leisure. But other than that, we appreciate you guys coming out to join us and thanks so much, Dan, for presenting for us. DAN TOLBERT: Cheers, everybody. TRACY: Bye.