> ## Documentation Index
> Fetch the complete documentation index at: https://docs.unsiloed.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Parse Document (Presigned Upload)

> Parse and segment PDFs, images, and Office files into meaningful sections using advanced AI models with flexible customization options.

## Overview

The v2 presigned upload endpoint decouples document delivery from job creation. Instead of uploading your file through the API server, you:

1. **POST** to `/v2/parse/upload` with your configuration: the API creates a parse job and returns a short-lived presigned URL.
2. **PUT** your file directly to the presigned URL: the API server is never in the transfer path.
3. Once the upload completes, the job is automatically enqueued for processing.
4. **Poll** `GET /parse/{job_id}` to track progress and retrieve results (the same endpoint as v1).

<Tip>
  This is the recommended endpoint for large files and high-throughput workloads. It supports faster uploads, larger file sizes, and higher concurrency than the standard [Parse Document](/api-reference/parser/parse-document) endpoint.
</Tip>

<Note>
  The job status starts as `AwaitingUpload`. It transitions to `Queued` once the upload completes, then to `Processing`, and finally `Succeeded` or `Failed`. See [Get Parse Job Status](/api-reference/parser/get-parse-job-status) for the full response schema and polling examples.
</Note>

## Request

<ParamField body="file_name" type="string" required>
  File name with extension (e.g. `"report.pdf"`). Determines the MIME type for the presigned URL. Supported formats: PDF, images (PNG, JPG/JPEG, TIFF/TIF, WebP), office documents (DOC, DOCX, PPT, PPTX, XLS, XLSX), and HTML (HTML/HTM).
</ParamField>

<ParamField body="use_high_resolution" type="boolean">
  Enhanced image quality: enables high-resolution processing with upscaling algorithms. Improves OCR accuracy on low-quality scans by enhancing clarity and contrast. Latency penalty: \~2–3 seconds per page. Defaults to `false`.
</ParamField>

<ParamField body="layout_analysis" type="string">
  Layout analysis strategy: `smart_layout_detection` (default), `page_by_page`, or `advanced_layout_detection`.

  * `"smart_layout_detection"` **(default)**: Identifies document structure, headers, sections, and content relationships across the entire document using bounding boxes.
  * `"page_by_page"`: Analyzes each page independently as a single segment. Faster for simple documents.
  * `"advanced_layout_detection"`: Higher-accuracy layout detection for complex pages — multi-column layouts, dense tables/figures. Slower than `smart_layout_detection`.
</ParamField>

<ParamField body="ocr_strategy" type="string">
  OCR strategy: `auto_detection` (default) or `force_ocr`.

  * `"auto_detection"` **(default)**: Intelligently detects bad quality PDFs, scanned documents, and images, then applies OCR only where needed.
  * `"force_ocr"`: Runs OCR on the entire document regardless of quality.
</ParamField>

<ParamField body="ocr_engine" type="string">
  OCR engine to use for text recognition: `UnsiloedHawk` (default, recommended), `UnsiloedBeta`, or `UnsiloedStorm`.

  * `"UnsiloedHawk"` **(Recommended, default)**: Higher accuracy for complex layouts and mixed content.
  * `"UnsiloedBeta"`: Handles rotated/warped text and irregular bounding boxes.
  * `"UnsiloedStorm"`: Enterprise-grade accuracy optimized for 50+ languages.
</ParamField>

<ParamField body="agentic_ocr" type="string">
  Per-segment OCR enhancement: re-runs a dedicated agentic OCR model on each detected segment after layout detection for higher accuracy. Omit or leave empty to disable.

  * `"standard"`: Good balance of speed and accuracy.
  * `"advanced"`: Higher quality, best for complex layouts, rotated text, and mixed-language content.
</ParamField>

<ParamField body="extract_strikethrough" type="boolean">
  Strikethrough detection: detects and preserves strikethrough formatting in HTML and Markdown output. Defaults to `false`.
</ParamField>

<ParamField body="merge_tables" type="boolean">
  Cross-page table consolidation: detects and combines table segments across page breaks. Reconstructs complete table structure by matching headers and columns. Defaults to `false`.
</ParamField>

<ParamField body="merge_batch_size" type="integer">
  Maximum number of tables per merge group. Groups larger than this are split. Defaults to `20`; values below 2 are clamped to 2.
