Frequently Asked Questions
One effective way to reduce appointment no-shows is to implement automated SMS reminders. This guide details building an application with Next.js and MessageBird to send timely reminders, improving customer communication and minimizing missed appointments.
MessageBird, a Communication Platform as a Service (CPaaS), is used for validating phone numbers via its Lookup API and sending scheduled SMS reminders through its Messages API. Its developer-friendly SDK and direct scheduling capabilities make it ideal for this purpose.
Next.js is chosen for this project due to its excellent developer experience, performance optimizations, and built-in API route capabilities, making it suitable for both frontend UI and backend logic.
SMS appointment reminders should be sent a few hours before the appointment, giving enough notice but not too far in advance. This application is set up to send reminders 3 hours prior to the appointment time.
The MessageBird Lookup API is used to validate phone numbers. The API route checks number validity and format, ensuring it's a mobile number capable of receiving SMS. It also uses a default country code for convenience.
PostgreSQL is used as the database for storing appointment data. Its robustness and reliability make it suitable for storing and managing this information.
Prisma is a next-generation ORM for Node.js and TypeScript. It simplifies database interactions, manages migrations, and enhances type safety in the application.
Obtain your MessageBird API key from the MessageBird Dashboard under Developers > API access > Live API Key. Then, paste this key into the `.env` file as `MESSAGEBIRD_API_KEY`, ensuring it's kept confidential.
The MessageBird originator is the sender ID that recipients see on their phones when they receive the SMS. This can be an alphanumeric string (with restrictions) or an E.164 formatted phone number.
Alphanumeric sender IDs are possible but not universally supported. Check MessageBird's documentation for country-specific restrictions. Numeric sender IDs (phone numbers) are generally recommended for broader compatibility.
The application stores the user's time zone and uses `moment-timezone` to accurately calculate reminder times in UTC for MessageBird. This ensures reminders are delivered at the correct local time for each user.
You'll need Node.js v18 or later, npm/yarn, a MessageBird account and API key, access to a PostgreSQL database, and basic knowledge of React, Next.js, and asynchronous JavaScript.
The MessageBird Messages API is used for scheduling SMS messages. The `messagebird.messages.create` function is called with the recipient, message body, and scheduled time in ISO 8601 format.
The application uses extensive `try...catch` blocks and logs errors using `console.error`. It returns informative JSON error responses with appropriate HTTP status codes for client and server errors.
This guide provides a complete walkthrough for building a production-ready web application using Next.js and MessageBird to allow users to book appointments and automatically receive SMS reminders. We'll cover everything from project setup to deployment, focusing on best practices, security, and error handling.
The final application will feature a simple web form for booking appointments and leverage MessageBird's API for phone number validation and scheduling SMS messages to be sent at a specific time before the appointment. This solves the common problem of no-shows for appointment-based services by providing timely, automated nudges to customers.
Project Overview and Goals
System Architecture
Here's a high-level overview of how the components interact:
/api/book
).1. Setting up the Project
Let's initialize our Next.js project and set up the necessary tools and configurations.
1. Create Next.js App: Open your terminal and run:
We're using TypeScript, ESLint, Tailwind CSS, the
src/
directory structure, and the App Router for this project.2. Install Dependencies: Install the MessageBird SDK, Prisma client, and Moment.js (for date/time manipulation). Next.js has built-in support for
.env
files, so explicitdotenv
installation is usually not required.messagebird
: Official SDK for interacting with the MessageBird API.@prisma/client
: Auto-generated database client based on your schema.prisma
: CLI tool for database migrations and schema management.moment
,moment-timezone
: Libraries for robust date, time, and timezone handling. Note: Moment.js is in maintenance mode. For new projects, consider alternatives like Day.js or date-fns for smaller bundle sizes and better immutability.3. Initialize Prisma: Set up Prisma to manage our database schema and connections.
This creates:
prisma/schema.prisma
: Your database schema definition file..env
: A file for environment variables (automatically added to.gitignore
). Next.js automatically loads variables from this file.4. Configure Environment Variables: Open the
.env
file created in the project root and add your MessageBird API key and database connection URL.DATABASE_URL
: Get this from your PostgreSQL provider or local setup. Ensure the database (reminders
in the example) exists.MESSAGEBIRD_API_KEY
: Find this in your MessageBird Dashboard under Developers > API access > Live API Key.MESSAGEBIRD_ORIGINATOR
: This is the sender ID that appears on the recipient's phone. It can be an alphanumeric string (like""BeautyBird""
) or a phone number purchased through MessageBird. Important: Alphanumeric sender IDs are not supported in all countries (e.g., the US). Check MessageBird's country restrictions and use a valid number if needed.DEFAULT_COUNTRY_CODE
: An ISO 3166-1 alpha-2 country code (e.g.,""US""
,""GB""
,""NL""
). Providing this helps the MessageBird Lookup API parse phone numbers entered without an international prefix.5. Define Database Schema: Open
prisma/schema.prisma
and define the model for storing appointments.appointmentTime
asDateTime
(which Prisma maps to timestamp with timezone in Postgres, effectively storing it in UTC).timeZone
explicitly to handle scheduling correctly across different regions.phoneNumber
stores the E.164 formatted number returned by MessageBird Lookup.reminderSentAt
andmessageBirdId
can be useful for tracking.@@index
) for potentially common query fields.6. Create Initial Database Migration: Apply the schema to your database. Prisma will create the
Appointment
table and indexes.Follow the prompts. This creates SQL migration files in
prisma/migrations
and updates your database.7. Set up Prisma Client: Create a utility file to instantiate the Prisma client, ensuring we reuse the same instance across our application (important for Next.js API routes).
8. Configure MessageBird Client: Similarly, create a utility for the MessageBird client.
This setup provides a solid foundation for our application logic.
2. Implementing Core Functionality (Frontend Form)
Let's build the user interface for booking appointments. We'll use React state and Tailwind CSS within a Next.js page component.
Create a new page file
src/app/book/page.tsx
.This form collects the necessary details, performs basic client-side checks, sends the data to our yet-to-be-created API endpoint, and displays success or error messages.
3. Building the API Layer
Now, let's create the Next.js API route (
src/app/api/book/route.ts
) that will handle the form submission, interact with MessageBird, and save data to the database.Key points in the API route:
messagebird.lookup.read
to validate the phone number format and type (must be mobile). It handles specific errors (like code 21 for invalid format) and uses the normalizedphoneNumber
returned by the API for consistency. We wrap the callback in a Promise for cleaner async/await usage.moment-timezone
to correctly parse the input date/time in the user's specified timezone. It then calculates the reminder time, also considering the timezone, and formats it into the ISO 8601 string required by MessageBird'sscheduledDatetime
. Moment's.format()
without arguments outputs ISO 8601 in UTC, which is perfect.messagebird.messages.create
with theoriginator
,recipients
(using the validated number),scheduledDatetime
, and messagebody
. Again, a Promise wrapper is used. The MessageBird message ID is stored.prisma.appointment.create
) to save the appointment details, including the UTCappointmentTime
, thetimeZone
, and themessageBirdId
.try...catch
blocks extensively. Critically, it logs a warning if the database save fails after scheduling the message, highlighting the need for potential manual intervention or implementation of the cancellation logic. Returns informative JSON error responses with appropriate HTTP status codes.4. Integrating with Third-Party Services (MessageBird Details)
We've already integrated the MessageBird SDK, but let's detail the configuration aspects:
API Key (
MESSAGEBIRD_API_KEY
):MESSAGEBIRD_API_KEY
in your.env
file.Originator (
MESSAGEBIRD_ORIGINATOR
):.env
file.""BeautyBird""
,""MyClinic""
). Max 11 characters. Not supported in all countries (notably the US, Canada). If used where unsupported, messages may fail or be assigned a random shared number.+12015550123
). Required for sending to countries like the US. Ensure the number is SMS-enabled.Default Country Code (
DEFAULT_COUNTRY_CODE
):""555-123-4567""
instead of""+15551234567""
).""US""
,""GB""
,""DE""
,""NL""
)..env
. The API route code uses this value when callingmessagebird.lookup.read
.Fallback Mechanisms:
5. Error Handling, Logging, and Retry Mechanisms
Robust error handling is crucial for a production application.
try...catch
blocks for different logical steps (validation, lookup, scheduling, DB save). Errors are caught, logged to the console (usingconsole.error
), and a standardized JSON response{ success: false, error: '...' }
is returned with an appropriate HTTP status code (400 for client errors, 500 for server errors). The critical failure point (DB save after scheduling) is specifically noted in logs.console.log
for informational messages (e.g., successful scheduling/saving) andconsole.error
for errors, including the error object itself for detailed stack traces during development.console.log
/console.error
with a dedicated logging library (e.g.,pino
,winston
). Configure it to:messagebird.lookup.read
andmessagebird.messages.create
calls.async-retry
can simplify this.