Zapier AI video automation: two Zaps and one Sume webhook

One Zap starts a Sume video run with a Custom Request; a second catches Sume's signed webhook with Catch Raw Hook and verifies it in a Code step.

5 min readSume
All posts

For Zapier AI video automation with Sume, use two Zaps: Zap A starts a Format run with a Webhooks by Zapier Custom Request and sets communication.webhook_url to Zap B's Catch Raw Hook URL. Zap B receives Sume's one signed POST when the run completes or fails, checks the signature in a Code step, and hands the video URL to your next app.

Sume has no Zapier app; both Zaps make or take plain HTTPS calls. Sume facts come from Create a run and Run webhooks; Zapier behavior comes from Zapier's help center, read 2026-09-27. The signature scheme is explained in Signed webhooks for Sume video runs.

Why two Zaps instead of one?

A run answers its create call at once and then takes minutes; long-form host video typically finishes in 15 to 30 minutes. Instead of holding a Zap open, let Sume call Zap B. Its webhook URL changes only if the Zap is transferred to another user, so Zap A can keep it as a fixed value.

From Zapier's Send webhooks and Trigger Zap workflows from webhooks articles and Sume's Run webhooks, read 2026-09-27.
StepZapier pieceSume side
Zap A actionWebhooks by Zapier, Custom RequestPOST /v1/formats/sume/{slug}/runs with communication.webhook_url
Zap B triggerWebhooks by Zapier, Catch Raw HookOne signed POST when the run completes or fails
Zap B step 2Code by Zapier, JavaScriptHMAC-SHA256 over <timestamp>.<raw_body>
Zap B step 3Your publishing apppayload.primary_output_url, a durable media.sume.com URL

How do I start the run from Zap A?

Pick Custom Request: Zapier sends its Data field exactly as entered, and it is the option Zapier names for nested JSON. Set the method to POST and the URL to a catalog Format, such as https://api.sume.com/v1/formats/sume/sume-product-commercial/runs. Then fill in the headers and Data:

  • Authorization: Bearer <key>, and not x-api-key as well: a request with both is 401 unauthorized. Use a key made for this Zap, and rotate it from the Sume dashboard if it is ever exposed.
  • Content-Type: application/json.
  • Idempotency-Key from the trigger record's id plus a version. The same key and body returns 200 with the original run and no second charge.
  • Fill in Data. Left blank, Zapier sends every field from the previous step, and Sume rejects unknown top-level fields with 400 unknown_parameter.
{
  "instruction": "Make a vertical product commercial from the attached photo.",
  "attachments": [
    { "type": "input_image", "image_url": "https://example.com/product.jpg" }
  ],
  "generation_spend_cap_usd": 20,
  "communication": { "webhook_url": "<Zap B's Catch Raw Hook URL>" }
}

How does Zap B receive the result?

Catch Raw Hook keeps the body unparsed, up to 2 MB, and includes the headers, which the signature check needs. Zapier answers 200 by default, and Sume counts any 2xx within 10 seconds as delivered.

  • Sume inlines receipts up to 1 MiB. A larger one arrives with payload: null and an error.result_url to fetch it from.
  • If Zap B is turned off, Zapier keeps answering 200 for up to several hours before it switches to 404, so a delivered status does not prove Zap B ran. POST /v1/format-runs/{run_id}/webhook/redeliver with formats:write sends the receipt again, even after Sume's 10 automatic attempts are exhausted.
  • Canceled and skipped runs never deliver a webhook.

How do I verify the signature in a Code step?

Code by Zapier runs Node.js 22 with the standard library, and your code sees mapped values only through Input Data. Map the raw body, the x-sume-webhook-timestamp and x-sume-webhook-signature headers, and your signing secret, which is on the Sume dashboard's Webhooks tab and is not your API key. For 24 hours after a secret rotation the header carries two sume-v1= entries, so accept a match on either, and reject timestamps outside five minutes. The step's output gives later steps the fields they map.

const crypto = require("crypto");
const { body, timestamp, signature, secret } = inputData;
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
const digest = crypto
  .createHmac("sha256", secret)
  .update(`${timestamp}.${body}`)
  .digest("hex");
const expected = Buffer.from(`sume-v1=${digest}`);
// Check every entry: during a rotation the header carries two.
const matches = signature.split(",").filter((entry) => {
  const actual = Buffer.from(entry.trim());
  return (
    actual.length === expected.length &&
    crypto.timingSafeEqual(actual, expected)
  );
});
const verified = Boolean(secret) && fresh && matches.length > 0; // empty secret: never verified
const { event, status, request_id, payload } = verified ? JSON.parse(body) : {};
output = { verified, event, status, request_id, video_url: payload?.primary_output_url ?? null };

What should Zap B do with the result?

Let later steps run only when verified is true and event is format.run.terminal. Before a paid run, prove the check with Sume's Send test (/dashboard/webhooks, or POST /v1/webhooks/test-deliveries with account:write), which POSTs a signed webhook.test body to a URL you type; debugging webhook delivery covers it. The envelope rules in Sume Format run lifecycle come down to three checks in Zap B:

  • Dedupe on request_id. It equals the run id and repeats on every retry.
  • status is OK when the run completed and ERROR when it failed. On OK, publish video_url, a durable media.sume.com URL.
  • When payload arrived as null (a receipt over 1 MiB), or to double-check, read GET /v1/format-runs/{run_id} with your key from a Webhooks by Zapier GET step; its data is the same receipt.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume