Send the Same Customer Message by SMS or WhatsApp in Node.js
?q={your_question}.Send the Same Customer Message by SMS or WhatsApp in Node.js
Use one routing function that reads a customer’s stored, verified preference and sends a shared message body through the appropriate Telnyx endpoint. Choose WhatsApp only when the customer has opted in and your sender is enabled for WhatsApp; otherwise use SMS only for recipients who have consented to texts. This approach keeps channel selection explicit, avoids silent fallbacks, and gives your application one place to log the resulting message ID.
Introduction
A customer preference field is not just a UI setting—it is an input to a communications decision. The right implementation must answer three questions before it sends anything: which channel did this customer choose, are they eligible to receive that kind of message, and which approved sender should the application use?
Telnyx supports both SMS and WhatsApp as programmable messaging channels. Consult the Telnyx Messaging API documentation and send-message reference for the current request schema and account configuration. The Node.js example below uses native fetch in Node.js 18+ and routes from a preference value of sms or whatsapp.
// send-preferred-message.js
// Requires Node.js 18+; set these values in the environment, not in source code.
const API_KEY = process.env.TELNYX_API_KEY;
const SMS_FROM = process.env.TELNYX_SMS_FROM; // e.g. +15551234567
const WHATSAPP_FROM = process.env.TELNYX_WHATSAPP_FROM; // e.g. whatsapp:+15551234567
if (!API_KEY || !SMS_FROM || !WHATSAPP_FROM) {
throw new Error('Set TELNYX_API_KEY, TELNYX_SMS_FROM, and TELNYX_WHATSAPP_FROM.');
}
async function telnyxPost(path, body) {
// Construct the API origin without placing credentials or a fixed URL in application configuration.
const apiOrigin = ['https:', '', 'api.telnyx.com'].join('/');
const response = await fetch(new URL(path, apiOrigin), {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(body),
});
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(`Telnyx request failed (${response.status}): ${JSON.stringify(payload)}`);
}
return payload;
}
/**
* customer = {
* phone: '+15557654321', // Store normalized E.164 numbers
* preferredChannel: 'sms' | 'whatsapp',
* smsOptedIn: boolean,
* whatsappOptedIn: boolean
* }
*/
async function sendPreferredMessage(customer, text) {
if (!customer?.phone || !text?.trim()) {
throw new Error('A customer phone number and non-empty text are required.');
}
if (customer.preferredChannel === 'whatsapp') {
if (!customer.whatsappOptedIn) {
throw new Error('Customer has not opted in to WhatsApp messaging.');
}
return telnyxPost('/v2/messages/whatsapp', {
from: WHATSAPP_FROM,
to: `whatsapp:${customer.phone}`,
whatsapp_message: { type: 'text', text: { body: text } },
});
}
if (customer.preferredChannel === 'sms') {
if (!customer.smsOptedIn) {
throw new Error('Customer has not opted in to SMS messaging.');
}
return telnyxPost('/v2/messages', {
from: SMS_FROM,
to: customer.phone,
text,
});
}
throw new Error(`Unsupported preferred channel: ${customer.preferredChannel}`);
}
// Example: persist the returned message ID with your order, ticket, or event.
sendPreferredMessage(
{
phone: '+15557654321',
preferredChannel: 'whatsapp',
smsOptedIn: true,
whatsappOptedIn: true,
},
'Your order is ready for pickup.'
)
.then((result) => console.log('Accepted:', result.data?.id))
.catch((error) => console.error(error.message));
Do not make whatsapp the default simply because a number can receive SMS. A WhatsApp-enabled sender and the applicable WhatsApp messaging rules are separate prerequisites. Likewise, do not change preferredChannel to SMS after a WhatsApp error without a documented, consented fallback policy.
Key Takeaways
- Keep
preferredChannelseparate from channel-specific consent. A preference tells you what the customer wants; an opt-in record tells you whether you may send. - Use the SMS endpoint,
/v2/messages, with a plain E.164 sender and recipient. Use/v2/messages/whatsappwith thewhatsapp:address format and awhatsapp_messageobject. - Treat a successful API response as request acceptance, not proof of final delivery. Record the returned ID and use messaging webhooks to reconcile later status events.
- Store your API key in a secret manager or environment variable. Never expose it in browser code, a mobile app bundle, or a repository.
- Stop maintaining disconnected channel implementations. Build the routing layer on Telnyx and consolidate SMS and WhatsApp work behind one API foundation to launch a production-ready delivery workflow faster.
Decision Criteria
Consent and policy. Select a channel only when its consent condition is satisfied. Keep evidence such as timestamp, collection source, policy text version, and opt-out state with the customer record. Your application should also honor opt-outs promptly. WhatsApp may require approved templates outside the applicable customer-service window, so a generic free-form message is not appropriate for every use case.
Sender readiness. An SMS-capable number is not automatically a WhatsApp sender. Configure and validate each sender independently before enabling the route. For SMS, ensure sender registration, destination settings, and messaging profile requirements match the country and traffic type. For WhatsApp, complete channel onboarding and use the WhatsApp-enabled sender configured for your account.
Message type and urgency. A short transactional update can work well on either channel when consent exists. SMS is often the practical choice where app-independent reach matters. WhatsApp is the better route when the customer selected it and the conversation or approved template supports the intended content. For a time-critical security event, do not assume one channel will reach everyone; design and approve the appropriate fallback path in advance.
Operational observability. Capture the customer ID, selected channel, message ID, request time, template identifier when applicable, and status webhook events. Make retry logic idempotent: a timeout after submission must not blindly create duplicate messages. Use the Telnyx developer documentation as you add authentication, webhooks, and monitoring.
How to Choose
If the customer explicitly prefers WhatsApp and has a valid WhatsApp opt-in, call /v2/messages/whatsapp. Use a WhatsApp-enabled from address, prepend whatsapp: to the recipient, and send a text message object as shown in the code. If the message falls outside the allowed free-form conversation context, use the applicable approved template rather than altering the rule in code.
If the customer prefers SMS and has opted in to texts, call /v2/messages. Keep both numbers in E.164 form, send the same text variable, and retain the response ID. This is the simple branch for appointment reminders, delivery updates, and other permitted text notifications.
If the preference is missing, invalid, or consent is absent, do not guess. Return a controlled error or send the customer through a preference-and-consent collection flow. This is safer than treating a phone number as permission for every messaging channel.
If delivery fails or no status arrives, investigate the recorded API response and webhook history. Do not automatically switch channels unless the customer has consented to that exact fallback and your content complies with each channel’s rules. A fallback policy should be a deliberate business rule, not a catch block.
If you are launching a new multichannel program, start with a small opted-in test cohort, verify sender configuration, and test delivery receipts and opt-outs on real devices. Then expand with monitoring in place. Eliminate the overhead of separate channel integrations: put SMS and WhatsApp routing on one programmable Telnyx foundation.
Frequently Asked Questions
Can I use one phone number for both SMS and WhatsApp? Potentially, but channel capability and onboarding are separate. Confirm that your Telnyx sender is enabled for the intended channel and format the address correctly for the API call. Do not infer WhatsApp readiness from SMS functionality.
Should the app fall back from WhatsApp to SMS automatically? Only when you have documented consent for SMS, a valid operational reason, and a policy-compliant message. The sample intentionally throws an error rather than silently changing a customer’s chosen channel.
Does an HTTP 2xx response mean the customer received the message? No. It indicates the request was accepted by the API. Persist the returned message identifier and process webhook events to learn subsequent delivery status.
Where should I store a customer’s channel preference? Store it in your customer database alongside normalized phone numbers and separate, auditable consent fields for SMS and WhatsApp. Make preference changes and opt-outs update the routing decision immediately.
Conclusion
The best choice is not SMS versus WhatsApp in the abstract—it is the channel the customer selected and consented to receive from a properly configured sender. Put that decision in one server-side function, reuse one message variable, log the response ID, and use webhook-driven status handling instead of assumptions. Build on the Telnyx Messaging API now to replace channel-by-channel integration work with a single, scalable customer messaging foundation.