</ParamField>

<ParamField body="enhance_reading_order" type="boolean">
  Fix the reading order of detected segments. Useful for multi-column layouts. Defaults to `false`.
</ParamField>

<ParamField body="detect_pii" type="boolean">
  Run a PII pre-check before parsing. If PII is found at the configured severity, the task is rejected without parsing. Defaults to `false`.
</ParamField>

<ParamField body="pii_block_severity" type="string">
  Severity threshold to block on when `detect_pii` is enabled: `any` (default), `low`, `medium`, or `high`. `any` blocks on any PII found; `low` adds quasi-identifiers (names, dates, locations); `medium` blocks on contact PII (email, phone) or higher; `high` blocks only on direct identifiers (SSN, passport, credit card). Ignored if `detect_pii` is false.
</ParamField>

<ParamField body="pii_engine" type="string">
  PII detector engine: `standard` (default) or `advanced` (higher precision, additional processing cost). Any other value falls back to `standard`. Ignored if `detect_pii` is false.
</ParamField>

<ParamField body="extract_links" type="boolean">
  Attach hyperlink URLs from PDF annotations to OCR results. Defaults to `false`.
</ParamField>

<ParamField body="extract_colors" type="boolean">
  Transfer text color from the PDF text layer to OCR results. Defaults to `false`.
</ParamField>

<ParamField body="xml_citation" type="boolean">
  Citation extraction: extracts academic citations from PDF documents and hyperlinks them in the markdown output. Generates structured bibliography metadata. PDFs only. Defaults to `false`.
</ParamField>

<ParamField body="page_range" type="string">
  Specify which pages to process. Leave empty to process all pages.

  Examples: `"1-5"` (pages 1 to 5), `"2,4,6"` (specific pages), `"[1,3,5]"` (array format), `"1-3,7,10-15"` (combination).
</ParamField>

<ParamField body="expires_in" type="integer">
  Seconds the returned presigned upload URL stays valid. Defaults to 15 minutes (900 seconds). Once the client uploads the file, the task's expiry is cleared and the task lifetime is no longer bounded by this value.
</ParamField>

<ParamField body="export_format" type="array">
  Export format(s) to generate after processing. Supported: `["docx", "markdown", "json"]`. The exported files are available via the `exports` field in the task response.
</ParamField>

<ParamField body="segment_type_naming" type="string">
  Segment type naming convention. `"Unsiloed"` (default) uses names like `PageHeader`, `ListItem`, `Picture`. `"Other"` uses alternative names like `Header`, `List Item`, `Figure`.
</ParamField>

<ParamField body="segment_filter" type="string">
  Comma-separated segment types to keep in the output, or `"all"` to include every type. Defaults to `"all"`.
</ParamField>

<ParamField body="validate_segments" type="array">
  Segment validation: uses a vision model to validate and correct segment types, fixing misclassified segments. Example: `["Table", "Formula", "Picture"]`. Defaults to `["Table", "Picture"]`; an empty or unparseable value also falls back to that default, so Table and Picture validation runs even when this field is omitted.
</ParamField>

<ParamField body="validate_table_segments" type="boolean">
  Legacy option to validate table segments using a vision model. Prefer `validate_segments` instead.
</ParamField>

<ParamField body="chunk_processing" type="object">
  JSON object controlling how segments are grouped into chunks. See the [v1 parse endpoint](/api-reference/parser/parse-document) for full schema details.
</ParamField>

