Skip to contentSkip to navigationSkip to topbar
On this page

Workflow Automation with C# and ASP.NET MVC


One of the more abstract concepts you'll handle when building your business is what the workflow will look like.

At its core, setting up a standardized workflow is about enabling your service providers (agents, hosts, customer service reps, administrators, and the rest of the gang) to better serve your customers.

To illustrate a very real-world example, today we'll build a C# and ASP.NET MVC webapp for finding and booking vacation properties — tentatively called Airtng.

Here's how it'll work:

  1. A host creates a vacation property listing
  2. A guest requests a reservation for a property
  3. The host receives an SMS notifying them of the reservation request. The host can either Accept or Reject the reservation
  4. The guest is notified whether a request was rejected or accepted

Learn how Airbnb used Twilio SMS to streamline the rental experience for 60M+ travelers around the world.(link takes you to an external page)


Workflow Building Blocks

workflow-building-blocks page anchor

We'll be using the Twilio REST API to send our users messages at important junctures. Here's a bit more on our API:

  • Sending Messages with Twilio API

Notify the host

notify-the-host page anchor

AirTNG.Web/Domain/Reservations/Notifier.cs

1
using System;
2
using System.Linq;
3
using System.Text;
4
using System.Threading.Tasks;
5
using AirTNG.Web.Domain.Twilio;
6
using AirTNG.Web.Models;
7
using AirTNG.Web.Models.Repository;
8
9
namespace AirTNG.Web.Domain.Reservations
10
{
11
public interface INotifier
12
{
13
Task SendNotificationAsync(Reservation reservation);
14
}
15
16
public class Notifier : INotifier
17
{
18
private readonly ITwilioMessageSender _client;
19
private readonly IReservationsRepository _repository;
20
21
public Notifier() : this(
22
new TwilioMessageSender(),
23
new ReservationsRepository()) { }
24
25
public Notifier(ITwilioMessageSender client, IReservationsRepository repository)
26
{
27
_client = client;
28
_repository = repository;
29
}
30
31
public async Task SendNotificationAsync(Reservation reservation)
32
{
33
var pendingReservations = await _repository.FindPendingReservationsAsync();
34
if (pendingReservations.Count() < 2)
35
{
36
var notification = BuildNotification(reservation);
37
await _client.SendMessageAsync(notification.To, notification.From, notification.Messsage);
38
}
39
}
40
41
private static Notification BuildNotification(Reservation reservation)
42
{
43
var message = new StringBuilder();
44
message.AppendFormat("You have a new reservation request from {0} for {1}:{2}",
45
reservation.Name,
46
reservation.VacationProperty.Description,
47
Environment.NewLine);
48
message.AppendFormat("{0}{1}",
49
reservation.Message,
50
Environment.NewLine);
51
message.Append("Reply [accept] or [reject]");
52
53
return new Notification
54
{
55
From = PhoneNumbers.Twilio,
56
To = reservation.PhoneNumber,
57
Messsage = message.ToString()
58
};
59
}
60
}
61
}

Ready to go? Boldly click the button right after this sentence.


For this use case to work we have to handle authentication. We will rely on ASP.NET Identity(link takes you to an external page) for this purpose.

Each User will need to have a phone_number that we will use later to send SMS notifications.

AirTNG.Web/Models/IdentityModels.cs

1
using System.Collections.Generic;
2
using System.Data.Entity;
3
using System.Security.Claims;
4
using System.Threading.Tasks;
5
using Microsoft.AspNet.Identity;
6
using Microsoft.AspNet.Identity.EntityFramework;
7
8
namespace AirTNG.Web.Models
9
{
10
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
11
public class ApplicationUser : IdentityUser
12
{
13
public string Name { get; set; }
14
15
public virtual IList<VacationProperty> VacationProperties { get; set; }
16
17
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
18
{
19
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
20
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
21
// Add custom user claims here
22
return userIdentity;
23
}
24
}
25
26
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
27
{
28
public ApplicationDbContext()
29
: base("AirTNGConnection", false)
30
{
31
}
32
33
public static ApplicationDbContext Create()
34
{
35
return new ApplicationDbContext();
36
}
37
38
public DbSet<VacationProperty> VacationProperties { get; set; }
39
public DbSet<Reservation> Reservations { get; set; }
40
}
41
}

