Receive inbound SMS messages and send replies using the Sinch Conversation API. The same code works for WhatsApp, Messenger, and any other channel your Conversation app supports.
Declare secrets in .env with empty values and store them with sinch secrets add:
sinch secrets add CONVERSATION_APP_ID your-app-id
sinch secrets add PROJECT_ID your-project-id
sinch secrets add PROJECT_ID_API_KEY your-key-id
sinch secrets add PROJECT_ID_API_SECRET your-key-secretimport type { FunctionContext } from '@sinch/functions-runtime';
import {
ConversationController,
getText,
getChannel,
getContactId,
} from '@sinch/functions-runtime';
import type { MessageInboundEvent } from '@sinch/functions-runtime';
class SmsHandler extends ConversationController {
async handleMessageInbound(event: MessageInboundEvent): Promise<void> {
const text = getText(event);
if (!text) return;
const channel = getChannel(event);
const contactId = getContactId(event);
console.log(`Inbound from ${channel}: "${text}" (contact: ${contactId})`);
await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, `You said: ${text}`),
});
}
}
export async function setup(context: FunctionContext) {
const handler = new SmsHandler(context.conversation, {
CONVERSATION_APP_ID: context.env.CONVERSATION_APP_ID,
FROM_NUMBER: context.env.FROM_NUMBER,
});
return handler.getWebhookHandler();
}import {
getText,
getChannel,
getContactId,
getConversationId,
getIdentity,
getMedia,
getPostbackData,
isTextMessage,
isMediaMessage,
isPostback,
} from '@sinch/functions-runtime';
const text = getText(event); // 'Hello'
const channel = getChannel(event); // 'SMS', 'WHATSAPP', 'MESSENGER'
const contactId = getContactId(event); // Sinch contact ID
const identity = getIdentity(event); // Sender's phone/PSID/etc.await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, 'Thanks for your message!'),
});await this.conversation!.messages.send({
sendMessageRequestBody: {
app_id: this.config.CONVERSATION_APP_ID,
recipient: { contact_id: contactId },
message: { text_message: { text: 'A proactive message' } },
},
});For SMS you must set SMS_SENDER with your Sinch number. this.reply() / Reply() does this automatically using FROM_NUMBER. For manual sends:
await this.conversation!.messages.send({
sendMessageRequestBody: {
app_id: this.config.CONVERSATION_APP_ID,
recipient: { contact_id: contactId },
message: { text_message: { text: 'Reply via SMS' } },
channel_priority_order: ['SMS'],
channel_properties: {
SMS_SENDER: this.config.FROM_NUMBER,
},
},
});async handleMessageInbound(event: MessageInboundEvent): Promise<void> {
const text = getText(event)?.trim().toUpperCase();
if (!text) return;
let reply: string;
if (text === 'HELP') {
reply = 'Text HOURS for our hours, or STOP to unsubscribe.';
} else if (text === 'HOURS') {
reply = 'We are open Monday through Friday, 9 AM to 5 PM EST.';
} else if (text === 'STOP') {
reply = 'You have been unsubscribed. Text START to re-subscribe.';
} else {
reply = 'Thanks for reaching out. Text HELP for available commands.';
}
await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, reply),
});
}Use getChannel() to branch per channel:
async handleMessageInbound(event: MessageInboundEvent): Promise<void> {
const channel = getChannel(event);
const text = getText(event);
if (channel === 'WHATSAPP') {
await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, `WhatsApp: ${text}`),
});
} else if (channel === 'SMS') {
const shortReply = (text ?? '').substring(0, 100);
await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, shortReply),
});
} else {
await this.conversation!.messages.send({
sendMessageRequestBody: this.reply(event, `Got your message on ${channel}.`),
});
}
}The conversation webhook is at POST /webhook/conversation (C#) or via handler.getWebhookHandler() (Node.js). Configure this URL in the Sinch Dashboard under your Conversation app's webhook settings.
For sinch.json configuration and environment variables, see Templates and Configuration & Secrets.