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..envfile.System Architecture:
Prerequisites:
npm install -g @vonage/cli). While not strictly required for this guide's code_ it's useful for managing your account.ngrokinstalled 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.jsonfile.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.gitignorefile 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.envfile 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.keyfile 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 with1if using a standard US long code without A2P 10DLC registration.Configure
nodemon: Add start scripts to yourpackage.jsonfor 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.keyfile that downloads. Place it in your project root directory (or the path specified inVONAGE_PRIVATE_KEY_PATHin 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
.envFile: Fill in theVONAGE_APPLICATION_ID,VONAGE_PRIVATE_KEY_PATH(e.g.,./private.key), andVONAGE_NUMBERin your.envfile 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.prismafile, and updates.envwith a templateDATABASE_URL. Ensure your actual connection string is in.env.Define Schema (
prisma/schema.prisma): Replace the contents ofprisma/schema.prismawith the following models:subscribedflag for opt-outs. ThephoneNumberis 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_CONTENToverVONAGE_PRIVATE_KEY_PATHfor better production security.VonageSDK instance.sendSmsfunction usesvonage.messages.sendwith the correctMessageclass 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.sendSmsfor each.setTimeout(waitfunction) to pause betweensendSmscalls based onVONAGE_SEND_RATE_LIMIT_MPS. Includes a note about its single-process limitation.SENTorFAILED) andvonageMessageId(if successful) to theMessageLogtable using Prisma.vonageService) and database logging (with more specific advice).> Important Note on Rate Limiting & A2P 10DLC: > > * Crucial for US Traffic: You must register for Application-to-Person (A2P) messaging via the 10DLC (10-Digit Long Code) system for sending to US numbers. This is mandatory. > * The default 1 message/second limit for US long codes is very slow for bulk campaigns and may lead to blocking without registration. > * Your approved 10DLC registration will grant higher throughput limits determined by carriers. You'll need to adjust
VONAGE_SEND_RATE_LIMIT_MPSaccordingly. > * Toll-Free numbers and Short Codes offer significantly higher throughput but have different registration processes and costs. > * Contact Vonage support if you need API concurrency limits raised beyond the default (around 30 requests/sec).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)./healthcheck endpoint.Campaign Routes (
src/routes/campaigns.js): Define endpoints for creating and sending campaigns.POST /: Creates campaign/recipients. Includes specific error handling for the globalphoneNumberunique constraint (P2002). Adds suggestion for validation libraries.POST /:id/send: TriggersstartCampaignasynchronously (.catch()for background errors) and returns202 Acceptedimmediately.GET /:id/status: (Optional) Provides a summary of message statuses for a given campaign using Prisma'sgroupBy.