Trigger.dev public tokens show Sume progress without the API key
Hand the browser a Trigger.dev read-only public token for one run while the task holds the Sume key. The Sume key never leaves the server.

The browser should hold a Trigger.dev token, not a Sume key. Trigger.dev's public access tokens are read-only and scoped to runs, while the task that calls Sume keeps the API key on the server, where the Sume docs say it must stay.
Trigger.dev facts are from its Realtime authentication docs and v4.6.0 changelog; Sume facts from the SDK overview and Authentication, read 2026-10-01.
What does a public token allow?
The docs say public access tokens are scoped to runs, tasks, tags or batches and are read and subscribe only. They expire after 15 minutes by default and cannot exceed 30 days. A token returned when you trigger a task is already scoped to the triggered run. For Sessions, the v4.6.0 changelog says reading the .in channel needs a secret key, and a public token gets a 403 there.
| Credential | Lives in | Can do |
|---|---|---|
| Sume API key | Task environment on the server | Spend credits, submit jobs |
| Trigger.dev secret key | Your server | Trigger tasks, mint tokens |
| Trigger.dev public token | Browser | Read one run (15 minutes by default) |
Why not call Sume from the browser?
A Sume API key spends your credits and there is no browser-safe variant. The docs say never to ship one to client JavaScript, a mobile bundle or a NEXT_PUBLIC_* variable. Poll on the server and expose results through your own endpoint.
How does the route hand it over?
Trigger the task from a server route and return only the run id and the token.
import { tasks } from "@trigger.dev/sdk";
export async function POST(req: Request) {
const { orderId, prompt } = await req.json();
const handle = await tasks.trigger("make-video", {
orderId,
prompt,
revision: 1,
});
return Response.json({
runId: handle.id,
publicAccessToken: handle.publicAccessToken, // read scope, this run
});
}What does the task do on the Sume side?
Submit with an Idempotency-Key, poll GET /v1/jobs/{id}/status and honor next_poll_after_seconds. Reads and writes have separate per-minute budgets, so polling cannot 429 your submits. The read and write limits post has the numbers.
Sources
Related posts
More in Developers
- Trigger.dev retries and Sume errors: skip 4xx, wait on retry-after
Trigger.dev retries any uncaught throw. Use catchError to skip Sume errors that cannot succeed, and read the retry-after header on a 429.
- Trigger.dev replay reuses the payload: key the Sume submit from it
A Trigger.dev replay starts a new run with the same payload. Derive the Sume Idempotency-Key from that payload, not a random id, or a replay bills twice.
- Trim video without re-encoding, then resize: why it fails
A stream-copy trim cannot resize: output width, height and fps fail video_trim_output_requires_exact. Pick a goal below and the call that fits it.
- Trim video longer than 15 minutes: video_trim_range_empty
Video trim refuses a range with end at or before start, or longer than 900 seconds, as video_trim_range_empty. Cut a long source in 15-minute ranges.
Written by Sume