MailGraf
TR
API and webhooks

Verify webhook signatures

Confirm that a webhook request came from MailGraf before processing its payload.

Updated 23 Aug 2026Open Webhooks in app

Every MailGraf webhook delivery includes an HMAC signature. Verify it before trusting the payload so a third party cannot send a forged event to the same endpoint.

Read the signing values

Open the endpoint under Settings > Webhooks. Use Reveal beside Signing secret, copy the value beginning with whsec_ and store it in the receiving service's secret manager. Do not put the secret in source code or logs.

Each request carries this header:

X-MailGraf-Signature: t=<unix_timestamp>,v1=<hex_signature>

MailGraf signs the exact request body with HMAC-SHA256. The signed message is:

<timestamp>.<raw_request_body>

Verify before parsing JSON

  1. Capture the raw body

    Read the request body as bytes before a JSON parser or middleware changes it.
  2. Read t and v1

    Split the X-MailGraf-Signature header into its timestamp and hexadecimal signature.
  3. Calculate the expected signature

    Join the timestamp, a full stop and the raw body. Sign that value with the endpoint's signing secret and HMAC-SHA256.
  4. Compare in constant time

    Compare the expected and received signatures with a timing-safe function. Process the event only when they match.

Node.js

Pass the unmodified body as a Buffer:

const crypto = require("crypto");

function verifyMailGrafSignature(secret, signatureHeader, rawBody) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((part) => part.trim().split("=", 2))
  );

  if (!parts.t || !parts.v1) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.`)
    .update(rawBody)
    .digest();
  const received = Buffer.from(parts.v1, "hex");

  return (
    received.length === expected.length &&
    crypto.timingSafeEqual(received, expected)
  );
}

Python

Pass the unmodified body as bytes:

import hashlib
import hmac


def verify_mailgraf_signature(secret, signature_header, raw_body):
    parts = dict(
        part.strip().split("=", 1) for part in signature_header.split(",")
    )
    timestamp = parts.get("t")
    received = parts.get("v1")
    if not timestamp or not received:
        return False

    message = timestamp.encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received)

After verification, queue any slow work and return a 2xx response within seven seconds. MailGraf treats a slower response as a failed delivery even if the service finishes processing later.

Rotate the signing secret

Use Rotate when the secret may have been exposed or as part of your own security schedule. Rotation invalidates the old secret immediately. Copy the new value, update the receiving service and click Send test event to confirm the change.

Reject requests that do not match

Do not process an event when the header is missing, malformed or does not match the calculated signature. Never fall back to accepting an unsigned payload.

Where the full reference lives

The API reference describes MailGraf's public API. Webhook signing values and verification examples are available on each endpoint under Settings > Webhooks.

Frequently asked questions

Can I parse the JSON before verification?

No. Verify the signature against the exact raw request body first. Parsing and serialising the JSON can change the bytes and invalidate the signature.

What happens when I rotate the signing secret?

The old secret stops working immediately. Update the receiving service with the new secret and send a test event.

What should I do when verification fails?

Do not process the payload. Check the endpoint's current signing secret, the signature header and how the server captures the raw body.

Related articles

Was this helpful?

Still need help?

Write to us and a person will answer.

Contact us
MailGraf

Professional email marketing platform.

Don't miss out

Get the latest email marketing tips and exclusive updates.

ISO CertifiedGDPR CompliantCSA Certified

MailGraf is a trading name of MailGraf Digital Ltd, registered in England and Wales, No. 13282175. ICO ZB250899.