Skip to main content
RelayWA

ENGLISH DEVELOPER TUTORIAL · 9 MIN READ

Node.js WhatsApp API: Send Messages and Verify Webhooks

Build a Node.js WhatsApp API integration with QR-linked sessions, Bearer auth, idempotent sends and signed webhook verification.

Published · Last verified · RelayWA Engineering

Architecture disclosure: RelayWA uses QR-linked WhatsApp Web sessions. It is not Meta's official WhatsApp Cloud API or a Meta Business Solution Provider. Do not send unsolicited messages.

Prepare a QR-linked session and server-side credentials

RelayWA is a QR-linked WhatsApp Web session API, not Meta’s official WhatsApp Cloud API. First create an account, connect one WhatsApp number through Linked devices, and issue a session-bound API key with messages.send scope.

Install Node.js 20 or newer. Store RELAYWA_SESSION_KEY and RELAYWA_WEBHOOK_SECRET in your server environment, not browser JavaScript, public repositories, or mobile app bundles. Ask recipients for meaningful opt-in and respect applicable WhatsApp account rules.

  • Send endpoint: POST https://relaywa.com/api/send-message
  • Required JSON: to (international digits), text, and a stable clientMessageId for each business event
  • Send and webhook response are transport events, not proof a recipient read the message

Send an order update with a stable idempotency key

Your app must make the call from a trusted server. The runnable example has a createRelayWaClient function that validates inputs and accepts an injectable fetch implementation for offline tests.

Use an order-specific clientMessageId such as order-123-ready-v1. The API stores it per session to reduce duplicate message creation. If the request times out, delivery may already be in progress: look up the message or retry only with the same key after checking current status.

const sendText = createRelayWaClient({ sessionKey: process.env.RELAYWA_SESSION_KEY });
await sendText({
  to: "12025550123",
  text: "Your order #123 is ready.",
  clientMessageId: "order-123-ready-v1"
});

Verify signed webhooks before processing events

Configure a public HTTPS webhook in your workspace. RelayWA signs the exact raw HTTP request body with HMAC-SHA256 using the secret returned during webhook creation, preceded by the x-relaywa-timestamp and a period. It sends x-relaywa-signature in sha256=HEX format.

Do not parse and then reserialize JSON before hashing: whitespace and key order matter. Use crypto.timingSafeEqual for the 32-byte digest and reject stale timestamps to limit replay. Persist event.id with a database UNIQUE constraint and acknowledge duplicates before scheduling downstream processing.

const computed = createHmac("sha256", webhookSecret)
  .update(timestamp).update(".").update(rawBodyBuffer).digest();
const valid = submitted.length === computed.length &&
  timingSafeEqual(submitted, computed);

Handle failures without promising guaranteed delivery

401 signals invalid credentials, 403 indicates a missing scope or authorization, and 409 can indicate a disconnected session. In these cases, fix the cause before sending again. Transport errors or 503 can have an uncertain outcome; use the returned message ID when available and inspect session/message status.

RelayWA does not operate an outbound Redis queue or automatically retry failed outbound sends. Scheduling, pacing, and bounded retry backoff belong in your application. Webhook delivery retry behavior is separate from outgoing message retry behavior.

  • Test with a connected number you control and consenting recipient
  • Run the included Node fixture tests locally or in GitHub CI before adapting the code
  • Never promise ban immunity, guaranteed recipient delivery, or official Meta authorization

Use the tested examples and canonical API reference

Browse the complete example source at RelayWA's example source on GitHub. Keep production secrets private. Run the tests before integrating with real customer accounts.

REST API quickstart · Signed webhook specification · Compare plans

← All developer tutorials