Ingest a batch of content items
Queue many content items in one call and get one accept envelope back.
GET /v1/ingest/{jobId}/status or list them with GET /v1/jobs. Items
that cannot be queued (empty content, oversized content, a non-object
metadata) are reported in results with a reason and counted in
skipped; they never fail the batch.
Jobs are written to the effective subject’s partition: the key owner by
default, or a provisioned subject selected with the X-Exo-Subject header
(an unprovisioned selector is refused by the shared subject seam before this
handler runs). An Idempotency-Key makes the accept replay-safe: a retry
with the same key and body returns the original envelope, with the original
job ids, without queueing the batch a second time.
Authorization
This route requires thewrite scope.
A missing or invalid credential returns 401 authentication_error. A valid credential without the scope returns 403 permission_denied, and the problem body names the exact scope required.
Headers
These are request conventions the contract does not declare as parameters, so they do not appear in the schema tables below.Success responses
Errors
idempotency_in_flight, idempotency_key_reuse. Every problem body names its own code, and the type URI always resolves to the matching page.
Every error is an RFC 9457 application/problem+json body carrying a stable code, a requestId, and a suggestedAction where Exo has one. See Errors.
Notes
- One call, many jobs. Every valid item becomes its own durable job, so one slow item cannot hold up the rest.
jobIdslists them in the order they were queued. - Unqueueable items are reported, not fatal. Empty content, oversized content and a non-object
metadataare counted inskippedand explained inresults. The batch still returns 202. - A replayed
Idempotency-Keyreturns the original envelope, including the original job ids, without queueing anything a second time.
Authorizations
An Exo API key (exo_...) sent as the X-Exo-API-Key header.
Body
Body for POST /v1/ingest/batch.
items keeps the legacy free-form {content, source_type?, metadata?}
element shape (plus the title the MCP already sends) so existing callers
need no change; per-item validation is reported inside results rather
than failing the whole batch. The reserved subject selector (spec 4.2) is
consumed by the shared subject seam from the raw body and is deliberately NOT
declared here, matching every other public request model.
Content items to ingest. Each is an object with 'content' (required, up to 100000 characters) plus optional 'title', 'source_type' and 'metadata'. An item that fails validation is reported in 'results' and does not fail the batch.
500Response
Every valid item was durably queued for ingestion
202 envelope for a batch accept.
The first four fields are the legacy wire contract, unchanged and in their
original order; ingested / skipped / jobIds are additive.
total - succeeded. Includes skipped items (legacy arithmetic).
Alias of 'succeeded' for batch-ingest clients.
Job ids for the accepted items; poll GET /v1/ingest/{jobId}/status.
Per-item outcome, in request order: accepted items carry {status:'accepted', node_id:'', chunks:0, jobId}; rejected items carry {status:'skipped', reason}.
Items rejected before enqueue.
Items accepted for ingestion.
Items received.