Make phone calls with Crystal and Twilio
Tags: crystal, twilio
Published: 2017-07-03 13:12:37

---
title: Make phone calls with Crystal and Twilio
published: true
description: Discover how to build web servers with Crystal and receive webhooks from Twilio.
tags: crystal, twilio
cover_image: https://thepracticaldev.s3.amazonaws.com/i/x2obdnhowuib3u3cxc4r.png
canonical_url: https://www.twilio.com/blog/2016/12/make-phone-calls-crystal-twilio.html
---

We've already learned how to send <a href="https://dev.to/philnash/send-sms-messages-with-crystal-and-twilio">SMS messages with Crystal</a>, a new language that looks like Ruby but runs like C. Doing that required making a simple <a href="https://dev.to/philnash/send-sms-messages-with-crystal-and-twilio">HTTP request</a> from our Crystal application to the Twilio API.

Now it's time to dig a little deeper and see how to <a href="https://www.twilio.com/docs/api/rest/making-calls">make phone calls</a> with Crystal.  Making a call is going to require not only an HTTP request but also <a href="https://www.twilio.com/docs/api/twiml/twilio_request">receiving a webhook</a> which we will need to respond to with <a href="https://www.twilio.com/docs/api/twiml">TwiML</a>.

## Getting started

If you want to join in, you'll need to install Crystal, there are <a href="https://crystal-lang.org/docs/installation/index.html">installation instructions in the documentation</a>. You will also need <a href="https://www.twilio.com/try-twilio">a Twilio account, which you can get for free</a> if you don't have one yet, and a <a href="https://www.twilio.com/console/phone-numbers/incoming">Twilio number</a> that is capable of making phone calls.

We'll use the code from <a href="https://dev.to/philnash/send-sms-messages-with-crystal-and-twilio">the previous post</a> as the starting point for this project. You can run through that post and learn <a href="https://dev.to/philnash/send-sms-messages-with-crystal-and-twilio">how to send a text message with Crystal</a> then come back here to make a call, or just <a href="https://github.com/philnash/crystal-experiments/blob/master/src/sms.cr">grab that post's completed code from GitHub</a>.

## Making a phone call with Crystal

Let's start by creating a new Crystal file to write the code that will make phone calls.

```bash
$ touch phone.cr
```

We'll use the code we wrote in the <a href="https://github.com/philnash/crystal-experiments/blob/master/src/sms.cr">`sms.cr`</a> file as the starting point for making phone calls so open that file and copy and paste the code from it into `phone.cr`.

Now we can make a few modifications so we're making calls instead of sending messages.

First, make a simple JSON mapping for our call object. 

Next, rename the function from `send_sms` to `make_calls` and change the last parameter from `body` to `url`.  The `url` will need to point to an application that can handle incoming HTTP requests. When the call connects, Twilio will make a request, known as a webhook, to the `url` to find out what to do next. We'll see how that is implemented later.

Change the `post_form` URL so that we're calling the <a href="https://www.twilio.com/docs/api/rest/making-calls">Calls resource</a>.

Finally parse the response from Twilio into a Call object. Here's the completed code:

```crystal
require "http/client"
require "json"

class Call
  JSON.mapping(
    sid: String
  )
end

class Error
  JSON.mapping(
    message: String,
    status: Int32
  )
end

def make_call(to, from, url)
  client = HTTP::Client.new("api.twilio.com", 443, true) do |client|
    client.basic_auth(ENV["TWILIO_ACCOUNT_SID"], ENV["TWILIO_AUTH_TOKEN"])
    response = client.post_form("/2010-04-01/Accounts/#{ENV["TWILIO_ACCOUNT_SID"]}/Calls.json", {
      "To"   => to,
      "From" => from,
      "Url" => url,
    })
    if response.success?
      call = Call.from_json(response.body)
      puts call.sid
    else
      error = Error.from_json(response.body)
      puts error.status, error.message
    end
  end
end
```

Add one final line to this file that calls `make_call` with your own phone number, your Twilio number and the URL set to `"https://dl.dropboxusercontent.com/u/2554/crystal-call.xml"`.

```crystal
make_call(YOUR_NUMBER, YOUR_TWILIO_NUMBER, "https://dl.dropboxusercontent.com/u/2554/crystal-call.xml")
```

Save the file, compile and run it.

```bash
$ crystal phone.cr
```

You should receive a phone call which then tells you what to do next.

Hint: it tells you to read on.

