Zapier Catch Raw Hook: verify a Sume webhook signature

Zapier's Catch Raw Hook returns unparsed data plus headers, which a Sume HMAC check needs. Here is what to verify and the size limits to know.

4 min readSume
All posts

Use Catch Raw Hook if you want to verify a Sume webhook inside Zapier. Sume signs the raw JSON body, and Zapier documents Catch Raw Hook as returning unparsed data together with headers, so it is the trigger the page describes as keeping the bytes and the x-sume-webhook-signature header you need.

Zapier facts are from its help article; Sume facts from Webhooks, both read 2026-09-30.

What does Sume sign?

When signing is configured, Sume computes HMAC SHA 256 over <timestamp>.<raw_body> and sends x-sume-webhook-timestamp and x-sume-webhook-signature: sume-v1=<hex_signature>. During a secret rotation the header carries one entry per live secret, so accept the delivery when any sume-v1= entry matches. Reject a timestamp outside your tolerance; five minutes is the docs' reasonable default.

Why Catch Raw Hook and not Catch Hook?

A parsed body is not the raw body, so re-serialising it can change the bytes and break the HMAC. Zapier's page says Catch Hook parses the request body, while Catch Raw Hook returns it unparsed and includes headers.

Zapier size limits from its help article, read 2026-09-30
TriggerMax payload
Catch Hook10 MB
Catch Raw Hook2 MB, includes headers

Do Zapier's size limits matter for Sume?

Job webhooks carry a small public result with artifact URLs. For Format run receipts, Sume's Run webhooks page says a receipt over 1 MiB is not delivered inline: the body has payload: null and a result_url to fetch. 1 MiB is below Zapier's 2 MB raw limit, so an oversized receipt reaches the Zap as that small envelope.

What does a verifier look like?

A Node check, for example in a Code step or your own endpoint. It refuses an empty secret and compares in constant time.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifySume(rawBody, timestamp, header, secret) {
  if (!secret) throw new Error("signing secret is empty");
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;
  const want = createHmac("sha256", secret)
    .update(timestamp + "." + rawBody)
    .digest("hex");
  return header.split(",").some((part) => {
    const got = part.trim().replace(/^sume-v1=/, "");
    return (
      got.length === want.length &&
      timingSafeEqual(Buffer.from(got), Buffer.from(want))
    );
  });
}

What if verification fails?

Do not act on the event. Read the job by id from status_url instead, and treat job_id as the idempotency key. For a delayed or missing delivery see Zapier 200 then delayed.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume