The hidden cost of reinventing WhatsApp in every project
The request shows up in almost every scope: the scheduling system needs to remind the patient, the ERP needs to send the invoice, the portal needs to notify the agent. At many software shops, each team solves it their own way. One project uses an open source library running on the client's server, another uses a different vendor, a third has a script someone wrote and nobody else understands.
The price shows up after delivery, in the maintenance contract. Each variant has its own way of going down, its own way of reconnecting and its own knowledge concentrated in one person. When that person leaves, the project becomes a risk. And since WhatsApp maintenance is rarely priced in, the contract margin disappears into hours nobody bills.
Standardizing on a managed WhatsApp API solves the tedious part: the infrastructure stays with the vendor, and your team only writes each client's business logic, which is what the contract pays for.
Who handles what
Make it clear in the scope, and to your own team, where the agency's responsibility ends and the API provider's begins.
| Responsibility | Dev agency | D-API |
|---|---|---|
| Rules for when and what to send | Yes | No |
| Handling replies and events in the system | Yes | No |
| Keeping the connection up and reconnecting | No | Yes, with continuous self-healing |
| Servers, IP and per-number isolation | No | Yes, each connection isolated with a dedicated IP |
| Resending webhooks when your system is down | Respond fast with a 2xx status | Up to 7 retries with exponential backoff |
| Guiding the client on number usage | Yes | Support for your team |
This split is the same build-or-buy discussion covered in detail in self-hosted vs managed.
A reusable WhatsApp module
The real gain comes when WhatsApp becomes an internal package, with the same interface in every project. What usually goes into that module:
- Per-project config: API key and session name come from environment variables, never from code.
- A send service that takes a phone number and content and hides the call details.
- A webhook endpoint with the same shape in every system, which checks the incoming
sessionIdand queues the event before processing. - Standard error handling: the API returns
success,errorandstatusCode, which allows a single mapping for logs and alerts. - Retries on your side. The SDK makes a plain HTTP call and doesn't retry on its own. Decide in the module which errors deserve a retry and with what interval.
In a Node project, the core of that module uses the official SDK:
import { DApi } from 'd-api-sdk'
const dapi = new DApi({ apiKey: process.env.DAPI_API_KEY! })
const SESSION = process.env.DAPI_SESSION_ID! // e.g. client-acme-production
export async function provision(webhookUrl: string) {
await dapi.sessions.create({ sessionId: SESSION, connectionMode: 'qr', webhookUrl })
const { qrCodeImage } = await dapi.sessions.getQRCode(SESSION)
return qrCodeImage // show it on the settings screen of the client's system
}
export function notify(phone: string, text: string) {
return dapi.messages.sendText({ sessionId: SESSION, to: phone, text })
}Install details and available methods are in the Node.js SDK and the WhatsApp API in Node.js guide. Projects on other stacks call the same REST API; the WhatsApp API in Python guide shows the pattern outside Node.
One session per client, one session per environment
Each client of the agency has its own session, which isolates number, credentials and webhook. Within the same client, separate environments too. A simple convention prevents the most common mistake in any project, a test message reaching a real customer:
acme-dev: the team's test number, webhook pointing to a local tunnel.acme-staging: the client's test number, used for acceptance testing.acme-production: the client's real number, production server webhook.
If one of the projects is a product the client will sell to many end users, with one number per user, the design changes scale and gets closer to SaaS. In that case, read WhatsApp API for SaaS.
Delivery, handover and maintenance contracts
Custom projects have a delivery date, and the WhatsApp part is usually what generates the most tickets after it. Three practices cut that volume:
- Document who owns the account. If the client will take over the system, the account and API key should be in their name from the start. Transferring later is rework.
- Monitor connection status. Subscribe to the
connection.statusevent and show in the system dashboard whether the number isconnected,disconnectedorlogged_out. The client solves many cases alone once they can see the problem. - Price maintenance by connection. D-API bills per connection, not per message, and the price per connection goes down as volume grows. So the WhatsApp cost of each contract is fixed and goes into the proposal without an inflated safety margin. The tiers are on pricing.
To validate the module on a pilot project, there's a 3-day free trial, no credit card. If your shop also serves agencies or runs automations for third parties, WhatsApp API for agencies shows the portfolio operating model.