<ParamField body="segment_processing" type="object">
  Configure how different segment types are processed: table models, image descriptions, and formula processing.

  <Note>
    On this endpoint the field is named `segment_processing`. The v1 alias `segment_analysis` is not accepted here and is silently ignored.
  </Note>

  ```json theme={null}
  {
    "Table": {"html": "VLM", "markdown": "VLM", "model_id": "us_table_v2"},
    "Picture": {"html": "VLM", "markdown": "VLM", "model_id": "nova"},
    "Formula": {"html": "Auto", "markdown": "VLM", "model_id": "nova"}
  }
  ```

  Options per segment type:

  * `html` / `markdown`: `"VLM"` or `"Auto"`
  * `model_id` (Table): `"astra"`, `"us_table_v1"`, `"us_table_v2"`
  * `model_id` (Picture/Formula): `"nova"`, `"luna"`, `"sol"`
  * `use_table_ocr` (Table only): Advanced OCR for bordered cells and complex table layouts.
  * `vlm`: Custom prompt for the VLM model. Use this to give the model specific instructions for extracting or describing these segment types.
  * `translation`: Optional per-segment translation, e.g. `{"provider": "Auto", "target_language": "en"}`. `provider` is `"Auto"` for fast machine translation or `"VLM"`/`"LLM"` for model-based translation; `target_language` is an ISO 639-1 code, or `"auto"` to auto-detect the source and translate to English.

  For full details, see [Parse Document (v1)](/api-reference/parser/parse-document).
</ParamField>

<ParamField body="llm_processing" type="object">
  JSON object for LLM processing configuration.
</ParamField>

<ParamField body="output_fields" type="object">
  JSON object controlling which fields are included in the output. Set fields to `false` to exclude them and reduce response size. Available fields: `html`, `markdown`, `ocr`, `image`, `content`, `bbox`, `confidence`, `embed`, `chart_data`. All default to `true`. Ignored when `response_profile` is `slim` or `full` (the profile wins).
</ParamField>

<ParamField body="response_profile" type="string">
  Response shape selector: `slim`, `full`, or `custom`. Omit to return the full shape.

  * `"slim"`: Returns only the essentials per chunk — `embed`, `bbox`, `page_number`, `segment_id`, `segment_type`, and HTML for tables / Markdown for everything else. Drops `content`, `image`, `ocr`, `confidence`, `chart_data`, `page_height`, `page_width`. Best for embedding-only workflows where you want the smallest payload.
  * `"full"`: Every field returned (equivalent to omitting this param).
  * `"custom"`: Honor `output_fields` verbatim.

  When both `response_profile` and `output_fields` are provided, the profile wins — `output_fields` is only consulted for `custom` or when the profile is omitted.
</ParamField>

<ParamField body="error_handling" type="string">
  How to handle per-page errors during processing. `"Continue"` (default) skips failed pages and continues processing the rest. `"Fail"` aborts the entire job on the first error.
</ParamField>

## Response

<ResponseField name="job_id" type="string">
  Unique identifier for the parse job. Use this with `GET /parse/{job_id}` to poll status and retrieve results.
</ResponseField>

<ResponseField name="upload_url" type="string">
  Presigned PUT URL. Upload your file directly to this URL; no `api-key` header is needed for the upload itself. Valid until `expires_at`.
</ResponseField>

<ResponseField name="expires_at" type="string">
  RFC 3339 timestamp when `upload_url` expires. Upload must complete before this time. The presigned URL is valid for 15 minutes by default; when `expires_in` is set (and positive), the upload URL lifetime is set to that same value, which can shorten or extend the window.
</ResponseField>

<ResponseField name="upload_method" type="string">
  Always `"PUT"`. Use an HTTP PUT request when uploading to `upload_url`.
</ResponseField>

<ResponseField name="upload_headers" type="object">
  Key-value headers you MUST include in your PUT request. Always includes `Content-Type` set to the MIME type inferred from `file_name`.
</ResponseField>

<ResponseField name="credit_used" type="integer">
  Number of page credits deducted as an initial reservation, reconciled on upload.
</ResponseField>

<ResponseField name="quota_remaining" type="integer">
  Remaining page credits after deduction.
</ResponseField>

## Step-by-Step Guide

### Step 1: Create the parse job

POST to `/v2/parse/upload` with your file name and configuration. The API returns a `job_id` and a short-lived presigned URL.

