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
Learn how to build a production-ready RedwoodJS inbound SMS webhook that receives SMS and WhatsApp messages via the Vonage Messages API. This comprehensive guide covers webhook setup, signature validation, database integration, and two-way messaging implementation for customer engagement applications.
What You'll Build:
Target Audience: JavaScript and Node.js developers building messaging applications. RedwoodJS experience helps but isn't required – this guide covers setup from scratch. Assumes familiarity with basic REST API concepts and webhook patterns.
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-redwoodwith TypeScript enabled.Navigate into the Project:
Initialize Environment Variables: RedwoodJS uses
.envfor environment variables. Create one in the project root:Populate this file later with Vonage credentials. Redwood automatically loads variables from
.envintoprocess.env.Install Vonage SDK: Install the Vonage Node.js Server SDK to interact with the API and validate webhooks in the
apiworkspace:@vonage/server-sdk: The main SDK for API calls (v3.25.1 as of September 2024; verify current version at npm).@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:8910andhttp://localhost:8911respectively. Stop the server for now (Ctrl+C).2. Configuring Vonage Webhook Settings
Configure Vonage to send webhook events to your RedwoodJS application before writing code. This section covers essential webhook signature validation setup for secure inbound message handling.
Log in to Vonage Dashboard: Access your Vonage API Dashboard.
Retrieve API Credentials:
.envfile:YOUR_API_KEYandYOUR_API_SECRETwith your actual credentials)Create a Vonage Application: Vonage Applications group settings like webhook URLs and capabilities.
ngrok..envfile:YOUR_SIGNATURE_SECRETwith the generated secret).envfile:YOUR_APPLICATION_IDwith your actual Application ID)private.keyfile and save it securely (e.g., in your project root, ensuring you add it to.gitignore). Add its path to.env:Link Your Vonage Number:
.env:YOUR_VONAGE_NUMBERwith 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 anngroktunnel running.3. Creating the Database Schema with Prisma
Define a Prisma model to store received and sent messages for your inbound SMS webhook application.
Define the
MessageModel: Openapi/db/schema.prismaand 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 (see Vonage error codes).@updatedAt: Prisma directive that automatically sets this field to the current timestamp on every update.@@index: Performance optimization for common query patterns. ThevonageMessageIdalready has a unique constraint which creates an index, but explicit indexes ondirectionandcreatedAtimprove 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
Messagetable in your development database (SQLite by default, unless you configured otherwise).4. Implementing the RedwoodJS Webhook Handler
Create the core webhook handler component that receives inbound SMS and WhatsApp messages 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-graphqlflag indicates it's a standard HTTP endpoint, not part of the GraphQL API.Implement the Handler Logic: Open
api/src/functions/vonageWebhook.tsand replace its contents with the following:Explanation:
VONAGE_WEBHOOK_SECRET(Signature Secret from the Vonage Application settings) and the@vonage/server-sdk'sSignatureclass. 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...catchblocks log errors using Redwood's built-inlogger. For production, implement more robust error tracking. Error details from status webhooks pass to the service.200 OKresponse within specific timeouts to acknowledge receipt. The timeout for establishing the HTTP connection is 3 seconds, and the timeout for receiving a response once the connection establishes is 15 seconds (source). Failure to respond within these timeouts causes Vonage to retry the webhook. Vonage retries webhooks that respond with HTTP 429 or 5xx codes using exponential backoff starting at 5 seconds, doubling with each retry up to 15 minutes maximum, for up to 24 hours before discarding them (source). Handle time-consuming processing after sending the response or asynchronously.sendVonageMessageservice function.5. Implementing RedwoodJS Services for Two-Way Messaging
Services encapsulate business logic, including database interactions and calls to external APIs like Vonage. This section implements the two-way messaging functionality.
Generate the Service:
This creates
api/src/services/messages/messages.tsand related test/scenario files.Implement Service Logic: Open
api/src/services/messages/messages.tsand 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.createto save message data.updateMessageStatus: Usesdb.message.updateto find a message by its uniquevonageMessageIdand update itsstatus,errorCode, anderrorReason. Includes basic handling for cases where the message might not exist (e.g., if the status webhook arrives before the inbound message webhook processes fully). EnsureserrorCodestores 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.outboundrecord in the database immediately with status 'submitted'. The actual delivery status arrives later via the Status webhook.6. Local Development & Testing with
ngrokExpose your Redwood development server to the public internet using
ngrokto test your inbound SMS webhook 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:ngrokdisplays forwarding URLs (e.g.,https://<random-subdomain>.ngrok-free.app). Copy thehttpsURL.ngrokHTTPS URL plus the path to your function:/api/vonageWebhook.Update Vonage Webhook URLs:
ngrokHTTPS 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:
ngrokConsole: Check thengrokterminal window for aPOST /api/vonageWebhookrequest with a200 OKresponse.yarn rw devfor logs from thevonageWebhookfunction:createMessageservice.createMessagefor the outbound record.Messagetable (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:ngrokURL in Vonage (HTTPS, correct path/api/vonageWebhook).ngrokis still running. Freengroksessions time out.ngrok.logger.debug) and compare them carefully with what Vonage expects. EnsureVONAGE_WEBHOOK_SECRETis correct. Sometimes proxies (includingngrokunder certain configurations) can subtly alter headers or body encoding.7. Security Considerations for Webhook Signature Validation
VONAGE_WEBHOOK_SECRETas shown in the function handler. Never disable this check in production. Vonage uses SHA-256 HMAC for signature generation..envfile and ensure it's in your.gitignore. Use your deployment platform's secret management for production.ngrokfor local development and ensure production deployments use HTTPS.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.dataorerror.message). Refer to Vonage Messages API error codes for detailed descriptions.createMessage,updateMessageStatus). Log details and decide how to respond (e.g., retry, notify admin, or ignore if non-critical like a duplicate status update).errorCodeanderrorReason. 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. Production Deployment
Deploy your RedwoodJS inbound SMS webhook 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/vonageWebhookdepending 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_KEYVONAGE_API_SECRETVONAGE_WEBHOOK_SECRETVONAGE_APPLICATION_IDVONAGE_NUMBERVONAGE_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_CONTENTbase64 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_SECRETif 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'sSignatureclass. 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 implement two-way SMS messaging with Vonage? Store the sender's number from inbound webhooks and use the
sendVonageMessageservice function to reply. Set the sender's number as thetoparameter and your Vonage number asfrom. The webhook payload includes achannelfield – use the same channel (SMS or WhatsApp) for replies to ensure proper delivery.How do I handle both SMS and WhatsApp messages in the same webhook? The webhook payload includes a
channelfield 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 -vto 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
ngrokto expose your local RedwoodJS API server (port 8911) to the internet with HTTPS. Runngrok http 8911to get a public HTTPS URL. Configure this URL plus/api/vonageWebhookin 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@uniquealready 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
sendVonageMessageservice function with the sender's number as thetoparameter and your Vonage number as thefromparameter. Use the samechannel(SMS or WhatsApp) for the reply. The example code includes a commented-out auto-reply section you can enable.What are Vonage's webhook timeout and retry policies? Vonage requires a
200 OKresponse within 3 seconds for connection establishment and 15 seconds for the actual response. If your endpoint responds with HTTP 429 or 5xx codes, Vonage retries using exponential backoff starting at 5 seconds, doubling with each retry up to 15 minutes maximum, continuing for up to 24 hours before discarding the message. 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
errorCodeanderrorReasonfields when messages fail. Store these in your database via theupdateMessageStatusservice. Common error codes: 1020 (invalid params), 1170 (invalid/missing phone number), 1320 (message already sent), 1360 (TTL expired), 1380 (invalid resource), 1420 (invalid sender), 1430 (invalid recipient). Implement logic to handle specific codes as needed. See the complete list at Vonage Messages API errors.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 processes (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 inbound SMS webhook application handling two-way messaging with Vonage. Consider these enhancements:
Explore the RedwoodJS documentation and Vonage Messages API guides for more advanced features.