Frequently Asked Questions
You can send SMS messages by creating a RedwoodJS service that uses the Vonage Node.js SDK. This service interacts with the Vonage Messages API to send messages programmatically, ideal for notifications or marketing campaigns within your RedwoodJS application.
The Vonage Messages API is a unified API that allows developers to send and receive messages across multiple channels, including SMS. This guide focuses on using the API for SMS communication within a RedwoodJS application.
RedwoodJS uses GraphQL for its API layer to provide a structured and efficient way to communicate between the front-end and back-end. This facilitates data fetching and mutations, like sending an SMS message, using a strongly typed schema.
ngrok is essential during local development with Vonage webhooks. Because your local RedwoodJS server isn't publicly accessible, ngrok creates a secure tunnel to expose it, allowing Vonage to send webhook data to your application for testing incoming SMS messages.
Yes, a free ngrok account is sufficient for development and testing purposes. It provides the necessary functionality to create a temporary public URL for your local server, enabling Vonage webhook integration.
You can receive SMS messages by setting up a webhook handler in your RedwoodJS application. Vonage will send incoming message data to this webhook, which you can then process and store using a RedwoodJS function and Prisma.
A Vonage Application ID is a unique identifier for your Vonage application settings and configurations. It groups your Vonage numbers and API settings, enabling you to manage your SMS integrations effectively. You need this to initialize the Vonage Node.js SDK.
You need Node.js version 20 or higher for this integration. The recommendation is to use NVM (Node Version Manager) to effectively manage and switch between Node.js versions as needed for different projects.
SMS message logs are stored using Prisma, RedwoodJS's default ORM. You define a schema in schema.prisma
to structure your data and then use Prisma Client in your service and serverless functions to interact with the database.
While not explicitly covered in this guide, securing webhooks is crucial in production. Consider verifying the webhook signature using the Vonage SDK to ensure requests are genuinely from Vonage and haven't been tampered with. Never expose your webhook secrets publicly.
Setting the Default SMS API to "Messages API" in your Vonage Dashboard is crucial for correct integration with the '@vonage/server-sdk'. It ensures that incoming SMS messages are routed through the correct API and are processed as expected by your RedwoodJS application.
RedwoodJS uses .env
files for managing environment variables, including sensitive API keys. Add your .env
file and your private.key
to your .gitignore
file to prevent accidentally committing these credentials to your repository.
You'll need Node.js 20+, Yarn Classic (v1.x), a Vonage API account (with API Key and Secret), a Vonage virtual number, and ngrok for local testing.
The private.key
file contains your Vonage Application's private key, crucial for authenticating your application with the Vonage API securely. Save this file securely and never expose it publicly or commit it to version control.
Developer Guide: Implementing Vonage SMS in RedwoodJS with Node.js
Build SMS marketing campaigns and two-way messaging in your RedwoodJS application using the Vonage Messages API. Send outbound SMS messages for notifications or marketing campaigns and receive inbound messages via webhooks, leveraging RedwoodJS's full-stack architecture.
By completing this guide, you'll build a RedwoodJS application that:
This setup provides robust, scalable SMS communication for alerts, verification, and user engagement.
Prerequisites:
node -v
). Use nvm to manage Node versions.yarn -v
).Project Overview and Goals
Build a RedwoodJS application with these SMS components:
Technologies:
@vonage/server-sdk
: The official Vonage Node.js SDK for interacting with the API.System Architecture:
How Do You Set Up a RedwoodJS Project for Vonage SMS?
Create a new RedwoodJS project and configure the necessary Vonage components.
1.1 Create RedwoodJS App:
Open your terminal and run the Create Redwood App command. Use TypeScript (default) and initialize a git repository.
1.2 Environment Variables Setup:
Securely storing API keys and sensitive information is crucial. RedwoodJS uses
.env
files for this.Create a
.env
file in the root of your project:Add the following environment variables to your
.env
file. We'll get these values in the next steps.Important: Add
.env
andprivate.key
to your.gitignore
file to prevent committing secrets. The default RedwoodJS.gitignore
should already include.env
, but double-check and addprivate.key
.1.3 Vonage Account Setup:
.env
file.14155551212
) intoVONAGE_VIRTUAL_NUMBER
in your.env
file.@vonage/server-sdk
features correctly. Save the changes.1.4 Create Vonage Application:
Vonage Applications group your numbers and configurations.
private.key
file that downloads. Save it in the root of your RedwoodJS project (or updateVONAGE_PRIVATE_KEY_PATH
in.env
if you save it elsewhere, or useVONAGE_PRIVATE_KEY_CONTENT
).https://example.com/webhooks/inbound
andhttps://example.com/webhooks/status
). We will update these later with our ngrok URL during development.https://example.com/webhooks/inbound
https://example.com/webhooks/status
VONAGE_APPLICATION_ID
in your.env
file.1.5 Install Vonage SDK:
Install the Vonage Node.js SDK specifically in the
api
workspace.How Do You Implement SMS Sending with Vonage in RedwoodJS?
Create a RedwoodJS service to handle sending SMS messages via Vonage.
2.1 Create SMS Service:
Use the RedwoodJS CLI to generate the service files.
This creates
api/src/services/sms/sms.ts
and related test/scenario files.2.2 Configure Vonage Client:
It's good practice to initialize the Vonage client centrally. We can create a utility file for this.
Create
api/src/lib/vonage.ts
:VONAGE_PRIVATE_KEY_CONTENT
if available, falling back to the path specified inVONAGE_PRIVATE_KEY_PATH
. Redwood's logger provides structured logging.2.3 Implement
sendSms
Function:Now, edit the generated service file (
api/src/services/sms/sms.ts
) to add the sending logic.send
method, and handles potential errors usingtry...catch
. It returns a structured response indicating success or failure. Logging provides visibility into the process.How Do You Build a GraphQL Mutation for SMS in RedwoodJS?
Define a GraphQL mutation to expose your
sendSms
service function.3.1 Define GraphQL Schema (SDL):
Edit the schema definition file
api/src/graphql/sms.sdl.ts
.SmsResponse
: Specifies the fields returned by the mutation.Mutation
: Defines the available mutations.sendSms
takesto
andtext
as non-nullable String arguments and returns anSmsResponse
.@skipAuth
: For development only. This disables authentication. In a real application, you would replace this with@requireAuth
to ensure only logged-in users can trigger the mutation, implementing RedwoodJS Authentication.3.2 Test the Mutation:
Start the development server:
Open your browser to the RedwoodJS GraphQL Playground:
http://localhost:8911/graphql
.In the left panel, enter the following mutation (replace
`YOUR_REAL_PHONE_NUMBER`
with your actual phone number in E.164 format):Click the ""Play"" button.
You should receive an SMS on your phone, and the GraphQL response should look like:
If you get
success: false
, check theerror
message and review the terminal output whereyarn rw dev
is running for logs fromapi/src/services/sms/sms.ts
. Common issues include incorrect API credentials, wrong phone number formats, or the private key file not being found/read correctly.3.3
curl
Example:You can also test the GraphQL endpoint using
curl
:Replace
`YOUR_REAL_PHONE_NUMBER`
accordingly.How Do You Receive SMS Messages via Webhooks in RedwoodJS?
Configure Vonage to send incoming SMS messages to your application via webhooks.
4.1 Expose Localhost with ngrok:
Since your RedwoodJS app is running locally, Vonage can't reach it directly. We use
ngrok
to create a secure tunnel.If
yarn rw dev
is running, stop it (Ctrl+C).Start
ngrok
to forward to Redwood's API port (usually 8911):ngrok
will display a public ""Forwarding"" URL (e.g.,https://<unique-id>.ngrok-free.app
). Copy the HTTPS version of this URL.4.2 Update Vonage Application Webhook URLs:
YOUR_NGROK_HTTPS_URL/webhooks/inbound
YOUR_NGROK_HTTPS_URL/webhooks/status
YOUR_NGROK_HTTPS_URL
with the URL you copied from ngrok).POST
.4.3 Create RedwoodJS Webhook Handler:
RedwoodJS functions are perfect for webhooks. Generate a function:
This creates
api/src/functions/vonageWebhook.ts
.4.4 Implement Webhook Logic:
Modify
api/src/functions/vonageWebhook.ts
to handle incoming messages and status updates. This version includes logic to handle both JSON and form-urlencoded payloads.parseRequestBody
helper to handle bothapplication/json
andapplication/x-www-form-urlencoded
content types, making it more robust. It uses theevent.path
to determine if it's an inbound message or a status update. It parses the payload, logs relevant information, and importantly, returns a200 OK
status code promptly to acknowledge receipt to Vonage. Error handling ensures issues are logged without causing Vonage to endlessly retry (unless a 500 is returned).4.5 Test Receiving SMS:
ngrok
is running and pointing to port 8911.yarn rw dev
.yarn rw dev
. You should see log entries fromapi/src/functions/vonageWebhook.ts
indicating an ""inbound SMS"" was received, along with details like the sender number (msisdn
) and the message text. Check for any parsing warnings or errors.http://127.0.0.1:4040
), including theContent-Type
header sent by Vonage.How Do You Implement Error Handling and Logging for SMS?
try...catch
blocks in the service (sendSms
) and the webhook handler. InsendSms
, we catch errors from the Vonage SDK and return a{ success: false, error: '...' }
object. In the webhook, we catch errors during processing but still try to return200 OK
to Vonage, logging the error internally. The webhook now also handles potential body parsing errors. More specific error types could be handled (e.g., distinguishing network errors from API errors from Vonage).src/lib/logger
) is used. We log key events: attempts to send, success/failure of sending, reception of webhooks, specific details from payloads, and errors.logger.info
,logger.warn
,logger.error
, andlogger.debug
are used appropriately. For production, configure log levels and destinations (e.g., sending logs to a service like Datadog or Logflare).sendSms
doesn't implement retries. For critical messages, you could wrap thevonage.messages.send
call in a retry loop with exponential backoff (using libraries likeasync-retry
orp-retry
) for transient network errors or temporary Vonage API issues.200 OK
status within a reasonable time (a few seconds). Our handler is designed to return 200 quickly, even if background processing fails, to prevent unnecessary Vonage retries.Example: Testing Error Scenario (Send SMS)
Modify the
sendSms
service temporarily to force an error, e.g., provide an invalidto
number format known to cause Vonage API errors, or temporarily change the API key in.env
to something invalid. Then trigger thesendSms
mutation and observe the logged error and the{ success: false, ... }
response.How Do You Create a Database Schema for SMS Logging?
Let's log sent and received messages to the database using Prisma.
6.1 Define Prisma Schema:
Edit
api/db/schema.prisma
. Add aSmsLog
model.vonageId
is unique to prevent duplicate entries from retried webhooks (though upsert logic is better) and uses a CUID as a fallback if the Vonage ID isn't obtained.6.2 Create and Run Migration:
Apply the schema changes to your database.
6.3 Update Service and Webhook to Log Data:
Modify the
sendSms
service and thevonageWebhook
function to interact with the database. You might need to add thecuid
package:yarn workspace api add cuid
.api/src/services/sms/sms.ts
(sendSms):api/src/functions/vonageWebhook.ts
(handler):Why: The
sendSms
service now logs the attempt to theSmsLog
table, recording whether it was initially submitted or failed. ThevonageWebhook
handler logs incoming messages (INBOUND
) and updates the status of existing outbound messages based on status webhooks (OUTBOUND
). It usesupsert
orupdate
logic (hereupdate
for status,create
for inbound with duplicate check) to handle message states correctly, including potential retries from Vonage for inbound messages. Error handling for database operations is included.Frequently Asked Questions About Vonage SMS in RedwoodJS
What Node.js version does RedwoodJS require for Vonage integration?
RedwoodJS requires Node.js version 20 or higher as of 2025. Check your version with
node -v
and use nvm (Node Version Manager) to switch versions if needed. Vonage's@vonage/server-sdk
works seamlessly with Node.js 20 LTS. Avoid Node.js 21+ for production deployments, as it may cause compatibility issues with certain deployment targets like AWS Lambda.How do I get Vonage API credentials for RedwoodJS?
Log into the Vonage API Dashboard and locate your API Key and Secret on the main page. Create a new Application under "Applications" > "Create a new application", enable the Messages capability, and generate a private key file. Download the
private.key
file immediately and store it in your project root. Copy the Application ID into your.env
file. Purchase an SMS-capable virtual number from "Numbers" > "Buy numbers" and link it to your application.What is E.164 phone number format and why does Vonage require it?
E.164 is the ITU-T international telephone numbering standard that ensures globally unique phone numbers. The format starts with a + sign, followed by the country code (1-3 digits) and subscriber number, with a maximum of 15 digits total (e.g., +14155551234). Vonage requires E.164 format to eliminate routing ambiguity across international SMS networks. Use the regex pattern
/^\+?[1-9]\d{1,14}$/
for basic validation, or implementlibphonenumber-js
for production-grade validation.How do I secure Vonage webhooks in RedwoodJS?
Vonage webhooks in RedwoodJS functions handle incoming SMS and status updates. Secure them by validating the webhook signature using Vonage's JWT verification (check the
Authorization
header), implementing IP whitelist restrictions for Vonage's webhook IPs, using HTTPS endpoints only (required by Vonage), and storing webhook URLs as environment variables. Always return HTTP 200 status within a few seconds to prevent Vonage from retrying failed webhook deliveries.What are SMS character limits with Vonage Messages API?
SMS messages use two encoding types: GSM-7 encoding supports 160 characters per segment (standard Latin characters), while UCS-2 encoding supports 70 characters per segment (required for emojis, Arabic, Chinese, Korean, Japanese, or Cyrillic scripts). Messages exceeding these limits split into multiple segments automatically, with each segment consuming additional credits. Special characters like
| ^ € { } [ ] ~ \
require escape codes in GSM-7, consuming two character positions each.How do I implement SMS marketing campaigns with rate limiting in RedwoodJS?
For marketing campaigns, create a batch processing service that reads recipient lists from your Prisma database and sends messages with controlled rate limiting. Implement exponential backoff using libraries like
p-retry
orasync-retry
to handle Vonage API rate limits (typically 10-20 messages per second). Store campaign status in your database to track delivery states. Use Vonage's status webhooks to update delivery receipts and handle failures. Always comply with TCPA (US), GDPR (Europe), and local SMS marketing regulations requiring user opt-in.Can I send bulk SMS messages to multiple recipients with this RedwoodJS setup?
Yes, extend the
sendSms
service to accept an array of recipients and implement batch processing with rate limiting. Create a GraphQL mutation that queues messages in your database, then use RedwoodJS background jobs (via@redwoodjs/jobs
) to process the queue asynchronously. This prevents timeout issues with large campaigns. Monitor Vonage API rate limits and implement exponential backoff for failed messages. Track campaign progress using Prisma database queries and provide real-time updates via GraphQL subscriptions.How do I handle SMS delivery failures and retries in production?
Vonage sends delivery status updates to your status webhook endpoint. Update the
SmsLog
database record with the status (DELIVERED, FAILED, etc.) when receiving status webhooks. For failed messages, implement a retry strategy: store failed messages in a separate queue, implement exponential backoff (1 minute, 5 minutes, 30 minutes), and limit total retry attempts to 3-5. Log all failures with error details from Vonage's response. Consider implementing dead letter queues for permanently failed messages that require manual review.What security best practices should I follow for Vonage credentials in RedwoodJS?
Never commit
.env
files orprivate.key
files to version control – add them to.gitignore
. Use environment variables for all sensitive credentials (API Key, Secret, Application ID, Private Key). For production, use secrets management services like AWS Secrets Manager, HashiCorp Vault, or Vercel Environment Variables. Rotate credentials every 90 days and audit API usage through the Vonage Dashboard. Set file permissions to 600 for private key files (chmod 600 private.key
on Unix systems). UseVONAGE_PRIVATE_KEY_CONTENT
environment variable for containerized deployments to avoid file system dependencies.