<CodeGroup>
  ```python Python theme={null}
  import requests

  API_KEY = "your-api-key"
  BASE_URL = "https://prod.visionapi.unsiloed.ai"

  response = requests.post(
      f"{BASE_URL}/v2/parse/upload",
      headers={"api-key": API_KEY, "Content-Type": "application/json"},
      json={
          "file_name": "report.pdf",
          "use_high_resolution": True,
          "layout_analysis": "smart_layout_detection",
          "ocr_strategy": "auto_detection",
          "extract_strikethrough": False,
          "merge_tables": False,
      },
  )
  response.raise_for_status()
  data = response.json()

  job_id = data["job_id"]
  upload_url = data["upload_url"]
  upload_headers = data["upload_headers"]

  print(f"Job ID: {job_id}")
  print(f"Upload URL expires: {data['expires_at']}")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = "your-api-key";
  const BASE_URL = "https://prod.visionapi.unsiloed.ai";

  const response = await fetch(`${BASE_URL}/v2/parse/upload`, {
    method: "POST",
    headers: { "api-key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      file_name: "report.pdf",
      use_high_resolution: true,
      layout_analysis: "smart_layout_detection",
      ocr_strategy: "auto_detection",
      extract_strikethrough: false,
      merge_tables: false,
    }),
  });

  const { job_id, upload_url, upload_headers, expires_at } = await response.json();

  console.log(`Job ID: ${job_id}`);
  console.log(`Upload URL expires: ${expires_at}`);
  ```

  ```bash cURL theme={null}
  curl -X POST https://prod.visionapi.unsiloed.ai/v2/parse/upload \
    -H "api-key: your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "file_name": "report.pdf",
      "use_high_resolution": true,
      "layout_analysis": "smart_layout_detection",
      "ocr_strategy": "auto_detection",
      "extract_strikethrough": false,
      "merge_tables": false
    }'
  ```
</CodeGroup>

The response contains the presigned URL and required headers:

```json theme={null}
{
  "job_id": "a3f1c2d4-7e8b-4a9f-b2c1-123456789abc",
  "upload_url": "https://upload.visionapi.unsiloed.ai/uploads/a3f1c2d4.../report.pdf?signature=...",
  "expires_at": "2025-10-22T07:06:16Z",
  "upload_method": "PUT",
  "upload_headers": {
    "Content-Type": "application/pdf"
  },
  "credit_used": 1,
  "quota_remaining": 1000
}
```

### Step 2: Upload the file

Use the `upload_url` and `upload_headers` from the response to PUT your file. No API key is needed for this request.

<CodeGroup>
  ```python Python theme={null}
  with open("report.pdf", "rb") as f:
      put_response = requests.put(upload_url, headers=upload_headers, data=f)
  put_response.raise_for_status()

  print("Upload complete, job is now queued for processing")
  ```

  ```javascript JavaScript theme={null}
  import fs from "fs";

  const fileBuffer = fs.readFileSync("report.pdf");

  const putResponse = await fetch(upload_url, {
    method: "PUT",
    headers: upload_headers,
    body: fileBuffer,
  });

  if (!putResponse.ok) {
    throw new Error(`Upload failed: ${putResponse.status}`);
  }

  console.log("Upload complete, job is now queued for processing");
  ```

  ```bash cURL theme={null}
  curl -X PUT "$UPLOAD_URL" \
    -H "Content-Type: application/pdf" \
    --data-binary @report.pdf
  ```
</CodeGroup>

<Warning>
  You must include every header listed in `upload_headers`. A missing or mismatched `Content-Type` will cause the upload to be rejected with a `403`.
</Warning>

### Step 3: Poll for results

Once the upload completes, the job transitions from `AwaitingUpload` → `Queued` → `Processing` → `Succeeded`. Poll `GET /parse/{job_id}` using the same endpoint as v1.

<CodeGroup>
  ```python Python theme={null}
  import time

  while True:
      status_response = requests.get(
          f"{BASE_URL}/parse/{job_id}",
          headers={"api-key": API_KEY},
      )
      status_response.raise_for_status()
      job = status_response.json()
      print(f"Status: {job['status']}")

      if job["status"] == "Succeeded":
          print(f"Done! {job['total_chunks']} chunks extracted.")
          break
      elif job["status"] == "Failed":
          raise RuntimeError(f"Job failed: {job.get('message')}")

      time.sleep(5)
  ```

  ```javascript JavaScript theme={null}
  while (true) {
    const statusResponse = await fetch(`${BASE_URL}/parse/${job_id}`, {
      headers: { "api-key": API_KEY },
    });
    const job = await statusResponse.json();
    console.log(`Status: ${job.status}`);

    if (job.status === "Succeeded") {
      console.log(`Done! ${job.total_chunks} chunks extracted.`);
      break;
    } else if (job.status === "Failed") {
      throw new Error(`Job failed: ${job.message}`);
    }

    await new Promise((r) => setTimeout(r, 5000));
  }
  ```

  ```bash cURL theme={null}
  curl -X GET "https://prod.visionapi.unsiloed.ai/parse/{job_id}" \
    -H "api-key: your-api-key"
  ```
