Skip to main content
Wavy Node uses HMAC-SHA256 to sign every request it sends to your integration. This guarantees that requests are legitimate and haven’t been tampered with in transit. Think of it as a seal of authenticity: only someone with the shared SECRET can generate a valid signature.

How it works

Step by step

  1. Wavy Node builds a canonical message from: HTTP method, path, body, and timestamp
  2. Signs that message with HMAC-SHA256 using the shared SECRET
  3. Sends the request with two extra headers:
    • x-wavynode-hmac — the generated signature
    • x-wavynode-timestamp — the timestamp in milliseconds
  4. Your server receives the request
  5. Recalculates the signature using the same SECRET and request data
  6. If signatures match → the request is legitimate (200). If not → reject it (401)

The shared SECRET

The SECRET is a 32-character hex string that both sides know:
615d9b7ea991acfa33f823c374c3a062
It must be configured in two places:
LocationHow to configure
Wavy Node dashboardSettings → Integración → Integration Secret
Your server (.env)SECRET=615d9b7ea991acfa33f823c374c3a062
Both values must be exactly the same. If they differ, signatures won’t match and all requests will fail with 401.

Generate a new SECRET

openssl rand -hex 16

Authentication headers

Every request from Wavy Node includes these headers:
HeaderTypeDescription
x-wavynode-hmacstringBase64 encoded HMAC-SHA256 signature of the canonical message
x-wavynode-timestampstringTimestamp in milliseconds (epoch)

Canonical message

The signature is not calculated on the raw request, but on a standardized canonical message. This ensures both sides build exactly the same string before signing. The canonical message is formed by concatenating these values separated by :::
  • The uppercase HTTP method (GET, POST, etc.)
  • The lowercase request path (e.g., /webhook)
  • The stringified request body with keys sorted alphabetically, or {} if no body
  • The timestamp from the x-wavynode-timestamp header
GET::/users/123::{}::1757050233763

Using @wavynode/utils

The easiest way to verify requests is with the validateSignature function:
import { validateSignature } from '@wavynode/utils';

const isValid = validateSignature({
    method: 'POST',
    path: '/webhook',
    body: req.body,
    timestamp: parseInt(req.headers['x-wavynode-timestamp']),
    secret: process.env.SECRET,
    timeTolerance: 300000,
    signature: req.headers['x-wavynode-hmac']
});

if (!isValid) {
    // The request is not from Wavy Node
    // or has been tampered with.
    // Reject the request.
}
ParameterTypeDescription
methodstringThe HTTP method of the request
pathstringThe path of the request
bodyobjectThe request body
timestampnumberThe timestamp from the x-wavynode-timestamp header
secretstringYour integration’s secret from Settings → Integración in the dashboard
timeTolerancenumberTime tolerance in milliseconds to prevent replay attacks (recommended: 300000)
signaturestringThe signature from the x-wavynode-hmac header
You can also build the canonical message manually with formCanonicalMessage:
import { formCanonicalMessage } from "@wavynode/utils";

const canonicalMessage = formCanonicalMessage({
    method: "GET",
    path: "/users/user-123",
    body: {},
    timestamp: 1712764800000
});

Manual authentication

If you are not using the @wavynode/utils package, you can implement the authentication logic yourself.
1

Create the canonical string

Concatenate the values separated by :: as described above:
GET::/users/123::{}::1757050233763
2

Create the HMAC signature

Create a sha256 HMAC of the canonical string using your integration’s secret as the key. Base64 encode the result.
import crypto from 'node:crypto';

const createHmacSignature = (message, secret) => {
    const hmac = crypto.createHmac('sha256', secret);
    hmac.update(message);
    return hmac.digest('base64');
};
3

Compare the signatures

Compare the signature you created with the one from the x-wavynode-hmac header. If they match, the request is authentic.

Why is HMAC necessary?

Without HMAC authentication, anyone who knows your integration URL could:
  • Send fake webhooks simulating transactions that never happened
  • Query user data without authorization
  • Inject false notifications into your system
HMAC guarantees that only Wavy Node can communicate with your integration server.