We've made a call and responded to the incoming webhook using static TwiML. To make more interactive applications it would be better to create dynamic TwiML in Crystal. Let's investigate how to do that, first with Crystal's `HTTP::Server` class.

## Building HTTP Servers in Crystal

Create a new Crystal file for our server.

```bash
$ touch server.cr
```

Open that file and start by requiring the `http/server` module from the standard library.

```crystal
require "http/server"
```

The `HTTP::Server` is quite straightforward to get started with. Create an instance of `HTTP::Server` passing the port you want it to listen on. We also pass a block that will handle incoming requests.

```crystal
require "http/server"

server = HTTP::Server.new(3000) do |context|

end
```

The `context` is an <a href="https://crystal-lang.org/api/0.20.0/HTTP/Server/Context.html">`HTTP::Server::Context`</a> that is passed to our handler when the server receives a request. It has references to request and response objects. We can use the response to set the content type to `text/xml` as we'll be returning TwiML.

```crystal
require "http/server"

server = HTTP::Server.new(3000) do |context|
  context.response.content_type = "text/xml" 
end
```

Then, for the purposes of this post, we can print our TwiML straight to the response too. To show that this is creating the TwiML dynamically we'll print a message that will change over time.

```crystal
require "http/server"

server = HTTP::Server.new(3000) do |context|
  context.response.content_type = "text/xml" 
  time = Time.now
  message = if time.hour < 12
              "Good morning"
            elsif time.hour < 18
              "Good afternoon"
            else
              "Good night"
            end
  context.response.print "<Response><Say>#{message} from Crystal!</Say></Response>"
end
```

To finish up with this very simple server we need to tell the server to start listening for incoming connections. We also print a message to the terminal so that we can see the server has started.

```crystal
require "http/server"

server = HTTP::Server.new(3000) do |context|
  context.response.content_type = "text/xml"
  time = Time.now
  message = if time.hour < 12
              "Good morning"
            elsif time.hour < 18
              "Good afternoon"
            else
              "Good night"
            end
  context.response.print "<Response><Say>#{message} from Crystal!</Say></Response>"
end

puts "Listening on http://0.0.0.0:3000"
server.listen
```

Compile and run the server:

```bash
$ crystal server.cr
```

Open your browser to <a href="http://localhost:3000">http://localhost:3000</a> and you'll see your TwiML. We can now use this server to drive our call. To do so, we need to open up our development server to external requests. <a href="https://www.twilio.com/blog/2015/09/6-awesome-reasons-to-use-ngrok-when-testing-webhooks.html">I like to do this with ngrok, you can find out how to install and use it here</a>. If you have ngrok setup open up a tunnel to port 3000.

```bash
$ ngrok http 3000
```

Copy your ngrok URL and open up `phone.cr` again. Replace the DropBox link in the call to `make_call` with your ngrok URL and compile and run `phone.cr` again.

```bash
$ crystal phone.cr
```

This time when your phone rings you'll hear the message you wrote in `server.cr`.

## Better servers

We've built a very primitive server so far using the standard library. We could do the work to build in support for dynamic paths, body parsing, HTTP verbs, etc, but as in any language, it's good to see what the community has provided. Crystal comes with an integrated package manager and there are already <a href="http://crystalshards.xyz/">a number of packages, known as shards, available</a>. 

To improve our server offering, we're going to replace our basic HTTP server with <a href="http://kemalcr.com/">Kemal</a>, the Crystal equivalent of Ruby's <a href="http://www.sinatrarb.com/">Sinatra</a>.

This time, we're going to generate our project with Crystal's `init` tool. You can initialise an app or a library, we want an app for now.

```bash
$ crystal init app phone_server
$ cd phone_server
$ ls
LICENSE        README.md         shard.yml        spec        src
```

`init` gives us a basic project structure including a `shard.yml` file. Open that up and add the dependency on Kemal.

```yaml
name: phone_server
version: 0.1.0

license: MIT

dependencies:
  kemal:
    github: kemalcr/kemal
    branch: master
```

On the command line install the dependencies:

```bash
$ shards install
```

Open up `src/phone_server.cr`. and replace everything in the file with:

```crystal
require "kemal"
```

By default Twilio makes webhooks via POST request. In Kemal we define routes by the HTTP verb that we want to handle, our simple `HTTP::Server` responded to everything. To handle the webhook create an endpoint for a POST request at "/voice". Handlers in Kemal get a similar context object to the `HTTP::Server`.

```crystal
require "kemal"

post "/voice" do |context|

end
```

We can use the response property of the context to set headers.

