Your backend in front of Sume video: return 202 and a job id

A 30-line Node server that submits a Sume video job with POST /v1/videos and answers your browser 202 with a job id, instead of holding the request open.

4 min readSume
All posts

Why not hold the request

Sume documents the safe shape for browsers and mobile apps: the client calls your backend, and your backend attaches the API key. The temptation is to make that backend call wait for the video. Don't. Sume's video docs say generation usually takes from 30 seconds to several minutes, every edge closes an idle request at some point, and Sume's own sync wait is capped at 30 seconds.

Do what Sume does: accept, hand back an id, let the client ask again.

The contract

Your two routes, built on Sume job endpoints read 2026-10-05
Your routeCalls SumeReturns
POST /renderPOST /v1/videos202 and your job id
GET /render/:idGET /v1/videos/{id}Sume status object (pending, in_progress, completed, failed)

The server

Run it with node server.mjs on Node 18 or newer. It validates the id shape before forwarding, so the browser cannot make your server fetch arbitrary Sume paths, and it passes the browser's Idempotency-Key through so a double click does not bill twice.

import http from "node:http";
const H = { Authorization: "Bearer " + process.env.SUME_API_KEY, "Content-Type": "application/json" };
const send = (res, code, body) => {
  res.writeHead(code, { "Content-Type": "application/json", "Cache-Control": "no-store" });
  res.end(JSON.stringify(body));
};
http.createServer(async (req, res) => {
  if (req.method === "POST" && req.url === "/render") {
    let raw = "";
    for await (const c of req) raw += c;
    const { prompt } = JSON.parse(raw);
    const key = req.headers["idempotency-key"];
    if (!prompt || !key) return send(res, 400, { error: "prompt and Idempotency-Key required" });
    const r = await fetch("https://api.sume.com/v1/videos", {
      method: "POST", headers: { ...H, "Idempotency-Key": key },
      body: JSON.stringify({ model: "sume/auto", prompt }) });
    const j = await r.json();
    return send(res, r.ok ? 202 : r.status, r.ok ? { job_id: j.id } : j);
  }
  const m = /^\/render\/([A-Za-z0-9_-]+)$/.exec(req.url);
  if (req.method === "GET" && m) {
    const r = await fetch("https://api.sume.com/v1/videos/" + m[1], { headers: H });
    return send(res, r.status, await r.json());
  }
  send(res, 404, { error: "not found" });
}).listen(3000);

Details that matter

  • Check your own authorization before you forward anything; the Sume key carries your workspace spend.
  • Send Cache-Control: no-store on the status route so no CDN serves a stale status.
  • Return the Sume error body to the client unchanged on failures; it holds a request id safe to share with support.
  • A 2xx from Sume means the job exists and paid work is in flight, not that it finished.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume