indexing

Index Supabase Manifest

Changed on

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:

{"uri": "s3://documents/inbox/2026-q4-msa.pdf", "source_identity": "contract-8841", "custom_metadata": {"department": "legal", "counterparty": "Northwind"}}
{"uri": "s3://documents/inbox/2026-q4-sow.pdf", "source_identity": "contract-8842", "size": 482113, "etag": "9b2cf535f27731c974343645a3985328"}
{"uri": "s3://documents/archive/2025-renewal.pdf", "source_identity": "contract-7310"}
{"uri": "s3://documents/inbox/handbook.pdf"}

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.pdf the identity contract-8841. Move the file to s3://documents/archive/2026-q4-msa.pdf and 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_identity and the identity is the uri. 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-level custom_metadata, key by key, for this file only. A line's value wins on a shared key.
  • checksum and version_id: recorded on the document. A checksum that is the object's ETag, written bare or as etag:..., 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 example sha256:...; it is recorded but not verified. version_id pins the object version that is read.
  • size and etag: 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" }

post/v2/collections/{collection_name}/index/supabase/manifest

Request

  • Base URL
    https://api.captain.dev
  • URL
    https://api.captain.dev/v2/collections/{collection_name}/index/supabase/manifest
  • Auth
    none declared

Path parameters

collection_namestring required

Headers

authorizationstring nullable

Request body

access_key_idstring required

Supabase S3 access key ID (Storage → S3 Connection → access key).

bucket_namestring required
custom_metadataobject nullable

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].

endpoint_urlstring required

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.

manifest_uristring 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.

mask_piiboolean

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.

max_filesinteger nullable

Stop after this many manifest lines, in file order.

overwrite_existingboolean

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.

parsing_scriptstring nullable

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.

pii_engine'kev' | 'jev' | 'presidio' | 'captain-jev' | 'captain-presidio'

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.

pii_instructionsstring nullable

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.

processing_type'advanced' | 'basic' required

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.

regionstring

Region configured for your Supabase project's S3 endpoint (Storage → S3 Connection). Required by the S3 protocol; defaults to 'us-east-1'.

secret_access_keystring required

Supabase S3 secret access key (shown once when the S3 access key is created).

skip_existingboolean

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.

transcription_languagestring nullable

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

Successful Response

job_idstring required
statusstring
custom_metadataobject nullable

The custom_metadata Captain accepted for this job, echoed back as validated. Null when none was supplied.

Changes

    • breaking

      the request property type changed from object to no type

    • warning

      removed the request property /

    • warning

      removed the request property /

    • info

      added the new captain-jev enum value to the request property

    • info

      added the new captain-presidio enum value to the request property

    • info

      added subschema #2 to the request property oneOf list

    • breaking

      removed the enum value captain-jev of the request property

    • breaking

      removed the enum value captain-presidio of the request property

    • breaking

      the request property type changed from boolean to object

    • breaking

      the response's body type changed from no type to object for status

    • warning

      removed the optional property from the response with the status

    • info

      added the new optional request property /

    • info

      added the new optional request property /

    • info

      the request property default value changed from captain-jev to kev

    • info

      the request property default value true was removed

    • info

      added the new jev enum value to the request property

    • info

      added the new kev enum value to the request property

    • info

      added the new presidio enum value to the request property

    • info

      added the optional property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status

    • info

      added the required property to the response with the status