Idempotency-Key from a body hash plus a take number (Node, Sume)

Build an Idempotency-Key for Sume /v1/videos from a SHA-256 of the body plus a take counter, so a retry replays and a re-roll pays. Node code.

5 min readSume
All posts

A good Idempotency-Key for a video submit answers one question: is this the same purchase, or a new one? Hash the request body, keep the first 24 hex characters, and add a take number you increment on purpose. A network retry reuses the same key and gets the original job back; a deliberate re-roll bumps the take, gets a new key, and creates a new paid job. Sume documents both halves: a replay returns the original job, and the same key on a different payload returns 409 idempotency_conflict.

Why the body hash is not enough

If the key were only the body hash, a second submit of the same prompt would always replay the first job, so you could never ask for another take of the same prompt. If the key were a random UUID created on each call, a retry after a timeout would look like a new purchase and could bill twice. The take number separates the two cases: retries share it, re-rolls change it.

The sample uses seedance-2.5 at 480p for 4 seconds in 9:16. seedance-2.5 bills a minimum of 4 seconds, and 480p is $0.268677 per second, so one take costs 4 x 0.268677 = $1.074708, about $1.07. Three takes cost $3.22, which is the number to compare against your budget before a loop calls submit.

How the key behaves for the same body (Sume docs and price list, read 2026-10-09)
CallKeyOutcome
Submit, then retry after a timeoutdraft-<hash>-take1 both timesReplay: the original job is returned, one charge
Same prompt, new takedraft-<hash>-take2New key, new job, billed again ($1.074708)
Prompt edited, take number unchangednew hash, so new keyNew job; the edit changes the hash
Different body sent under an old keyold key409 idempotency_conflict, nothing runs

The Node code

The script hashes JSON.stringify(body). Build the object the same way every time, because key order changes the string. Run it as node submit.mjs 1 and then node submit.mjs 1 again; the second call returns the same job id. node submit.mjs 2 starts a second job.

import { createHash } from "node:crypto";

const body = {
  model: "seedance-2.5",
  prompt: "Espresso pour, macro shot, steam rising",
  duration: 4,
  resolution: "480p",
  aspect_ratio: "9:16",
};
const digest = createHash("sha256").update(JSON.stringify(body)).digest("hex");
const keyFor = (take) => `draft-${digest.slice(0, 24)}-take${take}`;

async function submit(take) {
  const res = await fetch("https://api.sume.com/v1/videos", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUME_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": keyFor(take),
    },
    body: JSON.stringify(body),
  });
  return { status: res.status, key: keyFor(take), job: await res.json() };
}

const take = Number(process.argv[2] ?? 1);
console.log(await submit(take));

Rules to keep

Use a key again only for an exact retry; that is the rule in the Sume admission docs. Do not put a timestamp in the key, because a retry then generates a new key. Do not put the API key or customer data into it, because keys are logged in your own systems.

If a submit times out and you do not know whether Sume received it, send the same call again with the same key. If it did, you get the existing job; if it did not, you get a new one. Either way you pay once.

The sample prints the HTTP status and the parsed body together. Read the status first: a 202 means a job exists (new or replayed), while a 4xx means nothing was created and the key is free to use again after you fix the request.

  • Store the key next to the job id in your database.
  • Increment the take only when a person asks for another variation.
  • Treat a 409 idempotency_conflict as a bug in your key derivation, not as something to retry.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume