neosourceDocs
Search docs

Read a job's log bytes from an offset

GET/api/jobs/{job_id}/logs

readJobLogs

Returns the job's raw log bytes from `from_offset` (default 0). The `x-neosource-log-next-offset` response header carries the byte offset to poll from next; an offset at/past the end yields a 200 with an empty body and the same header. `format` selects only how the bytes are labelled — the body is identical either way. `bytes` (the default) sends `application/octet-stream`, which browsers download and byte-polling clients expect; `text` sends `text/plain; charset=utf-8`, which a browser renders in a tab. Both carry `X-Content-Type-Options: nosniff`, since log bytes are attacker-influenced and the response is same-origin with the app.

Authentication optional — this operation also accepts anonymous requests. When authenticating, use a bearer token or a session cookie.

curl

curl -X GET 'https://neosource.dev/api/jobs/JOB_ID/logs' \
  -H 'Authorization: Bearer $NEOSOURCE_TOKEN'

fetch

fetch("https://neosource.dev/api/jobs/JOB_ID/logs", {
  method: "GET",
  headers: {
    Authorization: "Bearer $NEOSOURCE_TOKEN",
  },
});

Path parameters

job_idrequired

string

Query parameters

from_offset

Byte offset to read from. Defaults to 0 (the whole log).

integerint64

format

Response rendering: `bytes` (the default — `application/octet-stream`, which browsers download) or `text` (`text/plain; charset=utf-8`, which a browser tab renders). The body is the same either way.

JobLogFormat

string

How `GET /api/jobs/{job_id}/logs` should label the bytes it returns. A transport discriminator with no domain meaning — the body is byte-identical either way, only the `Content-Type` differs. Lives here rather than in `neosource-core` for that reason (same shelf as `RepoVisibilityInput`).

"bytes""text"

Responses

200Raw log bytes

application/octet-stream

string

403Forbidden — one of: forbidden, needs_scope

application/json

one of
  • ErrorForbidden
  • NeedsScopeError

    object

    `403` body returned when listing private repos but the linked identity lacks the required provider scope. The SPA turns this into an incremental-authorization prompt (Tier 2) that calls the `/elevate` OAuth endpoint with this `scope`.

    errorrequired

    string

    Always `needs_scope` — this body exists to carry the extra fields that kind needs.

    "needs_scope"

    scoperequired

    string

    The provider scope to request via elevation (e.g. `"repo"`).

Standard errors

Bodies documented once for the whole API — see standard errors.

  • 400Bad Request — one of: invalid_input
  • 404Not Found — one of: not_found
  • 429Rate limited — retry after the `Retry-After` header
  • 500Internal server error
  • 503Service temporarily unavailable / at capacity — retry after the `Retry-After` header
  • 504Gateway timeout — the request exceeded the server's handling budget