Async jobs
Some Spectral operations take longer than a request should wait, so they run in the background. These endpoints return a job straight away: a handle you poll until the work finishes.
The envelope
An endpoint that kicks off background work returns the resource it created and the job building it:
{...,"job": {"id": "job_8f2a1...","status": "queued","kind": "...","resource": { ... },"created_at": "2026-06-19T12:00:00Z",}}
The resource already has an id, but its status is queued or running: it exists, but it is not ready to use. Keep the job.id and poll it before relying on the resource.
The job object
| Field | Description |
|---|---|
id | The job identifier. Pass it to GET /api/v1/jobs/{job_id}. |
status | One of queued, running, succeeded, failed. |
kind | What the job is doing (for example, populate_knowledge_base, create_run, build_report). |
resource | The type and id of what the job is building. |
created_at, started_at, ended_at | Timestamps for the job's lifecycle. started_at and ended_at are null until the job reaches each stage. |
queued and running are in-progress states. succeeded and failed are terminal.
Polling a job
Fetch the job with GET /api/v1/jobs/{job_id} and repeat until status is terminal:
curl https://spectral.principled.app/api/v1/jobs/job_8f2a1... \-H "Authorization: Bearer ak_****"
{"id": "job_8f2a1...","status": "succeeded","kind": "populate_knowledge_base","resource": { "type": "knowledge_base", "id": "3b1f7..." },"created_at": "2026-06-19T12:00:00Z","started_at": "2026-06-19T12:00:03Z","ended_at": "2026-06-19T12:02:18Z"}
When status is succeeded, the resource is ready to use.
We recommend polling on an interval of at least 60 seconds. There is no need to poll more aggressively, and doing so may result in rate limiting.