Skip to Content
We are live but in Staging 🎉

Jobs — API Reference

Package: dodil.data.ingest.v1 · Service: IngestService

An ingest job is one execution of one pipeline against one object. The Jobs RPCs let you fire a one-shot ingest, look up a specific job, list jobs in a bucket with filters, or re-run a single failed one. See Core Concepts → IngestJob.

RPCHTTP
TriggerIngestPOST /:bucket/ingest
GetIngestStatusGET /:bucket/ingest/jobs/:job_id
ListIngestJobsGET /:bucket/ingest/jobs
RetryIngestJobPOST /:bucket/ingest/jobs/:job_id/retry

gRPC setup — grpcurl, endpoints, reflection, and field-name casing — is covered once in Conventions → Using gRPC.

Status

IngestStatus has six meaningful values plus the zero default. These exact strings are what the API emits and accepts.

ValueMeaning
INGEST_STATUS_UNSPECIFIEDZero value — never a real job state.
INGEST_STATUS_PENDINGRow created, not yet picked up.
INGEST_STATUS_PROCESSINGA worker is running the pipeline.
INGEST_STATUS_COMPLETEDTerminal success.
INGEST_STATUS_FAILEDTerminal failure.
INGEST_STATUS_PARTIALSome output landed, some did not.
INGEST_STATUS_RETRYINGA previous attempt failed transiently and NATS JetStream will redeliver. error carries "attempt N/M: <last error>" so progress is visible.

RETRYING is not terminal and needs no action from you — redelivery is automatic. FAILED and PARTIAL are terminal; those are the ones RetryIngestJob exists for.

Counters

IngestJob carries one counter set, and which fields are populated depends on the pipeline’s kind. A counter that does not apply stays 0 — a 0 does not mean the stage ran and produced nothing.

FieldPopulated forMeaning
chunks_createdvectorChunks the splitter produced.
embeddings_createdvectorEmbeddings computed.
embeddings_writtenvectorEmbeddings actually persisted — compare against embeddings_created to spot a partial write.
rows_writtenwarehouse (table)Rows landed in the Delta table.
objects_writtenobjectBlobs the ObjectWriter wrote at the destination. Always 0 or 1 today — one blob per source object — but typed as a count so future multi-output pipelines need no schema bump.
batches_receivedallStreamed yields consumed from the Scriptum thread.
output_sizeallBytes of Scriptum output.

vector_status is a separate free-form string on vector jobs: null, "success", "failed" or "skipped". thread_id links to the Scriptum thread; error_details carries the long-form failure text when error is only a summary.

TriggerIngest

One-shot ingest of a single object — skips discovery. Useful for testing pipelines, re-ingesting after a change, or piping in an object from an external system without going through the source sync layer.

By default K3 resolves which pipeline to run by matching the object against the bucket’s rules. Pass pipeline_id or rule_id to override.

Request

Resolve pipeline from matching rules:

curl -sS -X POST "https://api.data.dodil.io/kb-prod/ingest" \ -H "Authorization: Bearer $DODIL_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "object": { "bucket": "kb-prod", "key": "contracts/acme-2026.pdf" } }'

Force a specific pipeline + per-event option overlay:

curl -sS -X POST "https://api.data.dodil.io/kb-prod/ingest" \ -H "Authorization: Bearer $DODIL_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "object": { "bucket": "kb-prod", "key": "contracts/acme-2026.pdf" }, "pipelineId": "pipe_a1b2...", "options": { "chunk_size": "2000" } }'

Response

{ "job": { "jobId": "job_a1b2...", "object": { "bucket": "kb-prod", "key": "contracts/acme-2026.pdf" }, "status": "INGEST_STATUS_PENDING", "pipelineId": "pipe_a1b2...", "pipelineName": "embed-contracts", "pipelineKind": "vector", "createdAt": "1716843600000" } }

The response contains the freshly created IngestJob, typically in PENDING or already PROCESSING. Poll GetIngestStatus for terminal state, or watch it with dodil data ingest watch <job-id>.

Note that TriggerIngest is the one RPC in this service with no bucket field — it is annotated post: "/{object.bucket}/ingest", so the bucket is read from object.bucket in the body and mirrored into the path.

The options map on the request is a per-event overlay on top of the pipeline’s stored options — useful for one-off parameter sweeps without modifying the pipeline itself.

GetIngestStatus

Request

curl -sS "https://api.data.dodil.io/kb-prod/ingest/jobs/job_a1b2..." \ -H "Authorization: Bearer $DODIL_TOKEN"

Response

An IngestJob — see Counters above for which fields are populated per pipeline kind, and Core Concepts → IngestJob for the full type.

ListIngestJobs

Filter by status_filter, pipeline_id, or rule_id. Cursor pagination, default page size 50.

This endpoint’s query parameters are camelCase, unlike its siblings: the handler deserializes ListJobsQuery with rename_all = "camelCase", so it reads ?pipelineId=, ?ruleId=, ?pageSize= and ?pageToken=. Snake-case forms are silently ignored — a ?pipeline_id= filter returns the whole bucket rather than erroring. status_filter is the one exception: it keeps status and status_filter as explicit aliases.

Request

dodil data ingest jobs -b kb-prod --status failed dodil data ingest jobs -b kb-prod -p pipe_a1b2... --page-size 100 dodil data ingest jobs -b kb-prod -r rule_a1b2... --all

--status takes the short lowercase form: pending, processing, completed, failed, partial, retrying. --all follows pagination and returns every matching job.

Response

{ "jobs": [ { "jobId": "job_a1b2...", "object": { "bucket": "kb-prod", "key": "contracts/acme-2026.pdf" }, "status": "INGEST_STATUS_COMPLETED", "pipelineId": "pipe_a1b2...", "pipelineName": "embed-contracts", "pipelineKind": "vector", "ruleId": "rule_a1b2...", "chunksCreated": 47, "embeddingsCreated": 47, "rowsWritten": 0, "objectsWritten": 0, "createdAt": "1716843600000", "updatedAt": "1716843680000", "threadId": "thread_a1b2...", "vectorStatus": "success", "outputSize": 18429, "batchesReceived": 5, "embeddingsWritten": 47 } ], "pagination": { "nextPageToken": "", "totalCount": "1" } }

rowsWritten and objectsWritten are 0 here because this is a vector job, not because those stages produced nothing — see Counters.

RetryIngestJob

Re-runs a single failed or partial job in place. It resets the per-attempt state on the existing row and re-publishes an IngestEvent with the same job_id — no new row is created and no schema bump happens, so the job’s history stays on one record.

Use this for one bad object. For bulk replay across a source, use TriggerIngestion(retry_failed = true) instead.

Request

curl -sS -X POST "https://api.data.dodil.io/kb-prod/ingest/jobs/job_a1b2.../retry" \ -H "Authorization: Bearer $DODIL_TOKEN"

Response

The reset IngestJob, with status back to INGEST_STATUS_RETRYING and its counters cleared.

There is no dodil data command for single-job retry. The CLI’s retry surface is dodil data ingest trigger --retry-failed, which is the bulk TriggerIngestion path, not this one. Watch a retried job with dodil data ingest watch <job-id>.


See also