Next let's take a look at the Vacation Property model.


The Vacation Property Model

the-vacation-property-model page anchor

Our rental application will require listing properties.

The VacationProperty belongs to the User who created it (we'll call this user the host from this point on) and contains only two properties: a Description and an ImageUrl.

A VacationProperty can have many Reservations.

AirTNG.Web/Models/VacationProperty.cs

1
using System;
2
using System.Collections.Generic;
3
4
namespace AirTNG.Web.Models
5
{
6
public class VacationProperty
7
{
8
public int Id { get; set; }
9
public string UserId { get; set; }
10
public virtual ApplicationUser User { get; set; }
11
public string Description { get; set; }
12
public string ImageUrl { get; set; }
13
public DateTime CreatedAt { get; set; }
14
public virtual IList<Reservation> Reservations { get; set; }
15
}
16
}

Next, let's see what our Reservation model looks like.


The Reservation model is at the center of the workflow for this application.

It is responsible for keeping track of:

  • The VacationProperty it is associated with.
  • The User who owns that vacation property (the host). Through this property the user will have access to the host phone number indirectly.
  • The Status of the reservation

AirTNG.Web/Models/Reservation.cs

1
using System;
2
using System.ComponentModel.DataAnnotations.Schema;
3
4
namespace AirTNG.Web.Models
5
{
6
public class Reservation
7
{
8
public int Id { get; set; }
9
public string Name { get; set; }
10
public string PhoneNumber { get; set; }
11
public ReservationStatus Status { get; set; }
12
public string Message { get; set; }
13
public DateTime CreatedAt { get; set; }
14
public int VactionPropertyId { get; set; }
15
[ForeignKey("VactionPropertyId")]
16
public virtual VacationProperty VacationProperty { get; set; }
17
}
18
}

Now that our models are ready, let's have a look at the controller that will create reservations.


The reservation creation form holds only a single field: the message that will be sent to the host when one of her properties is reserved. The rest of the information needed to create a reservation is taken from the VacationProperty itself.

A reservation is created with a default status ReservationStatus.Pending. That way when the host replies with an accept or reject response the application knows which reservation to update.

The Reservations Controller

the-reservations-controller page anchor

AirTNG.Web/Controllers/ReservationsController.cs

1
using System;
2
using System.Threading.Tasks;
3
using System.Web.Mvc;
4
using AirTNG.Web.Domain.Reservations;
5
using AirTNG.Web.Models;
6
using AirTNG.Web.Models.Repository;
7
using AirTNG.Web.ViewModels;
8
using Twilio.AspNet.Mvc;
9
using Twilio.TwiML;
10
11
namespace AirTNG.Web.Controllers
12
{
13
[Authorize]
14
public class ReservationsController : TwilioController
15
{
16
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
17
private readonly IReservationsRepository _reservationsRepository;
18
private readonly IUsersRepository _usersRepository;
19
private readonly INotifier _notifier;
20
21
public ReservationsController() : this(
22
new VacationPropertiesRepository(),
23
new ReservationsRepository(),
24
new UsersRepository(),
25
new Notifier()) { }
26
27
public ReservationsController(
28
IVacationPropertiesRepository vacationPropertiesRepository,
29
IReservationsRepository reservationsRepository,
30
IUsersRepository usersRepository,
31
INotifier notifier)
32
{
33
_vacationPropertiesRepository = vacationPropertiesRepository;
34
_reservationsRepository = reservationsRepository;
35
_usersRepository = usersRepository;
36
_notifier = notifier;
37
}
38
39
// GET: Reservations/Create
40
public async Task<ActionResult> Create(int id)
41
{
42
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
43
var reservation = new ReservationViewModel
44
{
45
ImageUrl = vacationProperty.ImageUrl,
46
Description = vacationProperty.Description,
47
VacationPropertyId = vacationProperty.Id,
48
VacationPropertyDescription = vacationProperty.Description,
49
UserName = vacationProperty.User.Name,
50
UserPhoneNumber = vacationProperty.User.PhoneNumber,
51
};
52
53
return View(reservation);
54
}
55
56
// POST: Reservations/Create
57
[HttpPost]
58
public async Task<ActionResult> Create(ReservationViewModel model)
59
{
60
if (ModelState.IsValid)
61
{
62
var reservation = new Reservation
63
{
64
Message = model.Message,
65
PhoneNumber = model.UserPhoneNumber,
66
Name = model.UserName,
67
VactionPropertyId = model.VacationPropertyId,
68
Status = ReservationStatus.Pending,
69
CreatedAt = DateTime.Now
70
};
71
72
await _reservationsRepository.CreateAsync(reservation);
73
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
74
await _notifier.SendNotificationAsync(reservation);
75
76
return RedirectToAction("Index", "VacationProperties");
77
}
78
79
return View(model);
80
}
81
82
// POST Reservations/Handle
83
[HttpPost]
84
[AllowAnonymous]
85
public async Task<TwiMLResult> Handle(string from, string body)
86
{
87
string smsResponse;
88
89
try
90
{
91
var host = await _usersRepository.FindByPhoneNumberAsync(from);
92
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
93
94
var smsRequest = body;
95
reservation.Status =
96
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
97
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
98
? ReservationStatus.Confirmed
99
: ReservationStatus.Rejected;
100
101
await _reservationsRepository.UpdateAsync(reservation);
102
smsResponse =
103
string.Format("You have successfully {0} the reservation", reservation.Status);
104
}
105
catch (Exception)
106
{
107
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
108
}
109
110
return TwiML(Respond(smsResponse));
111
}
112
113
private static MessagingResponse Respond(string message)
114
{
115
var response = new MessagingResponse();
116
response.Message(message);
117
118
return response;
119
}
120
}
121
}

Next, let's see how we will send SMS notifications to the vacation rental host.


When a reservation is created we want to notify the owner of the property that someone is interested.

This is where we use Twilio C# Helper Library(link takes you to an external page) to send a SMS message to the host, using our Twilio phone number(link takes you to an external page). As you can see, sending SMS messages using Twilio takes just a few lines of code.

Next we just have to wait for the host to send an SMS response accepting or rejecting the reservation. Then we can notify the guest and host that the reservation information has been updated.

AirTNG.Web/Domain/Reservations/Notifier.cs

1
using System;
2
using System.Linq;
3
using System.Text;
4
using System.Threading.Tasks;
5
using AirTNG.Web.Domain.Twilio;
6
using AirTNG.Web.Models;
7
using AirTNG.Web.Models.Repository;
8
9
namespace AirTNG.Web.Domain.Reservations
10
{
11
public interface INotifier
12
{
13
Task SendNotificationAsync(Reservation reservation);
14
}
15
16
public class Notifier : INotifier
17
{
18
private readonly ITwilioMessageSender _client;
19
private readonly IReservationsRepository _repository;
20
21
public Notifier() : this(
22
new TwilioMessageSender(),
23
new ReservationsRepository()) { }
24
25
public Notifier(ITwilioMessageSender client, IReservationsRepository repository)
26
{
27
_client = client;
28
_repository = repository;
29
}
30
31
public async Task SendNotificationAsync(Reservation reservation)
32
{
33
var pendingReservations = await _repository.FindPendingReservationsAsync();
34
if (pendingReservations.Count() < 2)
35
{
36
var notification = BuildNotification(reservation);
37
await _client.SendMessageAsync(notification.To, notification.From, notification.Messsage);
38
}
39
}
40
41
private static Notification BuildNotification(Reservation reservation)
42
{
43
var message = new StringBuilder();
44
message.AppendFormat("You have a new reservation request from {0} for {1}:{2}",
45
reservation.Name,
46
reservation.VacationProperty.Description,
47
Environment.NewLine);
48
message.AppendFormat("{0}{1}",
49
reservation.Message,
50
Environment.NewLine);
51
message.Append("Reply [accept] or [reject]");
52
53
return new Notification
54
{
55
From = PhoneNumbers.Twilio,
56
To = reservation.PhoneNumber,
57
Messsage = message.ToString()
58
};
59
}
60
}
61
}

Now's let's peek at how we're handling the host's responses.


Handle Incoming Messages

handle-incoming-messages page anchor

The Reservations/Handle controller handles our incoming Twilio request and does four things:

  1. Check for the guest's pending reservation
  2. Update the status of the reservation
  3. Respond to the host
  4. Send notification to the guest

AirTNG.Web/Controllers/ReservationsController.cs

1
using System;
2
using System.Threading.Tasks;
3
using System.Web.Mvc;
4
using AirTNG.Web.Domain.Reservations;
5
using AirTNG.Web.Models;
6
using AirTNG.Web.Models.Repository;
7
using AirTNG.Web.ViewModels;
8
using Twilio.AspNet.Mvc;
9
using Twilio.TwiML;
10
11
namespace AirTNG.Web.Controllers
12
{
13
[Authorize]
14
public class ReservationsController : TwilioController
15
{
16
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
17
private readonly IReservationsRepository _reservationsRepository;
18
private readonly IUsersRepository _usersRepository;
19
private readonly INotifier _notifier;
20
21
public ReservationsController() : this(
22
new VacationPropertiesRepository(),
23
new ReservationsRepository(),
24
new UsersRepository(),
25
new Notifier()) { }
26
27
public ReservationsController(
28
IVacationPropertiesRepository vacationPropertiesRepository,
29
IReservationsRepository reservationsRepository,
30
IUsersRepository usersRepository,
31
INotifier notifier)
32
{
33
_vacationPropertiesRepository = vacationPropertiesRepository;
34
_reservationsRepository = reservationsRepository;
35
_usersRepository = usersRepository;
36
_notifier = notifier;
37
}
38
39
// GET: Reservations/Create
40
public async Task<ActionResult> Create(int id)
41
{
42
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
43
var reservation = new ReservationViewModel
44
{
45
ImageUrl = vacationProperty.ImageUrl,
46
Description = vacationProperty.Description,
47
VacationPropertyId = vacationProperty.Id,
48
VacationPropertyDescription = vacationProperty.Description,
49
UserName = vacationProperty.User.Name,
50
UserPhoneNumber = vacationProperty.User.PhoneNumber,
51
};
52
53
return View(reservation);
54
}
55
56
// POST: Reservations/Create
57
[HttpPost]
58
public async Task<ActionResult> Create(ReservationViewModel model)
59
{
60
if (ModelState.IsValid)
61
{
62
var reservation = new Reservation
63
{
64
Message = model.Message,
65
PhoneNumber = model.UserPhoneNumber,
66
Name = model.UserName,
67
VactionPropertyId = model.VacationPropertyId,
68
Status = ReservationStatus.Pending,
69
CreatedAt = DateTime.Now
70
};
71
72
await _reservationsRepository.CreateAsync(reservation);
73
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
74
await _notifier.SendNotificationAsync(reservation);
75
76
return RedirectToAction("Index", "VacationProperties");
77
}
78
79
return View(model);
80
}
81
82
// POST Reservations/Handle
83
[HttpPost]
84
[AllowAnonymous]
85
public async Task<TwiMLResult> Handle(string from, string body)
86
{
87
string smsResponse;
88
89
try
90
{
91
var host = await _usersRepository.FindByPhoneNumberAsync(from);
92
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
93
94
var smsRequest = body;
95
reservation.Status =
96
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
97
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
98
? ReservationStatus.Confirmed
99
: ReservationStatus.Rejected;
100
101
await _reservationsRepository.UpdateAsync(reservation);
102
smsResponse =
103
string.Format("You have successfully {0} the reservation", reservation.Status);
104
}
105
catch (Exception)
106
{
107
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
108
}
109
110
return TwiML(Respond(smsResponse));
111
}
112
113
private static MessagingResponse Respond(string message)
114
{
115
var response = new MessagingResponse();
116
response.Message(message);
117
118
return response;
119
}
120
}
121
}

Let's have closer look at how Twilio webhooks are configured to enable incoming requests to our application.


Incoming Twilio Requests

incoming-twilio-requests page anchor

In the Twilio console(link takes you to an external page), you must setup the SMS webhook to call your application's end point in the route Reservations/Handle.

SMS Webhook.

One way to expose your development machine to the outside world is using ngrok(link takes you to an external page). The URL for the SMS webhook on your number would look like this:

1
http://<subdomain>.ngrok.io/Reservations/Handle
2

An incoming request from Twilio comes with some helpful parameters, such as a from phone number and the message body.

We'll use the from parameter to look for the host and check if they have any pending reservations. If they do, we'll use the message body to check for 'accept' and 'reject'.

In the last step, we'll use Twilio's TwiML as a response to Twilio to send an SMS message to the guest.

Accept or reject a reservation

accept-or-reject-a-reservation page anchor

AirTNG.Web/Controllers/ReservationsController.cs

1
using System;
2
using System.Threading.Tasks;
3
using System.Web.Mvc;
4
using AirTNG.Web.Domain.Reservations;
5
using AirTNG.Web.Models;
6
using AirTNG.Web.Models.Repository;
7
using AirTNG.Web.ViewModels;
8
using Twilio.AspNet.Mvc;
9
using Twilio.TwiML;
10
11
namespace AirTNG.Web.Controllers
12
{
13
[Authorize]
14
public class ReservationsController : TwilioController
15
{
16
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
17
private readonly IReservationsRepository _reservationsRepository;
18
private readonly IUsersRepository _usersRepository;
19
private readonly INotifier _notifier;
20
21
public ReservationsController() : this(
22
new VacationPropertiesRepository(),
23
new ReservationsRepository(),
24
new UsersRepository(),
25
new Notifier()) { }
26
27
public ReservationsController(
28
IVacationPropertiesRepository vacationPropertiesRepository,
29
IReservationsRepository reservationsRepository,
30
IUsersRepository usersRepository,
31
INotifier notifier)
32
{
33
_vacationPropertiesRepository = vacationPropertiesRepository;
34
_reservationsRepository = reservationsRepository;
35
_usersRepository = usersRepository;
36
_notifier = notifier;
37
}
38
39
// GET: Reservations/Create
40
public async Task<ActionResult> Create(int id)
41
{
42
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
43
var reservation = new ReservationViewModel
44
{
45
ImageUrl = vacationProperty.ImageUrl,
46
Description = vacationProperty.Description,
47
VacationPropertyId = vacationProperty.Id,
48
VacationPropertyDescription = vacationProperty.Description,
49
UserName = vacationProperty.User.Name,
50
UserPhoneNumber = vacationProperty.User.PhoneNumber,
51
};
52
53
return View(reservation);
54
}
55
56
// POST: Reservations/Create
57
[HttpPost]
58
public async Task<ActionResult> Create(ReservationViewModel model)
59
{
60
if (ModelState.IsValid)
61
{
62
var reservation = new Reservation
63
{
64
Message = model.Message,
65
PhoneNumber = model.UserPhoneNumber,
66
Name = model.UserName,
67
VactionPropertyId = model.VacationPropertyId,
68
Status = ReservationStatus.Pending,
69
CreatedAt = DateTime.Now
70
};
71
72
await _reservationsRepository.CreateAsync(reservation);
73
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
74
await _notifier.SendNotificationAsync(reservation);
75
76
return RedirectToAction("Index", "VacationProperties");
77
}
78
79
return View(model);
80
}
81
82
// POST Reservations/Handle
83
[HttpPost]
84
[AllowAnonymous]
85
public async Task<TwiMLResult> Handle(string from, string body)
86
{
87
string smsResponse;
88
89
try
90
{
91
var host = await _usersRepository.FindByPhoneNumberAsync(from);
92
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
93
94
var smsRequest = body;
95
reservation.Status =
96
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
97
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
98
? ReservationStatus.Confirmed
99
: ReservationStatus.Rejected;
100
101
await _reservationsRepository.UpdateAsync(reservation);
102
smsResponse =
103
string.Format("You have successfully {0} the reservation", reservation.Status);
104
}
105
catch (Exception)
106
{
107
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
108
}
109
110
return TwiML(Respond(smsResponse));
111
}
112
113
private static MessagingResponse Respond(string message)
114
{
115
var response = new MessagingResponse();
116
response.Message(message);
117
118
return response;
119
}
120
}
121
}

Now that we know how to expose a webhook to handle Twilio requests, let's see how we generate the TwiML needed.


After updating the reservation status, we must notify the host that they have successfully confirmed or rejected the reservation. We also have to return a friendly error message if there are no pending reservations.

If the reservation is confirmed or rejected we send an additional SMS to the guest to deliver the news.

We use the verb Message from TwiML to instruct Twilio's server that it should send the SMS messages.

AirTNG.Web/Controllers/ReservationsController.cs

1
using System;
2
using System.Threading.Tasks;
3
using System.Web.Mvc;
4
using AirTNG.Web.Domain.Reservations;
5
using AirTNG.Web.Models;
6
using AirTNG.Web.Models.Repository;
7
using AirTNG.Web.ViewModels;
8
using Twilio.AspNet.Mvc;
9
using Twilio.TwiML;
10
11
namespace AirTNG.Web.Controllers
12
{
13
[Authorize]
14
public class ReservationsController : TwilioController
15
{
16
private readonly IVacationPropertiesRepository _vacationPropertiesRepository;
17
private readonly IReservationsRepository _reservationsRepository;
18
private readonly IUsersRepository _usersRepository;
19
private readonly INotifier _notifier;
20
21
public ReservationsController() : this(
22
new VacationPropertiesRepository(),
23
new ReservationsRepository(),
24
new UsersRepository(),
25
new Notifier()) { }
26
27
public ReservationsController(
28
IVacationPropertiesRepository vacationPropertiesRepository,
29
IReservationsRepository reservationsRepository,
30
IUsersRepository usersRepository,
31
INotifier notifier)
32
{
33
_vacationPropertiesRepository = vacationPropertiesRepository;
34
_reservationsRepository = reservationsRepository;
35
_usersRepository = usersRepository;
36
_notifier = notifier;
37
}
38
39
// GET: Reservations/Create
40
public async Task<ActionResult> Create(int id)
41
{
42
var vacationProperty = await _vacationPropertiesRepository.FindAsync(id);
43
var reservation = new ReservationViewModel
44
{
45
ImageUrl = vacationProperty.ImageUrl,
46
Description = vacationProperty.Description,
47
VacationPropertyId = vacationProperty.Id,
48
VacationPropertyDescription = vacationProperty.Description,
49
UserName = vacationProperty.User.Name,
50
UserPhoneNumber = vacationProperty.User.PhoneNumber,
51
};
52
53
return View(reservation);
54
}
55
56
// POST: Reservations/Create
57
[HttpPost]
58
public async Task<ActionResult> Create(ReservationViewModel model)
59
{
60
if (ModelState.IsValid)
61
{
62
var reservation = new Reservation
63
{
64
Message = model.Message,
65
PhoneNumber = model.UserPhoneNumber,
66
Name = model.UserName,
67
VactionPropertyId = model.VacationPropertyId,
68
Status = ReservationStatus.Pending,
69
CreatedAt = DateTime.Now
70
};
71
72
await _reservationsRepository.CreateAsync(reservation);
73
reservation.VacationProperty = new VacationProperty {Description = model.VacationPropertyDescription};
74
await _notifier.SendNotificationAsync(reservation);
75
76
return RedirectToAction("Index", "VacationProperties");
77
}
78
79
return View(model);
80
}
81
82
// POST Reservations/Handle
83
[HttpPost]
84
[AllowAnonymous]
85
public async Task<TwiMLResult> Handle(string from, string body)
86
{
87
string smsResponse;
88
89
try
90
{
91
var host = await _usersRepository.FindByPhoneNumberAsync(from);
92
var reservation = await _reservationsRepository.FindFirstPendingReservationByHostAsync(host.Id);
93
94
var smsRequest = body;
95
reservation.Status =
96
smsRequest.Equals("accept", StringComparison.InvariantCultureIgnoreCase) ||
97
smsRequest.Equals("yes", StringComparison.InvariantCultureIgnoreCase)
98
? ReservationStatus.Confirmed
99
: ReservationStatus.Rejected;
100
101
await _reservationsRepository.UpdateAsync(reservation);
102
smsResponse =
103
string.Format("You have successfully {0} the reservation", reservation.Status);
104
}
105
catch (Exception)
106
{
107
smsResponse = "Sorry, it looks like you don't have any reservations to respond to.";
108
}
109
110
return TwiML(Respond(smsResponse));
111
}
112
113
private static MessagingResponse Respond(string message)
114
{
115
var response = new MessagingResponse();
116
response.Message(message);
117
118
return response;
119
}
120
}
121
}

Congratulations! You've just learned how to automate your workflow with Twilio SMS.

Next, lets see what else we can do with the Twilio C# SDK.


If you're a .NET developer working with Twilio you know we've got a lot of great content here on the Docs site. Here are just a couple ideas for your next tutorial:

IVR: Phone Tree

You can route callers to the right people and information with an IVR (interactive voice response) system.

Automated Survey

Instantly collect structured data from your users with a survey conducted over a voice call or SMS text messages.

Need some help?

Terms of service

Copyright © 2024 Twilio Inc.