Frequently Asked Questions
Receive SMS messages by configuring a Vonage virtual number to send webhooks to your RedwoodJS application. Set up a webhook endpoint in your RedwoodJS app using a serverless function and expose it to the internet using a tool like ngrok during development. This endpoint will receive inbound message data from Vonage whenever an SMS is sent to your virtual number. Make sure to verify Vonage's webhook signatures for security and store the message data using Prisma.
The Vonage Messages API provides a unified way to send and receive messages across various channels, including SMS, MMS, WhatsApp, and Facebook Messenger. It simplifies the process of integrating messaging into your application by handling the complexities of different platforms.
Vonage uses webhook signatures to ensure the authenticity and integrity of incoming webhook requests. This cryptographic signature, generated using a shared secret, allows your application to verify that the request originated from Vonage and hasn't been tampered with, enhancing security.
Use ngrok during local development to create a secure tunnel that exposes your locally running RedwoodJS server to the public internet. This allows Vonage to deliver webhooks to your application for testing even though it's not yet deployed to a production environment.
Yes, you can send and receive WhatsApp messages using RedwoodJS with the Vonage Messages API. After setting up your Vonage account and linking a WhatsApp-enabled number, you can use the Vonage Node.js Server SDK within your RedwoodJS services to send and receive messages via the API's unified endpoint.
Set up Vonage webhooks by creating a serverless function in your RedwoodJS application. This function will act as the webhook endpoint, receiving inbound and status update messages. Configure your Vonage application's inbound and status URLs to point to this function's publicly accessible URL, usually via ngrok during development.
Prisma serves as the Object-Relational Mapper (ORM) in your RedwoodJS application, facilitating interactions with your database. When integrated with Vonage, Prisma allows you to efficiently store details about incoming and outgoing messages, including sender, recipient, content, timestamps, and message status.
Validate Vonage webhook signatures by using the @vonage/server-sdk's Signature class and your Vonage webhook secret. Compare the signature in the 'X-Vonage-Signature' header with the one generated using your secret and the raw request body. This step is crucial for ensuring the request genuinely comes from Vonage.
Vonage sends status updates for each message, indicating delivery success, failure, or other events. Your RedwoodJS application should handle these status updates by implementing a webhook endpoint that receives these updates and logs the status. You should save these status updates to your database using a dedicated service function.
Handle Vonage API errors within your RedwoodJS services, specifically when making calls to the Vonage API using the SDK. Implement robust error handling using try-catch blocks to catch potential errors during sending messages or other API interactions. Log the error details and implement appropriate retry mechanisms or user notifications.
The RedwoodJS service responsible for sending Vonage messages should be named 'messages' and be located at 'api/src/services/messages/messages.ts'. This service should contain a function that utilizes the Vonage Node.js Server SDK to interact with the Vonage Messages API, allowing your application to send outbound messages.
Store Vonage messages by creating a 'Message' model in your Prisma schema ('api/db/schema.prisma'). Define fields to capture essential message details like ID, direction, channel, sender, recipient, content, status, timestamps, and potential error codes. Use RedwoodJS services to interact with Prisma and perform create, read, update, and delete operations on message records.
Test Vonage webhooks locally by using ngrok to expose your RedwoodJS development server. Configure ngrok to forward requests to your webhook endpoint. Update your Vonage application settings to use the ngrok URL as your webhook URL. Send a test message to your Vonage number and observe logs in ngrok, your RedwoodJS console, and your database to verify functionality.
Using TypeScript in your RedwoodJS project when integrating Vonage enhances type safety and code maintainability. RedwoodJS supports TypeScript out-of-the-box, allowing you to define types for message data and API responses, which helps catch errors during development and improves overall code quality.
Vonage Inbound SMS & WhatsApp with RedwoodJS
Build a RedwoodJS application that receives inbound SMS or WhatsApp messages via the Vonage Messages API and enables two-way conversations. This guide covers project setup, Vonage configuration, secure webhook handling, message storage, reply functionality, and deployment.
You'll Build:
Target Audience: JavaScript and Node.js developers. RedwoodJS experience helps but isn't required – this guide covers setup from scratch. Assumes familiarity with basic API concepts and webhooks.
Technology Stack:
ngrok
: Expose local development servers to the internet for webhook testing (development only, not deployed)System Architecture:
Prerequisites:
npm install -g redwoodjs-cli
. This guide assumes RedwoodJS v7.0.0 or greater. Note: While you install the RedwoodJS CLI globally using npm, project-level commands and dependency management use Yarn, following standard RedwoodJS conventions.ngrok
: Download and install from ngrok.com. Authenticate your client for longer sessions. Important: Vonage webhooks require HTTPS; ngrok provides this automatically.1. Setting Up the RedwoodJS Project
Create a new RedwoodJS application with TypeScript for enhanced type safety.
Create the Redwood App: Open your terminal and run:
This scaffolds a new RedwoodJS project in
vonage-redwood
with TypeScript enabled.Navigate into the Project:
Initialize Environment Variables: RedwoodJS uses
.env
for environment variables. Create one in the project root:Populate this file later with Vonage credentials. Redwood automatically loads variables from
.env
intoprocess.env
.Install Vonage SDK: Install the Vonage Node.js Server SDK to interact with the API and validate webhooks in the
api
workspace:@vonage/server-sdk
: The main SDK for API calls (v3.25.1 as of September 2024).@vonage/jwt
: Required for generating JWTs for features like Secure Inbound Media or JWT authentication for API calls (basic key/secret auth suffices for sending messages).Verify Setup: Start the development server to ensure the basic Redwood app works:
You'll see output indicating both frontend (web) and backend (api) servers running on
http://localhost:8910
andhttp://localhost:8911
respectively. Stop the server for now (Ctrl+C
).2. Configuring Vonage
Configure Vonage to send webhook events to your application before writing code.
Log in to Vonage Dashboard: Access your Vonage API Dashboard.
Retrieve API Credentials:
.env
file:YOUR_API_KEY
andYOUR_API_SECRET
with your actual credentials)Create a Vonage Application: Vonage Applications group settings like webhook URLs and capabilities.
ngrok
..env
file:YOUR_SIGNATURE_SECRET
with the generated secret).env
file:YOUR_APPLICATION_ID
with your actual Application ID)private.key
file and save it securely (e.g., in your project root, ensuring it's added to.gitignore
). Add its path to.env
:Link Your Vonage Number:
.env
:YOUR_VONAGE_NUMBER
with your linked number)Configure Webhooks (Requires
ngrok
): Configure the actual webhook URLs in the "Local Development & Testing" section once you have your Redwood function endpoint and anngrok
tunnel running.3. Creating the Database Schema
Define a Prisma model to store received and sent messages.
Define the
Message
Model: Openapi/db/schema.prisma
and add the following model:vonageMessageId
: Crucial for correlating messages and status updates.direction
: Distinguishes between incoming and outgoing messages.errorCode
,errorReason
: Store failure details from status webhooks.@updatedAt
: Prisma directive that automatically sets this field to the current timestamp on every update.@@index
: Performance optimization for common query patterns. ThevonageMessageId
already has a unique constraint which creates an index, but explicit indexes ondirection
andcreatedAt
improve filtering performance.Apply Migrations: Run the Prisma migrate command to create/update the database schema and generate the Prisma client.
Follow the prompts (e.g., provide a name for the migration like "add message model"). This creates/updates the
Message
table in your development database (SQLite by default, unless configured otherwise).4. Implementing the RedwoodJS Webhook Handler
Create the core component that receives data from Vonage. Build a RedwoodJS Function – a serverless function deployed alongside your API.
Generate the Function: Use the Redwood CLI to generate a new function called
vonageWebhook
.This creates
api/src/functions/vonageWebhook.ts
. The--no-graphql
flag indicates it's a standard HTTP endpoint, not part of the GraphQL API.Implement the Handler Logic: Open
api/src/functions/vonageWebhook.ts
and replace its contents with the following:Explanation:
VONAGE_WEBHOOK_SECRET
(Signature Secret from the Vonage Application settings) and the@vonage/server-sdk
'sSignature
class. Important: The signature check requires the raw, unparsed request body string and the request headers.message_uuid
,status
,message.content.type
). Refine this based on the specific Vonage channels and events you handle.createMessage
,updateMessageStatus
) and sending replies (sendVonageMessage
) to Redwood Services (created next). This keeps the function focused on handling the HTTP request/response lifecycle.try...catch
blocks log errors using Redwood's built-inlogger
. For production, implement more robust error tracking. Error details from status webhooks pass to the service.200 OK
response within 3–5 seconds to acknowledge receipt. Failure to respond quickly leads to Vonage retrying the webhook multiple times (up to 3 retry attempts with exponential backoff) and eventually disabling it. Handle time-consuming processing after sending the response or asynchronously.sendVonageMessage
service function.5. Implementing RedwoodJS Services
Services encapsulate business logic, including database interactions and calls to external APIs like Vonage.
Generate the Service:
This creates
api/src/services/messages/messages.ts
and related test/scenario files.Implement Service Logic: Open
api/src/services/messages/messages.ts
and add the necessary functions:Explanation:
.env
. This happens only once. Error handling includes cases where credentials are missing or invalid.createMessage
: Straightforward function usingdb.message.create
to save message data.updateMessageStatus
: Usesdb.message.update
to find a message by its uniquevonageMessageId
and update itsstatus
,errorCode
, anderrorReason
. Includes basic handling for cases where the message might not be found (e.g., if the status webhook arrives before the inbound message webhook is fully processed). EnsureserrorCode
is stored as a string.sendVonageMessage
:vonage.messages.send()
which is the unified endpoint for various channels.message_type: "text"
,to
,from
(your Vonage number),text
, andchannel
.messageUuid
.outbound
record in the database immediately with status 'submitted'. The actual delivery status arrives later via the Status webhook.6. Local Development & Testing with
ngrok
Expose your Redwood development server to the public internet using
ngrok
to test webhooks locally.Start Redwood Dev Server: Open a terminal in your project root and run:
Note the API server port (usually
8911
).Start
ngrok
: Open a second terminal and runngrok
, pointing it to your Redwood API server's port:ngrok
displays forwarding URLs (e.g.,https://<random-subdomain>.ngrok-free.app
). Copy thehttps
URL.ngrok
HTTPS URL plus the path to your function:/api/vonageWebhook
.Update Vonage Webhook URLs:
ngrok
HTTPS URL including the path into both the Inbound URL and Status URL fields. The final URL should look like:https://<random-subdomain>.ngrok-free.app/api/vonageWebhook
.Send a Test Message:
Verify:
ngrok
Console: Check thengrok
terminal window for aPOST /api/vonageWebhook
request with a200 OK
response.yarn rw dev
for logs from thevonageWebhook
function:createMessage
service.createMessage
for the outbound record.Message
table (e.g., usingyarn rw prisma studio
) for a new record for the inbound message (and potentially an outbound one if reply is enabled).Troubleshooting
ngrok
:ngrok
URL in Vonage (HTTPS, correct path/api/vonageWebhook
).ngrok
is still running. Freengrok
sessions time out.ngrok
.logger.debug
) and compare them carefully with what Vonage expects. EnsureVONAGE_WEBHOOK_SECRET
is correct. Sometimes proxies (includingngrok
under certain configurations) can subtly alter headers or body encoding.7. Security Considerations
VONAGE_WEBHOOK_SECRET
as shown in the function handler. Never disable this check in production..env
file and ensure it's in your.gitignore
. Use your deployment platform's secret management for production.8. Error Handling and Logging
logger
(import { logger } from 'src/lib/logger'
). Log key events (webhook received, signature verified, message processed/sent) and errors. Use appropriate log levels (info
,warn
,error
,debug
).sendVonageMessage
) and log relevant details (often found inerror.response.data
orerror.message
).createMessage
,updateMessageStatus
). Log details and decide how to respond (e.g., retry, notify admin, ignore if non-critical like a duplicate status update).errorCode
anderrorReason
. Implement logic to handle specific error codes from Vonage if needed (e.g., number blocked, invalid number).async-retry
. However, avoid blocking the webhook response. If sending fails, log it and potentially queue it for later retry via a background job system (like RedwoodJS Background Jobs).9. Deployment
Deploy your RedwoodJS application following these key steps for Vonage integration:
Choose a Platform: Select a deployment provider supporting Node.js applications (e.g., Vercel, Netlify, Render, Fly.io, AWS Lambda). RedwoodJS provides deployment guides for popular platforms.
Function URL Pattern: After deployment, access your webhook function at
https://your-domain.com/.redwood/functions/vonageWebhook
(or/api/vonageWebhook
depending on your deployment configuration). Update your Vonage Application's Inbound URL and Status URL with this production URL.Configure Environment Variables: Set these environment variables securely in your deployment platform's settings:
DATABASE_URL
(pointing to your production database)VONAGE_API_KEY
VONAGE_API_SECRET
VONAGE_WEBHOOK_SECRET
VONAGE_APPLICATION_ID
VONAGE_NUMBER
VONAGE_PRIVATE_KEY_PATH
(if using JWT/Secure Media): For production, store the content of the private key directly in a secure environment variable (e.g.,VONAGE_PRIVATE_KEY_CONTENT
base64 encoded) instead of deploying the key file. This avoids file system complexities and enhances security. Adjust your SDK initialization logic (Section 5.2) to read the key content from the environment variable if the path variable isn't set. Example:SESSION_SECRET
if using auth). Check RedwoodJS deployment docs for specifics.Run Tests and Verification: Ensure production webhooks function correctly by:
Production Monitoring: Set up monitoring and alerting for:
Frequently Asked Questions
How do I receive inbound SMS messages with Vonage and RedwoodJS? Create a RedwoodJS serverless function to handle webhook POST requests from Vonage. Configure your Vonage Application's Inbound URL to point to your function endpoint (e.g.,
https://your-domain.com/.redwood/functions/vonageWebhook
). The function validates the signature, parses the message payload, and stores it in your Prisma database.What is webhook signature validation and why is it critical? Webhook signature validation verifies that incoming webhook requests genuinely came from Vonage and haven't been tampered with. Vonage signs each webhook using SHA-256 HMAC with your Signature Secret. Always validate signatures using the
@vonage/server-sdk
'sSignature
class. Never disable this check in production – it prevents unauthorized access and malicious payloads.What's the difference between Vonage API Secret and Signature Secret? The API Secret (
VONAGE_API_SECRET
) authenticates your API calls when sending messages to Vonage. The Signature Secret (VONAGE_WEBHOOK_SECRET
) validates incoming webhooks from Vonage to your application. These are completely different values – do not confuse them. Generate the Signature Secret in your Vonage Application settings under "Signed webhooks."How do I handle both SMS and WhatsApp messages in the same webhook? The webhook payload includes a
channel
field indicating the message type (sms
,whatsapp
, etc.). Store this in your database and use it when sending replies. The webhook handler code differentiates message types automatically. For WhatsApp, ensure your Vonage number has WhatsApp Business API enabled.What Node.js version does RedwoodJS v7 require? RedwoodJS v7+ requires exactly Node.js 20.x (not 18.x or 19.x). Use
node -v
to verify your version. Install Node 20.x via nodejs.org or use a version manager likenvm
. Additionally, use Yarn >=1.22.21 (Classic Yarn 1.x).How do I test Vonage webhooks locally during development? Use
ngrok
to expose your local RedwoodJS API server (port 8911) to the internet with HTTPS. Runngrok http 8911
to get a public HTTPS URL. Configure this URL plus/api/vonageWebhook
in your Vonage Application's Inbound URL and Status URL settings. Send a test SMS to your Vonage number and verify the webhook arrives in your local logs.What database indexes should I create for message storage? Create these indexes in your Prisma schema: 1)
@@index([vonageMessageId])
for fast UUID lookups (though@unique
already creates an index), 2)@@index([direction, createdAt])
for filtering by inbound/outbound and sorting by timestamp. These optimize common query patterns for message retrieval and status updates.How do I send automatic replies to inbound messages? In your webhook handler function, after storing the inbound message, call the
sendVonageMessage
service function with the sender's number as theto
parameter and your Vonage number as thefrom
parameter. Use the samechannel
(SMS or WhatsApp) for the reply. The example code includes a commented-out auto-reply section you can enable.What happens if my webhook doesn't respond within the timeout? Vonage requires a
200 OK
response within 3–5 seconds. If your endpoint doesn't respond in time, Vonage retries the webhook up to 3 times with exponential backoff. After exhausting retries, Vonage disables the webhook URL. Process time-consuming operations asynchronously after sending the 200 response to avoid timeouts.How do I handle Vonage error codes for failed messages? Status webhooks include
errorCode
anderrorReason
fields when messages fail. Store these in your database via theupdateMessageStatus
service. Common error codes: 1320 (invalid number format), 1360 (invalid message parameters), 1380 (not permitted/account restriction), 1390 (message rejected by carrier), 1420 (insufficient funds). Implement logic to handle specific codes as needed.Should I store the private key file in production deployments? No. Store the private key content in an environment variable (base64 encoded) instead of deploying the file. Example:
VONAGE_PRIVATE_KEY_CONTENT
. Read it withBuffer.from(process.env.VONAGE_PRIVATE_KEY_CONTENT, 'base64')
in your Auth initialization. This avoids file system complexities and improves security across deployment platforms.What's the Prisma error code P2025 in status update failures? P2025 means "Record not found." This occurs when a status webhook arrives before the corresponding inbound message webhook is processed (race condition). Handle this gracefully by logging a warning and returning null rather than throwing an error. The status update will succeed when retried or can be ignored if the message doesn't exist in your database yet.
Next Steps
You now have a fully functional RedwoodJS application handling inbound Vonage messages. Consider these enhancements:
Explore the RedwoodJS documentation and Vonage Messages API guides for more advanced features.