Index Supabase Manifest
Index an explicit list of objects from a Supabase Storage bucket, given as a JSON Lines manifest in the same bucket. One bulk job; nothing is listed or scanned.
One object per line, UTF-8, uncompressed. uri is required, the rest are optional:
Physical location vs. logical identity
uri is where the file currently lives: this bucket, this key. It must be inside bucket_name, and it is the only field Captain reads bytes from.
source_identity is what the document is, in your own terms. Captain keeps one document per identity, so the identity is what decides whether a later index is an update or a new document:
- Give
s3://documents/inbox/2026-q4-msa.pdfthe identitycontract-8841. Move the file tos3://documents/archive/2026-q4-msa.pdfand index it again with the same identity, and Captain updates that one document. Its chunks are replaced and the old ones are retired. - Omit
source_identityand the identity is theuri. The same file at a new key is a different identity, so you get a second document and both versions answer queries.
A repeat of an identity already in the collection is skipped while skip_existing is true (the default), and replaced when you send overwrite_existing: true or skip_existing: false. While it is skipping, Captain compares content. A repeat whose content is unchanged is skipped. A repeat whose content differs is an identity conflict: it is neither skipped nor indexed, and the job reports it under identity_conflicts so that you can send it again with overwrite_existing: true. When either version has no checksum, the repeat is skipped. Use your own record id, not a path, whenever content can move or be delivered twice.
The other line fields
custom_metadata: merged over the job-levelcustom_metadata, key by key, for this file only. A line’s value wins on a shared key.checksumandversion_id: recorded on the document. Achecksumthat is the object’s ETag, written bare or asetag:..., is compared with the object before anything is read, and a line that does not match is reported as unreachable instead of being indexed. Write any other algorithm with its prefix, for examplesha256:...; it is recorded but not verified.version_idpins the object version that is read.sizeandetag: when both are present, as in a bucket listing export, Captain does not read the object’s metadata before indexing. That is what lets a large manifest start quickly.
Limits and error handling
A manifest holds up to 1,000,000 lines and 500 MB, with each line at most 10 KB. It must be plain UTF-8 .jsonl or .ndjson; a compressed body is refused. Per line, custom_metadata takes at most 50 keys, keys up to 64 characters and string values up to 1,024 characters. The job-level custom_metadata is capped at 64 KB serialized. It is attached to every file in the job, so metadata size times file count must stay under 2 GB: a few hundred bytes fits the full million lines, and the full 64 KB fits about 32,000 files. Larger jobs should carry less job-level metadata or be split.
A wrong manifest_uri is a 400 before the job is created, as is a manifest larger than the size cap. Request bodies are capped at 1 MB, which is why a large object list is a manifest in storage rather than an inline array.
Unusable lines (invalid JSON, not a JSON object, a uri outside the bucket, a duplicate uri, an unknown key) and unreachable objects (missing, access denied, unsupported file type) are counted on the job. GET /v2/jobs/{job_id} returns a manifest object with the totals and a sample of up to 20 of each, giving the line number or uri and a reason. Neither fails the job: a million-line manifest with a few thousand bad lines indexes the rest.
Headers:
- Authorization: Bearer {api_key} - Captain API key for authentication
- X-Organization-ID: Organization UUID
Returns: { job_id, status: “pending” }
Path parameters
Headers
Request
Supabase S3 access key ID (Storage → S3 Connection → access key).
S3-compatible endpoint URL for your Supabase project’s Storage S3 connection, e.g. https://{project_ref}.storage.supabase.co/storage/v1/s3 (Storage → S3 Connection). Required.
URI of the manifest, inside bucket_name (s3://bucket/path/manifest.jsonl or supabase://bucket/path/manifest.jsonl). UTF-8 JSON Lines, uncompressed, one object per line: {“uri”: …} plus optional source_identity, custom_metadata, checksum, version_id, size and etag. Up to 1,000,000 lines, 500 MB total and 10 KB per line.
Document processing type. ‘advanced’ uses agentic OCR with AI-enhanced extraction for complex layouts, tables, figures, charts, and documents containing images. ‘basic’ provides reliable OCR optimized for general document indexing and high-volume processing. ‘auto’ inspects each PDF and uses advanced only when it has photos, drawings, tables or scanned pages (logos and signatures do not count), otherwise basic; each file bills at the tier it used.
Supabase S3 secret access key (shown once when the S3 access key is created).
Custom metadata attached to every indexed chunk. A line’s own custom_metadata is merged over it for that file. Keys are strings; values: str, int, float, bool, or List[str].
When true, detected PII (emails, phone numbers, SSNs, credit cards, names, and locations) is masked in the parsed content before it is embedded and stored — replaced with entity tags like <PERSON> and <EMAIL_ADDRESS>. For images (including images embedded in PDFs), PII text visible in the image is also pixel-redacted. Opt-in; defaults to false, which leaves content unchanged.
When true, files that already exist in the collection are re-indexed and replaced with zero downtime: the new version is built alongside the live one and atomically swapped in when complete, so the previous version keeps serving search results throughout the rebuild. The document keeps the same document_id across overwrites, and its status reads ‘updating’ in the document listing while the rebuild runs. Requires skip_existing=false. Setting both to true returns a 400 error.
Relative path to a JS parsing script for JSON files (e.g. ‘research/paper-parser’). When provided, .json files are processed through a sandboxed V8 isolate. Without this, .json files are indexed as raw text.
Masking engine for this job. kev (default): the Kev classifier model on Captain’s infrastructure; supports pii_fields and pii_instructions; no fallback, so a file kev cannot mask fails to index and the rest of the job continues. jev: TypeSafe’s hosted Jev model, slightly more accurate, best-effort availability; supports custom fields and instructions and may list fallbacks in pii_fallback. presidio: the legacy pattern engine, built-in categories only (rejects pii_fields or pii_instructions with 422), never a fallback. Requires mask_pii: true. The earlier names captain-jev and captain-presidio keep working as before; new requests should use kev, jev or presidio.
Ordered fallback engines for jev, as {"engines": [...], "retry_budget_seconds": n}. Omitted or an empty engines list means no fallback. Only valid with pii_engine: "jev"; with kev or presidio a non-empty list answers 422. On the multipart file upload endpoint, send it as a JSON string form field. With the earlier engine name captain-jev it is true (default, fall back to captain-presidio) or false.
Additional PII categories to mask for this job, on top of the built-in set. Requires mask_pii to be true. Up to 20 fields. Each masked value is replaced with the field’s tag in angle brackets and appears in the masking report under the field’s name.
Plain-language guidance that steers what counts as personal data for this job, for the built-in categories and any pii_fields. For example: ‘Names of hospital staff may stay; patient names, record numbers and bed assignments must be masked.’ Requires mask_pii to be true. Up to 1,000 characters.
Region configured for your Supabase project’s S3 endpoint (Storage → S3 Connection). Required by the S3 protocol; defaults to ‘us-east-1’.
When true, files already indexed in the collection are skipped and will not be re-indexed with incoming changes. When false, all incoming files are indexed regardless of whether they already exist.
AWS Transcribe language code for the spoken audio (e.g. ‘es-US’, ‘pt-BR’). Omit to auto-detect per file. Video and audio files only. Supported codes: https://docs.aws.amazon.com/transcribe/latest/dg/supported-languages.html
Response
The custom_metadata Captain accepted for this job, echoed back as validated. Null when none was supplied.