</CodeGroup>

See [Get Parse Job Status](/api-reference/parser/get-parse-job-status) for the full response schema.

<RequestExample>
  ```bash cURL theme={null}
  # Step 1: Create job and get presigned URL
  RESPONSE=$(curl -s -X POST https://prod.visionapi.unsiloed.ai/v2/parse/upload \
    -H "api-key: your-api-key" \
    -H "Content-Type: application/json" \
    -d '{
      "file_name": "report.pdf",
      "use_high_resolution": true,
      "layout_analysis": "smart_layout_detection",
      "ocr_strategy": "auto_detection",
      "extract_strikethrough": false,
      "merge_tables": false
    }')

  JOB_ID=$(echo "$RESPONSE" | jq -r '.job_id')
  UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.upload_url')

  # Step 2: Upload file
  curl -X PUT "$UPLOAD_URL" \
    -H "Content-Type: application/pdf" \
    --data-binary @report.pdf

  # Step 3: Poll for results
  curl -X GET "https://prod.visionapi.unsiloed.ai/parse/$JOB_ID" \
    -H "api-key: your-api-key"
  ```

  ```python Python theme={null}
  import requests
  import time

  API_KEY = "your-api-key"
  BASE_URL = "https://prod.visionapi.unsiloed.ai"

  # Step 1: Create job and get presigned URL
  resp = requests.post(
      f"{BASE_URL}/v2/parse/upload",
      headers={"api-key": API_KEY, "Content-Type": "application/json"},
      json={
          "file_name": "report.pdf",
          "use_high_resolution": True,
          "layout_analysis": "smart_layout_detection",
          "ocr_strategy": "auto_detection",
          "extract_strikethrough": False,
          "merge_tables": False,
      },
  )
  resp.raise_for_status()
  data = resp.json()
  job_id = data["job_id"]
  upload_url = data["upload_url"]
  upload_headers = data["upload_headers"]

  print(f"Job created: {job_id}")
  print(f"Upload URL expires: {data['expires_at']}")

  # Step 2: Upload file
  with open("report.pdf", "rb") as f:
      put_resp = requests.put(upload_url, headers=upload_headers, data=f)
  put_resp.raise_for_status()
  print("Upload complete, job is now queued for processing")

  # Step 3: Poll for results
  while True:
      status_resp = requests.get(
          f"{BASE_URL}/parse/{job_id}",
          headers={"api-key": API_KEY},
      )
      status_resp.raise_for_status()
      job = status_resp.json()
      print(f"Status: {job['status']}")

      if job["status"] == "Succeeded":
          print(f"Done! {job['total_chunks']} chunks extracted.")
          break
      elif job["status"] == "Failed":
          raise RuntimeError(f"Job failed: {job.get('message')}")

      time.sleep(5)
  ```

  ```javascript JavaScript theme={null}
  import fs from "fs";

  const API_KEY = "your-api-key";
  const BASE_URL = "https://prod.visionapi.unsiloed.ai";

  // Step 1: Create job and get presigned URL
  const createResp = await fetch(`${BASE_URL}/v2/parse/upload`, {
    method: "POST",
    headers: { "api-key": API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({
      file_name: "report.pdf",
      use_high_resolution: true,
      layout_analysis: "smart_layout_detection",
      ocr_strategy: "auto_detection",
      extract_strikethrough: false,
      merge_tables: false,
    }),
  });
  const { job_id, upload_url, upload_headers, expires_at } = await createResp.json();
  console.log(`Job created: ${job_id}, URL expires: ${expires_at}`);

  // Step 2: Upload file
  const fileBuffer = fs.readFileSync("report.pdf");
  await fetch(upload_url, {
    method: "PUT",
    headers: upload_headers,
    body: fileBuffer,
  });
  console.log("Upload complete, job is now queued");

  // Step 3: Poll for results
  while (true) {
    const statusResp = await fetch(`${BASE_URL}/parse/${job_id}`, {
      headers: { "api-key": API_KEY },
    });
    const job = await statusResp.json();
    console.log(`Status: ${job.status}`);

    if (job.status === "Succeeded") {
      console.log(`Done! ${job.total_chunks} chunks extracted.`);
      break;
    } else if (job.status === "Failed") {
      throw new Error(`Job failed: ${job.message}`);
    }

    await new Promise((r) => setTimeout(r, 5000));
  }
  ```
