Frequently Asked Questions
Use the `sendWhatsAppTextMessage` function in the provided `sinch.ts` service file. This function utilizes the Sinch Conversation API to send text messages via WhatsApp. It requires the recipient's phone number in E.164 format and the message content as input. The function handles the interaction with the Sinch SDK and returns the Sinch message ID upon successful delivery.
The Sinch Conversation API is a unified messaging API that allows developers to integrate various messaging channels, including WhatsApp, into their applications. It simplifies the process of sending and receiving messages, managing contacts, and handling other communication tasks, providing a more streamlined experience than directly integrating with each messaging platform's API.
Fastify is a high-performance, low-overhead web framework for Node.js. Its speed and efficiency make it well-suited for handling real-time communication, like receiving webhook notifications from Sinch. It offers a robust routing system for managing incoming messages and a flexible plugin system for extensibility.
ngrok is primarily used during development to create a publicly accessible URL for your local Fastify server. This allows Sinch to send webhook notifications to your application even while it's running locally. Consider alternatives or paid tiers for production for more stable URLs.
Yes, the Sinch Conversation API supports various message types, including text messages, images, and interactive templates. The provided example focuses on text messages, but you can extend the `sinch.ts` service file to include functions for sending other message types using the appropriate Sinch SDK methods.
In your Sinch app dashboard, go to "Conversation API" -> "Webhook Settings". Enter your public URL (e.g., ngrok URL) followed by `/webhooks/inbound` for incoming messages and `/webhooks/status` for delivery reports. Select the relevant event types (`MESSAGE_INBOUND`, `MESSAGE_DELIVERY`) for each endpoint.
The `waid:` prefix is a specific identifier used by the Sinch Conversation API to denote WhatsApp contacts. It's followed by the recipient's phone number in E.164 format (e.g., waid:12223334444). It ensures that the message is routed correctly through the Sinch system to the intended WhatsApp user.
Webhook security is paramount. Use the `verifySinchSignature` function in the example code. This function compares the signature provided by Sinch in the `x-sinch-signature` header with your calculated signature using the raw request body, timestamp, and your Sinch "Application Secret." This prevents unauthorized requests.
The `x-sinch-signature` header contains a cryptographic signature generated by Sinch for each webhook request. This signature, along with the `x-sinch-timestamp` header, allows your application to verify the authenticity of the webhook and ensure it originated from Sinch, preventing malicious actors from sending fake requests.
The provided code includes a global error handler in `server.ts` to catch unhandled exceptions within routes. Specific error handling within the webhook route and the `sendWhatsAppTextMessage` function provides contextual error logging and responses. Always respond with a 200 OK to Sinch webhooks even if internal processing fails to prevent retries.
The Sinch webhook signature verification process typically uses the *raw*, unparsed request body as part of the signed data. Accessing this raw body is crucial for verifying the integrity of the request. The provided Fastify server setup includes instructions and examples to ensure `request.rawBody` is populated correctly for use in signature verification. This can be accomplished using middleware or adjusting content parser settings
`SINCH_PROJECT_ID`, `SINCH_KEY_ID`, `SINCH_KEY_SECRET`, `SINCH_APP_ID`, `SINCH_APPLICATION_SECRET` (for webhooks), and `WHATSAPP_SENDER_ID` (your provisioned number). These are loaded from the `.env` file in the provided example, keeping credentials secure.
The article recommends a modular structure. Create a dedicated service file (`sinch.ts`) to encapsulate interactions with the Sinch SDK. Define separate route handlers in Fastify (`routes/webhooks.ts`) to manage incoming webhooks and outgoing message requests. Use environment variables (`.env`) for configuration.
This guide provides a complete walkthrough for building a robust Node.js application using the Fastify framework to send and receive WhatsApp messages via the Sinch Conversation API. We will cover everything from initial project setup and core messaging functionality to advanced topics like security, error handling, deployment, and verification.
By the end of this tutorial, you will have a functional application capable of handling two-way WhatsApp communication, complete with best practices for production environments. This integration enables businesses to programmatically interact with customers on WhatsApp for notifications, support, marketing, and more.
Project Overview and Goals
@sinch/sdk-core
Node.js SDK..env
file.ngrok
tiers have limitations like session expiry and changing URLs; consider paid tiers or alternatives likecloudflared
for more stable development URLs if needed.)ngrok
installed globally or available vianpx
.System Architecture
1. Setting up the Project
Let's initialize our Node.js project using Fastify.
Create Project Directory: Open your terminal and create a new directory for the project.
Initialize npm: This creates a
package.json
file.Install Dependencies: We need Fastify, the Sinch SDK, and
dotenv
.Install Development Dependencies (Optional but Recommended): For better development experience, especially if using TypeScript later.
Create Project Structure: Set up a basic structure for clarity.
Configure
.gitignore
: Create a.gitignore
file in the root directory to prevent committing sensitive information and unnecessary files.Set up Environment Variables (
.env
): Create a.env
file in the root directory. We will populate this with credentials obtained from the Sinch dashboard later..env
? It keeps sensitive credentials separate from code and makes configuration environment-specific. Remember to never commit your.env
file to version control.(Optional) Configure
tsconfig.json
(if using TypeScript): Create atsconfig.json
file in the root directory.(Optional) Add Run Scripts to
package.json
:Choose the appropriate
dev
script based on whether you're using JavaScript or TypeScript.2. Implementing Core Functionality (Sinch Service)
Let's create a dedicated service file to handle interactions with the Sinch SDK.
waid:
prefix: The Sinch Conversation API often requires a specific prefix for contact identifiers depending on the channel. For WhatsApp,waid:
followed by the E.164 number is standard. Check the latest Sinch API documentation if you encounter issues.3. Building the API Layer (Fastify Server and Webhook Route)
Now, let's set up the Fastify server and the webhook endpoint to receive incoming messages.
Important Caveat: Sinch API payload structures (like the format of
request.body
in webhooks) can change. Always refer to the official Sinch Conversation API documentation for the most up-to-date details. The code examples provided here might require adjustments based on the specific API version you are using.Fastify Server Setup
Webhook Route Implementation
This route will handle incoming POST requests from Sinch when a user sends a message to your WhatsApp number.
verifySinchSignature
function is critical. It prevents attackers from sending fake requests to your endpoint. It uses theApplication Secret
from your Sinch App settings. Crucially, it now relies on accessing the raw request body (request.rawBody
), which needs to be configured inserver.ts
as noted in the comments there. Verify Sinch's exact signing method (raw body vs. stringified JSON) if issues arise.request.body
depends on the Sinch event. Always consult the latest Sinch Conversation API documentation. Using TypeScript interfaces (likeSinchWebhookPayload
) is recommended for type safety.200 OK
quickly. Process asynchronously if needed./inbound
vs/status
: Logic forMESSAGE_DELIVERY
events is now only in the/status
endpoint to avoid redundancy./inbound
focuses onMESSAGE_INBOUND
.4. Integrating with Sinch (Configuration and Setup)
Obtain Sinch Credentials:
.env
file.App ID
.Application Secret
. This is used for signature validation. Copy both into your.env
file.447537400000
) without the+
in your.env
file (WHATSAPP_SENDER_ID
).Configure Webhooks in Sinch Dashboard:
ngrok
.ngrok
will display a forwarding URL likehttps://<random-string>.ngrok-free.app
. Copy this HTTPS URL. Remember that free tier URLs change frequently..env
: Paste the ngrok URL into theNGROK_URL
variable in your.env
file (optional, but helpful for reference).https://<random-string>.ngrok-free.app/webhooks/inbound
https://<random-string>.ngrok-free.app/webhooks/status
MESSAGE_INBOUND
is selected for the Target URL. EnsureMESSAGE_DELIVERY
(and potentially others likeMESSAGE_SUBMIT_FAILED
,MESSAGE_REJECTED
) are selected for the Delivery Report URL.Review Environment Variables: Double-check your
.env
file ensures all values obtained from Sinch are correctly copied.5. Implementing Error Handling, Logging, and Retries
Error Handling:
server.ts
usingserver.setErrorHandler
.try...catch
blocks in specific functions (e.g.,sendWhatsAppTextMessage
) for graceful handling of expected errors.403 Forbidden
) or other request errors.Logging:
pino
logger (logger: true
) is excellent.fastify.log.info()
,fastify.log.warn()
,fastify.log.error()
contextually.Retry Mechanisms (for Outgoing Messages):
async-retry
.sinch.ts
):