Hypertext Rails

Documentation

Getting Started

Communication Center

Procore / Project Groups

Other Features

SMS Tracking & Twilio Free Tier

What We Track for SMS

Event How it's tracked Where
Sent Recorded when Twilio API accepts the message SmsDeliverySendJob sets status: 'sent', stores external_message_id (Twilio Message SID), creates CommsEvent with event_type: 'sent'
Delivered Twilio POSTs to our webhook when status becomes delivered POST /webhooks/twilio/statusTwilio::WebhooksController#status finds delivery by MessageSid, updates CommsDelivery to status: 'delivered', creates CommsEvent
Failed/Undelivered Same webhook with status failed or undelivered Same controller updates status and creates CommsEvent

SMS does not support open or click tracking (no tracking pixel or link wrapping for SMS in MVP).


How the Status Callback Works

Twilio needs to POST to our app when message status changes (e.g. sentdelivered or failed). We support two ways to get that callback:

  1. Per-message callback (recommended)
    When sending each SMS, we pass status_callback and status_callback_method: 'POST' in the API request (TwilioService#send_sms). The URL is built from your app’s existing public host — the same one already in Rails config (action_mailer.default_url_options[:host]), or BASE_HOST_URL if you use that. No mail-related or Twilio-specific env vars are required for SMS; we just use the app’s public host.
    So every SMS asks Twilio to call https://YOUR_APP_HOST/webhooks/twilio/status. No Twilio Console setup required for the callback URL.

  2. Phone number–level callback
    You can also set the Status Callback URL on the Twilio phone number (or Messaging Service) in the Twilio Console, or via rails twilio:configure_webhooks[BASE_URL]. That sets a global callback for all messages from that number.

If both are set, Twilio will use the per-message URL when provided. Per-message callback is more reliable for trial/Messaging Service setups.


Twilio Free / Trial Tier

  • Trial accounts can only send SMS to verified phone numbers (verified in Twilio Console).
  • Webhook URL must be publicly reachable over HTTPS. Twilio will not call http://localhost:3000. For local dev use a tunnel (e.g. ngrok) and set your app’s host to that URL so status_callback is correct.
  • You don’t “confirm” a Twilio webhook like SNS. Twilio simply POSTs to the URL you give. If the URL is wrong or unreachable, you just won’t get status updates; sending can still succeed.

“Couldn’t confirm Twilio” on free tier

Often this means one of:

  1. Trial account – You can only send to numbers you’ve verified in Twilio Console. Verify the recipient number under Phone Numbers (or use the Messaging Sandbox and join with a code).
  2. Webhook URL not reachable – For status callbacks to work, https://YOUR_HOST/webhooks/twilio/status must be reachable from the internet. Use ngrok (or similar) in development and set BASE_HOST_URL or MAIL_APP_HOST (and action_mailer.default_url_options[:host]) to that host.
  3. Wrong host in config – The per-message status_callback uses the same host as the rest of your app (Rails default_url_options[:host], or BASE_HOST_URL if set). If that points to localhost or a stale URL, Twilio will POST to the wrong place.

Checklist for SMS delivery tracking on free tier

  • [ ] Twilio credentials set: TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER (or Messaging Service).
  • [ ] For trial: recipient number verified in Twilio Console, or use Messaging Sandbox and have recipient join with code.
  • [ ] App base URL is public HTTPS. Your app already has a host in config (action_mailer.default_url_options in config/environments/*.rb). For local dev with ngrok, set that host to your ngrok URL (e.g. in config or via BASE_HOST_URL).
  • [ ] No firewall blocking Twilio; /webhooks/twilio/status returns 200 and does not require auth (signature is validated inside the controller).

After sending an SMS, check comms_events for that delivery: you should see sent from the job and, once Twilio confirms delivery, delivered from the webhook.


Related