subscribeFormatRun onCreated: save the run id, set your own key
subscribeFormatRun makes a new Idempotency-Key per call unless you pass one. Save the run id in onCreated and pass a stable key so a restart cannot double-run.

subscribeFormatRun in the Sume SDK creates a Format run and waits for it. Two options decide whether a crash is safe: onCreated hands you the accepted run before polling starts, so you can save its id, and idempotencyKey defaults to a fresh UUID on every call, so a restarted process that does not pass a stable key starts a second run.
Both points come from Waiting for runs and jobs and the SDK source. This page shows how to combine them.
What does onCreated give me?
onCreated is called once, with the accepted PublicFormatRun, after the create call succeeds and before polling begins. At that point the run exists and is billing, so this is the moment to write its id to your database.
If your process dies during the wait, the id lets a new process resume with waitForRun(runId, { client, family: "format" }) or read the receipt with getFormatRun. Without it, the only trace of the run is whatever you can recover by idempotency key.
Why does the default key matter?
The docs list idempotencyKey with the default auto UUID, and the source computes crypto.randomUUID() per call when you pass nothing. A fresh UUID per call means the server sees two unrelated requests when your process restarts and calls again.
With a stable key, the second call is a replay: the docs say a replay of a finished run returns immediately, and the SDK returns the terminal receipt without polling. Pass null only if you want no key at all, which also turns off client retries of the create.
| Option | Default | Why it matters |
|---|---|---|
| idempotencyKey | Auto UUID per call | Pass a stable key per intent to make restarts safe |
| onCreated | none | Store the run id once the run exists |
| timeout | 20 minutes | Wait budget; the run is not cancelled when it elapses |
| signal | none | Aborts the wait and the in-flight request |
What does a restart-safe call look like?
Derive the key from your own business id, such as an order, and store the run id in onCreated. A replay with the same key and body returns the existing run; a different body under the same key is a 409 idempotency_conflict.
import { createSumeClient, subscribeFormatRun } from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });
export async function promo(orderId: string, productUrl: string) {
return subscribeFormatRun({
client,
path: { handle: "acme", slug: "product-promo" },
idempotencyKey: `order-${orderId}-promo-v1`,
body: { input: { product_url: productUrl } },
onCreated: (run) => console.log("saved run", orderId, run.id),
});
}What happens when the create itself fails?
Then no run exists and onCreated is never called. The helper throws SumeRunRequestError with the run id shown as (not created), for example for 403 workspace_key_required on a team Format called with a personal key. A failed create also releases its idempotency key, so fixing the key and retrying is safe.
A run that fails while running is different: subscribeFormatRun resolves with status: "failed" instead of throwing, and a retry needs a new key, because the old one is bound to the receipt you already have.
What if the timeout fires first?
The helper throws SumeRunTimeoutError, the run keeps going, and the id you stored in onCreated is how you pick it up again. Bump the key's version suffix only when you truly want a new run, not to recover an old one.
Sources
Related posts
More in Developers
- Sume API rate limits by plan: requests per minute for writes and reads
Sume gives every API key a per-minute budget set by plan: 120 writes on Free up to 1200 on Scale, with reads at forty times the write number. Table and headers.
- Client timeouts for Sume jobs: SDK defaults and the 30-second cap
Sume's sync wait caps at 30 seconds, waitForRun defaults to 10 minutes, subscribeFormatRun and waitForJob to 20. Pick a deadline per job type, keep the job id.
- Choosing a Sume Idempotency-Key: business key plus a payload version
A good Idempotency-Key is stable across retries and changes with the request. Build it from your order id and a payload hash, or hit 409 idempotency_conflict.
- Music 1.0 is retiring: switch to /v1/music-router/generate in one line
Sume's Music 1.0 routes keep working but now resolve through the Music Router. Which URL to change, what stays the same, and how to see which engine ran.
Written by Sume