OpenRouter video payload on Sume: drop seed, size, provider.options
Sume /v1/videos follows the OpenRouter shape but rejects seed, size and provider.options with a 400. A Node function that strips them and checks the model id.

A client written for the OpenRouter video API works on Sume after you change the base URL and key, with a short list of exceptions. Three request fields are rejected with 400: seed, size, and a non-empty provider.options. The model id must also be a bare Sume catalog id, not an org/slug id. The Node function below removes the three fields, reports what it removed, and throws when the model is not in your list of Sume ids.
What differs
Sume's /v1/videos reference says it agrees field-for-field with OpenRouter's video generation guide, and then lists the exceptions in one table. The rows that change a request body are these.
| Area | OpenRouter | Sume |
|---|---|---|
| Base URL | https://openrouter.ai/api/v1/videos | https://api.sume.com/v1/videos |
| Model ids | org/slug | Bare catalog ids such as seedance-2 |
| size | Accepted when the model lists supported_sizes | Every v1 model reports supported_sizes null, so 400 unsupported_parameter; use resolution plus aspect_ratio |
| provider.options | Forwarded to the matched provider | Non-empty value returns 400 unsupported_parameter |
| seed | Many models accept it | No v1 model accepts it; each reports seed false |
| Idempotency | None on this route | Idempotency-Key header; a replay returns the original job |
The function
forSume destructures seed, size and provider away from the body, and records a note for each one that was present. An empty provider object, or one with empty options, is allowed. The model check runs after the destructuring, so a wrong id fails before any network call.
Silent dropping hides a real change: a seeded request was asking for repeatable output, and Sume cannot promise it. Log the notes. If your product depends on seeds, run those jobs elsewhere rather than pretending the field was honored.
The demo call sends wan-3.0 for 8 seconds with all three bad fields, so it prints a body without them and a dropped list with three entries. Without the function, the same request would come back as a 400 whose error envelope names the field and carries a request_id, which is fine in a test and wasteful in production, because every rejected call still uses a write from your per-minute budget.
export function forSume(body, listedIds) {
const { seed, size, provider, ...rest } = body;
const dropped = [];
if (seed !== undefined) dropped.push("seed");
if (size !== undefined) dropped.push("size (use resolution + aspect_ratio)");
if (provider && Object.keys(provider.options ?? {}).length) dropped.push("provider.options");
if (!listedIds.includes(rest.model)) {
throw new Error(`${rest.model} is not in GET /v1/videos/models; pick a listed bare id`);
}
return { body: rest, dropped };
}
const listed = ["seedance-2.5", "wan-3.0", "minimax-h3", "gemini-omni-flash-1.1"];
const out = forSume(
{
model: "wan-3.0",
prompt: "A kite over a dune",
seed: 42,
size: "1280x720",
provider: { options: { foo: 1 } },
duration: 8,
},
listed,
);
console.log(out);What to do about size
size looks harmless because 1280x720 is the same shape as 720p at 16:9. Sume does not translate it. Pick resolution (480p, 720p, 1080p and so on, from supported_resolutions) and aspect_ratio from the model's lists. For a 1280x720 intent send resolution 720p with aspect_ratio 16:9.
After the body, check the rest of your client. Webhooks use Sume's own job envelope with x-sume-webhook-signature, not video.generation events. Billing is the workspace USD balance rather than credits, and usage.cost is the amount billed.
Finally, test the ported client against GET /v1/videos/models rather than against a recorded fixture. The list endpoint tells you which ids exist today, and the function above will throw for any id that is not in it. That one check catches both a typo and a model Sume does not list.
- Call GET /v1/videos/models and pass the ids to forSume.
- Fail on unknown models rather than guessing a provider.
- Replace any OpenRouter signature check before you move webhooks.
Sources
Related posts
More in Developers
- POST /v1/videos status codes: which of 10 are safe to retry
OpenAPI lists 202 plus ten error codes for POST /v1/videos. Which to retry with the same key, which to fix or stop on, a Python classifier and a backoff plan.
- Preflight a mixed 30 s batch: 2 Seedance and 4 Wan needs $49.67
Sum the reserve for two Seedance 2.5 720p clips and four Wan 3.0 720p clips (49,668,000 micros), compare with GET /v1/balance in integers, then submit.
- Fresh Idempotency-Key per proxy call: why a Sume retry bills twice
If your server proxy mints a new Idempotency-Key on every request, a browser retry becomes a second paid Sume job. Forward the client's key instead; TypeScript.
- Python asyncio loop for a Sume job: next_poll_after_seconds
A runnable httpx and asyncio loop for GET /v1/jobs/{id}/status that honors next_poll_after_seconds, backs off otherwise, and leaves the job running on timeout.
Written by Sume