# Twilio Binding

This binding integrates with the Twilio (opens new window) cloud communications platform. It allows sending and receiving SMS, MMS, and WhatsApp messages, as well as making and receiving voice calls with text-to-speech and DTMF input support.

Typical use cases include:

  • Sending SMS/MMS alerts when events occur (door opens, alarm triggers, temperature threshold)
  • Receiving SMS commands to control your smart home ("status", "arm alarm", "turn on lights")
  • Making voice calls for critical alerts with text-to-speech
  • Receiving incoming calls with an interactive voice menu (press 1 for X, press 2 for Y)
  • Sending and receiving WhatsApp messages

# Supported Things

Thing Type Description
account A Twilio account (bridge). Holds API credentials and shared settings.
phone A Twilio phone number. Sends/receives messages and calls. Requires an account bridge.

# Discovery

Once a Twilio Account bridge is added and goes online, the binding automatically discovers all phone numbers associated with the account and adds them to the inbox. You can also trigger a manual scan from the UI.

The account bridge itself must be created manually with your Twilio Console (opens new window) credentials.

# Thing Configuration

# account Bridge Configuration

Name Type Description Default Required Advanced
accountSid text Twilio Account SID (starts with AC) N/A yes no
authToken text Twilio Auth Token N/A yes no
publicUrl text Public-facing base URL for webhooks (e.g. https://my.domain.com) N/A no yes
autoConfigureWebhooks boolean Automatically set webhook URLs on Twilio phone numbers via API true no yes
useCloudWebhook boolean Use openHAB Cloud for webhook callbacks (no port forwarding needed) false no yes

The Account SID and Auth Token can be found in the Twilio Console (opens new window).

To receive incoming messages and calls, you need one of the following:

  • openHAB Cloud Webhooks (recommended): Set useCloudWebhook to true. Requires the openHAB Cloud Connector add-on to be installed and connected. No port forwarding or reverse proxy needed.
  • Public URL: Set publicUrl to a publicly-reachable URL for your openHAB instance. You can use a reverse proxy, port forwarding, or a service like ngrok.

# phone Thing Configuration

Name Type Description Default Required Advanced
phoneNumber text Twilio phone number in E.164 format (e.g. +15551234567) N/A yes no
voiceGreeting text TwiML template for incoming voice calls See below no yes
gatherResponse text TwiML returned after DTMF digits are gathered (fallback) <Response><Say>Thank you. Goodbye.</Say></Response> no yes
responseTimeout integer Seconds to wait for a rule to respond with TwiML (1-14) 10 no yes

The default voiceGreeting includes a <Gather> element that collects one DTMF digit:

<Response>
  <Gather numDigits="1" action="{gatherUrl}">
    <Say>Hello. This is the openHAB smart home system. Press any key.</Say>
  </Gather>
  <Say>No input received. Goodbye.</Say>
</Response>

The {gatherUrl} placeholder is automatically replaced with the correct webhook URL.

# Channels

# State Channels

Channel Type Description
last-message-body String Body text of the last received SMS/WhatsApp message
last-message-from String Phone number of the last message sender
last-message-date DateTime Timestamp of the last received message
last-message-media-url String URL of the first media attachment (MMS/WhatsApp)
last-message-sid String Twilio Message SID of the last received message
last-call-from String Phone number of the last incoming caller
last-call-status String Status of the last call (ringing, in-progress, completed)
last-call-date DateTime Timestamp of the last incoming call
last-dtmf-digits String Last DTMF digits received from a caller

# Trigger Channels

Channel Payload Description
sms-received JSON Triggered on incoming SMS/MMS
whatsapp-received JSON Triggered on incoming WhatsApp message
call-received JSON Triggered on incoming voice call
dtmf-received JSON Triggered when DTMF digits are gathered
message-status JSON Triggered on message delivery status change
call-status-update JSON Triggered on call status change

Trigger channel payloads are JSON objects. Example sms-received payload:

{"from":"+15559876543","to":"+15551234567","body":"Hello!","messageSid":"SM...","numMedia":"0","mediaUrls":[]}

# Rule Actions

Actions are available on phone things under the twilio scope.

# SMS Actions

Action Parameters Description
sendSMS String to, String message Send a plain SMS
sendSMS String to, String message, String mediaUrl Send an MMS with media. message is optional (may be null) to send media only.

# WhatsApp Actions

Action Parameters Description
sendWhatsApp String to, String message Send a WhatsApp message
sendWhatsApp String to, String message, String mediaUrl Send WhatsApp with media. message is optional (may be null) to send media only.

# Voice Actions

Action Parameters Description
makeCall String to, String twiml Make a call with raw TwiML
makeTTSCall String to, String text Make a call with text-to-speech
makeTTSCall String to, String text, String voice TTS with voice selection (e.g. "alice", "Polly.Joanna")
respondWithTwiml String callSid, String twiml Respond to an active call with TwiML (see Dynamic Voice below)

# Media URL Actions

Action Parameters Returns Description
createItemMediaUrl String itemName String (URL) Create a temporary public URL from an openHAB Image item
createProxyMediaUrl String sourceUrl String (URL) Create a temporary public URL that proxies a local/internal URL

These actions create time-limited (5 minute) public URLs for media that Twilio can fetch. This is useful for sending camera snapshots or locally-hosted media as MMS/WhatsApp attachments. Either publicUrl must be configured on the bridge or useCloudWebhook must be enabled for these actions to work.

Supported media types: Any content type works (images, audio, video, PDFs). For Image items, the MIME type is determined from the item's RawType state. For proxy URLs, the MIME type is detected from the source server's response.

# Dynamic Voice Calls (respondWithTwiml)

The binding supports fully interactive voice calls where rules control the call flow in real time. When an incoming call arrives or DTMF digits are pressed, the binding holds the HTTP response open and waits for a rule to provide TwiML via the respondWithTwiml action.

How it works:

  1. Twilio sends a webhook (incoming call or DTMF digits)
  2. The binding fires a trigger channel (call-received or dtmf-received)
  3. The binding waits up to responseTimeout seconds (default: 10) for a rule to call respondWithTwiml
  4. If the rule responds in time, that TwiML is returned to Twilio
  5. If the timeout expires, the default TwiML from the thing config is used as fallback

The {gatherUrl} placeholder can be used in your TwiML to create multi-step menus. It is automatically replaced with the correct URL for the phone thing's gather endpoint.

# Timeout Behavior

If a rule does not call respondWithTwiml within the configured responseTimeout (default: 10 seconds), the binding returns the default TwiML from the thing configuration:

  • For incoming calls: the voiceGreeting config parameter
  • For DTMF gather: the gatherResponse config parameter

This means existing rules that don't use respondWithTwiml continue to work as before. The timeout is configurable per phone thing via the responseTimeout advanced parameter (1-14 seconds).

# Webhook Setup

To receive incoming messages and calls, you need to configure webhooks so Twilio can reach your openHAB instance.

The simplest approach is to use the openHAB Cloud service to provide publicly-reachable webhook URLs. This eliminates the need for port forwarding, reverse proxies, or a public IP address.

Requirements:

Setup:

  1. Enable useCloudWebhook on the bridge (set to true)
  2. If not using auto-configure, copy the webhook URLs from the phone thing properties in the UI and paste them into the Twilio Console (opens new window)

The binding will register cloud webhook URLs (e.g. https://myopenhab.org/api/hooks/{uuid}) for each endpoint. These URLs are shown in the phone thing properties.

Bridge twilio:account:myaccount "Twilio Account" [ accountSid="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authToken="your_auth_token", useCloudWebhook=true ] {
    Thing phone myphone "My Twilio Number" [ phoneNumber="+15551234567" ]
}

# Option 2: Public URL

  1. Set the publicUrl on the bridge (e.g. https://my.domain.com)
  2. The binding will automatically configure the webhook URLs on your Twilio phone numbers via the API

If you disable autoConfigureWebhooks, you can manually copy the webhook URLs from the phone thing properties in the UI and paste them into the Twilio Console (opens new window):

  • Messaging > A Message Comes In: paste the smsWebhookUrl property value
  • Voice & Fax > A Call Comes In: paste the voiceWebhookUrl property value

# URL Structure

All webhook and media endpoints are served under /twilio/callback/ on your openHAB instance. If using a reverse proxy, you must forward this entire path prefix.

Path Method Purpose
/twilio/callback/{thingUID}/sms POST Incoming SMS/MMS messages
/twilio/callback/{thingUID}/whatsapp POST Incoming WhatsApp messages (equivalent to /sms)
/twilio/callback/{thingUID}/voice POST Incoming voice calls
/twilio/callback/{thingUID}/gather POST DTMF digit gather callbacks
/twilio/callback/{thingUID}/status POST Message/call status updates
/twilio/callback/media/{uuid} GET Temporary media serving (for MMS/WhatsApp attachments)

The {thingUID} is the full thing UID (e.g. twilio:phone:myaccount:myphone). The {uuid} is a randomly generated identifier for temporary media entries.

Both /sms and /whatsapp route incoming messages to the same handler; WhatsApp is detected from the From: prefix Twilio sends. Auto-configuration only sets a single Messaging webhook on Twilio (via /sms), which is sufficient for both SMS/MMS and WhatsApp when using a Twilio Messaging Service. The /whatsapp path is available for users who prefer to configure a separate WhatsApp webhook manually.

Example full URLs (assuming publicUrl is https://my.domain.com):

https://my.domain.com/twilio/callback/twilio:phone:myaccount:myphone/sms
https://my.domain.com/twilio/callback/twilio:phone:myaccount:myphone/voice
https://my.domain.com/twilio/callback/media/550e8400-e29b-41d4-a716-446655440000

# Full Example

# Thing Configuration

Bridge twilio:account:myaccount "Twilio Account" [ accountSid="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", authToken="your_auth_token", publicUrl="https://my.domain.com" ] {
    Thing phone myphone "My Twilio Number" [ phoneNumber="+15551234567" ]
}

# Item Configuration

String TwilioLastMessage "Last SMS [%s]" { channel="twilio:phone:myaccount:myphone:last-message-body" }
String TwilioLastFrom "From [%s]" { channel="twilio:phone:myaccount:myphone:last-message-from" }
DateTime TwilioLastDate "Received [%1$tF %1$tR]" { channel="twilio:phone:myaccount:myphone:last-message-date" }
String TwilioLastCallFrom "Last Caller [%s]" { channel="twilio:phone:myaccount:myphone:last-call-from" }
String TwilioLastDtmf "DTMF [%s]" { channel="twilio:phone:myaccount:myphone:last-dtmf-digits" }

# Rule Examples

# Send SMS Alert

    # Send MMS with openHAB Image Item

      # Send MMS with Local Camera URL (Proxy)

        # Send Image-Only MMS (No Text)

          # Receive SMS and Reply

            # SMS Command Menu

              # Handle DTMF Input with Dynamic Response

                # Make TTS Call for Critical Alert

                  # Outgoing Emergency Call with Confirmation

                    # Security Panel IVR

                      # Outgoing Alert Call with Confirmation

                        # Query Room Temperatures by Phone