Hookdeck passes gzip bytes unchanged: verify Sume on raw bytes

Hookdeck now forwards binary and gzip bodies byte for byte. Read the raw bytes before parsing and verify the Sume sume-v1 signature over them.

4 min readSume
All posts

Hookdeck's September 24 change helps a Sume receiver: the destination now gets the original bytes, and Sume's signature is computed over raw bytes. The rule on your side stays the same: read the body unparsed and verify before you touch it.

Hookdeck facts are from its changelog entry; Sume facts from Job webhooks and Verifying webhooks, read 2026-10-01. The Hookdeck page says nothing about signatures or header forwarding, so confirm in a test delivery that the x-sume-webhook-* headers arrive.

What did Hookdeck change?

The entry says binary content types and Content-Encoding: gzip or x-gzip are passed through as encoded bytes, including gzipped JSON. Destinations get the original bytes with unmodified Content-Type and Content-Length, and retries and replays send identical bytes. Transformations see body: null for these payloads, though headers, path and query can still be transformed.

Hookdeck pass-through behavior from its changelog, read 2026-10-01
AspectBehavior
BodyOriginal bytes, including gzipped JSON
Content-Type, Content-LengthUnmodified
Retries and replaysIdentical bytes
Transformationsbody is null; headers, path, query still transformable

Why does that matter for the signature?

Sume signs HMAC-SHA256 over <timestamp>.<raw_body> and sends sume-v1=<hex> in x-sume-webhook-signature with x-sume-webhook-timestamp. A parsed-and-reserialized body will not verify, because key order and whitespace are part of what was signed. Byte-identical retries mean a replayed delivery verifies the same as the first, inside the 300-second default replay window.

What does the receiver look like?

Read an ArrayBuffer, which verifyWebhook accepts, and refuse to run without a secret.

Return a fast 2xx after storing the event. Dedupe on job_id.

import { verifyWebhook } from "@sume-com/sdk";

export async function POST(request: Request) {
  const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET ?? "";
  if (!secret) return new Response("not configured", { status: 500 });

  const body = await request.arrayBuffer(); // raw bytes, before any parse
  const ok = await verifyWebhook({ body, headers: request.headers, secret });
  if (!ok) return new Response("bad signature", { status: 401 });

  const event = JSON.parse(new TextDecoder().decode(body));
  console.log(event.event, event.job_id);
  return new Response(null, { status: 204 });
}

Should the relay transform the body at all?

Not for a signed delivery. Keep any Hookdeck rules to headers, path or query, and leave the body alone. Also remember Sume's attempt timeout is 10 seconds per try, with up to 10 attempts, and any 2xx counts as success.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume