Frequently Asked Questions
Use Node.js with the Express framework and the Vonage Messages API. This combination allows you to build an application that can manage recipient lists, send templated SMS messages, and handle replies and delivery statuses efficiently.
The Vonage Messages API is a multi-channel communication platform, but in this tutorial, it's used specifically for sending SMS messages. It integrates seamlessly with Node.js using the official @vonage/server-sdk package.
The example application uses a 'subscribed' flag in its recipient database model. When a user replies with a keyword like 'STOP', the webhook handler can update this flag to prevent future messages to that number.
Rate limiting is crucial to avoid overwhelming carriers and being flagged as spam. The tutorial enforces this using setTimeout based on the VONAGE_SEND_RATE_LIMIT_MPS environment variable.
The tutorial uses PostgreSQL as its relational database to store campaign details, recipient lists, and message logs. Prisma, an ORM, simplifies database interactions.
Ngrok is recommended for creating a public URL that tunnels requests to your local development server, enabling you to test Vonage webhooks during development.
Registration for A2P 10DLC is mandatory if you're sending SMS messages *to* US numbers using standard long codes (+1). This must be done through Vonage after the initial project setup to ensure deliverability and compliance with carrier regulations.
Prisma is used as an Object-Relational Mapper (ORM) to simplify database operations. It allows you to interact with the PostgreSQL database using JavaScript, providing type safety and a clean, efficient way to manage database interactions.
You can create campaigns by sending a POST request to the /api/campaigns route. The request should include the campaign name, message body, and an array of recipient phone numbers. This endpoint uses the Vonage Messages API to send the created campaign and logs results.
The code uses setTimeout to introduce a delay between each message sent via the Vonage API. The delay is calculated based on the VONAGE_SEND_RATE_LIMIT_MPS environment variable, which defines the maximum messages sent per second.
The subscribed flag in the Recipient model tracks whether a recipient has opted out of receiving messages. It's crucial for managing opt-outs and ensuring compliance.
The optional GET /api/campaigns/:id/status endpoint can provide a summary of message statuses (e.g. sent, delivered, failed) for a given campaign ID.
For development, you can use the VONAGE_PRIVATE_KEY_PATH environment variable, but for production always prefer the VONAGE_PRIVATE_KEY_CONTENT environment variable. Never directly store keys in your code repository.
Set Inbound and Status URLs for your Vonage Application, pointing to /webhooks/inbound and /webhooks/status on your Express server. Use ngrok to expose these URLs to Vonage during development.
This guide provides a step-by-step walkthrough for building a robust system to send bulk SMS marketing campaigns using Node.js, Express, and the Vonage Messages API. We will cover everything from initial project setup and core sending logic to database integration, webhook handling, rate limiting, security, and deployment considerations.
By the end of this tutorial, you will have a functional application capable of managing recipient lists, sending templated messages, handling replies and delivery statuses, and respecting API rate limits – serving as a solid foundation for a production-grade SMS marketing platform.
Project Overview and Goals
What We're Building:
A Node.js application using the Express framework that exposes an API to:
Problem Solved:
This system provides a scalable and manageable way to execute SMS marketing campaigns, automating the sending process while incorporating essential features like rate limiting, status tracking, and reply handling, which are crucial for compliance and effectiveness.
Technologies Used:
@vonage/server-sdk
..env
file.System Architecture:
Prerequisites:
npm install -g @vonage/cli
). While not strictly required for this guide's code_ it's useful for managing your account.ngrok
installed and authenticated. Download here.> Important for US Traffic: If sending SMS to US numbers using standard long codes (+1 country code), you must register for A2P 10DLC (Application-to-Person 10-Digit Long Code) via Vonage after the initial setup. This is a mandatory carrier requirement for compliance and deliverability.
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
Create Project Directory:
Initialize Node.js Project:
This creates a
package.json
file.Install Dependencies:
express
: Web framework.@vonage/server-sdk
: Official Vonage Node.js SDK.dotenv
: Loads environment variables from.env
.@prisma/client
: Prisma's database client.prisma
: Prisma CLI for migrations and schema management.nodemon
: Utility to automatically restart the server during development.Project Structure: Create the following directories and files:
Configure
.gitignore
: Create a.gitignore
file to avoid committing sensitive files and unnecessary directories:> Note: Storing private keys directly in the project is convenient for development but highly insecure and strongly discouraged for production. Use secure secret management solutions.
Define Environment Variables (
.env
): Create a.env
file in the project root. You will obtain these values in the next section.VONAGE_APPLICATION_ID
: Found in your Vonage Application settings.VONAGE_PRIVATE_KEY_PATH
: Path to theprivate.key
file you'll download (for development only).VONAGE_NUMBER
: The Vonage virtual number linked to your application.DATABASE_URL
: Standard database connection string. Replace placeholders.PORT
: Port the Express server will listen on.VONAGE_SEND_RATE_LIMIT_MPS
: Crucial for throttling. Start with1
if using a standard US long code without A2P 10DLC registration.Configure
nodemon
: Add start scripts to yourpackage.json
for development and production:2. Vonage Account and Application Setup
Configure your Vonage account to use the Messages API and obtain the necessary credentials.
Log in to Vonage Dashboard: Access your Vonage API Dashboard.
Set Default SMS API:
Create a Vonage Application:
private.key
file that downloads. Place it in your project root directory (or the path specified inVONAGE_PRIVATE_KEY_PATH
in your.env
). > Security Warning: Only store the key file locally for development. In production, load the key content securely, e.g., via environment variables. Treat this key like a password – keep it secure.VONAGE_APPLICATION_ID
.ngrok
):http://localhost:3000/webhooks/inbound
(Method:POST
)http://localhost:3000/webhooks/status
(Method:POST
) > Note for US Sending: After this setup, you will need to proceed with A2P 10DLC registration through the Vonage dashboard or API if targeting US numbers.Link a Virtual Number:
VONAGE_NUMBER
.Update
.env
File: Fill in theVONAGE_APPLICATION_ID
,VONAGE_PRIVATE_KEY_PATH
(e.g.,./private.key
), andVONAGE_NUMBER
in your.env
file with the values obtained above. Remember the security implications ofVONAGE_PRIVATE_KEY_PATH
.3. Database Schema and Data Layer (Prisma/PostgreSQL)
We'll use Prisma to define our database schema and interact with PostgreSQL.
Initialize Prisma:
This creates the
prisma/
directory and aprisma/schema.prisma
file, and updates.env
with a templateDATABASE_URL
. Ensure your actual connection string is in.env
.Define Schema (
prisma/schema.prisma
): Replace the contents ofprisma/schema.prisma
with the following models:subscribed
flag for opt-outs. ThephoneNumber
is globally unique.Apply Schema to Database (Migration):
Prisma will create the SQL migration file in
prisma/migrations/
and apply it to your database, creating the tables.Generate Prisma Client: Prisma Client is generated automatically after
migrate dev
, but you can run it manually if needed:This generates the typed database client in
node_modules/@prisma/client
.Add Prisma Commands to
package.json
:npm run prisma:migrate -- --name <migration_name>
: Create new migrations.npm run prisma:studio
: Open a GUI to view/edit database data.4. Implementing Core Sending Logic
Now, let's implement the services to interact with Vonage and manage the campaign sending process, including rate limiting.
Vonage Service (
src/services/vonageService.js
): Initialize the Vonage SDK and provide a function to send a single SMS..env
, prioritizingVONAGE_PRIVATE_KEY_CONTENT
overVONAGE_PRIVATE_KEY_PATH
for better production security.Vonage
SDK instance.sendSms
function usesvonage.messages.send
with the correctMessage
class structure.Campaign Service (
src/services/campaignService.js
): Handles fetching campaign data, iterating recipients, applying rate limits, sending messages viavonageService
, and logging results to the database.vonageService.sendSms
for each.setTimeout
(wait
function) to pause betweensendSms
calls based onVONAGE_SEND_RATE_LIMIT_MPS
. Includes a note about its single-process limitation.SENT
orFAILED
) andvonageMessageId
(if successful) to theMessageLog
table using Prisma.vonageService
) and database logging (with more specific advice).> Important Note on Rate Limiting & A2P 10DLC: >
5. Building the API Layer (Express)
Create Express routes to manage campaigns and trigger sending.
Basic Server Setup (
src/server.js
): Set up the main Express application.express.json
,express.urlencoded
)./health
check endpoint.Campaign Routes (
src/routes/campaigns.js
): Define endpoints for creating and sending campaigns.POST /
: Creates campaign/recipients. Includes specific error handling for the globalphoneNumber
unique constraint (P2002
). Adds suggestion for validation libraries.POST /:id/send
: TriggersstartCampaign
asynchronously (.catch()
for background errors) and returns202 Accepted
immediately.GET /:id/status
: (Optional) Provides a summary of message statuses for a given campaign using Prisma'sgroupBy
.