</RequestExample>

<ResponseExample>
  ```json Success Response theme={null}
  {
    "job_id": "a3f1c2d4-7e8b-4a9f-b2c1-123456789abc",
    "upload_url": "https://upload.visionapi.unsiloed.ai/uploads/a3f1c2d4.../report.pdf?signature=...",
    "expires_at": "2025-10-22T07:06:16Z",
    "upload_method": "PUT",
    "upload_headers": {
      "Content-Type": "application/pdf"
    },
    "credit_used": 1,
    "quota_remaining": 1000
  }
  ```

  ```json Error Response - Unsupported File Type theme={null}
  {
    "error": "Unsupported file type: .exe"
  }
  ```

  ```json 429 - Rate Limit Exceeded theme={null}
  {
    "error": "rate_limit_exceeded",
    "message": "Rate limit of 10 requests per second exceeded. Retry after 1s.",
    "retry_after": 1
  }
  ```
</ResponseExample>

## Why Use v2

| Feature       | v1 (`POST /parse`)         | v2 (`POST /v2/parse/upload`) |
| ------------- | -------------------------- | ---------------------------- |
| File delivery | Through API server         | Direct via presigned URL     |
| Max file size | Limited by server upload   | Up to 5 GB via direct PUT    |
| Upload speed  | Bottlenecked by API server | Full client bandwidth        |
| Concurrency   | Shared server capacity     | No server contention         |
| Best for      | Quick uploads, small files | Large files, high volume     |

## Error Handling

| Status              | Cause                                                 | Action                                                  |
| ------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
| `400`               | Invalid or missing `file_name`, unsupported extension | Fix `file_name` and retry                               |
| `401`               | Missing or invalid `api-key`                          | Check your API key                                      |
| `402`               | Insufficient quota or expired credits                 | Add page credits to your account or renew your plan     |
| `403`               | Access has been revoked                               | Contact support                                         |
| `429`               | Rate limit exceeded                                   | Back off and retry after 1 second                       |
| `500`               | Internal server error                                 | Retry with exponential backoff                          |
| `503`               | Job queue is at capacity                              | Back off and retry after the `Retry-After` header value |
| `403` on upload     | Missing or wrong headers, expired URL                 | Check `upload_headers`, get a new URL                   |
| Job status `Failed` | Processing error                                      | Check `message` field in the status response            |

<Note>
  A job can move from `Queued` straight to `Failed` without ever reaching `Processing`: after the upload, the document is page-counted and rejected if it exceeds the page limit (default 2000 pages; split large documents first) or if the remaining page quota cannot cover it.
</Note>


## OpenAPI

````yaml api-reference/parser/openapi-v2.json POST /v2/parse/upload
openapi: 3.1.0
info:
  title: Unsiloed Parser API — v2
  description: >-
    Direct upload API. The client uploads the file directly using a presigned
    URL — the server is never in the file transfer path. Use GET /parse/{job_id}
    (v1 endpoint) to poll job status.
  contact:
    name: Unsiloed
    url: https://unsiloed.ai
    email: hello@unsiloed.com
  license:
    name: ''
  version: 2.0.0
servers:
  - url: https://prod.visionapi.unsiloed.ai
    description: Production
security: []
tags:
  - name: Parse v2 (Presigned Upload)
    description: Direct upload — no server buffering
