Jobs
Preview Any mutation that isn’t instantaneous —
a deploy, a build, a clone, a malware scan, a config change — returns
202 Accepted with a job. The jobs resource is the read side of all of them:
one place to list jobs, check a job’s status, and wait for it to finish. If you’ve
seen a 202 anywhere else in this API, this is how you find out what happened next.
For the bigger picture — why operations are async, and the three ways to consume a
202 — see async jobs. This page is the endpoint
reference.
Authorization
Section titled “Authorization”Reading jobs requires the jobs:read scope.
The jobs list is loaderless — there’s no resource in /v1/jobs to load, so
tenancy is resolved in-handler from your principal. A key’s resource constraint is
honored here: a key pinned to a site or team only ever lists jobs for resources that
key may see. There’s no jobs:write — you don’t create jobs directly, you create
them by calling the operation that returns one.
Endpoints
Section titled “Endpoints”| Method + path | Returns |
|---|---|
GET /v1/jobs |
a paginated list of your jobs |
GET /v1/jobs/{jobID} |
one job’s current status (returns an ETag) |
GET /v1/jobs/{jobID}/stream |
the job’s progress, live, over SSE |
List parameters
Section titled “List parameters”| Name | Type | Required | Description |
|---|---|---|---|
status |
string |
no | filter to queued, running, succeeded, or failed |
resource_type |
string |
no | filter to a kind, e.g. deployment, build, environment, site |
limit |
integer |
no | page size |
cursor |
string |
no | opaque pagination cursor |
The job envelope
Section titled “The job envelope”Every job — listed, polled, or streamed — has the same stable shape, so an SDK or a Terraform provider can wait on any operation with one code path:
{ "data": { "id": "job_01J9SC", "type": "deployment.deploy", "status": "running", "progress": 0.6, "created_at": "2026-06-24T01:02:00.110Z", "resource": { "type": "deployment", "id": "env_01J8D9", "site_id": "site_01J7AA", "env_id": "env_01J8D9" }, "result": null, "error": null, "links": { "self": "/v1/jobs/job_01J9SC", "stream": "/v1/jobs/job_01J9SC/stream" } }, "request_id": "req_01JAB2"}status walks queued → running → succeeded | failed. On success, result carries
the operation’s output (e.g. the new release id); on failure, error carries the
error object. resource always carries the owning site_id,
adds env_id for environment-scoped operations, and sets id to the most specific
subject — the environment when there is one, otherwise the site.
Watching a job — ETag long-poll
Section titled “Watching a job — ETag long-poll”GET /v1/jobs/{jobID} returns an ETag. Send it back as If-None-Match and the
server returns 304 Not Modified until the job actually changes, then 200 with the
new state. This is the polling path for SSE-hostile environments — Terraform
providers, CI runners behind proxies — and it’s cheap to loop on.
# first poll — note the returned ETagcurl -i https://api.managed.dev/v1/jobs/job_01J9SC \ -H "Authorization: Bearer mfk_live_9aF2…" \ -H "Forge-Version: 2026-06-23"# → ETag: "w/job-3"
# subsequent polls — 304 until the job advancescurl -i https://api.managed.dev/v1/jobs/job_01J9SC \ -H "Authorization: Bearer mfk_live_9aF2…" \ -H "Forge-Version: 2026-06-23" \ -H 'If-None-Match: "w/job-3"'etag := ""for { req, _ := http.NewRequest("GET", "https://api.managed.dev/v1/jobs/job_01J9SC", nil) req.Header.Set("Authorization", "Bearer mfk_live_9aF2…") req.Header.Set("Forge-Version", "2026-06-23") if etag != "" { req.Header.Set("If-None-Match", etag) } resp, _ := http.DefaultClient.Do(req) if resp.StatusCode == http.StatusNotModified { time.Sleep(2 * time.Second) continue // 304 — no change yet } etag = resp.Header.Get("ETag") // decode body; break when status is succeeded or failed}mf jobs wait job_01J9SCWatching a job — SSE stream
Section titled “Watching a job — SSE stream”GET /v1/jobs/{jobID}/stream holds the connection open and pushes the job envelope
on every state change — ideal for a CLI or UI that wants live progress. See the
SSE contract.
curl -N https://api.managed.dev/v1/jobs/job_01J9SC/stream \ -H "Authorization: Bearer mfk_live_9aF2…" \ -H "Forge-Version: 2026-06-23" \ -H "Accept: text/event-stream"mf jobs watch job_01J9SCevent: jobdata: {"id":"job_01J9SC","status":"running","progress":0.6}
event: jobdata: {"id":"job_01J9SC","status":"succeeded","progress":1,"result":{"release_id":"rel_01JE7"}}