> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Index Azure File

POST https://api.captain.dev/v2/collections/{collection_name}/index/azure/file
Content-Type: application/json

Index a single file from Azure Blob Storage into a collection.

Headers:

* Authorization: Bearer \{api\_key} - Captain API key for authentication
* X-Organization-ID: Organization UUID

Args:
collection\_name: Name of the collection (path parameter)
body: Azure Blob file configuration with file\_uri

Returns:
\{ job\_id, status: "pending" }

Reference: https://docs.captain.dev/reference/indexing/azure/file

## Request

### Path parameters

- `collection_name` (string, required)

### Headers

- `authorization` (string, optional, nullable)

### Body (application/json)

This endpoint expects an IndexAzureFileRequest.

- `account_key` (string, required)
- `account_name` (string, required)
- `container_name` (string, required)
- `file_uri` (string, required)
- `processing_type` (enum, 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. '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.
  - Allowed values: `advanced`, `basic`, `auto`
- `custom_metadata` (map from string to IndexAzureFileRequestCustomMetadata, optional, nullable) — Custom metadata to attach to all chunks from this file. Keys must be strings. Values: str, int, float, bool, or List[str].
- `overwrite_existing` (boolean, optional, default: false) — 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_script` (string, optional, 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.
- `skip_existing` (boolean, optional, default: true) — 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.
- `source_identity` (string, optional, nullable) — Stable identity for this document, independent of where the object is read from. Indexing any location with the same source_identity updates the same document instead of creating another, and skip_existing matches on it. Defaults to the object's own URI. Opaque to Captain; one line, no newlines.
- `mask_pii` (boolean, optional, default: false) — 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.
- `pii_engine` (enum, optional, default: kev) — 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`.
  - Allowed values: `kev`, `jev`, `presidio`, `captain-jev`, `captain-presidio`
- `pii_fallback` (IndexAzureFileRequestPiiFallback, optional) — 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`.
- `pii_fields` (list of PIIField, optional, nullable) — 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.
- `pii_instructions` (string, optional, 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.
- `transcription_language` (string, optional, 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

### 200

Successful Response

- `job_id` (string, required)
- `status` (string, optional, default: pending)
- `custom_metadata` (map from string to any, optional, nullable) — The custom_metadata Captain accepted for this job, echoed back as validated. Null when none was supplied.

## Errors

### 400 Bad Request Error

overwrite_existing and skip_existing cannot both be true

- `error` (string, required) — Error kind. `HTTP_ERROR` for errors raised by the route.
- `message` (string, required) — What went wrong.
- `path` (string, required) — The request path.
- `status_code` (integer, optional) — The HTTP status code.

## Types

### IndexAzureFileRequestCustomMetadata

- string
- integer
- double
- boolean
- list of string

### IndexAzureFileRequestPiiFallback

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

- PiiFallbackPolicy: PiiFallbackPolicy
- boolean

### PIIField

A customer-defined PII category to mask in addition to the built-in ones.

- `name` (string, required) — Tag for this category. Uppercase letters, digits and underscores, 2 to 40 characters, unique within the request and not one of the built-in tags. Masked values are replaced with the tag in angle brackets (a field named EMPLOYEE\_ID produces \<EMPLOYEE\_ID>) and the masking report lists them under this name.
- `description` (string, required) — What the category covers, in plain language, as you would explain it to a colleague. This is what decides whether a value is masked, so say what the values look like and where they appear.
- `examples` (list of string, optional, nullable) — Up to 10 sample values, used only to make the description concrete. They are not a pattern: values that do not look like the examples are still masked when the description covers them.

### PiiFallbackPolicy

Fallback policy for `pii_engine: "jev"`. If every engine fails, the file fails to index and the rest of the job continues.

- `engines` (list of enum, optional, default: []) — Engines to try, in order, when `jev` fails with a rate limit, timeout, capacity limit, provider error or invalid answer. `jev-openrouter` and `jev-ai-gateway` are the same Jev model served through OpenRouter and Vercel AI Gateway. Custom fields and instructions apply on every engine. `presidio` is not allowed, and an engine may appear only once.
  - Allowed values: `jev-openrouter`, `jev-ai-gateway`, `kev`
- `retry_budget_seconds` (integer, optional, default: 60) — Extra retry and wait time per engine, for each piece of a file, before moving to the next engine. Time spent on successful answers does not count. 0 means one attempt per engine. Normal request timeouts still apply.

## Examples

**Request**

```json
{
  "account_key": "base64encodedaccountkey==",
  "account_name": "mystorageaccount",
  "container_name": "my-azure-documents",
  "file_uri": "https://mystorageaccount.blob.core.windows.net/my-azure-documents/reports/annual-review.pdf",
  "processing_type": "advanced"
}
```

**Response**

```json
{
  "job_id": "job_azurefile_abc123",
  "status": "pending"
}
```

**SDK Code**

```python Python REST
import requests

BASE_URL = "https://api.captain.dev"
API_KEY = "your_api_key"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    f"{BASE_URL}/v2/collections/my_documents/index/azure/file",
    headers=headers,
    json={
        "container_name": "my-azure-documents",
        "file_uri": "https://mystorageaccount.blob.core.windows.net/my-azure-documents/reports/annual-review.pdf",
        "account_name": "mystorageaccount",
        "account_key": "base64encodedaccountkey==",
        "processing_type": "advanced"
    },
    timeout=60.0
)

if response.status_code in [200, 201]:
    data = response.json()
    print(f"Job started! ID: {data['job_id']}")
else:
    print(f"Error: {response.status_code}")
```

```typescript TypeScript REST
const BASE_URL = "https://api.captain.dev";
const API_KEY = "your_api_key";

const response = await fetch(
    `${BASE_URL}/v2/collections/my_documents/index/azure/file`,
    {
        method: "POST",
        headers: {
            "Authorization": `Bearer ${API_KEY}`,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            container_name: "my-azure-documents",
            file_uri: "https://mystorageaccount.blob.core.windows.net/my-azure-documents/reports/annual-review.pdf",
            account_name: "mystorageaccount",
            account_key: "base64encodedaccountkey==",
            processing_type: "advanced"
        })
    }
);

if (response.ok) {
    const data = await response.json();
    console.log(`Job started! ID: ${data.job_id}`);
} else {
    console.error(`Error: ${response.status}`);
}
```