Skip to main content

Walk-through: Send and receive texts via Twilio

Darklang allows you to build backends (API endpoints, workers, cron, and data storage) by writing only your business logic, using production traces.

You can access you canvas at (or USERNAME-CANVASNAME). For this project we recommend

We recommend building a hello world API endpoint to get a feel for Darklang, as follows:


All the major handlers work the same way, but the key for many requests is working directly with incoming data.

Daily Text Reminder App

We’re building this app: It’s an app that will send a text message once per day at a given time to an end user. In this case, we’ll ask the user “did you drink enough water today?” and track their response.

To try out the app and see your traces in the sample canvas text “start” to +12482653257.

Twilio Credentials

To build your own, you’ll need to set up a Twilio account. Your Twilio SID & Token are on the Dashboard.


You’ll also need to add a phone number from the “more” menu on the left-hand side, then to the phone number section.


Once you’ve added a phone number, you can configure a webhook:


Sending a Message

Darklang has a built in Twilio::sendText function. We can call it in a REPL with our own number to verify our webhook.


Receiving Responses

After we receive a response, we’ll see a 404 in the side bar section for our webhook, which we can create by hitting the “+” button.


Once the handler is created, we’re able to see the full incoming trace.


Processing & Storing Responses

In Darklang, you can work directly with incoming traces. More on this in Trace Driven Development. For this handler, we can parse out the things we care about: a user deciding to start/stop using our service, or telling us if they drank enough water or not.


At each expression, we can see what the expression evaluates to for a given trace. Here, we see the From number. In this case we also do some cleaning on the response in case the user had a capital letter or an extraneous space.

For processing the various responses, we’ll use a match statement. More on Match is available, but the tutorial example has enough context to understand how it works for this case.

Sign up & Quit

Now, let’s do the “start” case. This allows us to give the user our Twilio number, and have them text “start” to begin receiving a daily reminder. We’ll need a data store to keep track of our users, which can be created from the omnibox (Cmd/Ctrl-k) or the side bar.


Then, we can write the logic to add users to the datastore when they text “start.” In this case we use the user’s phone number as the unique key.


The stop case is very similar, but removes the user from the datastore.


To test, you can always send a new reply to the text message (or just message your number) and walk through the trace to see the live values at each step.


Water Tracking

Now we’ll want the cases for when the user responds to the daily question of “did you drink enough water today?”

We’ll want another data store so we can track the daily response by user (side bar or Cmd/Ctrl-k).


In this case since we do not want to use the phone number as the unique key, we generate a unique key when storing using the built in DB::generateKey function:


Catch-all Case

Finally, we need a case for when we don’t match on one of our earlier responses. We’ll want to send a text back to tell the user about the error. To prevent this from blocking other actions, we’ll emit to a background worker.

Here, we emit the user’s number (user) to a background worker named Misunderstood (and see the event as a trace). Once you’ve hit “play” on the emit expression, you should have a 404 for the worker in the side bar that you can hit the + to add. If it does not appear in the sidebar, wait ~30s or refresh the browser.


Then we can write the code similar to our earlier REPL to send a message asking for a more clear response:


Sending Reminder Messages Once A Day

To send each user a reminder once per day, we’ll want to have a cron set to run daily. In this case we’ll get all the users, then emit to a background worker to send the messages.


Much like before, this will add the worker to the 404 section where you can add it, with the traces.


The code is identical to the code in our REPL:


This is now a fully functioning application.

You could do similar ones for:

  • A reminder to stand up once per hour
  • A daily reminder to do a task (fill in a personal CRM, floss)
  • A weekly reminder to take out the trash

Additional Topics (for Fun!)

Cron at Time of Day

If you’d like to set the reminder to occur at a specific time of day, you can set this using a datastore. There’s a sample canvas here. For this application, add the “reminder time” in the Users table, and add a case to allow users to specify a time (that you would then convert to a Date object).

Seeing/Displaying Responses

This API handler shows all checkins for a given user.

This API handler shows the percentage of “yes” responses for a given user.