Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rmz.gg/llms.txt

Use this file to discover all available pages before exploring further.

Every RMZ webhook delivery is signed with HMAC-SHA256. Verifying the signature ensures the request genuinely came from RMZ and was not tampered with in transit.
Signature verification is the only reliable way to confirm a webhook is from RMZ. Anyone who learns your webhook URL can POST to it — without verification, they could grant themselves paid access (e.g., on a SaaS paywall) or trigger fulfillment on fake orders.

How signing works

  1. RMZ serializes the webhook payload to JSON
  2. The JSON string is signed using HMAC-SHA256 with your webhook’s secret key
  3. The resulting hex-encoded hash is sent in the Signature header
  4. Your server recomputes the hash and compares it to the header
POST /your-endpoint HTTP/1.1
Content-Type: application/json
Signature: a1b2c3d4e5f6...
X-RMZ-REQUEST-ID: 12345
X-RMZ-WEBHOOKS: 1.2

{"event":"order.created","data":{...}}

Where to find your secret key

In the dashboard:
  1. Go to الإعداداتالويب هوك (Webhooks)
  2. Click معاينة on the webhook you want to verify
  3. The المفتاح السري للتوقيع (HMAC Signing Key) field shows the secret. Click the eye icon to reveal it, the copy icon to copy.
If you ever suspect the secret has leaked, click the regenerate (↻) button next to the key. The old key stops working immediately — make sure your server-side verification is updated before the next webhook fires.

Verification steps

1

Extract the signature

Read the Signature header from the incoming request.
2

Get the raw request body

Read the raw body as a string. Do not parse to JSON first — the signature is computed on the exact bytes RMZ sent. Re-serializing changes whitespace and key ordering.
3

Compute the expected signature

Calculate HMAC-SHA256(raw_body, secret_key) and hex-encode the result.
4

Compare in constant time

Use a constant-time comparison (hash_equals in PHP, hmac.compare_digest in Python, crypto.timingSafeEqual in Node) to avoid timing attacks. Reject the request with HTTP 401 on mismatch.

Code examples

const crypto = require("crypto");
const express = require("express");

const app = express();

function verifySignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  // Both buffers must be the same length for timingSafeEqual.
  if (signature.length !== expected.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// IMPORTANT: use express.raw, not express.json — verification needs raw bytes.
app.post(
  "/webhooks/rmz",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["signature"];
    const secret = process.env.RMZ_WEBHOOK_SECRET;

    if (!verifySignature(req.body.toString(), signature, secret)) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const payload = JSON.parse(req.body);
    // Safe to process — handle payload.event and payload.data here.
    res.status(200).json({ received: true });
  }
);

Common pitfalls

The signature is computed on the exact JSON string RMZ sends. If you parse and re-serialize it, whitespace and key ordering can change — producing a different hash and a false mismatch. Always verify against the raw request body.
Most common causes:
  • You’re computing the hash on parsed JSON instead of the raw body
  • Your framework strips trailing whitespace or BOM from the request body
  • You copied the secret with leading/trailing whitespace
  • You’re using the wrong webhook’s secret (each webhook has its own key)
  • The webhook was rotated and you haven’t updated your server with the new key
Check that Content-Length matches the actual body length, and log both the expected and received hashes during debugging (then remove the logs).
In the dashboard:
  1. Open the webhook in معاينة mode
  2. Click the regenerate (↻) button next to the key
  3. Confirm — the old key stops working immediately
  4. Update your server’s RMZ_WEBHOOK_SECRET environment variable with the new key
  5. Redeploy
Plan a brief outage window when rotating, since any in-flight webhook delivered between the regeneration and your redeploy will fail verification.
Yes. Every retry uses the current webhook secret. If you rotate the secret while a webhook is being retried, the next retry will be signed with the new key.
Use it for idempotency in your handler — if you receive two requests with the same X-RMZ-REQUEST-ID, treat them as the same event. RMZ may retry on transient failures (5xx, network errors) up to the configured tries count.
Even with signature verification, validate the payload shape before acting on it: check that event is one you handle and that data contains the expected fields. Defensive parsing prevents bugs from breaking-but-still-signed payloads (e.g., during RMZ rolling deploys).