Frequently Asked Questions
Use RedwoodJS's GraphQL API, integrated with the Sinch SMS API, to schedule and send SMS reminders. Create a Redwood service that interacts with both the Sinch API and your database to manage reminder scheduling and data storage. Build a frontend form in RedwoodJS to collect user input and trigger the reminder scheduling process via the GraphQL API.
The Sinch SMS API enables your RedwoodJS application to send SMS messages. It handles the complexities of SMS delivery, allowing you to focus on application logic. Specifically, the 'send_at' feature allows scheduling SMS messages for future delivery without managing background jobs within RedwoodJS itself.
RedwoodJS offers a full-stack, serverless-friendly framework that simplifies development by providing integrated frontend (React), backend (GraphQL API, Prisma), and database access. This structure makes it easier to handle user interactions, data management, and external API integrations like Sinch.
Luxon is highly beneficial when working with dates and times, especially when dealing with different time zones. It's used to parse, manipulate, and format dates/times accurately in your RedwoodJS application, avoiding common time zone issues. In this SMS reminder app, Luxon ensures accurate calculation of the reminder time based on the user's specified time zone and converts it to UTC for consistent storage and Sinch API interaction.
Yes, you can use SQLite for local development and simpler projects. RedwoodJS defaults to SQLite and handles the basic setup automatically if you use provider = "sqlite" in your schema.prisma file. For production environments, PostgreSQL is generally recommended for its scalability and robustness, requiring you to configure the DATABASE_URL environment variable appropriately.
RedwoodJS uses .env files for environment variables. Create a .env file in your project's root directory and add your sensitive information, such as API keys and database URLs. Ensure .env is added to .gitignore to prevent committing secrets to version control.
Prisma acts as an Object-Relational Mapper (ORM) for your database. It simplifies database interactions by allowing you to work with data using JavaScript objects and methods. In this application, Prisma facilitates storing and retrieving reminder details like patient name, appointment time, and phone number.
The user interacts with a React frontend, which communicates with a GraphQL API. The API interacts with a Redwood service that handles logic, using Prisma to manage data in a database (PostgreSQL/SQLite) and the Sinch API to schedule SMS messages. Sinch sends the SMS at the designated time.
You need Node.js (v20 or higher recommended), Yarn v1, a Sinch account (with API credentials and a provisioned number), access to a terminal, and basic understanding of JavaScript, React, GraphQL, and databases.
The Sinch SMS API expects times in UTC for the send_at parameter. Use a library like Luxon to convert user-provided times (along with their time zone) into UTC before passing to the Sinch API. Ensure your database also stores all DateTime fields in UTC for consistency.
Define a 'Reminder' model in your api/db/schema.prisma file specifying fields like patientName, phoneNumber, appointmentTime, reminderTime, and status. Use appropriate data types and consider an index on status and reminderTime for efficient querying.
The 'PENDING' status indicates that an SMS reminder has been scheduled but has not yet been sent by Sinch. Other status options could be SENT or FAILED for enhanced tracking and reporting.
E.164 is an international standard format for phone numbers (e.g., +15551234567) that ensures consistency and compatibility with global communication systems. Using E.164 simplifies validation and reduces ambiguity when sending SMS messages internationally.
Implement try...catch blocks around API calls and database operations to handle potential errors gracefully. Log errors using Redwood's logger and provide user feedback through toasts or other notification mechanisms. Consider retry mechanisms for API calls and compensation logic for database errors to enhance robustness.
Schedule SMS Reminders with Sinch in RedwoodJS: Complete Implementation Guide
Build a RedwoodJS application that schedules and sends SMS reminders using the Sinch SMS API. Create an appointment reminder system where users enter appointment details and the application automatically schedules an SMS reminder via Sinch at a specified time before the appointment.
This guide solves the common need for automated, time-sensitive notifications without requiring complex background job infrastructure. Instead, leverage Sinch's built-in
send_at
scheduling capabilities to handle the job scheduling for you.Real-world applications:
Key Technologies:
send_at
feature for scheduling.System Architecture:
Data flow:
Prerequisites:
Obtaining Sinch credentials:
Sinch API Scheduling Requirements:
send_at
YYYY-MM-DDThh:mm:ss.SSSZ
(e.g.,2025-08-22T14:30:00.000Z
)expire_at
send_at
(maximum value)+[country_code][number]
(e.g.,+12025550187
)Regional Endpoints:
us.sms.api.sinch.com
eu.sms.api.sinch.com
au.sms.api.sinch.com
br.sms.api.sinch.com
ca.sms.api.sinch.com
Sinch API Error Codes:
40001
40003
send_at
timestamp in the past40004
send_at
beyond 3-day window40101
50000
Source: Sinch SMS Batches API Documentation
Final Outcome:
A RedwoodJS application with a simple UI to schedule appointment reminders. The backend validates input, stores appointment details, and uses the Sinch Node SDK to schedule an SMS to be sent 2 hours before the scheduled appointment time.
How Do You Set Up a RedwoodJS Project for SMS Scheduling?
Initialize a new RedwoodJS project and configure the necessary environment.
Create Redwood App: Open your terminal and navigate to the directory where you want to create your project. Run the following command, replacing
<your-app-name>
with your desired project name (e.g.,redwood-sinch-reminders
):--typescript
for enhanced type safety, recommended for production applications.yes
(recommended)Initial commit
(or your preferred message)yes
Navigate to Project Directory:
Install Additional Dependencies: Install the Sinch SDK and
luxon
for robust date/time manipulation.@sinch/sdk-core
: The official Sinch Node.js SDK.luxon
: A powerful library for handling dates, times, and time zones.Why Luxon over native Date or other libraries?
Luxon provides:
America/New_York
).toISO()
method matches Sinch API requirements exactlyappointmentDateTime.minus({ hours: 2 })
vs. complex Date arithmeticAlternative libraries like Moment.js are deprecated, and Day.js lacks robust time zone support without plugins.
Environment Variable Setup: Redwood uses
.env
files for environment variables. The Sinch SDK requires credentials. Create a.env
file in the root of your project:Add the following variables to your
.env
file, replacing the placeholder values with your actual Sinch credentials and configuration:SINCH_KEY_ID
,SINCH_KEY_SECRET
,SINCH_PROJECT_ID
: Find these in your Sinch Dashboard under your project's API credentials/Access Keys section. TreatSINCH_KEY_SECRET
like a password – never commit it to Git.SINCH_FROM_NUMBER
: A virtual number you've acquired through Sinch, enabled for SMS, and associated with your Project ID. Must be in E.164 format (e.g.,+12025550187
).SINCH_SMS_REGION
: The regional endpoint for the Sinch API. Available regions:us
(United States),eu
(European Union),au
(Australia),br
(Brazil),ca
(Canada). Use the region closest to your user base or where your account is homed.DEFAULT_COUNTRY_CODE
: Used as a fallback if the user enters a local number format. Best practice requires E.164 format input.Source: Sinch SMS API Reference – Regional Endpoints
Add
.env
to.gitignore
: Ensure your.env
file (containing secrets) is not committed to version control. Open your project's root.gitignore
file and add.env
if it's not already present.Initial Commit (if not done during creation): If you didn't initialize Git during
create redwood-app
:How Do You Create the Database Schema for Reminder Scheduling?
Define a database table to store the details of scheduled reminders.
Define Prisma Schema: Open
api/db/schema.prisma
and define a model forReminder
:DateTime
fields in UTC to avoid time zone issues.status
tracks the state of the reminder (PENDING
means scheduled but not yet sent).sinchBatchId
can be useful for tracking the message status within Sinch later.status
andreminderTime
could be useful for querying pending jobs or cleanup tasks.Status lifecycle and state transitions:
sinchBatchId
in Sinch dashboard for details)Enhanced schema for production:
DATABASE_URL
in the.env
file points to your database (e.g.,postgresql://user:password@host:port/database
). For local development, Redwood defaults to SQLite, which requires no extra setup if you stick with the defaultprovider = "sqlite"
.Troubleshooting database connection issues:
Can't reach database server
brew services start postgresql
(macOS)Authentication failed
DATABASE_URL
Database does not exist
createdb your_database_name
Connection timeout
Run Database Migration: Apply the schema changes to your database:
schema.prisma
changes and applies it to your development database. Provide a name for the migration when prompted (e.g.,create reminder model
).How Do You Implement the Sinch SMS Scheduling Service in RedwoodJS?
Create the RedwoodJS service that handles the logic for scheduling reminders.
Generate Service Files: Use the Redwood generator to create the necessary service and GraphQL files:
This creates:
api/src/services/reminders/reminders.ts
(Service logic)api/src/services/reminders/reminders.scenarios.ts
(Seed data for testing)api/src/services/reminders/reminders.test.ts
(Unit tests)api/src/graphql/reminders.sdl.ts
(GraphQL schema definition)Implement the
scheduleReminder
Service Function: Openapi/src/services/reminders/reminders.ts
and add the logic to create a reminder record and schedule the SMS via Sinch.validate
and custom logic for the phone number and date/time. The phone number normalization explicitly notes its limitations and recommendslibphonenumber-js
for production.luxon
to parse the input date/time with the provided time zone_ calculate the reminder time_ and convert both to UTC for storage and the API call. Validates that the reminder time is in the future.sinchClient.sms.batches.send
method (you'll definesinchClient
next).send_at
Parameter: Passes the calculatedreminderTimeUtc
in ISO-8601 format to Sinch'ssend_at
parameter. Maximum scheduling window: 3 days (Sinch defaultexpire_at
).'PENDING'
and thesinchBatchId
.try...catch
blocks. The database error handling after a successful Sinch call includes warnings and suggestions for compensation logic.Transaction handling and idempotency:
The current implementation has a race condition: if the SMS schedules successfully but the database write fails_ you have an orphaned scheduled message. Production systems should:
PENDING_SCHEDULE
before calling Sinch_ then update toPENDING
after successCanceling scheduled messages:
Add this function to handle cancellations:
Source: Sinch SMS Batches API – send_at Parameter
Create Sinch Client Library: Centralize the Sinch SDK client initialization.
Create a new file:
api/src/lib/sinch.ts
SinchClient
using the environment variables.SINCH_SMS_REGION
environment variable for correct API endpoint targeting and advises checking Sinch SDK docs for the specific mechanism if needed.Configuring regional endpoints:
If the SDK doesn't automatically route based on
SINCH_SMS_REGION
, configure the base URL explicitly:Check the @sinch/sdk-core documentation for the exact initialization parameters.
How Do You Build the GraphQL API for SMS Reminders?
Define the GraphQL mutation to expose the
scheduleReminder
service function.Define GraphQL Schema: Open
api/src/graphql/reminders.sdl.ts
. Redwood generators created a basic structure. Modify it to define theScheduleReminderInput
and thescheduleReminder
mutation.!
marks fields as required.Reminder
type that mirrors our Prisma model and will be returned by the mutation on success.scheduleReminder
mutation, taking the input and returning aReminder
.@requireAuth
: Initially added by the generator. It has been removed here for easier initial testing. Important: Removing authentication is only for initial development convenience. You MUST re-enable or implement proper authentication before deploying to any non-local environment.Testing the API Endpoint: Once the development server is running (
yarn rw dev
), test the mutation using the Redwood GraphQL Playground (usually athttp://localhost:8911/graphql
) or a tool likecurl
or Postman.GraphQL Playground Mutation:
Expected Response:
Common Errors:
Invalid phone number format
+
and country codeCalculated reminder time is in the past
Failed to schedule SMS: 40101
.env
Database error: unique constraint
Debugging Tips:
yarn rw dev
logger.debug({ sinchResponse }, 'Full response')
in serviceCurl Example:
appointmentDate
andappointmentTime
result in areminderTime
(2 hours prior) that is in the future from when you run the test.Reminder
record.yarn rw dev
output) for logs from the service function.How Do You Create the React Frontend for Scheduling Reminders?
Create a React page with a form to schedule reminders.
Generate Page: Create a new page component for the reminder form.
This creates
web/src/pages/ReminderSchedulerPage/ReminderSchedulerPage.tsx
.Build the Form: Open
web/src/pages/ReminderSchedulerPage/ReminderSchedulerPage.tsx
and implement the form using Redwood Form components.useMutation
hook to call thescheduleReminder
GraphQL mutation.<Form>
,<TextField>
,<DateField>
,<TimeField>
,<SelectField>
,<Submit>
,<FieldError>
,<FormError>
) for structure, validation, and error handling.required
,pattern
). More complex validation happens in the service.Toaster
for displaying success/error messages.<SelectField>
for selecting the appointment's time zone, crucial for correct calculation. It attempts to default to the user's browser time zone.formMethods.reset()
Add Route: Ensure the route is defined in
web/src/Routes.tsx
:Run and Test: Start the development server:
Navigate to
http://localhost:8910/schedule
(or your configured port). Fill out the form with valid data (ensure the appointment is far enough in the future) and submit. Check the browser console, API server logs, and Sinch dashboard for confirmation. You should receive the SMS 2 hours before the specified appointment time.How Do You Handle Errors and Implement Retry Logic?
Error Handling:
FieldError
andFormError
,useMutation
'sonError
callback, andtoast
notifications.try...catch
blocks around critical operations (validation, date/time parsing, API calls, database writes). Includes specific error messages and logs errors using Redwood's logger. Highlights the critical failure case where the SMS is scheduled but the database write fails, suggesting robust compensation logic for production.Logging:
logger
on the API side (src/lib/logger.ts
). Logs key events like receiving requests, preparing data, successful API calls, database writes, and errors. Avoid logging sensitive data likeSINCH_KEY_SECRET
.Implementing Retry Logic with async-retry:
Install the library:
Update the service to include retry logic:
Error Code Handling with Recovery Strategies:
reminderTime > DateTime.now()
before API callMonitoring and Alerting for Production:
Track metrics: Monitor reminder scheduling success rate, average latency, and error rates
Set up alerts: Configure notifications for:
PENDING
status for >24 hoursImplement dead letter queue: Store failed reminders in a separate table for manual review and retry
Frequently Asked Questions
What is the maximum scheduling window for Sinch SMS messages?
Sinch SMS API allows scheduling messages up to 3 days in the future using the
send_at
parameter. Theexpire_at
parameter defaults to 3 days aftersend_at
, which is also the maximum allowed value. If you need to schedule messages further in advance, implement your own queueing system or use a job scheduler like Redwood's job system to trigger the Sinch API call closer to the desired send time.Implementing longer-term scheduling:
How do you format the send_at timestamp for Sinch API?
Use ISO-8601 format with timezone information:
YYYY-MM-DDThh:mm:ss.SSSZ
. For example,2025-08-22T14:30:00.000Z
represents August 22, 2025 at 2:30 PM UTC. Luxon'stoISO()
method automatically formats DateTime objects in this format. Always convert your local appointment times to UTC before passing to the Sinch API to ensure accurate scheduling across time zones.Which Sinch regional endpoints are available for SMS?
Sinch provides five regional SMS API endpoints:
us.sms.api.sinch.com
eu.sms.api.sinch.com
au.sms.api.sinch.com
br.sms.api.sinch.com
ca.sms.api.sinch.com
Region selection criteria:
How do you handle time zones correctly in RedwoodJS SMS scheduling?
Use Luxon's
DateTime
object with explicit time zone information. Accept the user's time zone as input (using IANA time zone names likeAmerica/New_York
), parse the appointment date/time in that zone usingDateTime.fromISO(dateTimeString, { zone: timeZone })
, then convert to UTC with.toUTC()
before storing in the database and sending to Sinch. This ensures reminders send at the correct local time regardless of server location.What database fields do you need for SMS reminder tracking?
Store these essential fields in your Prisma schema:
phoneNumber
+12025550187
appointmentTime
2025-08-22T14:00:00.000Z
reminderTime
2025-08-22T12:00:00.000Z
status
PENDING
,SENT
,FAILED
sinchBatchId
01HXYZ123ABC...
createdAt
2025-08-20T10:30:00.000Z
updatedAt
2025-08-22T12:00:15.000Z
Index
status
andreminderTime
together for efficient queries when building admin dashboards or cleanup jobs:How do you validate phone numbers for Sinch SMS in RedwoodJS?
Use E.164 format validation (
/^\+[1-9]\d{1,14}$/
regex) for basic checking, but implement thelibphonenumber-js
library for production applications.Installing and using libphonenumber-js:
Supported input formats (all convert to
+12025550187
):+1 202 555 0187
(international with spaces)(202) 555-0187
(US local format)202-555-0187
(US dashed format)2025550187
(digits only, with country hint)What happens if the SMS scheduling fails but the database save succeeds?
This is a critical consistency issue. The example code includes a
try...catch
around the database save after successful Sinch scheduling. If the database write fails, the SMS is already scheduled but you have no record. Production systems need compensation logic: save the reminder with statusPENDING
before calling Sinch, then update toSCHEDULED
after success. Alternatively, log thesinchBatchId
to a failure tracking system and implement a reconciliation job that queries Sinch API to identify orphaned scheduled messages.How do you implement retry logic for failed Sinch API calls in RedwoodJS?
Use the
async-retry
npm package in your service function. Wrap thesinchClient.sms.batches.send()
call with retry logic that handles transient network errors (connection timeouts, 5xx responses) but not permanent failures (4xx authentication or validation errors). Implement exponential backoff (e.g., 1s, 2s, 4s delays) with a maximum of 3-5 retry attempts. Log each retry attempt using RedwoodJS logger and update the reminder status toFAILED
if all retries exhaust.See the complete implementation in the "How Do You Handle Errors and Implement Retry Logic?" section above.