job_scope_forbidden: why an agent run cannot list every Sume job

A thread-bound Sume credential lists only its own thread's jobs, and unattended runs cannot ask for the whole workspace. What to use instead.

5 min readSume
All posts

If an unattended agent run asks Sume for scope=workspace when listing jobs, it gets job_scope_forbidden. That is by design. A Studio Agent turn sees only the jobs its own thread created, and only an interactive turn may widen to the whole workspace. An ordinary API key, in contrast, defaults to the workspace. So the same GET /v1/jobs call can return different sets depending on which credential made it.

The fix is not a workaround. It is choosing the credential that matches the question you are asking.

What does the spec say about scope?

The Sume OpenAPI spec describes the list route this way: scope defaults to thread for a thread-bound credential and to workspace for an ordinary API key. run_id narrows to one automation run without widening past the thread. thread_id may only name the caller's own thread, so it never widens a bound credential.

Who sees which jobs, Sume spec read 2026-10-04
CallerDefault scopeCan ask for workspace?
Ordinary API keyworkspaceAlready is
Thread-bound credential, interactive turnthreadYes, with scope=workspace
Thread-bound credential, unattended runthreadNo, job_scope_forbidden

What should an agent do instead?

Ask about what it made. Keep the job ids your own run created and read them by id, or list with the default thread scope. Reporting across the workspace is a job for a service that holds an ordinary API key, not for an unattended run.

  • Store each job id when you submit it.
  • Use run_id to narrow within a run.
  • Do not retry a scope error; it will not change.

What does the call look like?

A default-scope list that works for either credential kind:

import os, requests

H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}

r = requests.get("https://api.sume.com/v1/jobs", headers=H,
                 params={"status": "completed", "limit": 20}, timeout=30)
print(r.status_code)
if r.ok:
    for job in r.json()["data"]["jobs"]:
        print(job["id"], job["type"], job["status"])
else:
    print(r.text[:300])

Where do audit lists belong?

On a service with an ordinary key, paging through status filters as in listing failed audio jobs. For job ids and hosted artifacts as a portability habit, see keeping a TTS pipeline portable.

Sources

Related posts

More in Agents

All Agents posts

Written by Sume