Handling webhooks is a very common pattern in backend development. You call a third party API, and the API immediately returns successfully. Then the third party does something nifty like sending a message, or using a neural network. and when it’s done, it calls _your_ API with the results.\n\n!(https://hackernoon.com/hn-images/1*gj7Uh-9K0udWSN9N1CJdew.png)\n\nIt’s a pretty straightforward pattern that makes a whole lot of sense on paper. But it can get complicated in practice. Once you’ve called the API, you’ve completely lost state. When the service calls your webhook, you need to figure out which of your entities it’s talking about. And then you need to recreate the state it was in before calling the API. This generally involves a lot of calls back to your database to recreate what you had seconds ago.\n\nI’ve built countless webhook handlers over the years. I felt like I had optimized the webhook pattern as much as possible. _And then I built one with Azure Durable Functions._\n\nIf you’re new to Durable Functions, see my [initial impressions](https://hackernoon.com/durable-functions-backend-development-made-easy-and-cheap-cbab7acc7050) or Microsoft’s [overview](https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-overview). Basically, you define workflows in code, all while enjoying the benefits of serverless.\n\n### Durable Function Webhooks\n\nFor webhooks, the [human interaction](https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-phone-verification) pattern is what we’ll use. This pattern involves:\n\n* Executing some number of actions\n* Waiting an arbitrary amount of time for some external event (often a human) to be raised.\n* Continuing execution.\n\nIn this case, the external event will be a webhook. This recipe is going to involve sending an SMS message using the popular Twilio service. After sending a message, Twilio can respond back with the delivery status. For example, you may want to do some business logic if a message failed to send.\n\nI’ve build Twilio handlers in many languages for many use cases. But Durable Functions make it the easiest by far. It usually goes something like this:\n\nI’ve got all sorts of information about the person I’m about to send a message to. I send the message, mark down a unique message ID that Twilio gives me, and that’s it. Everything is gone. Now I wait.\n\nSome time later (usually within seconds), Twilio hits my API with a unique message ID. This is generally in a completely separate part of the app. I use the ID to lookup the message, and now I’ve got to recreate everything I had before sending to Twilio. But I _just_ had it! It may not seem like much, but at scale, all of these trips back to the data store really add up.\n\nIn Durable Functions, the webhook logic happens **one line** after sending the message. Let’s see how it works.\n\nVideo Tutorial for This Article\n\n### Getting Setup\n\nI’ll be running VS Code on a Mac, but this will work in any OS. We’ll start by using the [Azure Functions](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) extension in VS Code to create a new C# function project in an empty directory.\n\nWe’ll then need some packages. Run these commands to get the extensions for Durable Functions, Azure Table Storage, and Twilio.\n\n`dotnet add package Microsoft.Azure.WebJobs.Extensions.DurableTask --version 1.6.2 dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage --version 3.0.1 dotnet add package Microsoft.Azure.WebJobs.Extensions.Twilio --version 3.0.0`\n\nCreate a `Models` directory and file called `MessageInput.cs` in it. This will contain the properties needed to send a message:\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\n### The Starter\n\nWe need some way to start the sending of a message. We’ll use an HTTP trigger. If you’re not familiar with Durable Functions, you can ignore the function parameters.\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nWe read the body (as a `MessageInput` object that we created earlier) and pass it to an orchestrator to start the workflow. Then we return with the orchestration status. That’s it! Now we need to create an orchestrator.\n\n### The Orchestrator\n\nThis is where the real magic happens. The entire workflow for sending a message and getting its response happens in five lines of code.\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nEach orchestration has a unique ID. This ID should generally be kept secret, so we’ll create a mapping that maps the orchestration ID to a unique ID. We’ll send the unique ID to the third party, and when they give it back to us, we’ll use it to lookup the orchestration ID.\n\n!(https://hackernoon.com/hn-images/1*cL85TxMc8M2LYcU55zZLkg.png)\n\nWe send the message, and then call `WaitForExternalEvent`. This pauses the orchestration indefinitely until the `TwilioCallback` event is fired. At this point, we’ll have the status and can continue with our business logic.\n\n### The Activities\n\n#### Creating a Mapping\n\nWe’ll use Azure Table Storage (which Durable Functions use anyway) to save our mapping. Azure Functions support a [Table Binding](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-table) which allows you to pull data out of the database with little code.\n\nWe need a class to hold the mapping:\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nTable Storage needs a `PartitionKey` and a `RowKey`. We’ll save the `OrchestrationId` and use the `RowKey` to send to Twilio, and to find our orchestration again.\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nThis function simply saves the mapping to Table Storage and returns the `RowKey` for later use. We get to use the Table binding to specify which table we’re interested in in the function parameters.\n\n#### Sending a Message\n\nAzure functions also support [Twilio Binding](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-twilio)s, which send SMS messages with little code. Here, we create a `CreateMessageOptions` object and populate it with the inputted number.\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nThe `StatusCallback`, is the URL Twilio will hit to give us the message’s status. We include the `RowKey` in the URL to lookup our mapping. We’ll create that route next.\n\n#### Webhook Handler\n\nThe last thing to do is handle when Twilio responds back to our API with the status. This, of course, is just another HTTP triggered function. The function parameters aren’t much different than the last HTTP function.\n\nThe only difference is it uses the Table binding to lookup the ID that Twilio passed back. This returns a record with the orchestration ID, which means we can now raise an event to that orchestration.\n\n!(https://avatars3.githubusercontent.com/u/8009582?s=400&v=4)\n\nIf you’ll recall, the orchestrator is currently waiting for a `TwilioCallback` event to fire, so let’s keep it waiting no more! We pass in the status and the orchestration can resume _right_ where it left off.\n\n_Note: Azure functions currently do not support x-www-form-urlencoded responses, which is what Twilio provides._ `_ParseForm_` _handles this for us. See the_ [_full code_](https://github.com/KevinDJones/DF_Webhook/blob/master/Starters.cs#L47) _for details._\n\n#### Conclusion\n\nThat’s it! Five functions to send messages _and_ handle callbacks. Best of all, we can pick up _right_ where we left off before we started waiting for the webhook. No more duplicate trips back to the database. It’s the cleanest way to implement webhooks that I’ve experienced.\n\nCheck out the [full code](https://github.com/KevinDJones/DF_Webhook) or the video above for the full details!