```crystal
require "kemal"

post "/voice" do |context|
  context.response.content_type = "text/xml"
end
```

Then we just need to return our TwiML as we did in the plain `HTTP::Server` example.

```crystal
require "kemal"

post "/voice" do |context|
  context.response.content_type = "text/xml"
  time = Time.now
  message = if time.hour < 12
              "Good morning"
            elsif time.hour < 18
              "Good afternoon"
            else
              "Good night"
            end
  "<?xml version="1.0" encoding="UTF-8"?>
  <Response>
    <Say>#{message} from Kemal and Crystal!</Say>
  </Response>"
end
```

And finally run Kemal.

```crystal
require "kemal"

post "/voice" do |context|
  context.response.content_type = "text/xml"
  time = Time.now
  message = if time.hour < 12
              "Good morning"
            elsif time.hour < 18
              "Good afternoon"
            else
              "Good night"
            end
  "<?xml version="1.0" encoding="UTF-8"?>
  <Response>
    <Say>#{message} from Kemal and Crystal!</Say>
  </Response>"
end

Kemal.run
```

If you haven't already, stop your old server with Ctrl C. Now compile and run the new, Kemal based server:

```bash
$ crystal src/phone_server.cr
[development] Kemal is ready to lead at http://0.0.0.0:3000
```

Hopefully you still have ngrok running, otherwise start it again and copy the ngrok URL to your `make_call` function in `phone.cr`. Make sure to add the "/voice" path this time. Your URL should look like, "<a href="http://random.ngrok.io/voice">http://RANDOM.ngrok.io/voice</a>".

Compile and run `phone.cr` once more. This time when you answer your ringing phone you will hear the message you wrote in your Kemal server.

## I told you it was fast

I mentioned in my first post on Crystal that it is blazingly fast. Before you kill your Kemal server I want you to take a note of the response time it reported. Here's what I saw:

<img alt="The server logs show that the request was served in 54 micro seconds." src="https://twilioinc.wpengine.com/wp-content/uploads/2016/12/MoNIbOHfYSM8J7vB1ERXGeiMifhjbFyM9tkyBwDlfs3pUyQgY9bF_5FW4UUyekgg10S-6h7e9WAqv5nb0aQOEgxtZcayjWTjKA3jPpoV4jcu0OJA3bao_EQtSzlGg7QUGagoojc.png" class="aligncenter">

Yes, that says 54 micro seconds. I realise this endpoint is very simple, but that sort of raw performance from a framework does impress me. That's not all though. This was compiled in debug mode. Compile the application for release with:

```bash
$ crystal build src/phone_server.cr &mdash;release
```

And then run the executable and things can get even quicker.

<img alt="A few runs of the server compiled in release mode show response times of between 22 and 48 micro seconds." src="https://twilioinc.wpengine.com/wp-content/uploads/2016/12/9vAmf8IVXpxqzEyVJw9_dMR3iJoC9UuHX28flUH-DBpvjowP7HPqYkg9BcylctIV0Tt8DHsJ11VmmeHh9K_KRZ0NU81Q8ze_qruXXKkMhQvf7bk0WhazS3PaO1_fKBPTeHGtCfk.png" class="aligncenter">

## Take Crystal for a spin

So there we have it, we've sent <a href="https://dev.to/philnash/send-sms-messages-with-crystal-and-twilio">SMS messages</a> and made phone calls with Crystal. We've seen a bit on how to use the `HTTP::Client`, JSON mappings, the `HTTP::Server`, shards and Kemal. You can check out all the code we wrote in both blog posts in <a href="https://github.com/philnash/crystal-experiments">this GitHub repo</a>.

If you want to explore Crystal more then take a look through <a href="https://crystal-lang.org/docs/">the documentation</a>, the <a href="https://crystal-lang.org/api/0.19.4/">standard library</a>, <a href="http://kemalcr.com/docs/getting_started/">Kemal's documentation</a>, the list of available <a href="http://crystalshards.xyz/">CrystalShards</a>, or even the <a href="https://github.com/crystal-lang/crystal">source code for Crystal</a> (it's all written in Crystal!).

Like the look of Crystal? Let me know in the comments below or drop me an <a href="mailto:philnash@twilio.com">email</a> or a note on Twitter at <a href="https://twitter.com/philnash">@philnash</a>.

___

_[Make phone calls with Crystal and Twilio](https://www.twilio.com/blog/2016/12/make-phone-calls-crystal-twilio.html) was originally published on the [Twilio blog](https://www.twilio.com/blog) on December 2, 2016._