Frequently Asked Questions
Use the /schedule
endpoint of the Node.js Express app, sending a POST request with recipient, message, and sendAt (ISO 8601 format) in the body. The app uses node-cron
to schedule and the Vonage Messages API to send the SMS at the specified time. A unique job ID is returned for tracking.
The Vonage Messages API is a versatile communication API that allows sending messages across various channels, including SMS. This Node.js app utilizes it to deliver the scheduled SMS messages reliably and efficiently.
Vonage offers reliable, scalable communication APIs with global reach and various features. The Messages API provides a unified approach for different communication channels, though this guide focuses on SMS capabilities.
A persistent store like Redis or a database is essential in production. The in-memory storage used in this guide is only for development; server restarts will clear scheduled jobs. Production apps need persistence and ideally a queue manager (BullMQ, Agenda).
Yes, send a DELETE request to /api/schedule/{jobId}
, providing the job ID you received when scheduling. This will stop the scheduled task if it hasn't executed yet and remove it from the system.
Create a Vonage Application, get a virtual number, and link them. Put API keys, Application ID, private key path, and Vonage number in a .env
file (never commit to Git). The Node.js app loads these for Vonage API interaction.
Node-cron is a task scheduler for Node.js. It allows you to trigger functions at specific intervals or times. The provided example uses it to execute the SMS sending function at the user-defined 'sendAt' time.
The guide includes basic error handling and logging. For production, enhance this with a structured logger, retry logic within the scheduler, and custom error classes. Exponential backoff is a good practice for retries.
The /schedule
endpoint is a POST endpoint that accepts the SMS scheduling requests. It takes the recipient number, the message content, and the desired sending time as input and schedules the SMS message accordingly.
Use tools like curl
or Postman to send requests to the local server after starting it with npm start
. Verify the responses, check your phone for the SMS, and monitor server logs. Automated unit and integration tests are recommended.
Implement input validation, rate limiting, proper authentication (API keys, JWTs), and secure headers. The provided code includes examples of rate limiting and helmet.js for header security.
The project uses Node.js, Express, the Vonage Messages API and Server SDK, node-cron
for scheduling, dotenv
for environment variables, and uuid
for unique identifiers.
The client interacts with the Node.js/Express API server, which uses scheduling logic (node-cron
) and interacts with the Vonage API via the SDK. Messages are then sent from the Vonage cloud to the recipient.
The guide recommends creating directories for src
, src/routes
, src/services
, and src/config
for organizing application code, route definitions, service logic, and configuration respectively.
.env
Build a production-ready Node.js application using Express to schedule SMS messages for future delivery via the Plivo Messages API. Create an API endpoint that accepts scheduling requests, manages pending messages with node-cron, and sends automated appointment reminders at the specified time.
Project Overview and Goals
What You'll Build:
You'll create a backend API service that enables users to schedule SMS messages. The core functionality includes:
POST /schedule
) that accepts SMS scheduling requests (recipient number, message content, desired send time)node-cron
) that triggers SMS sending at the correct timeProblem Solved:
Send automated SMS communications at specific future dates and times – such as appointment reminders, follow-up messages, or time-sensitive alerts – without manual intervention when sending.
Technologies Used:
plivo
SDK)node-cron
: Simple cron-like job scheduler for Node.js that triggers tasks at specific timesdotenv
: Manages environment variables securelyuuid
: Generates unique identifiers for scheduled jobsWhy Plivo?
Plivo provides reliable and scalable communication APIs with extensive features and global reach. The Messages API offers a unified way to handle multiple channels, though this guide focuses on SMS scheduling and appointment reminders.
System Architecture:
Prerequisites:
npm install -g plivo-cli
Final Outcome:
You'll have a running Node.js Express application with a
/schedule
endpoint that accepts SMS scheduling requests and sends messages at the specified future time using Plivo.1. Setting Up the Project
Initialize your Node.js project and install the necessary dependencies.
Step 1: Create Project Directory
Create a new directory for your project and navigate into it:
Step 2: Initialize Node.js Project
Initialize the project using npm (or yarn) – the
-y
flag accepts default settings:This creates a
package.json
file.Step 3: Install Dependencies
Install Express_ the Plivo SDK_
node-cron
_dotenv
_ anduuid
:express
: Web frameworkplivo
: Official Plivo SDK for Node.jsnode-cron
: Task schedulerdotenv
: Loads environment variables from a.env
fileuuid
: Generates unique IDs for tracking jobsStep 4: Project Structure (Recommended)
Create a basic structure for better organization:
src/
: Contains your main application codesrc/routes/
: Holds Express route definitionssrc/services/
: Contains business logic (interacting with Plivo or the scheduler)src/config/
: Configuration files (Plivo SDK initialization)Step 5: Create Basic Express Server (
src/app.js
)Create
app.js
inside thesrc
directory with this basic Express setup:Step 6: Create Start Script
Modify the
scripts
section in yourpackage.json
:Test the basic server by running
npm start
and navigating tohttp://localhost:3000
or usingcurl http://localhost:3000
. You should see the JSON message. Stop the server withCtrl+C
.2. Setting Up Plivo Credentials and SDK
Get credentials and a virtual phone number to interact with the Plivo API. The Plivo SDK uses Auth ID and Auth Token for authentication.
Step 1: Get Your Plivo API Credentials
.env
file)Step 2: Obtain a Plivo Virtual Number
Get a Plivo phone number to send SMS messages from.
Using the Plivo Console:
Step 3: Configure Environment Variables
Create
.env
in your project root (same level aspackage.json
). Never commit this file to Git.Replace
YOUR_AUTH_ID
,YOUR_AUTH_TOKEN
, andYOUR_PLIVO_NUMBER
with your actual values from the Plivo Console.Add
.env
to your.gitignore
:Step 4: Initialize Plivo SDK (
src/config/plivoClient.js
)Initialize the SDK instance using your Auth ID and Auth Token:
This creates a Plivo client instance using your Auth ID and Token. Basic checks ensure critical environment variables are present.
3. Implementing the SMS Scheduling Logic with Node-Cron
Use
node-cron
to run tasks at specific times and a simple in-memory object to track scheduled jobs.Important Caveat: This in-memory storage is not suitable for production. If the server restarts, all scheduled jobs are lost. For production, use a persistent store like Redis or a database with a dedicated job queue library (e.g., BullMQ, Agenda).
Step 1: Create Scheduler Service (
src/services/schedulerService.js
)Create a scheduler service to manage job scheduling and storage:
Explanation:
scheduledJobs
: Simple in-memory database – keys arejobId
, values contain thenode-cron
task object and job detailsscheduleSms
:Date
object (sendAt
)sendAt
is a valid future datejobId
usinguuid
cron.schedule(sendAt, ...)
to schedule the task for the specified date and time – provides aDate
object, whichnode-cron
supports for one-off taskscron.schedule
runs atsendAt
and callssmsService.sendSms
scheduledJobs
after execution (usingfinally
)timezone
explicitly (UTC recommended for servers)scheduledJobs
jobId
cancelSms
: Finds a job by ID, stops thecron
task usingtask.stop()
, and removes it from memorylistScheduledJobs
: Returns details of all pending jobs (useful for admin/status endpoints)4. Implementing the SMS Sending Service with Plivo API
Encapsulate the Plivo SDK interaction to send SMS messages.
Step 1: Create SMS Service (
src/services/smsService.js
)Explanation:
plivoClient
instancePLIVO_SMS_FROM_NUMBER
from environment variables and exits if not setsendSms
function isasync
– the SDK call is asynchronous (plivoClient.messages.create
returns a Promise)plivoClient.messages.create
with required parameters:src
,dst
,text
try...catch
for error handling – logs detailed errors and rethrows a generic error to signal failure to the schedulermessageUuid
returned by Plivo (useful for tracking)5. Building the API Layer (Express Routes for SMS Scheduling)
Create the Express route that uses your scheduler service.
Step 1: Create Schedule Route (
src/routes/scheduleRoutes.js
)Explanation:
express.Router
andschedulerService
POST /
route:recipient
,message
, andsendAt
fromreq.body
sendAt
is a valid ISO 8601 date string representing a future time (useexpress-validator
orjoi
for production)schedulerService.scheduleSms
with validated data202 Accepted
(appropriate for tasks accepted for later processing) and thejobId
message
field for security – avoid returning sensitive message content in production APIsGET /
route: CallsschedulerService.listScheduledJobs
to return pending job detailsDELETE /:jobId
route: ExtractsjobId
fromreq.params
, callsschedulerService.cancelSms
, returns success or 404 if job not foundStep 2: Mount the Router in
app.js
Update
src/app.js
to use this router.This adds
scheduleRoutes
under the/api/schedule
path and includes a basic global error handler middleware.6. Error Handling, Logging, and Retries
Production apps need more robust error handling beyond the basics included here.
Consistent Error Strategy: Use the global error handler middleware (in
app.js
) to catch unhandled errors. Define custom error classes if needed (e.g.,ValidationError
,NotFoundError
).Logging: Replace
console.log
/console.error
with a structured logger likepino
orwinston
. This enables different log levels (debug, info, warn, error), JSON formatting, and sending logs to files or external services.Example with
pino
:Retries: The current
schedulerService
logs errors but doesn't retry. For critical reminders, implement a retry mechanism within thecatch
block of thecron.schedule
task function.for
loop with delaysasync-retry
simplify this7. Adding Security Features
Input Validation: Use a dedicated library (
express-validator
,joi
) for robust validation ofrecipient
(E.164 format),message
(length, content), andsendAt
. Sanitize inputs to prevent injection attacks.Rate Limiting: Protect the
/api/schedule
endpoint from abuse usingexpress-rate-limit
:API Key/Authentication: Currently the API is open. For production, protect it with API keys, JWT tokens, or other authentication mechanisms.
Secure Headers: Use middleware like
helmet
to set HTTP headers for security (XSS protection, content sniffing prevention, etc.):8. Database Schema and Persistence (Production Consideration)
The in-memory
scheduledJobs
store is not persistent. For production:Choose a Database:
Schema/Data Model: Create a table/collection for
ScheduledJobs
with fields:jobId
(Primary Key, UUID)recipient
(String)message
(Text)sendAt
(Timestamp/DateTime)status
(Enum: "PENDING", "SENT", "FAILED", "CANCELLED")createdAt
(Timestamp)updatedAt
(Timestamp)plivoMessageUuid
(String, nullable – store after successful send)failureReason
(Text, nullable)retryCount
(Integer, default 0)Job Queue Library: Libraries like
BullMQ
(Redis-based) orAgenda
(MongoDB-based) handle job persistence, scheduling, retries, concurrency, and worker processes more reliably thannode-cron
with manual persistence logic.9. Verification and Testing
Step 1: Manual Verification (
curl
/ Postman)npm start
http://localhost:3000/api/schedule
Using
curl
:Replace
YOUR_PERSONAL_PHONE_NUMBER
and adjustsendAt
to be a minute or two in the future using ISO 8601 format (UTC "Z" or specify offset).Using Postman: Create a POST request, set the URL, select Body → raw → JSON, and paste the JSON payload.
202 Accepted
response with ajobId
npm start
is running – you should see logs for scheduling, then later for execution and sending (or failure)YOUR_PERSONAL_PHONE_NUMBER
at approximately the scheduled timeStep 2: List Pending Jobs
Send a GET request to
http://localhost:3000/api/schedule
– you should see your scheduled job (until it executes):Step 3: Cancel a Job
jobId
from the responsehttp://localhost:3000/api/schedule/YOUR_JOB_ID
:/api/schedule
) – the cancelled job should be goneStep 4: Unit & Integration Tests (Recommended)
For robust applications, write automated tests:
jest
,mocha
): Test individual functions in isolation – mock dependencies likenode-cron
and theplivo
SDK, test validation logic, date parsing, error handlingsupertest
): Test API endpoints – start the Express server, send HTTP requests usingsupertest
, assert responses, mock external dependencies to avoid actual API calls10. Best Practices for SMS Appointment Reminders
Timing Considerations:
Message Content Best Practices:
Example reminder message:
Production Enhancements:
Related Resources
Conclusion
You've built a complete SMS scheduling application with Plivo, Node.js, and Express. This foundation supports appointment reminders, time-sensitive alerts, and automated notification systems. For production deployment, implement persistent storage, robust error handling, and monitoring to ensure reliable message delivery.
Ready to scale your SMS capabilities? Sign up for Plivo and start building enterprise-grade communication solutions.