Frequently Asked Questions
Use Next.js API routes to create a webhook endpoint. This endpoint will receive incoming SMS messages forwarded from your MessageBird virtual number via the MessageBird Flow Builder. This allows your Next.js application to process incoming texts and react accordingly.
MessageBird Flow Builder is used to route incoming SMS messages to your Next.js webhook. You configure the Flow Builder to trigger the webhook URL with the message data whenever an SMS is received on your designated MessageBird virtual number.
Next.js API routes provide a serverless function environment ideal for handling webhooks. They offer an easy way to create server-side logic without managing a separate backend server, simplifying development and deployment, especially on Vercel.
ngrok is essential during local development. It creates a public tunnel to your local server so MessageBird can reach your webhook endpoint while you are testing. For production, deploy to Vercel and use the Vercel app URL.
First, create a Next.js API route. Then, in your MessageBird Flow Builder, add an HTTP endpoint step. Paste your ngrok HTTPS URL (for local development) or your Vercel app URL (for production) along with the API endpoint path and your secret as a query parameter.
Use ngrok to create a public URL for your local development server. Configure MessageBird Flow Builder with this URL, then send an SMS to your MessageBird number. Observe logs in your Next.js terminal for successful webhook delivery. Also, you can simulate MessageBird POST requests with curl. Check the logs for validation success and received payloads.
This secret, stored in your .env.local file, adds security to your webhook. By including it as a query parameter in your webhook URL, you ensure only requests with the correct secret, presumably from MessageBird, are processed by your application.
The webhook sends data as application/x-www-form-urlencoded. Key fields include 'originator' (sender's number), 'payload' (the message content), 'recipient' (your virtual number), 'messageId', and 'createdDatetime'. Logging the full received data is recommended.
Use a strong, randomly generated secret as a query parameter in the URL. Verify this secret on your Next.js API route to ensure only MessageBird is triggering the webhook. Also, implement input sanitization, rate limiting, and possibly timestamp checks.
MessageBird typically handles long SMS (concatenated messages) automatically. You should receive the entire reassembled message in the 'payload' field of your webhook. Testing long messages is recommended.
Avoid using SQLite in production on serverless platforms like Vercel due to ephemeral file systems. Use a managed database service like Vercel Postgres, PlanetScale, or Supabase, especially if you need persistent data storage. For local development and testing, SQLite is convenient.
Prisma is an Object-Relational Mapper (ORM) that simplifies database interactions. It helps define your database schema, perform migrations, and access data easily within your API route handler. It's particularly useful for modeling and handling database relations for complex systems.
The guide recommends Prisma with SQLite for development and testing. For production, use a persistent, managed database solution. In your webhook handler, after validating the request, use Prisma Client to create a new database entry with the message details.
Implement thorough error handling and logging in your webhook handler. Catch potential errors, log them, and return appropriate status codes. Consider using a structured logging library like 'pino'. Critically, try to always return 200 OK to MessageBird quickly, even on errors, unless you specifically want MessageBird to retry.
.env.local
Build a Next.js application that receives inbound SMS messages sent to your MessageBird virtual number. Create a webhook endpoint using Next.js API routes, configure MessageBird's Flow Builder to forward incoming messages, and deploy to Vercel.
Use this to build automated responders, two-way chat systems, notification triggers, or data collection services via text message.
What You'll Build
Technologies Used
System Architecture
(Ensure your publishing platform supports Mermaid diagram rendering)
Prerequisites
ngrok
installed globally or available vianpx
for local testing.Note on Next.js Versions:
This guide is compatible with Next.js 14 and 15. If using Next.js 15, be aware that request-specific APIs (headers, cookies, params, searchParams) are now asynchronous, which may require adjustments if extending this implementation beyond the webhook handler shown.
Final Outcome
A deployed Next.js application with a publicly accessible API endpoint that logs incoming SMS messages sent to your configured MessageBird number. The endpoint will be secured with a basic shared secret.
1. Setting up the Project
Create a new Next.js application with the required configuration.
1.1 Create Next.js App:
Run this command in your terminal, replacing
messagebird-inbound-app
with your project name:Select these options when prompted:
src/
directory? Yes1.2 Navigate to Project Directory:
1.3 Project Structure:
Your project structure with
src/
directory and App Router:1.4 Environment Variables:
Create
.env.local
in your project root to store sensitive configuration. Never commit this file to version control. It should be in.gitignore
by default.Generate a strong random secret:
Add the secret to
.env.local
:Configuration Purpose:
create-next-app
: Scaffolds a modern Next.js application with best practices..env.local
: Stores environment-specific variables securely, keeping secrets out of your codebase.MESSAGEBIRD_WEBHOOK_SECRET
verifies that webhook requests originate from MessageBird.2. Implementing the Webhook Handler
Create an API route in your Next.js application to receive webhook calls from MessageBird.
2.1 Create the API Route File:
Inside the
src/app/api/
directory, create a new folder namedmessagebird-webhook
. Inside this folder, create a file namedroute.js
.Directory Structure:
src/app/api/messagebird-webhook/route.js
2.2 Implement the Handler:
Paste the following code into
src/app/api/messagebird-webhook/route.js
:Code Explanation:
NextResponse
: Used for sending responses from API routes.POST
Function: This is the primary handler for incoming webhook requests from MessageBird, which uses thePOST
HTTP method for SMS webhooks.MESSAGEBIRD_WEBHOOK_SECRET
is loaded correctly. If not, it logs an error but returns a200 OK
to prevent MessageBird from endlessly retrying due to a server configuration issue. You must fix the environment variable setup if this occurs.secret
query parameter from the incoming request URL (e.g.,https://yourapp.com/api/messagebird-webhook?secret=your_secret
).providedSecret
with the one stored in your environment variables (process.env.MESSAGEBIRD_WEBHOOK_SECRET
).403 Forbidden
status, rejecting the request.application/x-www-form-urlencoded
.request.formData()
parses this into aFormData
object, which we convert to a plain JavaScript object (messageData
).originator
,payload
,recipient
, etc.).originator
,payload
). If missing, it returns a400 Bad Request
.200 OK
response usingNextResponse
. MessageBird expects this to confirm successful receipt. The response body is ignored by MessageBird for SMS webhooks.try...catch
block catches any unexpected errors during processing, logs them, and returns a500 Internal Server Error
.GET
Function (Optional): A simple handler forGET
requests is included. This isn't used by MessageBird for SMS webhooks but can be useful for manually checking if the endpoint is reachable or for setting up simple external health checks.3. API Endpoint Details (Webhook)
While not a traditional user-facing API, the webhook endpoint acts as an API for MessageBird.
/api/messagebird-webhook
POST
?secret=YOUR_SECRET
).application/x-www-form-urlencoded
originator
: String (Sender's phone number in international format)payload
: String (The SMS message content)recipient
: String (Your MessageBird virtual number)messageId
: String (Unique MessageBird ID)createdDatetime
: String (ISO 8601 timestamp)200 OK
(Empty body or simple text confirmation)403 Forbidden
: Invalid or missingsecret
query parameter.400 Bad Request
: Missing required fields (originator
orpayload
).500 Internal Server Error
: Unexpected server-side error during processing.200 OK
(with server-side error logged): IfMESSAGEBIRD_WEBHOOK_SECRET
is not configured.Testing with
curl
(Simulating MessageBird):You'll need your local server running (
npm run dev
) andngrok
exposing it (see Section 4). ReplaceYOUR_NGROK_URL
andYOUR_SECRET
accordingly. Use a realistic past or generic date forcreatedDatetime
.Note on
curl
quoting:The command above uses double quotes for data fields and the URL. This generally works in bash/zsh. If you encounter issues in other shells or if your secret contains special shell characters, try putting single quotes around the entire URL:
'YOUR_NGROK_URL/api/messagebird-webhook?secret=YOUR_SECRET'
.Check your terminal running
npm run dev
for the log output from theroute.js
handler.4. Integrating with MessageBird
Configure MessageBird to send incoming SMS messages to your Next.js application.
4.1 Local Development Setup (ngrok):
MessageBird needs a publicly accessible URL to send webhooks to. During development, your local machine isn't typically accessible. Use
ngrok
to create a secure tunnel.Start your Next.js dev server:
It usually runs on
http://localhost:3000
.Start ngrok:
Open another terminal window and run:
Copy the ngrok URL:
ngrok
will display forwarding URLs. Copy thehttps
URL (e.g.,https://random-subdomain.ngrok-free.app
). This is your temporary public URL.4.2 Configure MessageBird Flow Builder:
Log in to your MessageBird Dashboard.
Navigate to Flow Builder from the left-hand menu.
Click Create new flow > Create Custom Flow.
Give your flow a name (e.g., "Next.js Inbound SMS").
Choose SMS as the trigger. Click Next.
Configure the Trigger:
Add the Webhook Step:
Configure the HTTP Endpoint Step:
https
URL (from step 4.1.3) and append the API route path and the secret query parameter:https://your-random-subdomain.ngrok-free.app/api/messagebird-webhook?secret=your_generated_secret_string_here
(Replace the URL and secret with your actual values from.env.local
)Publish the Flow:
Click the Publish button in the top-right corner. Confirm the publication.
Diagram of Flow Builder Setup:
(Ensure your publishing platform supports Mermaid diagram rendering)
Explanation of Environment Variables:
MESSAGEBIRD_WEBHOOK_SECRET
(used in.env.local
and later in Vercel): This secret string is added as a query parameter to the webhook URL configured in Flow Builder. The Next.js API route verifies this secret upon receiving a request, ensuring that only requests knowing the secret (presumably only MessageBird via your Flow Builder config) are processed.Testing Local Integration:
Send an SMS message from your phone to the MessageBird virtual number you configured in Flow Builder.
npm run dev
. You should see theconsole.log
output from yourroute.js
file, including the "Webhook secret validated successfully" message and the "Received MessageBird Payload".ngrok
. You should see an incomingPOST
request to/api/messagebird-webhook
with a200 OK
response status.If it works, your local setup is correctly receiving messages!
5. Implementing Error Handling and Logging
The
route.js
file already includes basic error handling and logging:MESSAGEBIRD_WEBHOOK_SECRET
. Logs error server-side, returns200 OK
to MessageBird.403 Forbidden
.400 Bad Request
.try...catch
block captures unexpected errors during processing. Logs error, returns500 Internal Server Error
.console.log
,console.warn
, andconsole.error
for different levels of information. The entire payload is logged for debugging.Improvements for Production:
Structured Logging:
Use a dedicated logging library like
pino
for structured JSON logs, which are easier to parse and analyze in log management systems.Adapt
route.js
to usepino
.Error Tracking Services:
Integrate with services like Sentry or Bugsnag to capture, report, and analyze errors automatically.
Retry Mechanisms (Application Level):
If your processing logic involves external services that might fail temporarily, implement retry logic (e.g., using
async-retry
) within your handler after sending the200 OK
to MessageBird, or preferably by pushing the message to a queue (like Redis, RabbitMQ, or AWS SQS) for background processing with retries. Do not delay the200 OK
response to MessageBird.Testing Error Scenarios:
Invalid Secret:
Send a
curl
request (Section 3) with the wrong or nosecret
parameter. Expect a403 Forbidden
.Missing Payload:
Send a
curl
request without theoriginator
orpayload
form data fields. Expect a400 Bad Request
.Simulate Internal Error:
Temporarily add
throw new Error('Simulated processing error');
inside thetry
block before the finalreturn
. Send a valid request. Expect a500 Internal Server Error
response and an error log in the console.6. Creating a Database Schema and Data Layer (Optional)
For most real-world applications, you'll want to store incoming messages. Let's add basic persistence using Prisma and SQLite.
⚠️ Critical Production Warning – SQLite on Vercel:
SQLite is NOT supported for production deployments on Vercel or other serverless platforms. This is a fundamental architectural incompatibility, not a configuration issue:
For Production on Vercel (as of 2025):
The following steps demonstrate SQLite for local development and learning purposes only. Replace with a proper database service before deploying to production.
6.1 Install Prisma:
6.2 Initialize Prisma:
This creates a
prisma
directory with aschema.prisma
file and updates.env.local
with aDATABASE_URL
.6.3 Define Schema:
Open
prisma/schema.prisma
and define a model for incoming messages:6.4 Create Initial Migration:
This creates the SQLite database file (e.g.,
prisma/dev.db
based on yourDATABASE_URL
) and theIncomingMessage
table. Make sureprisma/dev.db
is added to your.gitignore
.6.5 Implement Data Layer:
Create a utility function to instantiate and share the Prisma client instance.
6.6 Update Webhook Handler to Save Message:
Modify
src/app/api/messagebird-webhook/route.js
:Key Changes:
@/lib/prisma
).try...catch
block specifically for the database operation (prisma.incomingMessage.create
).prisma.incomingMessage.create
to save the relevant data extracted frommessageData
.P2002
) specifically on themessageBirdId
field. If it's a duplicate, log a warning and return200 OK
.messageId
to the required fields check.200 OK
response is now sent after the database operation attempt (unless it was a handled duplicate).Retest by sending an SMS. You should see the "saved to DB" log message in your development console. You can inspect the
prisma/dev.db
file (using tools like DB Browser for SQLite) to see the saved record.7. Adding Security Features
Beyond the shared secret, consider these:
Input Sanitization:
While Prisma helps prevent SQL injection, if you use the
payload
elsewhere (e.g., displaying in a UI), sanitize it to prevent Cross-Site Scripting (XSS) attacks (e.g., using libraries likedompurify
).Rate Limiting:
Implement rate limiting on the API route, especially if processing is resource-intensive, to prevent abuse or accidental loops. Libraries like
rate-limiter-flexible
or platform features (like Vercel's built-in IP rate limiting) can be used.Timestamp Verification (Optional):
Check the
createdDatetime
from MessageBird against the current server time. Reject requests that are too old (e.g., > 5 minutes) to mitigate simple replay attacks, but be cautious about potential clock skew between MessageBird's servers and yours.HTTPS Enforcement:
Vercel deployments enforce HTTPS by default. Always ensure your
ngrok
tunnel useshttps
and your production webhook URL useshttps
. Never use plainhttp
for webhooks handling sensitive data or secrets.Signature Verification (Advanced):
MessageBird can sign requests for certain webhook types (check their documentation for specifics, as it might not apply to basic Flow Builder SMS webhooks). If available and configurable for your setup, verifying a cryptographic signature (e.g., HMAC-SHA256) using a separate secret provides stronger assurance of authenticity than just a secret in the URL.
Testing Security:
curl
or a similar tool to send requests without thesecret
, with an incorrectsecret
, or with malformed data to confirm your validation logic returns403 Forbidden
or400 Bad Request
appropriately.8. Handling Special Cases
Character Encoding:
SMS messages use specific encodings (
GSM-7
orUCS-2
). MessageBird typically handles the decoding and provides the messagepayload
as a standard UTF-8 string in the webhook. Be aware of this if your application needs to interact with external systems expecting specific SMS encodings.Concatenated Messages (Long SMS):
MessageBird usually reassembles long SMS messages (split into multiple parts over the air) before triggering the webhook. The
payload
you receive should contain the full message content. Test with messages longer than 160 characters (GSM-7) or 70 characters (UCS-2) to confirm this behavior.Message Ordering:
Webhooks are delivered over HTTP and network conditions or MessageBird retries can mean they don't necessarily arrive in the exact order the original SMS messages were sent or received by MessageBird. If strict ordering is critical for your application logic, use timestamps (
createdDatetime
from MessageBird or yourreceivedAt
timestamp) to order messages during processing or retrieval.Non-Text Content (MMS):
This guide focuses purely on standard SMS text messages. If you need to receive and process MMS messages (containing images, videos, etc.), the webhook payload structure and potentially the Flow Builder configuration will differ significantly. Consult MessageBird's specific MMS documentation.
Internationalization:
Phone numbers (
originator
,recipient
) are generally provided in the standard E.164 format (e.g.,+14155552671
). Ensure your system stores and processes these numbers correctly. If your application logic depends on the content (payload
) of the message (e.g., language detection, keyword analysis), consider internationalization and localization requirements.9. Implementing Performance Optimizations
For a simple webhook receiver primarily logging or storing data, performance is usually manageable unless facing very high message volumes. Key considerations include:
Vercel Serverless Function Limits (as of 2025):
Understanding platform constraints helps you design within boundaries and avoid runtime failures:
Source: Vercel official documentation (https://vercel.com/docs/functions/limitations, verified October 2025)
Fast Responses:
The absolute most critical performance factor is responding with
200 OK
to MessageBird as quickly as possible (ideally within 1-2 seconds). Defer any potentially slow or unreliable operations (like complex database queries, external API calls, sending replies) until after the response has been sent.Asynchronous Processing / Queues:
For tasks that take longer than a few hundred milliseconds or involve external dependencies, push the incoming message data (or just an ID) onto a message queue (e.g., Redis, RabbitMQ, AWS SQS, Vercel KV Queue) immediately after validation. Have separate background workers process messages from the queue. This decouples message reception from processing, ensuring fast webhook responses.
Database Connection Management:
In serverless environments, manage database connections efficiently. Use connection pooling (Prisma handles this) and be mindful of connection limits, especially on free tiers of managed databases. The Prisma client instantiation pattern shown earlier helps reuse connections in development but creates new ones in production per function invocation, which is generally correct for serverless but needs monitoring under load.
Caching:
If processing involves frequently accessed data that doesn't change often, implement caching (e.g., using Redis, Vercel KV, or in-memory caches with appropriate invalidation) to speed up lookups.
Code Optimization:
Profile your code (using
console.time
/console.timeEnd
or dedicated profiling tools) to identify bottlenecks within the handler if performance issues arise. Optimize algorithms and data structures.Platform Scaling:
Leverage the auto-scaling capabilities of your deployment platform (Vercel). Ensure your database and any external services can also handle the potential load.
Testing Performance:
k6
orartillery.io
to simulate high volumes of concurrent webhook requests to your deployed endpoint (not justngrok
).