paths:
  /v2/parse/upload:
    post:
      tags:
        - Parse v2 (Presigned Upload)
      summary: POST /v2/parse/upload
      description: >-
        Creates a parse job and returns a presigned S3 PUT URL.

        The client uploads the file directly to S3 (server not involved in the
        transfer).

        Once the upload completes, the system enqueues the job automatically via
        S3 event notification.

        Poll GET /parse/{job_id} to track progress.
      operationId: create_presigned_upload
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PresignedUploadRequest'
        required: true
      responses:
        '200':
          description: Upload URL created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PresignedUploadResponse'
        '400':
          description: Invalid file_name or unsupported file type
        '401':
          description: Unauthorized
        '402':
          description: Insufficient quota — not enough page credits remaining.
        '403':
          description: Forbidden — access has been revoked.
        '429':
          description: Rate limit exceeded
        '500':
          description: Internal server error.
        '503':
          description: >-
            Service unavailable — job queue is at capacity. Retry after the
            duration indicated in the `Retry-After` header.
      security:
        - api_key: []
components:
  schemas:
    PresignedUploadRequest:
      type: object
      description: |-
        Request body for POST /v2/parse/upload.
        Configuration fields mirror the existing /parse multipart form fields.
      required:
        - file_name
      properties:
        agentic_ocr:
          type:
            - string
            - 'null'
          description: >-
            Enable per-segment agentic OCR for higher accuracy. Pass
            `"standard"` or `"advanced"`.
        chunk_processing:
          description: JSON object for chunk processing configuration.
        detect_pii:
          type:
            - boolean
            - 'null'
          description: >-
            Run a PII pre-check before parsing. When enabled, the document is
            scanned

            for personally identifiable information before any extraction work
            happens.

            If PII is found at or above `pii_block_severity`, the task is
            rejected and

            no parsing occurs (the job ends in a failed state with a PII
            reason).

            Defaults to `false`.
          default: false
        enhance_reading_order:
          type:
            - boolean
            - 'null'
          description: >-
            Fix the reading order of detected segments. Useful for multi-column
            layouts. Defaults to `false`.
        error_handling:
          type:
            - string
            - 'null'
          description: 'Error handling strategy: `Continue` (default) or `Fail`.'
        expires_in:
          type:
            - integer
            - 'null'
          format: int32
          description: >-
            Seconds the returned presigned upload URL stays valid. Defaults to
            the

            `PRESIGNED_UPLOAD_EXPIRY_SECS` env value (typically 900 / 15 min).
            Once

            the client uploads the file, the task's `expires_at` is cleared and
            the

            task lifetime is no longer bounded by this value.
        export_format:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/ExportFormat'
          description: >-
            Export format(s) to generate after processing. Supported: `["docx",
            "markdown", "json"]`.
        extract_colors:
          type:
            - boolean
            - 'null'
          description: >-
            Transfer text color from the PDF text layer to OCR results. Defaults
            to `false`.
        extract_links:
          type:
            - boolean
            - 'null'
          description: >-
            Attach hyperlink URLs from PDF annotations to OCR results. Defaults
            to `false`.
        extract_strikethrough:
          type:
            - boolean
            - 'null'
          description: >-
            Preserve strikethrough formatting in HTML/Markdown output. Defaults
            to `false`.
        file_name:
          type: string
          description: File name with extension. Required. Determines content-type.
        layout_analysis:
          type:
            - string
            - 'null'
          description: >-
            Layout analysis strategy: `smart_layout_detection` (default),
            `page_by_page`,

            or `advanced_layout_detection` (higher-accuracy layout detection for
            complex

            pages — multi-column layouts, dense tables/figures; slower than the
            default).
        llm_processing:
          description: JSON object for LLM processing configuration.
        merge_batch_size:
          type:
            - integer
            - 'null'
          format: int32
          description: >-
            Maximum number of tables per merge group. Groups larger than this
            are split. Defaults to 20.
        merge_tables:
          type:
            - boolean
            - 'null'
          description: >-
            Merge tables that span multiple pages into a single unified
            structure. Defaults to `false`.
        ocr_engine:
          type:
            - string
            - 'null'
          description: >-
            OCR engine: `UnsiloedHawk` (default, recommended), `UnsiloedBeta`,
            or `UnsiloedStorm`.
        ocr_strategy:
          type:
            - string
            - 'null'
          description: 'OCR strategy: `auto_detection` (default) or `force_ocr`.'
        output_fields:
          description: >-
            JSON object filtering which fields appear on each segment / chunk.

            Each key defaults to `true`; set a key to `false` to drop the field.

            Keys: `bbox`, `chart_data`, `confidence`, `content`, `embed`,
            `html`,

            `image`, `markdown`, `ocr`. Ignored when `response_profile` is
            `slim`

            or `full`.
        page_range:
          type:
            - string
            - 'null'
          description: >-
            Page range to process. Formats: `"1-5"`, `"2,4,6"`, `"[1,3,5]"`.
            Defaults to all pages.
        pii_block_severity:
          type:
            - string
            - 'null'
          description: >-
            Severity threshold at which a detected PII finding blocks the task.

            Ignored when `detect_pii` is `false`. Findings strictly below the
            threshold

            are allowed through; findings at or above it reject the task.

            - `any` (default) — block on any detection, regardless of severity.

            - `low` — block on low, medium, or high severity findings.

            - `medium` — block on medium or high severity findings.

            - `high` — block only on high severity findings.
          default: any
        pii_engine:
          type:
            - string
            - 'null'
          description: >-
            PII detector engine to use when `detect_pii` is `true`. Ignored
            otherwise.

            - `standard` (default) — fast pattern-based detector; low latency,
              well-suited to bulk pre-screening.
            - `advanced` — model-based detector; slower but catches contextual
              cases that pattern matching misses (e.g. handwritten names,
              partially redacted IDs, document-style references to a person).
          default: standard
        response_profile:
          type:
            - string
            - 'null'
          description: |-
            Response shape selector: `slim`, `full`, or `custom`.
            - `slim`: chunk `embed` + bbox + page_number + segment_id +
              segment_type + HTML for tables / Markdown otherwise. Drops
              content/image/ocr/confidence/chart_data/page_height/page_width.
            - `full`: every field returned (equivalent to omitting this param).
            - `custom`: honor `output_fields` verbatim.

            When both `response_profile` and `output_fields` are provided, the
            profile wins.
          example: slim
        segment_filter:
          type:
            - string
            - 'null'
          description: >-
            Segment filter: comma-separated segment types to keep, or "all".
            Defaults to `"all"`.
        segment_processing:
          description: JSON object for segment processing/analysis configuration.
        segment_type_naming:
          type:
            - string
            - 'null'
          description: 'Segment type naming convention: `Unsiloed` (default) or `Other`.'
        use_high_resolution:
          type:
            - boolean
            - 'null'
          description: >-
            Use high-resolution images for cropping and post-processing.
            Defaults to `true`.
        validate_segments:
          description: >-
            JSON array of segment types to validate with VLM. Example:
            `["Table","Formula"]`.
        validate_table_segments:
          type:
            - boolean
            - 'null'
          description: >-
            Legacy: validate table segments using VLM. Prefer
            `validate_segments` instead.
        xml_citation:
          type:
            - boolean
            - 'null'
          description: >-
            Extract and hyperlink bibliography citations in the markdown output.
            Defaults to `false`.
    PresignedUploadResponse:
      type: object
      description: Response from POST /v2/parse/upload.
      required:
        - job_id
        - upload_url
        - expires_at
        - upload_method
        - upload_headers
        - credit_used
        - quota_remaining
      properties:
        credit_used:
          type: integer
          format: int32
          description: >-
            Number of page credits deducted (initial reservation, reconciled on
            upload).
        expires_at:
          type: string
          description: RFC 3339 timestamp when upload_url expires.
        job_id:
          type: string
          description: Use this ID to poll GET /parse/{job_id} for status.
        quota_remaining:
          type: integer
          format: int32
          description: Remaining page credits after deduction.
        upload_headers:
          type: object
          description: Headers the client MUST include in the PUT request.
          additionalProperties:
            type: string
          propertyNames:
            type: string
        upload_method:
          type: string
          description: Always "PUT".
        upload_url:
          type: string
          description: S3 presigned PUT URL. Valid until expires_at.
    ExportFormat:
      type: string
      description: >-
        File format for exporting parsed results. When specified in a parse
        request,

        the pipeline generates the requested export file after processing
        completes.

        The exported file is available via the `exports` field in the task
        response.
      enum:
        - docx
        - markdown
        - json
  securitySchemes:
    api_key:
      type: http
      scheme: bearer
      description: API key for authentication. Use 'Bearer <your_api_key>'

````