Frequently Asked Questions
Use the Sinch SMS API's send_at
parameter within the @sinch/sdk-core
Node.js SDK. Provide the recipient's number, message content, and the desired send time in UTC ISO 8601 format. Sinch's system handles the scheduling and sending, eliminating the need for local scheduling libraries or background processes for the sending mechanism.
The Sinch SMS API allows developers to integrate SMS messaging into applications. In this example, it schedules and sends appointment reminders, demonstrating its reliability and robustness for a production-ready reminder system.
Sinch API credentials authenticate your application, authorizing it to access and utilize Sinch's services like sending SMS messages. These credentials include Project ID, Key ID, and Key Secret, which are vital for secure communication between your app and Sinch. Never expose the secret key publicly.
Luxon is especially useful when working with dates and times in different time zones, as it provides robust tools for managing and converting between them. It's crucial for accurately scheduling reminders using Sinch's send_at
parameter, which requires UTC ISO 8601 format.
Session storage is inadequate for production as data is lost upon server restart. For scheduled jobs, persistent storage like a database (PostgreSQL, MySQL, MongoDB) is essential for maintaining appointment details.
You need Node.js, npm or yarn, a Sinch account with API credentials, a verified recipient number for initial testing, and basic familiarity with Node.js, Express, JavaScript, HTML, REST APIs.
Install the official Sinch Node.js SDK using npm with the command: npm install @sinch/sdk-core
. This package provides the necessary functions for interacting with Sinch's services, including SMS.
The .env
file stores sensitive information, like your Sinch API credentials and database connection strings. Dotenv loads these variables into process.env
. Never commit this file to version control.
The user submits appointment data through a web form. The Node.js/Express app processes the data, using the Sinch SMS API to schedule a reminder. Sinch then sends the SMS at the designated time to the patient.
Environment variables offer a secure method to store sensitive information like API keys without directly embedding them in your code. This protects your credentials and makes it easy to manage configurations across different environments (development, testing, production).
While the example shows basic formatting, for production use a library like libphonenumber-js
to validate and format phone numbers reliably into E.164 format, essential for international compatibility with Sinch.
Express.js acts as the web framework, handling routing, requests, and responses, serving static files like HTML and CSS, allowing us to create web applications easily using Node.js.
Create directories for public assets (CSS, JS), views (EJS templates), and routes. Configure app.js as the entry point, handling middleware, and routes.js for request processing.
Always use a database for production reminder systems. Databases (like PostgreSQL, MySQL, or MongoDB) persist data across sessions and server restarts, ensuring the reminders are sent as scheduled even if there are interruptions or downtime.
The send_at
parameter in the Sinch SMS API lets you specify when to send the SMS message in the future. It's essential for scheduling reminders and must be in UTC ISO 8601 format.
Learn how to build a production-ready SMS appointment reminder system using Sinch SMS API, Node.js, and Express. This comprehensive guide shows you how to schedule SMS messages with the
send_at
parameter—no cron jobs or background workers required. Sinch handles all message scheduling server-side, simplifying your infrastructure.The hospital appointment reminder example adapts to multiple use cases:
Sinch's built-in scheduling eliminates complex local scheduling libraries and persistent background processes. The API handles message delivery server-side, reducing your infrastructure complexity.
Prerequisites:
Environment:
Sinch Account & Credentials:
Dependencies:
@sinch/sdk-core
v1.2.1@sinch/sms
v1.2.1Technical Knowledge:
Source: Sinch Node.js SDK npm repository (@sinch/sdk-core v1.2.1, @sinch/sms v1.2.1); Node.js LTS release schedule; Sinch SMS API documentation (send_at parameter accepts ISO 8601 format, defaults to UTC if no offset specified)
What You'll Build:
send_at
parameterSystem Architecture:
send_at
parameter to schedule deliverysend_at
timestamp1. Project Setup: Installing Sinch Node.js SDK and Dependencies
Create Project Directory:
Initialize Node.js Project:
Create Project Structure:
.env
: Environment variables (API keys, secrets) – never commit to Git.gitignore
: Files and folders Git should ignoreapp.js
: Express application entry pointroutes.js
: Application routing and request logicpublic/
: Static assets (CSS, JavaScript, images)views/
: EJS templatesInstall Dependencies:
express
: Web framework for Node.js@sinch/sdk-core
: Official Sinch Node.js SDKdotenv
: Loads environment variables from.env
luxon
: Date/time library (v3.7.2) with native timezone supportexpress-session
: Session management middlewareconnect-flash
: Temporary message display middlewareejs
: HTML templating engineNote: Avoid
sessionstorage-for-nodejs
in production – data is lost on server restarts. Useexpress-session
for temporary data and a database for persistent storage (see Section 6).Source: Luxon npm package (v3.7.2); Luxon documentation (native timezone support)
Install Development Dependency:
Configure
.gitignore
:Configure Environment Variables (
.env
):Replace placeholder values with your actual credentials. Generate
SESSION_SECRET
using a cryptographically secure method:KEY_ID
,KEY_SECRET
,PROJECT_ID
: Access Keys page in Sinch Customer Dashboard (treat Secret like a password)FROM_NUMBER
: SMS-enabled virtual number from Sinch (E.164 format required)SMS_REGION
: Sinch API region (us
,eu
) affecting endpoints and compliancePORT
: Express application portSESSION_SECRET
: Cryptographically secure random string (32+ bytes) for session cookie signingDEFAULT_COUNTRY_CODE
: Prepends country code to phone numbers without oneSet Up
nodemon
Script:Run
npm run dev
to start the development server.Configure Application Entry Point (
app.js
):Key configurations:
dotenv.config()
express.static
andexpress.urlencoded
middlewareexpress-session
with 1-hour cookie lifetimesecure
(HTTPS-only),httpOnly
(XSS prevention),sameSite
(CSRF protection)res.locals
routes.js
PORT
Source: Express security best practices documentation (secure cookies require HTTPS, httpOnly prevents client-side JS access, sameSite provides CSRF protection)
2. Implementing SMS Scheduling with Sinch send_at Parameter
The Sinch SMS API's
send_at
parameter is the key to serverless SMS scheduling. It accepts an ISO 8601 UTC timestamp and handles all message delivery server-side. When you make the API call, Sinch stores the message in a queue and automatically sends it at the specified time—no cron jobs, no background workers, no node-cron library needed. This architecture dramatically simplifies scheduled SMS implementation compared to traditional polling-based systems..env
with validation for missing variablesscheduleReminder
Function: Calls the Sinch API (sinchClient.sms.batches.send
) with UTC ISO 8601 forsend_at
/
: Redirects to appointment form/appointment
(GET): Renderspatient_details.ejs
/appointment
(POST):scheduleReminder
to schedule via Sinch/success
(GET): Retrieves and displays confirmation from session, then clears temporary data3. Building the Appointment Scheduling Interface
Create HTML forms for user interaction using EJS templates.
views/patient_details.ejs
:views/success.ejs
:public/css/style.css
:4. Sinch SMS API Integration and Credentials Setup
Integration happens primarily in
routes.js
:.env
SinchClient
instantiated using Project ID, Key ID, Key Secret, and RegionscheduleReminder
function usessinchClient.sms.batches.send
, passing recipient (to
), sender (from
), message body, and thesend_at
parameter (UTC ISO 8601 timestamp)Obtaining Credentials:
Key ID
andKey Secret
– the Secret is only shown once. Store these securely in your.env
file.env
+1xxxxxxxxxx
) and add it asFROM_NUMBER
in.env
us
,eu
, etc.) associated with your service plan/project and setSMS_REGION
in.env
5. Production Error Handling and Logging Best Practices
The current implementation includes basic error handling:
routes.js
checks for essential Sinch variables on startup and exits if missingscheduleReminder
function usestry...catch
aroundsinchClient.sms.batches.send
, logs errors, and returns failure status/appointment
handler, with flash messages for user feedbacktry...catch
in POST/appointment
handler catches unexpected errors during processingconsole.log
andconsole.error
with consistent formattingProduction Enhancements:
Structured Logging: Use
winston
orpino
for structured JSON logsReplace
console.log
/console.error
with logger instances (e.g.,logger.info()
,logger.error()
). Configure transports to write logs to files or external logging services.Detailed API Error Parsing: Check
error.response.data
from Sinch API errors for specific error codes to provide more informative feedback or trigger specific actions (e.g., retries for transient network issues)Centralized Error Handling Middleware: Implement more robust Express error-handling middleware in
app.js
to catch unhandled errors gracefullyRetry Mechanisms: For transient network errors, consider implementing a simple retry strategy (1-2 retries with short delay). However, since Sinch handles the sending schedule, retrying the scheduling call might result in duplicate scheduled messages if the first call succeeded but the response was lost. Log failures and alert administrators instead.
6. Database Persistence for Scheduled SMS Appointments
Using only
express-session
is NOT persistent storage. Scheduled jobs require data that survives server restarts and deployments. If the server restarts between scheduling via the API and rendering the success page (or if the user's session expires), confirmation data will be lost. The application has no record of scheduled appointments after server restarts.A database is mandatory for production.
Conceptual Database Schema (PostgreSQL or MySQL):
Data Layer Implementation Steps:
npm install pg
(driver) ornpm install sequelize
/npm install @prisma/client
(ORMs)npm install mysql2
(driver) ornpm install sequelize
/npm install @prisma/client
(ORMs)npm install mongodb
(driver) ornpm install mongoose
(ODM)routes.js
:/appointment
handler, after successfully scheduling with Sinch (scheduleResult.success
), save appointment details (includingpatientName
,doctorName
,formattedPhone
,appointmentDateTimeLocal.toJSDate()
,reminderDateTimeLocal.toJSDate()
,scheduleResult.batchId
) to databasereq.session.appointmentId = newAppointmentRecord.id
/success
handler, retrieveappointmentId
from session and query database for full appointment details. Rendersuccess.ejs
using database data. Clearreq.session.appointmentId
afterwardslibphonenumber-js
before saving to database and sending to Sinch. Ensure numbers are stored consistently (E.164 format).This ensures appointment data persists reliably even after application restarts, providing a foundation for future features like viewing scheduled appointments, cancellation, or status tracking.
Frequently Asked Questions
How does Sinch SMS scheduling work with the send_at parameter?
The Sinch SMS API's
send_at
parameter accepts an ISO 8601 timestamp in UTC format. When you include this parameter in your API call, Sinch stores the message and automatically sends it at the specified future time. You don't need local cron jobs or background workers – Sinch handles the entire scheduling mechanism server-side.What version of the Sinch Node.js SDK should I use for SMS scheduling?
Use
@sinch/sdk-core
v1.2.1 and@sinch/sms
v1.2.1 (latest as of January 2025). These versions provide full support for thesend_at
scheduling parameter and are compatible with Node.js v16+ (v18 or v20 LTS recommended for production as of 2025).How do I handle timezones when scheduling SMS reminders with Luxon?
Luxon (v3.7.2) provides native timezone support. Parse user input in local time with
DateTime.fromISO()
, perform calculations (like subtracting 2 hours for reminders), then convert to UTC using.toUTC().toISO()
before sending to Sinch. Always store appointment times with timezone information (TIMESTAMPTZ in PostgreSQL) for accurate scheduling across regions.Why is express-session not suitable for production appointment storage?
The default MemoryStore for express-session loses all data when your server restarts, which means scheduled appointments would be lost. For production, you must use a persistent database (PostgreSQL, MySQL, MongoDB) to store appointment details. Use express-session only for temporary data like flash messages or passing confirmation IDs between requests.
What secure cookie settings should I use for Express session in production?
Enable three critical security options:
secure: true
(HTTPS-only cookies),httpOnly: true
(prevents XSS attacks by blocking client-side JavaScript access), andsameSite: 'strict'
(CSRF protection). Also setapp.set('trust proxy', 1)
if running behind a load balancer or reverse proxy.Can I schedule SMS messages more than 24 hours in advance with Sinch?
Yes, Sinch supports scheduling SMS messages days or even weeks in advance using the
send_at
parameter. There's no documented hard limit, but for very long-term scheduling (months), consider storing appointments in your database and implementing a daily or weekly job to schedule upcoming reminders within a reasonable window (e.g., 7 days ahead).How do I validate international phone numbers before sending to Sinch?
Use the
libphonenumber-js
library for robust phone number validation:npm install libphonenumber-js
. Parse numbers withparsePhoneNumber(input, defaultCountry)
, validate with.isValid()
, and format to E.164 using.format('E.164')
. This ensures all numbers are correctly formatted (e.g.,+442071234567
) before sending to Sinch.What happens if my Express server restarts after scheduling an SMS?
If you only store appointment data in
express-session
, it's lost on restart. However, the SMS message itself is already scheduled with Sinch's servers and will send at the specified time. For production reliability, persist all appointment details (including Sinch batch IDs) to a database so you can track, modify, or cancel scheduled messages even after server restarts.How do I cancel or update a scheduled SMS reminder in Sinch?
Use the Sinch batch ID returned when scheduling (stored in
response.id
) to modify or cancel messages. CallsinchClient.sms.batches.update()
with the batch ID to change the message or send time, orsinchClient.sms.batches.cancel()
to prevent delivery. Store batch IDs in your database alongside appointment records for full management capabilities.Example:
What database schema should I use for storing scheduled appointment reminders?
Create an
appointments
table with:id
(primary key),patient_name
,doctor_name
,patient_phone
(E.164 format),appointment_datetime
(TIMESTAMPTZ in UTC),reminder_datetime
(TIMESTAMPTZ in UTC),sinch_batch_id
(unique, for tracking),status
(scheduled/sent/failed/cancelled), andcreated_at
/updated_at
timestamps. Add indexes onpatient_phone
andappointment_datetime
for efficient queries.