---
title: "Job status + logs (?stream=&offset=&tail=&attempt=&links=)"
method: GET
path: "/preview/jobs/{id}"
tags: ["jobs"]
---

# Job status + logs (?stream=&offset=&tail=&attempt=&links=)

`GET /preview/jobs/{id}`

## Request

- Base URL: `https://autoresearch.sfcompute.com`
- URL: `https://autoresearch.sfcompute.com/preview/jobs/{id}`
- Auth: HTTP bearer

Show one job by id: status, GPU, attempts, and while queued its position and a wait estimate (moves both ways; a forecast, never a countdown; queue.wait_reason names the gate; null beside "queue_order" = no signal yet). Hours queued is normal (the spot holds for queue_ttl_minutes): pace polling minutes apart off the estimate; failure reason and exit code arrive once terminal. While building, `build` carries live progress (phase, byte/layer counts, updated_at): multi-GB images download for minutes, `updated_at` advancing means it is moving, and byte counts hold still past `fetching` by design - read the phase, and stream: "build" for what the build is doing. While starting, `startup` is the same shape for the run's own image pull (absent fields = no signal yet, never stalled). gman://docs/llms.txt covers `attempt_history` (end_cause, end_reason) and mid-run telemetry (`checkpoint_capture`, `preview`, `samples`). A completed attempt carries `result` (the $GMN_RESULT_PATH JSON, verbatim; result_error names the failure) and `artifact` (the $GMN_OUTPUT_DIR capture, a presigned download on the object store hostname, currently fly.storage.tigris.dev - an egress allowlist must permit it; a severed attempt uploads nothing); with `output_to` declared it instead carries `output: {connection, uri}` - the bytes land in your own store. Pass `stream` ("build" or "run") for incremental logs (`offset` to advance, `tail: true` for the last 4 KiB, `attempt` for an earlier attempt). A run-log slice answers the poll question: `attempt_state` is "not_started" (no run log YET; poll the JOB), "live", "complete", "never_ran" (no container, NO log ever; an empty slice is final), or "unknown" (our read failed; read again), and `eof: true` means stop; `note` names an unreadable range (skipped at `next_offset`, never spliced) or a job that ran no build. Logs live 30 days; status checks never touch a worker. `links: false` omits download links. Task sets add `tasks`. Poll it after submit_job.

## Path parameters

- `id` string, required: Job id from submit_job (e.g. "job-8x2mf").

## Query parameters

- `stream` string, nullable: Which log stream to include: "build" or "run" (omit for status only).
- `offset` integer, nullable: Read logs from this byte offset (incremental polling).
- `tail` boolean, nullable: Return only the last 4 KiB ("what's it doing right now").
- `attempt` integer, nullable: Which run attempt's log to read (1-based: attempt 1 is the first run; default: the current attempt). Earlier attempts' logs survive a preemption/restart - pass attempt: 1 to read what the first run printed after a rerun began.
- `rank` integer, nullable: The machine whose run log to read, for a job with nodes. Set it from 0 to nodes - 1. The default is 0 (rank 0). Omit it for a job on one machine.
- `links` boolean, nullable: Default true. Pass false to leave the download links out of the answer (`artifact.download`, `preview.download` and `tasks.outputs.download`, each a presigned URL that is a credential for that one file) and change nothing else, so a poll whose answer is printed or logged never carries a live link. `artifact.state` still says when the output is ready; `get_artifact`, called from the job's own workspace, mints a link on demand.
- `workspace` string

## Response `200`

The capability's JSON result.

- Result: The capability's result: the same object the MCP tool returns as structuredContent. Resources carry an `object` type discriminator and an immutable id; money is integer micro-dollars in `*_usd_micros` fields.
  - `object` string: The resource type, when the result is one resource.

## Other responses

- `400`: Malformed request: bad JSON, a parameter that does not parse, or a value outside its range. Code `invalid`.
- `401`: No bearer token, or one this API cannot verify. Code `unauthenticated`.
- `403`: Authenticated, but the token's scopes or the account's role do not reach this operation. The message names the scope word to request. Code `forbidden`.
- `404`: No such route. Code `not_found`; the body names the OpenAPI document.
- `422`: A well-formed request the platform declined: no such node, a cap reached, a state that forbids the move. The message says which, and what to do instead. Code `refused`.
- `429`: Over this door's per-customer budget. Wait `Retry-After` seconds. Code `rate_limited`.
- `500`: Our fault. Retry; if it persists, quote the request-id. Code `internal`.

---

[API](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api.md) · [All operations](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/givemeanode/apis/autoresearch-customer-api/revisions/f88dcb70ac6c?raw)
