Jobs
Any endpoint that does work longer than a handful of seconds launches a background job and returns 202 Accepted with a job_id. The jobs endpoint group is the common ledger — you poll it, you cancel it, you retry it when a provider hiccup wrecked the embed phase.
Today /v1/jobs is backed by the import_jobs table. Ingest, commit, eval-campaign runs, and large-corpus deletes all flow through it. Future work (tracked as J-009) will generalize it into a richer execution-ledger, but the contract described below is stable.
List jobs
GET /v1/jobs?status=running
Query parameters:
| Param | Notes |
|---|---|
status | Filter by status (pending, running, succeeded, failed, cancelled, abandoned) |
Returns a compact ImportJobSummary[] — id, type, status, created/updated timestamps, progress percent, and a small detail map. Use the detail endpoint for the full object.
Known limitation (J-009). The list is capped at 50 results and does not yet paginate. When you need deep history, query
GET /v1/corpora/{id}/commitsinstead (which is properly paginated).
Get a job
GET /v1/jobs/{id}
Returns the full ImportJobResponse:
{
"id": "…",
"job_type": "batch_ingest",
"status": "running",
"phase": "embedding",
"progress_percent": 42.3,
"created_at": "2026-04-19T15:49:00Z",
"updated_at": "2026-04-19T16:07:11Z",
"documents_ingested": 12530,
"documents_failed": 0,
"error_message": null,
"corpus_id": "787cfadf…",
"batch_set_id": "0f9a01c5…",
"failed_document_ids": [],
"params": { … full request params that launched the job … }
}
Key fields:
| Field | Notes |
|---|---|
status | See the state table below |
phase | Sub-state inside running — e.g. chunking, embedding, promoting. Free-form per job_type |
progress_percent | Best-effort, per-phase progress. Reported by the worker; may jump non-monotonically when the phase changes |
batch_set_id | Populated when the job is a batch ingest — link to the Batch-set for the full attempt history and provider batch ids |
failed_document_ids | Populated on failure; gives you the exact ids to correct and re-ingest |
error_message | Human-readable error for operator display. Structured error classification lives on the batch-set’s attempts log |
The handler performs an on-demand live provider status fetch (J-013) the first time you hit GET /v1/jobs/{id} in any 5-second window for a given batch-set. That fetch asks the provider (OpenAI, Nebius, Voyage) for a fresh status and rewrites this job’s detail with live numbers before returning. This is what keeps the portal’s jobs page from drifting stale while a 2-hour batch is in flight.
Status values
| Status | Terminal? | Meaning |
|---|---|---|
pending | no | Queued; worker has not picked it up yet |
running | no | Worker is processing. phase tells you which sub-phase |
succeeded / complete | yes | Completed successfully. Historical name complete may appear in old rows |
failed | yes | Terminal failure. Inspect error_message and failed_document_ids; for batch ingests see also the batch-set’s attempts |
cancelled | yes | Cancelled by operator via /cancel |
abandoned | yes | Explicitly abandoned via /abandon, or auto-abandoned after the batch-set’s 24h ttl_expires_at passed |
Cancel
POST /v1/jobs/{id}/cancel
Marks the job cancelled in Enscrive and (best-effort) signals any running worker to stop. Safe to call at any time; a no-op on already-terminal jobs.
Cancellation is effective for in-Enscrive work (chunking, promote). For jobs in the embedding phase waiting on a remote provider batch, the provider continues processing — we can’t un-submit a 10k-request OpenAI batch. What cancel does in that case is stop us from applying the results once they come back. Tokens already consumed by the provider are billed; see D5 in the tracker for the broader “tokens consumed on abandoned work” disclosure.
Client death does NOT cancel a job. Once
POST /v1/ingest(or any other job-launching endpoint) returns202with ajob_id, the job runs server-side independent of the client that submitted it. If your process crashes, is killed, or simply stops polling, the job keeps running to completion (or failure) — it does not self-cancel. If you meant to stop it, callPOST /v1/jobs/{id}/cancelexplicitly. A client that submits many jobs and dies before cancelling them leaves an orphaned backlog; operators can drain one with the bulkPOST /v1/admin/jobs/drainendpoint (Admin capability — cancels every non-terminal job for a tenant and/or an explicit id list in one call).
Retry and abandon
These verbs apply to batch ingest jobs — the ones where the underlying work is an externally-submitted provider batch that can fail recoverably and be re-submitted.
Retry
POST /v1/jobs/{id}/retry
Re-runs only the sub-batches whose outcome was failed. Already-embedded sub-batches are not re-embedded (this is how we avoid billing twice for the same tokens). A retry appends a new entry to the batch-set’s attempts log so the full history is preserved.
Response:
{
"job_id": "…",
"batch_set_id": "…",
"retried_sub_batches": [3, 6],
"state": "embedding"
}
Retry is only valid for jobs whose batch-set is in state failed_recoverable. Attempting to retry a committed or abandoned batch-set returns 400.
Abandon
POST /v1/jobs/{id}/abandon
Moves the batch-set to abandoned, drops any remaining staging data, and records the operator-initiated abandonment. Use this when you know the ingest itself was wrong — bad metadata, wrong voice, wrong corpus — and retrying would just succeed at embedding bad data. Tokens already consumed on the provider side remain billed.
Response:
{
"job_id": "…",
"batch_set_id": "…",
"state": "abandoned"
}
Polling cadence
- UI polling —
GET /v1/jobs/{id}is fine at 2–5 second intervals. The live-status fetch is throttled at 5s per batch-set regardless of how often you poll, so rapid refreshes don’t hammer the provider. - Scripts / CI — 10–30 seconds is enough to observe terminal state without wasting requests.
- Long-running (hours) — 1 minute is plenty.
If you need immediate notification of job completion, webhooks are on the roadmap but not yet on the public surface. For now, poll with the cadence above.
Cross-references
- Batch-sets — state machine, attempts log, provider batch ids, and why the atomic-promote model exists.
- Ingest — request shape that launches these jobs.
- Corpora — commit history — paginated view of a specific corpus’s job history.
- Errors — HTTP-level errors and status codes.