# Workflow Automation with Node.js and Express

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 Node.js and Express 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.](https://customers.twilio.com/en-us/airbnb)*

## Workflow Building Blocks

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

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

## Authenticate Our Users

For this workflow to work, we need to handle user authentication. We're going to rely on [Passport](https://passportjs.org/) for Node.js.

Each `User` will need to have a `countryCode` and a `phoneNumber` which will be required to send SMS notifications later.

```js title="User Model to handle authentication" description="models/user.js"
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');

var userSchema = new mongoose.Schema({
  email:       { type: String, required: true },
  username:    { type: String, required: true },
  password:    { type: String },
  countryCode: { type: String, required: true },
  phoneNumber: { type: String, required: true },
  date:        { type: Date, default: Date.now },
});

userSchema.plugin(passportLocalMongoose);

var user = mongoose.model('user', userSchema);

module.exports = user;
```

Next let's model the vacation properties.

## Vacation Property Model

In order to build our rentals company we'll need a way to create the property listings.

The `Property` belongs to the `User` who created it (we'll call this user the *host* moving forward) and contains only two properties: a `description` and an `imageUrl`.

```js title="The Vacation Property Model" description="models/property.js"
var mongoose = require('mongoose');

var propertySchema = new mongoose.Schema({
  description: { type: String, required: true },
  imageUrl:    { type: String, required: true },
  date:        { type: Date, default: Date.now },
  owner:       {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'user'
  }
});

var property = mongoose.model('property', propertySchema);

module.exports = property;
```

Next up, the reservation model.

## Reservation Model

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

It is responsible for keeping track of:

* The `guest` who performed the reservation
* The `vacation_property` the guest is requesting (and associated *host*)
* The `status` of the reservation: `pending`, `confirmed`, or `rejected`

```js title="The Reservation Model" description="models/reservation.js"
var mongoose = require('mongoose');
var deepPopulate = require('mongoose-deep-populate')(mongoose);

var reservationSchema = new mongoose.Schema({
  message:  { type: String, required: true },
  status:   { type: String, default: 'pending' },
  date:     { type: Date, default: Date.now },
  property: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'property'
  },
  guest:     {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'user'
  }
});

reservationSchema.plugin(deepPopulate, {});

var reservation = mongoose.model('reservation', reservationSchema);

module.exports = reservation;
```

Next, let's look at triggering the creation of a new reservation.

## Create a Reservation

The reservation creation form holds only a single field: the message that will be sent to the *host* when reserving one of her properties. The rest of the information necessary to create a reservation is taken from the vacation property.

A reservation is created with a default status `pending`, so when the *host* replies with an `accept` or `reject` response our application knows which reservation to update.

```js title="Reservation creation" description="routes/reservations.js"
// !mark(10:32)
var MessagingResponse = require('twilio').twiml.MessagingResponse;
var twilio = require('twilio');
var express = require('express');
var router = express.Router();
var Property = require('../models/property');
var Reservation = require('../models/reservation');
var User = require('../models/user');
var notifier = require('../lib/notifier');

// POST: /reservations
router.post('/', function (req, res) {
  var propertyId = req.body.propertyId;
  var user = req.user;

  Property.findOne({ _id: propertyId })
  .then(function (property) {
    var reservation = new Reservation({
      message: req.body.message,
      property: propertyId,
      guest: user.id
    });

    return reservation.save();
  })
  .then(function () {
    notifier.sendNotification();
    res.redirect('/properties');
  })
  .catch(function(err) {
    console.log(err);
  });
});

// POST: /reservations/handle
router.post('/handle', twilio.webhook({validate: false}), function (req, res) {
  var from = req.body.From;
  var smsRequest = req.body.Body;

  var smsResponse;

  User.findOne({phoneNumber: from})
  .then(function (host) {
    return Reservation.findOne({status: 'pending'});
  })
  .then(function (reservation) {
    if (reservation === null) {
      throw 'No pending reservations';
    }
    reservation.status = smsRequest.toLowerCase() === "accept" ? "confirmed" : "rejected";
    return reservation.save();
  })
  .then(function (reservation) {
    var message = "You have successfully " + reservation.status + " the reservation";
    respond(res, message);
  })
  .catch(function (err) {
    var message = "Sorry, it looks like you do not have any reservations to respond to";
    respond(res, message);
  });
});

var respond = function(res, message) {
  var messagingResponse = new MessagingResponse();
  messagingResponse.message({}, message);

  res.type('text/xml');
  res.send(messagingResponse.toString());
}

module.exports = router;
```

In the next step, we'll take a look at how the SMS notification is sent to the host when the reservation is created.

## Notify The Host

When a reservation is created, we want to notify the owner of said property that someone has made a reservation.

This is where we use [Twilio Node SDK](/docs/libraries/reference/twilio-node) to send an SMS message to the *host*, using our [Twilio phone number](/user/account/phone-numbers/incoming). As you can see, sending SMS messages using Twilio is just a few lines of code.

Now we just have to wait for the host to send an SMS response accepting or rejecting the reservation so we can notify the guest and host that the reservation information is updated.

```js title="Send out reservation notifications" description="Notify through SMS the host of the new reservation"
// !mark(5:29)
var config = require('../config');
var client = require('twilio')(config.accountSid, config.authToken);
var Reservation = require('../models/reservation');

var sendNotification = function() {
  Reservation.find({status: 'pending'})
  .deepPopulate('property property.owner guest')
  .then(function (reservations) {
    if (reservations.length > 1) {
      return;
    }

    var reservation = reservations[0];
    var owner = reservation.property.owner;

    // Send the notification
    client.messages.create({
      to: phoneNumber(owner),
      from: config.phoneNumber,
      body: buildMessage(reservation)
    })
    .then(function(res) {
      console.log(res.body);
    })
    .catch(function(err) {
      console.log(err);
    });
  });
};

var phoneNumber = function(owner) {
  return "+" + owner.countryCode + owner.phoneNumber;
};

var buildMessage = function(reservation) {
  var message = "You have a new reservation request from " + reservation.guest.username +
    " for " + reservation.property.description + ":\n" +
    reservation.message + "\n" +
    "Reply accept or reject";

  return message;
};

exports.sendNotification = sendNotification;
```

The next step shows how to handle and configure the host's SMS response.

## Handle Incoming Messages

The `reservations/handle` endpoint handles our incoming Twilio request and does three things:

1. Checks for a pending reservation from the incoming user.
2. Updates the status of the reservation.
3. Responds to the host and sends notification to the guest.

## Set Up Twilio Webhooks

In the [Twilio console](https://twilio.com/console), you should change the 'A Message Comes In' webhook to call your application's endpoint in the route `/handle:`

![Messaging configuration with webhook URL set to https://yourserver.com/sms.](https://docs-resources.prod.twilio.com/f25966b33e960a4214a186c1dbdd0d35aff94f3b3c704f7db85ba8a082ec20d2.png)

One way to expose your machine to the world during development is to use [ngrok](https://ngrok.com/). Your URL for the SMS web hook on your phone number should look something like this:

```bash
http://<subdomain>.ngrok.io/handle
```

An incoming request from Twilio comes with some helpful including the `From` phone number and the message `Body`.

We'll use the `From` parameter to lookup the host and check if she has any pending reservations. If she does, we'll use the message body to check if she accepted or rejected the reservation.

```js title="Deal with host responses" description="routes/reservations.js"
// !mark(34:60,62:68)
var MessagingResponse = require('twilio').twiml.MessagingResponse;
var twilio = require('twilio');
var express = require('express');
var router = express.Router();
var Property = require('../models/property');
var Reservation = require('../models/reservation');
var User = require('../models/user');
var notifier = require('../lib/notifier');

// POST: /reservations
router.post('/', function (req, res) {
  var propertyId = req.body.propertyId;
  var user = req.user;

  Property.findOne({ _id: propertyId })
  .then(function (property) {
    var reservation = new Reservation({
      message: req.body.message,
      property: propertyId,
      guest: user.id
    });

    return reservation.save();
  })
  .then(function () {
    notifier.sendNotification();
    res.redirect('/properties');
  })
  .catch(function(err) {
    console.log(err);
  });
});

// POST: /reservations/handle
router.post('/handle', twilio.webhook({validate: false}), function (req, res) {
  var from = req.body.From;
  var smsRequest = req.body.Body;

  var smsResponse;

  User.findOne({phoneNumber: from})
  .then(function (host) {
    return Reservation.findOne({status: 'pending'});
  })
  .then(function (reservation) {
    if (reservation === null) {
      throw 'No pending reservations';
    }
    reservation.status = smsRequest.toLowerCase() === "accept" ? "confirmed" : "rejected";
    return reservation.save();
  })
  .then(function (reservation) {
    var message = "You have successfully " + reservation.status + " the reservation";
    respond(res, message);
  })
  .catch(function (err) {
    var message = "Sorry, it looks like you do not have any reservations to respond to";
    respond(res, message);
  });
});

var respond = function(res, message) {
  var messagingResponse = new MessagingResponse();
  messagingResponse.message({}, message);

  res.type('text/xml');
  res.send(messagingResponse.toString());
}

module.exports = router;
```

In the last step, we'll use [Twilio's TwiML](/docs/glossary/what-is-twilio-markup-language-twiml) and instruct Twilio to send SMS messages to the *guest*.

## TwiML Response

After updating the reservation status, we must notify the *host* that they have successfully confirmed or rejected the reservation. If no reservation is found, we send an error message instead.

If a reservation is *confirmed* or *rejected* we send an additional SMS to the guest to pass along the news.

We use the verb [Message](/docs/messaging/twiml/message) from TwiML to instruct Twilio's server that it should send SMS messages.

```js title="Build the TwiML Response" description="routes/reservations.js"
// !mark(62:68)
var MessagingResponse = require('twilio').twiml.MessagingResponse;
var twilio = require('twilio');
var express = require('express');
var router = express.Router();
var Property = require('../models/property');
var Reservation = require('../models/reservation');
var User = require('../models/user');
var notifier = require('../lib/notifier');

// POST: /reservations
router.post('/', function (req, res) {
  var propertyId = req.body.propertyId;
  var user = req.user;

  Property.findOne({ _id: propertyId })
  .then(function (property) {
    var reservation = new Reservation({
      message: req.body.message,
      property: propertyId,
      guest: user.id
    });

    return reservation.save();
  })
  .then(function () {
    notifier.sendNotification();
    res.redirect('/properties');
  })
  .catch(function(err) {
    console.log(err);
  });
});

// POST: /reservations/handle
router.post('/handle', twilio.webhook({validate: false}), function (req, res) {
  var from = req.body.From;
  var smsRequest = req.body.Body;

  var smsResponse;

  User.findOne({phoneNumber: from})
  .then(function (host) {
    return Reservation.findOne({status: 'pending'});
  })
  .then(function (reservation) {
    if (reservation === null) {
      throw 'No pending reservations';
    }
    reservation.status = smsRequest.toLowerCase() === "accept" ? "confirmed" : "rejected";
    return reservation.save();
  })
  .then(function (reservation) {
    var message = "You have successfully " + reservation.status + " the reservation";
    respond(res, message);
  })
  .catch(function (err) {
    var message = "Sorry, it looks like you do not have any reservations to respond to";
    respond(res, message);
  });
});

var respond = function(res, message) {
  var messagingResponse = new MessagingResponse();
  messagingResponse.message({}, message);

  res.type('text/xml');
  res.send(messagingResponse.toString());
}

module.exports = router;
```

And that's a wrap! Next let's take a look at other features you might enjoy in your application.

## Where to Next?

Node.js goes great with a helping of Twilio... let us prove it:

**[IVR: Phone Tree](/docs/voice/tutorials/build-interactive-voice-response-ivr-phone-tree/node)**

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

**[Automated Survey](/blog/automated-survey-nodejs-express)**

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