When to Use
- OTP and 2FA delivery: Send a one-time password via SMS as part of a multi-factor authentication workflow, with the code drawn from a previous node's output.
- Appointment reminders: Trigger an SMS or voice call reminder 24 hours before a scheduled appointment using a date-based expression in the
message field.
- Order status notifications: Notify a customer by SMS when their order ships, is out for delivery, or requires action, using
{{ $output.order.phone }} as the destination number.
- Emergency alerts: Place automated voice calls to an on-call roster when a monitoring workflow detects a critical failure, using the call/make operation with a spoken TwiML message.
- Automated voice surveys: Initiate outbound calls that collect voice input via TwiML
<Gather>, routing callers through a decision tree and recording responses for downstream processing.
Selecting the operation: Set resource to sms and operation to send for text messages, or set resource to call and operation to make for voice calls. The correct parameter set is loaded based on this combination — unused fields are ignored.
Configuration
Credentials (both operations)
| Field | Required | Description |
accountSid |
Required |
Twilio Account SID. Begins with AC. Found in the Twilio Console under Account Info. Store in BizFirst Credentials Manager and reference as {{ $credentials.twilio-account-sid }}. |
authToken |
Required |
Twilio Auth Token paired with the Account SID. Treat as a secret — never log or expose this value. Store in BizFirst Credentials Manager. |
Operation: sms/send
| Field | Required | Description |
resource |
Required |
Must be sms. |
operation |
Required |
Must be send. |
from |
Required |
Twilio phone number to send from in E.164 format (e.g. +14155551234). Must be a Twilio-owned number provisioned in your account. For WhatsApp, must be WhatsApp-approved. |
to |
Required |
Destination phone number in E.164 format (e.g. +14155552671). The node automatically prefixes whatsapp: when toWhatsapp is true. |
message |
Required |
SMS body text. Maximum 1600 characters for SMS; 4096 characters for WhatsApp. Messages over 160 characters are split into multiple segments and billed per segment. |
toWhatsapp |
Optional |
Default false. When true, the message is sent via WhatsApp by prepending whatsapp: to the to number. The from number must be enrolled in Twilio's WhatsApp programme. |
statusCallback |
Optional |
URL to receive Twilio delivery status POST callbacks (queued, sent, delivered, failed). Omit if you do not need asynchronous delivery tracking. |
Operation: call/make
| Field | Required | Description |
resource |
Required |
Must be call. |
operation |
Required |
Must be make. |
from |
Required |
Twilio phone number to call from in E.164 format. Must be a Twilio-owned number with voice capability. |
to |
Required |
Destination phone number in E.164 format to dial. |
message |
Required |
Plain text to speak, or raw TwiML when useTwiml is true. When plain text, the executor wraps it in <Response><Say>…</Say></Response> with XML encoding to prevent injection. |
useTwiml |
Optional |
Default false. When true, the message field is treated as raw TwiML and passed directly to the Twilio API without wrapping. Caller is responsible for valid, safe TwiML content. |
statusCallback |
Optional |
URL to receive call status POST callbacks (queued, ringing, in-progress, completed, failed). Omit if asynchronous call tracking is not required. |
Initial status is always queued. Both operations return immediately after Twilio accepts the request. The messageStatus / callStatus in the success output will be queued or accepted at this point. Final delivery or call outcome is reported asynchronously via the statusCallback URL, not through the node's output port.
Sample Configuration JSON — SMS Send
{
"resource": "sms",
"operation": "send",
"accountSid": "{{ $credentials.twilio-account-sid }}",
"authToken": "{{ $credentials.twilio-auth-token }}",
"from": "+14155558888",
"to": "{{ $output.customer.phone }}",
"message": "Your order #{{ $output.order.id }} has shipped. Track it at https://track.mystore.com/{{ $output.order.trackingCode }}",
"statusCallback": "https://app.mycompany.com/webhooks/sms-status"
}
Sample Configuration JSON — Call Make
{
"resource": "call",
"operation": "make",
"accountSid": "{{ $credentials.twilio-account-sid }}",
"authToken": "{{ $credentials.twilio-auth-token }}",
"from": "+14155558888",
"to": "{{ $output.oncall.phone }}",
"message": "Alert: Production API error rate has exceeded 5 percent. Please acknowledge within 10 minutes.",
"useTwiml": false,
"statusCallback": "https://app.mycompany.com/webhooks/call-status"
}
Validation Errors
| Error | Cause |
The 'resource' configuration key is required | resource is missing or empty. Must be sms or call. |
The 'operation' configuration key is required | operation is missing or empty. Must be send (for sms) or make (for call). |
The 'accountSid' configuration key is required | accountSid is empty or not set. |
The 'authToken' configuration key is required | authToken is empty or not set. |
'from' is required for SMS Send | from field is empty when resource=sms, operation=send. |
'to' is required for SMS Send | to field is empty when resource=sms, operation=send. |
'message' is required for SMS Send | message field is empty when resource=sms, operation=send. |
'from' is required for Call Make | from field is empty when resource=call, operation=make. |
'to' is required for Call Make | to field is empty when resource=call, operation=make. |
'message' is required for Call Make | message field is empty when resource=call, operation=make. |
Unsupported resource/operation: '…'/'…' | The resource/operation combination is not sms/send or call/make. |
Output
sms/send — Success Port
| Field | Type | Description |
messageSid | string | Unique Twilio message SID (e.g. SM3f...e4). Use for status lookups and support references. |
to | string | Destination number as returned by Twilio (E.164 or whatsapp:-prefixed). |
from | string | Sending number as returned by Twilio. |
messageStatus | string | Initial status: queued or accepted. Final status arrives via statusCallback. |
numSegments | number | Number of SMS segments used. Each segment is billed separately. 1 segment per 160 characters. |
price | string | Message cost. Empty at queue time; populated after delivery settlement. |
priceUnit | string | ISO 4217 currency code for price (e.g. USD). |
twilioErrorCode | number | Twilio numeric error code (21xxx series) for partial failures. null on success. |
twilioErrorMessage | string | Error description when twilioErrorCode is set. Empty string on success. |
call/make — Success Port
| Field | Type | Description |
callSid | string | Unique Twilio call SID (e.g. CA7b...c2). Use for status lookups. |
to | string | Destination number as returned by Twilio. |
from | string | Calling number as returned by Twilio. |
callStatus | string | Initial status: queued. Transitions to ringing, in-progress, completed, failed, busy, or no-answer — delivered via statusCallback. |
direction | string | Always outbound-api for programmatically initiated calls. |
price | string | Call cost. Empty while queued; populated after the call completes billing. |
priceUnit | string | ISO 4217 currency code for price (e.g. USD). |
Error Port
Fires when Twilio returns an error response (invalid number format, insufficient account balance, invalid credentials, unverified destination number in trial mode, or unsupported resource/operation combination). The error output contains errorCode and message fields.
Sample Output JSON — sms/send
{
"messageSid": "SM3f4a2b1c8d9e0f1a2b3c4d5e6f7a8b9c4",
"to": "+14155552671",
"from": "+14155558888",
"messageStatus": "queued",
"numSegments": 1,
"price": "",
"priceUnit": "USD",
"twilioErrorCode": null,
"twilioErrorMessage": ""
}
Expression Reference
| Expression | Value returned |
{{ $output.twilio.messageSid }} | Unique SMS message SID. |
{{ $output.twilio.messageStatus }} | Initial message status (queued). |
{{ $output.twilio.to }} | Destination phone number. |
{{ $output.twilio.numSegments }} | Number of SMS segments billed. |
{{ $output.twilio.callSid }} | Unique call SID (call/make only). |
{{ $output.twilio.callStatus }} | Initial call status (queued). |
{{ $output.twilio.direction }} | Always outbound-api for call/make. |
Node Policies & GuardRails
- Store credentials in Credentials Manager. Never embed raw
accountSid or authToken values in node configuration. Use {{ $credentials.name }} references. The Auth Token grants full account access and must be treated as a secret.
- Use E.164 format for all phone numbers. Numbers not in E.164 format (e.g. missing
+, local format) will be rejected by Twilio with a 21211 error. Validate numbers in a preceding Data Mapping node when the number comes from user input.
- Verify destination numbers in trial accounts. Twilio trial accounts can only send to verified phone numbers. Unverified numbers will cause a 21608 error. Upgrade to a paid account for production use.
- XML-encode TwiML content when using useTwiml: false. When plain text mode is active (the default), the executor handles XML encoding. When
useTwiml: true, the caller owns safety — validate and sanitise any user-supplied content before passing it as TwiML.
- Monitor
numSegments for cost control. Each additional 160-character segment is billed as a separate message. Pre-truncate or paginate long messages in an upstream node when per-message cost is a concern.
- Set
statusCallback for delivery audit trails. The success port only confirms that Twilio accepted the request. For compliance or retry workflows, configure a statusCallback endpoint that ingests delivery events and persists them.
Examples
OTP Delivery — Two-Factor Authentication
{
"resource": "sms",
"operation": "send",
"accountSid": "{{ $credentials.twilio-account-sid }}",
"authToken": "{{ $credentials.twilio-auth-token }}",
"from": "+14155558888",
"to": "{{ $output.user.phoneNumber }}",
"message": "Your BizFirstAI verification code is {{ $output.auth.otp }}. It expires in 5 minutes. Do not share this code."
}
The OTP generated by an upstream Code Execute node is injected into the message body via expression. No statusCallback is needed here — the success port confirming queued is sufficient before moving to the wait state.
Emergency Voice Alert — On-Call Paging
{
"resource": "call",
"operation": "make",
"accountSid": "{{ $credentials.twilio-account-sid }}",
"authToken": "{{ $credentials.twilio-auth-token }}",
"from": "+14155558888",
"to": "{{ $output.oncall.primaryPhone }}",
"message": "Critical alert from BizFirstAI. Service {{ $output.alert.serviceName }} is down. Incident ID {{ $output.alert.incidentId }}. Please acknowledge by pressing any key.",
"useTwiml": false,
"statusCallback": "https://ops.mycompany.com/webhooks/call-ack"
}
The plain-text message is XML-encoded and wrapped in a <Say> TwiML verb by the executor. The statusCallback receives the final call status so the incident workflow can escalate to the secondary on-call contact if the call goes unanswered.
WhatsApp Order Notification
{
"resource": "sms",
"operation": "send",
"accountSid": "{{ $credentials.twilio-account-sid }}",
"authToken": "{{ $credentials.twilio-auth-token }}",
"from": "+14155558888",
"to": "{{ $output.order.customerPhone }}",
"message": "Hi {{ $output.order.customerName }}, your order #{{ $output.order.id }} is out for delivery today. Estimated arrival: {{ $output.order.eta }}.",
"toWhatsapp": true
}
Setting toWhatsapp: true routes the message through WhatsApp instead of the SMS network. The from number must be a Twilio WhatsApp-approved sender. The message content must comply with WhatsApp Business messaging policies.