WWaSphere
GitHub
WaSphere Teambaileyswhatsapp-apiwebhooksproduction

Baileys WhatsApp API in Production: Webhooks, Sessions & Anti-Ban

Running a Baileys-based WhatsApp API in production means solving session persistence, signed webhooks, and anti-ban pacing. Here's how WaSphere does it.

Baileys WhatsApp API in Production: Webhooks, Sessions & Anti-Ban

Baileys is the most capable open-source library for talking to WhatsApp's multi-device protocol over WebSocket. It's also a library, not a product — the moment you put it in production you inherit a pile of operational problems Baileys doesn't solve for you: keeping sessions alive across restarts, delivering inbound events reliably, and pacing your sends so WhatsApp doesn't flag the number.

WaSphere wraps Baileys into a self-hosted platform and handles exactly those concerns. This post walks through the three that matter most: sessions, webhooks, and anti-ban.

Sessions: surviving restarts and running many at once

Baileys authenticates by writing credential and signal-key state to disk. If you lose that state, the session is gone and the user has to re-scan a QR code. Two things break naive setups:

  1. Ephemeral storage. Run Baileys in a container without a persistent volume and every redeploy logs you out.
  2. One process, many numbers. Production means multiple WhatsApp numbers. Each is a separate Baileys socket with its own auth state, lifecycle, and reconnect logic.

WaSphere isolates all Baileys code in a single WA Server process and persists each session's credentials to a mounted volume, so containers can restart without re-pairing. Each session is an independent connection with its own status (connecting, connected, disconnected) and its own QR lifecycle. The dashboard streams the QR for pairing; once linked, the credentials persist.

Architecturally this isolation matters for another reason: Baileys depends on libsignal (GPLv3). WaSphere keeps Baileys confined to the MIT-licensed WA Server binary and never lets other code import it directly — everything else talks to it over HTTP. If you're building your own stack, draw the same boundary early.

Webhooks: delivering inbound events you can trust

A WhatsApp API that can only send is half a product. You need inbound messages, delivery receipts, read receipts, and session-status changes pushed to your application. Baileys emits these as in-process events — turning them into reliable, verifiable HTTP deliveries is on you.

WaSphere POSTs an event to your endpoint for each of 10 event types — including message.received, message.sent, message.failed, message.delivered, message.read, and session.disconnected. Every delivery carries signing headers:

Content-Type: application/json
X-WaSphere-Event: message.received
X-WaSphere-Signature: v1,sha256=<hmac-hex>
X-WaSphere-Timestamp: 1748168400
X-WaSphere-Delivery-Id: abc123

The signature is HMAC-SHA256(signingSecret, "{timestamp}.{rawBody}"), formatted as v1,sha256={hex}. Verify it before processing — without verification, anyone who learns your webhook URL can forge events. Verification in Node:

import crypto from 'crypto';

function verify(rawBody, signature, timestamp, secret) {
  // Reject anything older than 5 minutes — replay-attack guard
  if (Math.abs(Date.now() / 1000 - parseInt(timestamp, 10)) > 300) return false;

  const expected = 'v1,sha256=' + crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Use the raw request body, not a re-serialized object — JSON re-encoding changes byte order and the HMAC won't match. Respond 2xx fast (under 10 seconds) and process asynchronously; if you block, the delivery is marked failed and retried, and you'll process duplicates. Failed deliveries retry with backoff (1s, 5s, 30s) before being marked failed in the delivery log.

Anti-ban: pacing so the number survives

WhatsApp watches for automation that doesn't behave like a human. Blasting hundreds of identical messages in a tight loop from a fresh number is the fastest way to a ban. Baileys will happily let you do exactly that — it has no opinion on pacing.

WaSphere applies platform-wide anti-ban defaults you can tune per session:

  • Randomized inter-message delay between a configurable min and max, so sends aren't metronomic.
  • Typing simulation — a typing indicator before text messages, scaled to message length.
  • Per-session rate limiting — messages over the limit are queued, not dropped.

Practical guidance that no library gives you:

  • Warm up new numbers. Start with low volume and ramp over days, not minutes.
  • Use real, opted-in recipients. Sending to numbers that block or report you is the strongest ban signal.
  • Dedicate the number to automation. Never run automation on a personal account.
  • Vary content. Identical message bodies at high volume look like spam.

These are heuristics, not guarantees — WhatsApp's enforcement is opaque. But pacing, warming, and consent move the odds heavily in your favor.

Sending without touching Baileys directly

Once the platform owns the hard parts, sending is a plain HTTP call. The session is in the path; your scoped API key authenticates:

curl -X POST \
  "https://api.your-domain.com/workspaces/{workspaceId}/proxy/api/sessions/{sessionId}/messages/text" \
  -H "Authorization: Bearer wsk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "to": "447700900123", "text": "Hello from production." }'

Fourteen message types are supported — text, image, video, audio, document, sticker, location, contact, poll, reaction, list, buttons, template, and link preview — all behind the same endpoint shape.

The takeaway

Baileys gives you the protocol; production gives you the problems. Persistent multi-session state, signed and retried webhooks, and anti-ban pacing are the difference between a demo and something you can put a business on. WaSphere packages all of it as a self-hosted, MIT-licensed Docker stack so you don't rebuild it yourself.

Get a session connected in a few minutes with the Quick Start guide.

Get Started

Ship a self-hosted WhatsApp API today

Clone the repo, set your secrets, and send your first message in minutes — Docker-based, MIT licensed, zero config.

Read the Quick Start