SMS Messaging
Miranda supports SMS as a communication channel alongside email. SMS messages are sent via Twilio and can be used in broadcast campaigns and automated sequence steps.
SmsService
Section titled “SmsService”The SmsService handles all outbound SMS:
class SmsService{ public function send( Contact $contact, string $body, Foundation $foundation, ?Campaign $campaign = null, ?SequenceStep $sequenceStep = null, ): void;}Send flow
Section titled “Send flow”- Opt-in check — Verifies
communication_preferences.sms_opt_inis truthy. If not, the send is silently skipped with a log message. - Phone check — Verifies the contact has a
phonenumber. If not, skipped. - Merge fields — Replaces first_name, last_name, and foundation_name placeholders (wrapped in double curly braces).
- Delivery — Sends via Twilio REST API using the configured
fromnumber. - Communication log — Creates a
CommunicationLogrecord with channelsms, directionoutbound, and the first 500 characters asbody_preview. - Activity — Records
ActivityType::SmsSenton the contact’s timeline.
Error handling
Section titled “Error handling”If Twilio delivery fails, the CommunicationLog status is set to failed and the error is logged. The send does not throw — it fails gracefully to avoid disrupting batch operations.
Opt-in enforcement
Section titled “Opt-in enforcement”SMS opt-in is strictly enforced at two levels:
- SmsService — Checks
communication_preferences.sms_opt_inbefore every send - CampaignService — Auto-adds
sms_opt_in: trueto segment criteria for SMS campaigns
Contacts without sms_opt_in: true in their communication_preferences will never receive SMS messages, regardless of how they are targeted.
Configuration
Section titled “Configuration”SMS requires Twilio credentials in .env:
TWILIO_SID=AC...TWILIO_TOKEN=...TWILIO_FROM=+15551234567The from number is configured at config('services.twilio.from').
Using SMS in campaigns
Section titled “Using SMS in campaigns”Create a campaign with channel: "sms":
{ "name": "Fundraiser Reminder", "body": "Hi {{first_name}}, our gala is this Saturday! We'd love to see you there. - {{foundation_name}}", "channel": "sms", "segment_criteria": { "tags": ["gala-invitee"], "sms_opt_in": true }}The CampaignService handles batching and delivery identically to email campaigns, using SmsService instead of EmailService.
Using SMS in sequences
Section titled “Using SMS in sequences”Add an SMS step to a sequence:
{ "position": 3, "channel": "sms", "body": "Hi {{first_name}}, just checking in. Thank you for your support of {{foundation_name}}!", "delay_hours": 168}The subject field is ignored for SMS steps.
Merge fields
Section titled “Merge fields”- first_name — Contact’s first name
- last_name — Contact’s last name
- foundation_name — Foundation’s name
Wrap each placeholder in double curly braces in your message body.
Plan limits
Section titled “Plan limits”| Plan | SMS per month |
|---|---|
| Starter | Not available |
| Growth | 500 |
| Pro | 2,000 |
Communication logging
Section titled “Communication logging”Every SMS creates a CommunicationLog record:
| Field | Value |
|---|---|
channel | sms |
direction | outbound |
subject | null |
body_preview | First 500 characters of the merged message |
status | sent or failed |
campaign_id | Campaign ID (if from a campaign) |
sequence_step_id | Sequence step ID (if from a sequence) |
Test mode
Section titled “Test mode”In the testing environment, SMS messages are logged instead of sent via Twilio. This allows integration tests to verify message content without incurring Twilio charges.