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 PHP and Laravel webapp for finding and booking vacation properties — tentatively called Airtng.
Here's how it'll work:
We'll be using the Twilio REST API to send our users messages at important junctures. Here's a bit more on our API:
_118<?php_118_118/*_118|--------------------------------------------------------------------------_118| Web Routes_118|--------------------------------------------------------------------------_118|_118| Here is where you can register web routes for your application. These_118| routes are loaded by the RouteServiceProvider within a group which_118| contains the "web" middleware group. Now create something great!_118|_118*/_118_118Route::get(_118 '/', ['as' => 'home', function () {_118 return response()->view('home');_118 }]_118);_118_118// Session related routes_118Route::get(_118 '/auth/login',_118 ['as' => 'login-index', function () {_118 return response()->view('login');_118 }]_118);_118_118Route::get(_118 '/login',_118 ['as' => 'login-index', function () {_118 return response()->view('login');_118 }]_118);_118_118Route::post(_118 '/login',_118 ['uses' => 'SessionController@login', 'as' => 'login-action']_118);_118_118Route::get(_118 '/logout',_118 ['as' => 'logout', function () {_118 Auth::logout();_118 return redirect()->route('home');_118 }]_118);_118_118// User related routes_118Route::get(_118 '/user/new',_118 ['as' => 'user-new', function () {_118 return response()->view('newUser');_118 }]_118);_118_118Route::post(_118 '/user/create',_118 ['uses' => 'UserController@createNewUser', 'as' => 'user-create', ]_118);_118_118// Vacation Property related routes_118Route::get(_118 '/property/new',_118 ['as' => 'property-new',_118 'middleware' => 'auth',_118 function () {_118 return response()->view('property.newProperty');_118 }]_118);_118_118Route::get(_118 '/properties',_118 ['as' => 'property-index',_118 'middleware' => 'auth',_118 'uses' => 'VacationPropertyController@index']_118);_118_118Route::get(_118 '/property/{id}',_118 ['as' => 'property-show',_118 'middleware' => 'auth',_118 'uses' => 'VacationPropertyController@show']_118);_118_118Route::get(_118 '/property/{id}/edit',_118 ['as' => 'property-edit',_118 'middleware' => 'auth',_118 'uses' => 'VacationPropertyController@editForm']_118);_118_118Route::post(_118 '/property/edit/{id}',_118 ['uses' => 'VacationPropertyController@editProperty',_118 'middleware' => 'auth',_118 'as' => 'property-edit-action']_118);_118_118Route::post(_118 '/property/create',_118 ['uses' => 'VacationPropertyController@createNewProperty',_118 'middleware' => 'auth',_118 'as' => 'property-create']_118);_118_118// Reservation related routes_118Route::post(_118 '/property/{id}/reservation/create',_118 ['uses' => 'ReservationController@create',_118 'as' => 'reservation-create',_118 'middleware' => 'auth']_118);_118_118Route::post(_118 '/reservation/incoming',_118 ['uses' => 'ReservationController@acceptReject',_118 'as' => 'reservation-incoming']_118);
Let's boldly go to the next step! Hit the button below to begin.
Our workflow will require allowing users to create accounts and log-in in order to attempt to reserve properties.
Each User
will need to have a phone_number
which will be required to send SMS notifications later.
database/migrations/2014_10_12_000000_create_users_table.php
_36<?php_36_36use Illuminate\Database\Schema\Blueprint;_36use Illuminate\Database\Migrations\Migration;_36_36class CreateUsersTable extends Migration_36{_36 /**_36 * Run the migrations._36 *_36 * @return void_36 */_36 public function up()_36 {_36 Schema::create('users', function (Blueprint $table) {_36 $table->increments('id');_36 $table->string('name');_36 $table->string('email')->unique();_36 $table->string('password', 60);_36 $table->string('phone_number');_36 $table->string('country_code');_36 $table->rememberToken();_36 $table->timestamps();_36 });_36 }_36_36 /**_36 * Reverse the migrations._36 *_36 * @return void_36 */_36 public function down()_36 {_36 Schema::drop('users');_36 }_36}
Next up, we will create a table that represents a Vacation Rental property.
We're going to need a way to create the property listings for Airtng to be a success.
The VacationProperty
model belongs to a User
who created it (we'll call this user the host moving forward) and contains only two properties: a description
and an image_url
.
It has two associations: it has many reservations and many users through those reservations.
database/migrations/2015_10_23_193814_create_vacation_properties_table.php
_37<?php_37_37use Illuminate\Database\Schema\Blueprint;_37use Illuminate\Database\Migrations\Migration;_37_37class CreateVacationPropertiesTable extends Migration_37{_37 /**_37 * Run the migrations._37 *_37 * @return void_37 */_37 public function up()_37 {_37 Schema::create('vacation_properties', function (Blueprint $table) {_37 $table->increments('id');_37 $table->integer('user_id')->unsigned();_37 $table->foreign('user_id')_37 ->references('id')->on('users')_37 ->onDelete('cascade');_37 $table->string('description');_37 $table->string('image_url');_37_37 $table->timestamps();_37 });_37 }_37_37 /**_37 * Reverse the migrations._37 *_37 * @return void_37 */_37 public function down()_37 {_37 Schema::drop('vacation_properties');_37 }_37}
Next at the plate: how we will model a reservation.
The Reservation
model is at the center of the workflow for this application. It is responsible for keeping track of:
guest
who performed the reservation
vacation_property
the guest is requesting (and associated
host
)
status
of the reservation:
pending
,
confirmed
, or
rejected
Since the reservation can only have one guest in this example, we simplified the model by assigning phone_number
directly to the model (but you'll want to move it out).
database/migrations/2015_10_23_194614_create_reservations_table.php
_43<?php_43_43use Illuminate\Database\Schema\Blueprint;_43use Illuminate\Database\Migrations\Migration;_43_43class CreateReservationsTable extends Migration_43{_43 /**_43 * Run the migrations._43 *_43 * @return void_43 */_43 public function up()_43 {_43 Schema::create('reservations', function (Blueprint $table) {_43 $table->increments('id');_43 $table->string('status')_43 ->default('pending');_43 $table->string('respond_phone_number');_43 $table->text('message');_43 $table->integer('vacation_property_id')->unsigned();_43 $table->foreign('vacation_property_id')_43 ->references('id')->on('vacation_properties')_43 ->onDelete('cascade');_43 $table->integer('user_id')->unsigned();_43 $table->foreign('user_id')_43 ->references('id')->on('users')_43 ->onDelete('cascade');_43_43 $table->timestamps();_43 });_43 }_43_43 /**_43 * Reverse the migrations._43 *_43 * @return void_43 */_43 public function down()_43 {_43 Schema::drop('reservations');_43 }_43}
Our tables are ready, now let's see how we would create a reservation.
The reservation creation form holds only a single field: the message that will be sent to the host user when reserving one of her properties.
The rest of the information necessary to create a reservation is taken from the user that is logged into the system and the relationship between a property and its owner.
A reservation is created with a default status pending
, so when the host replies with a confirm
or reject
response the system knows which reservation to update.
app/Http/Controllers/ReservationController.php
_109<?php_109namespace App\Http\Controllers;_109use Illuminate\Http\Request;_109use App\Http\Requests;_109use App\Http\Controllers\Controller;_109use Illuminate\Contracts\Auth\Authenticatable;_109use App\Reservation;_109use App\User;_109use App\VacationProperty;_109use Twilio\Rest\Client;_109use Twilio\TwiML\MessagingResponse;_109_109class ReservationController extends Controller_109{_109 /**_109 * Store a new reservation_109 *_109 * @param \Illuminate\Http\Request $request_109 * @return \Illuminate\Http\Response_109 */_109 public function create(Client $client, Request $request, Authenticatable $user, $id)_109 {_109 $this->validate(_109 $request, [_109 'message' => 'required|string'_109 ]_109 );_109 $property = VacationProperty::find($id);_109 $reservation = new Reservation($request->all());_109 $reservation->respond_phone_number = $user->fullNumber();_109 $reservation->user()->associate($property->user);_109_109 $property->reservations()->save($reservation);_109_109 $this->notifyHost($client, $reservation);_109_109 $request->session()->flash(_109 'status',_109 "Sending your reservation request now."_109 );_109 return redirect()->route('property-show', ['id' => $property->id]);_109 }_109_109 public function acceptReject(Request $request)_109 {_109 $hostNumber = $request->input('From');_109 $smsInput = strtolower($request->input('Body'));_109_109 $host = User::getUsersByFullNumber($hostNumber)->first();_109 $reservation = $host->pendingReservations()->first();_109_109 $smsResponse = null;_109_109 if (!is_null($reservation))_109 {_109 if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)_109 {_109 $reservation->confirm();_109 }_109 else_109 {_109 $reservation->reject();_109 }_109_109 $smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';_109 }_109 else_109 {_109 $smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';_109 }_109_109 return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');_109 }_109_109 private function respond($smsResponse, $reservation)_109 {_109 $response = new MessagingResponse();_109 $response->message($smsResponse);_109_109 if (!is_null($reservation))_109 {_109 $response->message(_109 'Your reservation has been ' . $reservation->status . '.',_109 ['to' => $reservation->respond_phone_number]_109 );_109 }_109 return $response;_109 }_109_109 private function notifyHost($client, $reservation)_109 {_109 $host = $reservation->property->user;_109_109 $twilioNumber = config('services.twilio')['number'];_109 $messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';_109_109 try {_109 $client->messages->create(_109 $host->fullNumber(), // Text any number_109 [_109 'from' => $twilioNumber, // From a Twilio number in your account_109 'body' => $messageBody_109 ]_109 );_109 } catch (Exception $e) {_109 Log::error($e->getMessage());_109 }_109 }_109}
Let's take a look at how the SMS notification is sent to the host when the reservation is created.
When a reservation is created for a property, we want to notify the owner of that property that someone has requested a reservation.
This is where we use Twilio's Rest API to send an SMS message to the host, using our Twilio phone number. Sending SMS messages using Twilio takes just a few lines of code.
Now we just have to wait for the host to send an SMS response to 'accept' or 'reject', notify the guest, and update the reservation.
app/Http/Controllers/ReservationController.php
_109<?php_109namespace App\Http\Controllers;_109use Illuminate\Http\Request;_109use App\Http\Requests;_109use App\Http\Controllers\Controller;_109use Illuminate\Contracts\Auth\Authenticatable;_109use App\Reservation;_109use App\User;_109use App\VacationProperty;_109use Twilio\Rest\Client;_109use Twilio\TwiML\MessagingResponse;_109_109class ReservationController extends Controller_109{_109 /**_109 * Store a new reservation_109 *_109 * @param \Illuminate\Http\Request $request_109 * @return \Illuminate\Http\Response_109 */_109 public function create(Client $client, Request $request, Authenticatable $user, $id)_109 {_109 $this->validate(_109 $request, [_109 'message' => 'required|string'_109 ]_109 );_109 $property = VacationProperty::find($id);_109 $reservation = new Reservation($request->all());_109 $reservation->respond_phone_number = $user->fullNumber();_109 $reservation->user()->associate($property->user);_109_109 $property->reservations()->save($reservation);_109_109 $this->notifyHost($client, $reservation);_109_109 $request->session()->flash(_109 'status',_109 "Sending your reservation request now."_109 );_109 return redirect()->route('property-show', ['id' => $property->id]);_109 }_109_109 public function acceptReject(Request $request)_109 {_109 $hostNumber = $request->input('From');_109 $smsInput = strtolower($request->input('Body'));_109_109 $host = User::getUsersByFullNumber($hostNumber)->first();_109 $reservation = $host->pendingReservations()->first();_109_109 $smsResponse = null;_109_109 if (!is_null($reservation))_109 {_109 if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)_109 {_109 $reservation->confirm();_109 }_109 else_109 {_109 $reservation->reject();_109 }_109_109 $smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';_109 }_109 else_109 {_109 $smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';_109 }_109_109 return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');_109 }_109_109 private function respond($smsResponse, $reservation)_109 {_109 $response = new MessagingResponse();_109 $response->message($smsResponse);_109_109 if (!is_null($reservation))_109 {_109 $response->message(_109 'Your reservation has been ' . $reservation->status . '.',_109 ['to' => $reservation->respond_phone_number]_109 );_109 }_109 return $response;_109 }_109_109 private function notifyHost($client, $reservation)_109 {_109 $host = $reservation->property->user;_109_109 $twilioNumber = config('services.twilio')['number'];_109 $messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';_109_109 try {_109 $client->messages->create(_109 $host->fullNumber(), // Text any number_109 [_109 'from' => $twilioNumber, // From a Twilio number in your account_109 'body' => $messageBody_109 ]_109 );_109 } catch (Exception $e) {_109 Log::error($e->getMessage());_109 }_109 }_109}
Let's see how we would handle incoming messages from Twilio and accept or reject reservations.
We're zoomed in for a closer look at the acceptReject
method. This method handles our incoming Twilio request and does three things:
app/Http/Controllers/ReservationController.php
_109<?php_109namespace App\Http\Controllers;_109use Illuminate\Http\Request;_109use App\Http\Requests;_109use App\Http\Controllers\Controller;_109use Illuminate\Contracts\Auth\Authenticatable;_109use App\Reservation;_109use App\User;_109use App\VacationProperty;_109use Twilio\Rest\Client;_109use Twilio\TwiML\MessagingResponse;_109_109class ReservationController extends Controller_109{_109 /**_109 * Store a new reservation_109 *_109 * @param \Illuminate\Http\Request $request_109 * @return \Illuminate\Http\Response_109 */_109 public function create(Client $client, Request $request, Authenticatable $user, $id)_109 {_109 $this->validate(_109 $request, [_109 'message' => 'required|string'_109 ]_109 );_109 $property = VacationProperty::find($id);_109 $reservation = new Reservation($request->all());_109 $reservation->respond_phone_number = $user->fullNumber();_109 $reservation->user()->associate($property->user);_109_109 $property->reservations()->save($reservation);_109_109 $this->notifyHost($client, $reservation);_109_109 $request->session()->flash(_109 'status',_109 "Sending your reservation request now."_109 );_109 return redirect()->route('property-show', ['id' => $property->id]);_109 }_109_109 public function acceptReject(Request $request)_109 {_109 $hostNumber = $request->input('From');_109 $smsInput = strtolower($request->input('Body'));_109_109 $host = User::getUsersByFullNumber($hostNumber)->first();_109 $reservation = $host->pendingReservations()->first();_109_109 $smsResponse = null;_109_109 if (!is_null($reservation))_109 {_109 if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)_109 {_109 $reservation->confirm();_109 }_109 else_109 {_109 $reservation->reject();_109 }_109_109 $smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';_109 }_109 else_109 {_109 $smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';_109 }_109_109 return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');_109 }_109_109 private function respond($smsResponse, $reservation)_109 {_109 $response = new MessagingResponse();_109 $response->message($smsResponse);_109_109 if (!is_null($reservation))_109 {_109 $response->message(_109 'Your reservation has been ' . $reservation->status . '.',_109 ['to' => $reservation->respond_phone_number]_109 );_109 }_109 return $response;_109 }_109_109 private function notifyHost($client, $reservation)_109 {_109 $host = $reservation->property->user;_109_109 $twilioNumber = config('services.twilio')['number'];_109 $messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';_109_109 try {_109 $client->messages->create(_109 $host->fullNumber(), // Text any number_109 [_109 'from' => $twilioNumber, // From a Twilio number in your account_109 'body' => $messageBody_109 ]_109 );_109 } catch (Exception $e) {_109 Log::error($e->getMessage());_109 }_109 }_109}
In order to route an SMS messages to and from the host, we need to setup Twilio webhooks. The next pane will show you the way.
This method handles the Twilio request triggered by the host's SMS and does three things:
In the Twilio console, you should change the 'A Message Comes In' webhook to call your application's endpoint in the route /reservation/incoming:
One way to expose your machine to the world during development is to use ngrok. Your URL for the SMS web hook on your phone number should look something like this:
_10http://<subdomain>.ngrok.io/reservation/incoming
An incoming request from Twilio comes with some helpful parameters. These include the From
phone number and the message Body
.
We'll use the From
parameter to look up the host and check if he or she has any pending reservations. If she does, we'll use the message body to check for the message 'accepted' or 'rejected'. Finally, we update the reservation status and send an SMS to the guest telling them the host accepted or rejected their reservation request.
In our response to Twilio, we'll use Twilio's TwiML to command Twilio to send an SMS notification message to the host.
app/Http/Controllers/ReservationController.php
_109<?php_109namespace App\Http\Controllers;_109use Illuminate\Http\Request;_109use App\Http\Requests;_109use App\Http\Controllers\Controller;_109use Illuminate\Contracts\Auth\Authenticatable;_109use App\Reservation;_109use App\User;_109use App\VacationProperty;_109use Twilio\Rest\Client;_109use Twilio\TwiML\MessagingResponse;_109_109class ReservationController extends Controller_109{_109 /**_109 * Store a new reservation_109 *_109 * @param \Illuminate\Http\Request $request_109 * @return \Illuminate\Http\Response_109 */_109 public function create(Client $client, Request $request, Authenticatable $user, $id)_109 {_109 $this->validate(_109 $request, [_109 'message' => 'required|string'_109 ]_109 );_109 $property = VacationProperty::find($id);_109 $reservation = new Reservation($request->all());_109 $reservation->respond_phone_number = $user->fullNumber();_109 $reservation->user()->associate($property->user);_109_109 $property->reservations()->save($reservation);_109_109 $this->notifyHost($client, $reservation);_109_109 $request->session()->flash(_109 'status',_109 "Sending your reservation request now."_109 );_109 return redirect()->route('property-show', ['id' => $property->id]);_109 }_109_109 public function acceptReject(Request $request)_109 {_109 $hostNumber = $request->input('From');_109 $smsInput = strtolower($request->input('Body'));_109_109 $host = User::getUsersByFullNumber($hostNumber)->first();_109 $reservation = $host->pendingReservations()->first();_109_109 $smsResponse = null;_109_109 if (!is_null($reservation))_109 {_109 if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)_109 {_109 $reservation->confirm();_109 }_109 else_109 {_109 $reservation->reject();_109 }_109_109 $smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';_109 }_109 else_109 {_109 $smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';_109 }_109_109 return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');_109 }_109_109 private function respond($smsResponse, $reservation)_109 {_109 $response = new MessagingResponse();_109 $response->message($smsResponse);_109_109 if (!is_null($reservation))_109 {_109 $response->message(_109 'Your reservation has been ' . $reservation->status . '.',_109 ['to' => $reservation->respond_phone_number]_109 );_109 }_109 return $response;_109 }_109_109 private function notifyHost($client, $reservation)_109 {_109 $host = $reservation->property->user;_109_109 $twilioNumber = config('services.twilio')['number'];_109 $messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';_109_109 try {_109 $client->messages->create(_109 $host->fullNumber(), // Text any number_109 [_109 'from' => $twilioNumber, // From a Twilio number in your account_109 'body' => $messageBody_109 ]_109 );_109 } catch (Exception $e) {_109 Log::error($e->getMessage());_109 }_109 }_109}
In the last step, we'll respond to Twilio's request with some TwiML instructing it to send an SMS to both the host and guest.
After updating the reservation status, we must notify the host that he or she has successfully confirmed or rejected the reservation. If the host has no pending reservation, we'll instead return an error message.
If we're modifying a reservation, we'll also send a message to the user who requested the rental delivering the happy or sad news.
We use the Message verb from TwiML to instruct Twilio to send two SMS messages.
app/Http/Controllers/ReservationController.php
_109<?php_109namespace App\Http\Controllers;_109use Illuminate\Http\Request;_109use App\Http\Requests;_109use App\Http\Controllers\Controller;_109use Illuminate\Contracts\Auth\Authenticatable;_109use App\Reservation;_109use App\User;_109use App\VacationProperty;_109use Twilio\Rest\Client;_109use Twilio\TwiML\MessagingResponse;_109_109class ReservationController extends Controller_109{_109 /**_109 * Store a new reservation_109 *_109 * @param \Illuminate\Http\Request $request_109 * @return \Illuminate\Http\Response_109 */_109 public function create(Client $client, Request $request, Authenticatable $user, $id)_109 {_109 $this->validate(_109 $request, [_109 'message' => 'required|string'_109 ]_109 );_109 $property = VacationProperty::find($id);_109 $reservation = new Reservation($request->all());_109 $reservation->respond_phone_number = $user->fullNumber();_109 $reservation->user()->associate($property->user);_109_109 $property->reservations()->save($reservation);_109_109 $this->notifyHost($client, $reservation);_109_109 $request->session()->flash(_109 'status',_109 "Sending your reservation request now."_109 );_109 return redirect()->route('property-show', ['id' => $property->id]);_109 }_109_109 public function acceptReject(Request $request)_109 {_109 $hostNumber = $request->input('From');_109 $smsInput = strtolower($request->input('Body'));_109_109 $host = User::getUsersByFullNumber($hostNumber)->first();_109 $reservation = $host->pendingReservations()->first();_109_109 $smsResponse = null;_109_109 if (!is_null($reservation))_109 {_109 if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)_109 {_109 $reservation->confirm();_109 }_109 else_109 {_109 $reservation->reject();_109 }_109_109 $smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';_109 }_109 else_109 {_109 $smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';_109 }_109_109 return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');_109 }_109_109 private function respond($smsResponse, $reservation)_109 {_109 $response = new MessagingResponse();_109 $response->message($smsResponse);_109_109 if (!is_null($reservation))_109 {_109 $response->message(_109 'Your reservation has been ' . $reservation->status . '.',_109 ['to' => $reservation->respond_phone_number]_109 );_109 }_109 return $response;_109 }_109_109 private function notifyHost($client, $reservation)_109 {_109 $host = $reservation->property->user;_109_109 $twilioNumber = config('services.twilio')['number'];_109 $messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';_109_109 try {_109 $client->messages->create(_109 $host->fullNumber(), // Text any number_109 [_109 'from' => $twilioNumber, // From a Twilio number in your account_109 'body' => $messageBody_109 ]_109 );_109 } catch (Exception $e) {_109 Log::error($e->getMessage());_109 }_109 }_109}
Congratulations! We've just automated a rental workflow with Twilio's Programmable SMS, and now you're ready to add it to your own application.
Next, let's take a look at some other easy to add features you might like to add.
PHP + Twilio? Excellent choice! Here are a couple other tutorials for you to try:
Put a button on your web page that connects visitors to live support or salespeople via telephone.
Instantly collect structured data from your users with a survey conducted over a voice call or SMS text messages.
Thanks for checking this tutorial out! If you have any feedback to share with us, please hit us up on Twitter and let us know what you're building!