Frequently Asked Questions
Build a backend system with NestJS and integrate the Sinch SMS API. This allows you to manage subscribers, create targeted campaigns, send messages reliably, handle opt-outs, and monitor performance, all within a scalable NestJS application.
Sinch is a reliable and scalable SMS API used to send and receive SMS messages globally. Its features include delivery reports, inbound message handling (webhooks), and support for various number types like shortcodes and toll-free numbers.
NestJS is a progressive Node.js framework offering a modular architecture, TypeScript support, and built-in features like dependency injection and validation pipes. These features make it ideal for building efficient, reliable, and scalable server-side applications for SMS marketing.
You can manage subscribers by implementing CRUD (Create, Read, Update, Delete) operations within a dedicated Subscribers module. This involves creating data transfer objects (DTOs) for validation and using a database service like Prisma to interact with the database.
PostgreSQL is a robust open-source relational database recommended for this project, although Prisma supports other databases like MySQL and SQLite. Docker can be used for containerizing the database for consistent development and deployment.
Create a dedicated Sinch module and service in your NestJS application. Use the @nestjs/axios package to make HTTP requests to the Sinch API, managing authentication and handling responses within the service.
Prisma is a modern database toolkit (ORM) for Node.js and TypeScript. It simplifies database access, schema migrations, and ensures type safety when interacting with the database from your NestJS application.
Using Docker is recommended for local database setup and for containerizing the NestJS application itself. This ensures consistent development and deployment environments, simplifying the process and reducing potential issues.
Sinch webhooks can be used to handle opt-outs. When a user replies with a keyword like "STOP", the webhook triggers a function in your NestJS app to update the subscriber's opt-in status in the database.
The Sinch API supports sending bulk messages. The provided example uses a batching mechanism and an exponential retry with backoff to handle Sinch rate limiting and network errors. This increases efficiency and reduces the risk of exceeding API limits.
You'll need Node.js (LTS recommended), npm or yarn, Docker and Docker Compose, a Sinch account with SMS API access, a Sinch phone number, and a basic understanding of TypeScript, NestJS, REST APIs, and databases.
Organize your project into modules for different functionalities (Subscribers, Campaigns, Sinch, Prisma, Config, etc.). This modular structure promotes maintainability, scalability, and code organization.
Implement robust error handling in the Sinch service to catch potential issues like network problems or API errors. Consider retry mechanisms and appropriate logging for debugging and monitoring.
Yes, the provided code demonstrates basic scheduling. You can implement simple campaign scheduling using the @nestjs/schedule package along with Prisma to store scheduled times and update campaign statuses after execution.
Build SMS Marketing Campaigns with Sinch, NestJS 11, and Node.js
Learn how to build a production-ready SMS marketing campaign system using NestJS 11 and the Sinch SMS API. This comprehensive tutorial walks you through creating a scalable NestJS application that manages subscriber lists, sends bulk SMS messages to 1,000 recipients per batch using Sinch's batch endpoint, processes webhook opt-outs, schedules automated campaigns with cron jobs, and integrates with PostgreSQL 17 using Prisma ORM 6. Master SMS marketing automation with TypeScript, implement TCPA/GDPR compliance, and deploy a reliable bulk messaging system.
Architecture Overview:
This tutorial covers project setup with Node.js v22 LTS and Docker, core functionality (subscriber management, campaign creation, Sinch API integration), security best practices, error handling, retry logic with exponential backoff, and production deployment considerations.
Why Use Sinch SMS API with NestJS for Marketing Campaigns?
What You're Building:
Build a NestJS-based API service that enables:
Problem Solved:
This system provides a foundational backend to power SMS marketing efforts, automating subscriber management and message delivery through Sinch. The platform ensures compliance with regulations like TCPA, GDPR, and CAN-SPAM by implementing opt-out mechanisms and honoring subscriber preferences. Sinch's batch messaging API supports up to 1,000 recipients per request (increased from 100), with regional endpoints and FIFO message queuing for reliability.
Key Compliance Features:
Sinch vs. Alternative Providers:
1. How to Set Up Your NestJS Project for SMS Marketing
Initialize your NestJS project and set up the basic structure and dependencies. This section uses Node.js v22 LTS (released October 2024), which provides Long Term Support through April 2027.
1. Install NestJS CLI:
Install the NestJS command-line interface globally.
2. Create New NestJS Project:
Generate a new project named
sms-campaign-service.3. Install Dependencies:
Install packages for configuration, HTTP requests, validation, database interaction, scheduling, and throttling.
Why Each Dependency Matters:
@nestjs/configclass-validator&class-transformer@nestjs/axios&axiosprisma&@prisma/client@nestjs/schedule@nestjs/throttler@nestjs/swaggerhelmet@nestjs/cache-manager4. Set up Prisma:
Initialize Prisma in your project. This creates a
prismadirectory with aschema.prismafile and a.envfile for database credentials.5. Define Prisma Schema:
Create your database schema in
prisma/schema.prisma.Run the migration to create your database tables:
6. Configure Environment Variables:
Update the
DATABASE_URLin the newly created.envfile with your PostgreSQL connection string.Add
.envto.gitignoreto prevent committing secrets to version control.7. Configure Docker for Local Database:
Create a
docker-compose.ymlfile in the project root for easy local PostgreSQL setup.Start the database:
8. Project Structure:
NestJS promotes a modular structure. Your project will follow this organization:
9. Configure Application Module:
Set up
@nestjs/configto load environment variables from.env.Explanation of Choices:
@nestjs/config: Standard way to manage environment variables securely in NestJS.2. How to Implement Subscriber Management and Campaign Logic
Build the core modules: Subscribers, Campaigns, and the Sinch interaction service. These modules work together: Subscribers stores your audience, Campaigns orchestrates bulk sends, and Sinch handles API communication with retry logic.
Generate Modules and Services:
Use the
--no-specflag here to keep the guide concise; however, generating and maintaining test spec files (.spec.ts) is a best practice in NestJS development.Prisma Service (
src/prisma/prisma.service.ts):Set up the Prisma client service.
Make PrismaService available globally by updating
PrismaModule.Import
PrismaModuleintoAppModule.Subscribers Module (
src/subscribers/):DTO (Data Transfer Object): Define shape and validation rules for subscriber data.
Service (
subscribers.service.ts): Implement CRUD logic using PrismaService. Thecreatemethod implements upsert logic: if a phone number already exists, it updates the record instead of throwing an error. This prevents duplicate subscriber errors during bulk imports. Alternative approaches include throwing aConflictException(strict validation) or returning the existing record (idempotent behavior).Controller (
subscribers.controller.ts): Define API endpoints for subscriber management.Campaigns Module (
src/campaigns/):DTO:
SMS Message Length Guidelines:
Service (
campaigns.service.ts): Implement campaign logic. Sending is delegated toSinchService. ThesendCampaignmethod fetches subscribers in batches of 1,000 to avoid loading all subscribers into memory. This approach scales efficiently for large subscriber lists and respects Sinch's batch size limit.Performance Considerations:
findAll()) fails with 10,000+ subscribers due to memory constraintsskipandtakeCampaigns Controller (
src/campaigns/campaigns.controller.ts):Sinch Module (
src/sinch/):Service (
sinch.service.ts): Handle interaction with the Sinch API. This service implements exponential backoff retry logic for transient errors (429 rate limits, 5xx server errors, network failures). The strategy starts with a 500ms delay and doubles on each retry, up to 3 attempts total. Use exponential backoff for production systems to handle temporary API failures without overwhelming the service.Webhooks Controller (
src/webhooks/webhooks.controller.ts):Handle inbound SMS messages from Sinch for opt-out processing. Sinch sends webhook payloads when users reply to your messages. Implement signature verification in production to ensure webhooks originate from Sinch.
Health Check Controller (
src/health/health.controller.ts):Monitor application health for production deployments.
Frequently Asked Questions About Sinch SMS Marketing with NestJS
How do I integrate Sinch SMS API with NestJS 11?
Integrate Sinch SMS API with NestJS 11 by installing
@nestjs/axiosandaxios, then creating a SinchService that usesHttpServiceto make authenticated requests to Sinch's regional endpoints (https://us.sms.api.sinch.comorhttps://eu.sms.api.sinch.com). Store your Service Plan ID and API Token in environment variables using@nestjs/config, and implement the service with retry logic for 429/5xx errors. Sinch supports batch messaging with up to 1,000 recipients per request for efficient bulk sending.What is the maximum batch size for Sinch SMS API?
Sinch SMS API supports up to 1,000 recipients per batch request (increased from 100 in previous versions). To send to more subscribers, implement batch processing by splitting your recipient list into chunks of 1,000 phone numbers and sending multiple requests. Use Prisma's
findManywithtakeandskipparameters to fetch subscribers in batches from your PostgreSQL database, avoiding memory issues with large lists.How do I handle SMS opt-outs in NestJS with Sinch webhooks?
Handle SMS opt-outs by configuring Sinch inbound message webhooks to POST to your NestJS endpoint (e.g.,
/api/webhooks/sinch-inbound). Parse incoming webhook payloads to detect opt-out keywords like "STOP", "UNSUBSCRIBE", or "OPTOUT", then update the subscriber'soptedInstatus tofalsein your PostgreSQL database using Prisma. Implement webhook signature verification for security and return 200 OK responses to acknowledge receipt. Filter opted-out subscribers from future campaigns usingwhere: { optedIn: true }in your Prisma queries.How do I schedule SMS campaigns in NestJS?
Schedule SMS campaigns in NestJS using
@nestjs/schedulewith cron jobs. Add a@Crondecorator to a method that checks for campaigns withstatus: SCHEDULEDandscheduledAttimestamp less than or equal to the current time. Run this check every minute usingCronExpression.EVERY_MINUTE, then trigger your existingsendCampaignmethod for matching campaigns. Store campaign schedules as ISO 8601 date strings in PostgreSQL, and update campaign status toSENDING→SENTorFAILEDbased on results.What are Sinch SMS API rate limits for NestJS applications?
Sinch SMS API rate limits vary by service plan and are measured in messages per second (not requests per second). Each recipient in a batch counts as one message toward your rate limit. Sinch queues messages per service plan in FIFO order, so new batches are accepted immediately but may be delayed if earlier batches are still processing. Implement exponential backoff retry logic for 429 (Too Many Requests) responses, and consider adding delays between batch sends if hitting rate limits frequently.
How do I send bulk SMS to 1,000+ subscribers with Sinch and NestJS?
Send bulk SMS to 1,000+ subscribers by implementing batch processing in your NestJS CampaignsService. Fetch subscribers in batches of 1,000 using Prisma's
findManywithtake: 1000and incrementingskipvalues. For each batch, callsinchService.sendBulkSms(phoneNumbers, message)wherephoneNumbersis an array of E.164-formatted numbers. Track batch IDs returned by Sinch for delivery monitoring. This approach scales efficiently without loading all subscribers into memory and respects Sinch's 1,000 recipient limit.How do I store Sinch SMS credentials securely in NestJS?
Store Sinch SMS credentials securely using
@nestjs/configwith environment variables. AddSINCH_SERVICE_PLAN_ID,SINCH_API_TOKEN, andSINCH_FROM_NUMBERto a.envfile (never commit to Git). InjectConfigServiceinto your SinchService and retrieve credentials usingconfigService.get<string>('SINCH_SERVICE_PLAN_ID'). Validate that all required credentials are present on service initialization and throw an error if any are missing. For production, use your deployment platform's secret management (Vercel, AWS Secrets Manager, HashiCorp Vault).What database schema do I need for SMS marketing campaigns with Prisma?
Design your Prisma schema with three main models:
Subscriber(id, phone, name, optedIn, createdAt, updatedAt),Campaign(id, name, message, status, targetSegment, scheduledAt, sentAt, statusReason, createdAt, updatedAt), and optionallyMessagefor tracking individual message delivery (id, subscriberId, campaignId, status, sinchBatchId, sentAt, deliveredAt). Use unique constraints onSubscriber.phoneto prevent duplicates. Define aCampaignStatusenum with values: DRAFT, SCHEDULED, SENDING, SENT, FAILED. Index frequently queried fields likeSubscriber.optedInandCampaign.statusfor query performance.How do I track SMS delivery status with Sinch webhooks?
Track SMS delivery status by configuring Sinch delivery report webhooks to POST to your NestJS endpoint. Sinch sends delivery reports with statuses:
queued,dispatched,aborted,rejected,delivered,failed,expired, andunknown. Store batch IDs returned fromsendBulkSmscalls in your Campaign model, then correlate incoming delivery webhooks using the batch ID. Create aMessagemodel to track individual message status per subscriber. Implement webhook signature verification to ensure authenticity.How do I handle international SMS sending with Sinch?
Handle international SMS by using E.164 phone number format (e.g.,
+442071234567for UK numbers) and understanding Sinch's pricing varies by destination country. Enable international sending in your Sinch account settings, and validate phone numbers withclass-validator's@IsPhoneNumber()decorator. Consider country-specific regulations: some countries require sender ID registration, while others restrict marketing messages. Check Sinch's country-specific requirements and pricing at https://www.sinch.com/products/messaging/sms/pricing/.Production Deployment Checklist
Before deploying your SMS marketing system, complete these critical tasks:
Security:
@nestjs/throttlerto prevent abuseDatabase:
npx prisma migrate deployMonitoring:
Testing:
Deployment:
Example Dockerfile:
Example GitHub Actions Workflow:
Troubleshooting Common Issues
Problem: "Sinch API returns 401 Unauthorized"
Check your
SINCH_API_TOKENandSINCH_SERVICE_PLAN_IDin.env. Verify the token hasn't expired and the service plan ID matches your Sinch account. Test credentials with curl:Problem: "Campaign sends but no messages delivered"
+1234567890)SINCH_FROM_NUMBERis registered and verifiedoptedInstatus in databaseProblem: "Database connection timeout errors"
Increase connection pool size in your
DATABASE_URL:Enable connection pooling with PgBouncer for production deployments.
Problem: "Memory errors with large subscriber lists"
Verify you're using batch processing with
takeandskipparameters, not fetching all subscribers at once withfindAll(). Each batch should fetch exactly 1,000 subscribers or fewer.Problem: "Webhook endpoint returns 404 Not Found"
Ensure your webhook route is registered in
AppModuleand the full URL matches your Sinch webhook configuration. Test locally with ngrok:Problem: "Scheduled campaigns not sending"
@nestjs/scheduleis imported inAppModulestatusis set toSCHEDULED, notDRAFTscheduledAtis in the past (scheduled campaigns only run ifscheduledAt <= now)Final Outcome:
You now have a production-ready NestJS API capable of managing SMS subscribers, sending bulk campaigns via Sinch to 1,000+ recipients per batch, handling opt-outs through webhooks, scheduling campaigns with cron jobs, and integrating with PostgreSQL using Prisma ORM. The system implements retry logic, exponential backoff, batch processing, caching, and compliance features for TCPA, GDPR, and CAN-SPAM regulations.
Next Steps: