Why use the SDK instead of building the requests yourself
The whole D-API WhatsApp API is REST, so nothing stops you from writing the calls with fetch or axios. The SDK pays off day to day: methods are named after actions (sessions.create, messages.sendText, interactive.sendList), parameters are typed, and the editor warns you about a missing required field before the code ships to production.
Node.js is the only language with an official D-API library. In other stacks you integrate with the language's own HTTP client, and the contract is the same. The Node.js SDK integration page summarizes the available modules.
Installation and a configured client
Install the package and create a single client for the whole application. Keep the API key in an environment variable; it goes in the Authorization header without the Bearer prefix, and the SDK handles that for you.
npm install d-api-sdk// src/dapi.ts
import { DApi } from 'd-api-sdk'
export const dapi = new DApi({
apiKey: process.env.DAPI_API_KEY!,
timeout: 10_000, // default is 30000 ms; inside an HTTP route, go lower
})baseUrl is optional and points to https://api.d-api.cloud by default. The timeout parameter applies to every call: when it expires, the SDK aborts the fetch and throws an error with status 408. Setting this value explicitly keeps a slow request from holding one of your app's routes for half a minute.
Create the connection and show the QR code
Each WhatsApp number is a session, identified by a sessionId you choose. You pass the webhook URL at creation time, so events start flowing as soon as the number connects.
import { dapi } from './dapi'
await dapi.sessions.create({
sessionId: 'downtown-store',
connectionMode: 'qr',
webhookUrl: 'https://your-app.com/webhooks/whatsapp/' + process.env.WEBHOOK_TOKEN,
})
const { qrCodeImage } = await dapi.sessions.getQRCode('downtown-store')
// qrCodeImage is a data URI (data:image/png;base64,...)
// just use it as the src of an <img> in your customer's dashboardThe QR code expires within seconds and the API renews it. On the connection screen, call getQRCode again at short intervals until the number connects, or listen for the connection.status event on the webhook and switch screens when data.status comes in as connected. Pairing details, including pairing by code instead of QR, are in WhatsApp API with QR code.
Send text and a list in a few lines
Once the number is connected, sending text takes three fields: the session, the recipient in international format without the plus sign, and the content. The interactive list adds the button label and the sections with the options.
await dapi.messages.sendText({
sessionId: 'downtown-store',
to: '14155550123',
text: 'Hi Anna! Your order 4812 has been approved.',
})
await dapi.interactive.sendList({
sessionId: 'downtown-store',
to: '14155550123',
description: 'When would you like it delivered?',
buttonText: 'See time slots',
sections: [
{
title: 'Delivery',
rows: [
{ rowId: 'morning', title: 'Morning', description: '8am to 12pm' },
{ rowId: 'afternoon', title: 'Afternoon', description: '1pm to 6pm' },
],
},
],
})The rowId is what comes back to your system when the person picks an option, so use values your code understands without an extra lookup. Limits on sections and rows, and what WhatsApp allows in buttons, are covered in WhatsApp buttons and lists.
Handle errors and timeouts
When the API responds with an error status, it returns a body shaped like { success: false, error, statusCode }. The SDK turns that into a DApiError exception, with message taken from the error field, the HTTP status, and the original body in response.
import type { DApiError } from 'd-api-sdk'
try {
await dapi.messages.sendText({ sessionId: 'downtown-store', to, text })
} catch (err) {
const e = err as DApiError
if (e.status === 408) {
// client timeout: the message may or may not have gone out
logger.warn({ to, status: e.status }, 'send got no response in time')
} else if (e.status >= 400 && e.status < 500) {
// invalid payload or unknown session: retrying won't help
logger.error({ status: e.status, error: e.message }, 'send rejected')
}
throw err
}Two decisions are worth making early. A 4xx error means something is wrong with the request, such as a missing field or a disconnected session, and repeating the call only adds noise. Timeouts and 5xx errors, on the other hand, may be temporary. Teams sending high volume usually put sends on a queue (BullMQ, for example), so your product's request responds fast and the send happens in the background.
Receive the webhook with Express
Incoming messages, read receipts and connection changes arrive by POST at the URL you registered. Every event has the same envelope: event, sessionId, data, timestamp and traceId. The route below validates the secret token in the URL, responds immediately, and only then handles the list reply.
import express from 'express'
import { dapi } from './dapi'
const app = express()
app.post('/webhooks/whatsapp/:token', express.json({ limit: '1mb' }), (req, res) => {
if (req.params.token !== process.env.WEBHOOK_TOKEN) {
res.sendStatus(401)
return
}
res.sendStatus(200) // respond before any heavy work
const { event, sessionId, data } = req.body
if (event !== 'messages.received' || data.fromMe) return
if (data.type === 'list_response') {
const choice = data.data.selected_row_id // 'morning' or 'afternoon'
const number = data.from.jid.split('@')[0]
dapi.messages
.sendText({ sessionId, to: number, text: 'Great, your delivery is scheduled.' })
.catch((err) => logger.error({ err, traceId: req.body.traceId }, 'reply failed'))
}
})
app.listen(3000)The WhatsApp webhook does not carry an HMAC signature, so the token in the URL path acts as the secret. You can also check the User-Agent header, which arrives as Deliverify-Webhook/1.0, and whether the sessionId belongs to one of your customers. Available events and the retry policy are in WhatsApp webhooks.
Checklist before going to production
- API key and webhook token in environment variables, never in the repository.
- Client timeout tuned to the context: shorter inside an HTTP route, longer in a worker.
- Deduplication by the message's
data.id, because the same delivery can arrive more than once if your server is slow to respond. - Logging with the event's
traceId, which helps support trace a specific delivery. - One
sessionIdper number. If your product will have one connection per customer, see how that is organized on the WhatsApp API for SaaS page.
