Skip to content

Manual validation with a real WhatsApp account

Automated tests use the mock provider. Behaviour against real WhatsApp servers (QR pairing, encryption, receipts, restrictions) cannot be tested automatically and must be validated by hand before each release that changes the Baileys adapter, the session manager, or the Baileys version.

Use a dedicated test SIM/number that you own and a second phone as the recipient. Never use a customer's number.

Setup

  1. Run the stack (locally or on staging) with WHATSAPP_PROVIDER=baileys, BAILEYS_LOG_LEVEL=warn, LOG_LEVEL=info.
  2. Create an organization, project and API key (scopes instances:*, messages:*, media:*, webhooks:*, events:read).
  3. Register a webhook pointing to a receiver you can watch (e.g. the example receiver: WEBHOOK_PORT=4999 pnpm --filter @whatstig/example-node start behind a tunnel, or any request bin you control).

Checklist

Record pass/fail, time and the instance/message IDs for each step.

#StepExpected
1Create instance, POST /connect {"method":"qr"}, show qrDataUrlStatus QR_REQUIRED; instance.qr_required webhook
2Scan with the test phone (Linked devices → Link a device)Within seconds: CONNECTED, phoneNumber = test number, instance.connected webhook. Phone lists the linked device
3Leave the QR unscanned for 3+ minutes on a second instanceDISCONNECTED / QR_TIMEOUT; no further QR codes generated
4Pairing code: new instance, connect {"method":"pairing_code","phoneNumber":"+<test number>"}, enter the code on the phone (Link with phone number instead)CONNECTED
5Send a text to the recipient phoneQUEUED → PROCESSING → SENT; message arrives; DELIVERED when the recipient's phone receives it; READ after opening (if read receipts enabled); matching webhooks; messages.sent usage +1
6Upload a JPEG and a PDF, send bothArrive with caption / file name; statuses as in 5
7Reply from the recipient phone (text, then an image)message.received webhooks with sender number and content; listed under direction=inbound; with "Store inbound media" on, GET /v1/media/{id}/download returns the image
8Send to a number without WhatsAppFAILED with RECIPIENT_NOT_ON_WHATSAPP or INVALID_RECIPIENT
9Restart the worker container (docker restart …whatsapp-worker)Within ~30–60 s back to CONNECTED without a QR; messages queued during the restart are sent afterwards
10Kill the worker hard (docker kill) while sending a batch of 20 messagesAfter takeover: no duplicates on the recipient phone; any message caught mid-send is UNKNOWN (or upgraded by a later receipt); the rest are sent
11Disable network on the server briefly (or block WhatsApp's hosts)RECONNECTING, then CONNECTED when restored
12On the test phone remove the linked deviceLOGGED_OUT / LOGGED_OUT_FROM_PHONE, instance.logged_out webhook, no reconnect loop in the logs, credentials gone (has_credentials = false)
13POST /logout on a connected instanceDevice disappears from the phone's linked devices; LOGGED_OUT / MANUAL_LOGOUT
14DELETE an instance with queued messagesDevice unlinked, queued messages FAILED / INSTANCE_DELETED, other instances unaffected
15Hosted connect link: create, open on a laptop, scanPage shows QR then "WhatsApp connected"; reopening the link no longer shows a QR
16LogsNo QR payloads, keys or message text in any service log

Capacity measurement (optional)

To size WORKER_MAX_SESSIONS with real sessions, link N authorized test accounts to one worker and record process_resident_memory_bytes, nodejs_eventloop_lag_p99_seconds and CPU at idle and while sending, for N = 1, 5, 10, 25. Report these separately from the simulated numbers in Benchmarks. Do not use accounts you are not authorized to operate.