Contact

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:

ParamNotes
statusFilter 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}/commits instead (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:

FieldNotes
statusSee the state table below
phaseSub-state inside running — e.g. chunking, embedding, promoting. Free-form per job_type
progress_percentBest-effort, per-phase progress. Reported by the worker; may jump non-monotonically when the phase changes
batch_set_idPopulated when the job is a batch ingest — link to the Batch-set for the full attempt history and provider batch ids
failed_document_idsPopulated on failure; gives you the exact ids to correct and re-ingest
error_messageHuman-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

StatusTerminal?Meaning
pendingnoQueued; worker has not picked it up yet
runningnoWorker is processing. phase tells you which sub-phase
succeeded / completeyesCompleted successfully. Historical name complete may appear in old rows
failedyesTerminal failure. Inspect error_message and failed_document_ids; for batch ingests see also the batch-set’s attempts
cancelledyesCancelled by operator via /cancel
abandonedyesExplicitly 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) returns 202 with a job_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, call POST /v1/jobs/{id}/cancel explicitly. A client that submits many jobs and dies before cancelling them leaves an orphaned backlog; operators can drain one with the bulk POST /v1/admin/jobs/drain endpoint (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 pollingGET /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.