{
  "openapi": "3.1.0",
  "info": {
    "title": "LexSelect External API",
    "description": "Turn documents into structured, layout-aware output: a page tree with word coordinates, plain text, paragraphs, a clause outline and table data. Supported inputs are PDF, DOC, DOCX, ODT, RTF, EML and MSG, and text recognition covers more than 200 languages.\n\n## Start here\n\n- [Get started](/api/getting-started.html): create an API key, upload a document, wait for processing and read the results.\n- [Understand output](/api/understanding-output.html): slices, identifiers, word coordinates, tables, node classes and email attachments.\n- [Operations and limits](/api/operations.html): errors, safe retries, idempotency, retention, billing and limits.\n- [Webhooks](/api/webhooks.html): set up an endpoint, verify signatures and handle deliveries.\n- [Versions and changelog](/api/versioning.html): supported API versions and what changed in each.\n- [Server-side examples and CLI](https://github.com/LexSelect/lexselect-api-demo).\n\n## Essentials\n\n- **Base URL:** `https://api.lexselect.io/api`. Paths in this reference are relative to it.\n- **Authentication:** create an API key in [Studio → API keys](https://studio.lexselect.io/keys) and send it from your server as `Authorization: Bearer lxs_...`.\n- **Versions:** send `X-API-Version: 2026-11-05`, the latest version, on every request.\n- **Asynchronous processing:** an upload returns as soon as the file is accepted. Wait for `completed` from `GET /documents/{id}/processing/latest` or a signed webhook, then read the results.\n- **Retention:** API uploads are deleted 24 hours after upload or 1 hour after processing ends, whichever comes first. Save the results you need in your own system.\n- **Errors:** every error is an RFC 9457 Problem Details body (`application/problem+json`). [Operations and limits](/api/operations.html) explains each status, the problem `code` values and which requests are safe to retry.\n\nQuestions? Email [support@lexselect.io](mailto:support@lexselect.io) and include the request's `X-Request-Id`.\n",
    "version": "2026-11-05",
    "contact": {
      "name": "LexSelect API Support",
      "email": "support@lexselect.io",
      "url": "https://support.lexselect.io/en/help"
    }
  },
  "servers": [
    {
      "url": "https://api.lexselect.io/api",
      "description": "Production API"
    },
    {
      "url": "/api",
      "description": "Current documentation environment (relative to this page)"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Files",
      "description": "Documents and folders: upload, list, retrieve and queue deletion. See Get started for projects and ownership."
    },
    {
      "name": "Processing",
      "description": "Processing pipeline — track status and progress of document processing."
    },
    {
      "name": "Results",
      "description": "Processing results: page metadata, parsed structures, text and table projections."
    },
    {
      "name": "Usage",
      "description": "Account usage and spend — pages processed, documents stored, remaining balance, and cost to date."
    },
    {
      "name": "Webhooks",
      "description": "Receive signed notifications when your documents are queued, finish processing or fail. You manage webhook endpoints in **Studio → Webhooks**.\n\n[Open the dedicated Webhooks guide](/api/webhooks.html) ·\n[Download this guide as Markdown](/api/webhooks.md) ·\n[Download the Node.js signature verifier](/api/verify-webhook.mjs)\n\n## Integration flow\n\n1. In Studio, create and verify a webhook endpoint under the account that uploads your documents. Subscribe to `document.processing.completed` and `document.processing.failed`.\n2. Upload with `POST /documents/upload` and save the returned document `id`.\n3. When a delivery arrives, verify its signature against the **raw request bytes**, store or enqueue the event, then return `2xx`.\n4. On `document.processing.completed`, call `GET /documents/{id}/parse` with `data.document_id` and an API key from the same account, and save the results in your system. Events carry identifiers and status, not document content.\n5. Optionally call `DELETE /documents/{id}` for a document your integration uploaded, once you have saved everything you need. Check the ID against the uploads you recorded: `completed` and `failed` events also arrive for documents processed in Studio, so never delete because of an event alone.\n6. Alongside the receiver, run the reconciliation sweep from [Operations and limits](/api/operations.html) every few minutes. It picks up documents whose events you missed and runs that were canceled, which send no event.\n\n**Email uploads.** An EML or MSG upload is processed as one run, and the event is sent once, for the uploaded email. The email body and each attachment become child documents with their own pages: list them with `GET /documents?parent_id={id}` and call `/parse` for each child. The uploaded email itself has no pages, and deleting it also deletes its children. See [Understand output](/api/understanding-output.html).\n\n**Retention.** API uploads are deleted 24 hours after upload or 1 hour after processing ends, whichever comes first. Fetch results as soon as the completed event arrives. A delivery that succeeds only on a later retry, or one you resend with **Retry delivery**, can name a document that has already been deleted, so the sweep is the reliable path for results.\n\n**Ownership.** Webhook endpoints belong to an account. An endpoint receives events for documents owned by the account that created it. `document.processing.queued` is sent for API uploads; `completed` and `failed` are also sent for documents the account processes in Studio. Other workspace members' events never reach your endpoint. Use separate accounts and endpoints for development and production.\n\nEvent payloads are the same whatever `X-API-Version` you send. Webhook deliveries do not count toward your API rate limits; your receiver's calls back to the API, such as `/parse`, do.\n\n## Set up an endpoint\n\n1. In Studio, open **Webhooks**, choose **Create webhook**, enter a public HTTPS receiver URL that accepts `POST`, and select the events you need.\n2. Copy the complete signing secret, including the `whsec_` prefix. It is shown only once. Store it on your server, separately from your `lxs_` API key.\n3. Open **Verify domain**, add the TXT record that Studio shows at `_lexselect-webhook.<receiver-hostname>`, then choose **Check DNS**. DNS changes can take a while to propagate. If your account already has a verified endpoint on the same hostname, a new endpoint on that hostname is verified straight away.\n4. Once the endpoint is verified and active, it receives real events. Changing only the URL path keeps the verification; a different hostname needs its own verification.\n\nThe receiver URL must use HTTPS on a public hostname with a valid certificate; any port works. Localhost, private addresses and redirects cannot receive deliveries. **Send Test** works before domain verification, so a public HTTPS tunnel is enough to test signatures; to receive real events through a tunnel, use a hostname whose DNS records you control.\n\nDeliveries don't come from a published set of IP addresses, so authenticate each delivery by its signature rather than by its source address.\n\nAn endpoint keeps its signing secret for its whole life. To change secrets, create a second endpoint with the same events, accept both secrets in your receiver while you switch, then delete the old endpoint. Both endpoints send the same event IDs, so deduplication keeps working during the switch.\n\n## Events and payloads\n\n| Event and when it is sent | `data` fields |\n|---|---|\n| `document.processing.queued`: an API upload was accepted for processing | `document_id`, `document_name`, `status: \"queued\"` |\n| `document.processing.completed`: processing completed successfully | `document_id`, `document_name`, `processing_id`, `status: \"completed\"` |\n| `document.processing.failed`: processing failed | `document_id`, `document_name`, `processing_id`, `status: \"failed\"`, `error_code`, `error_message`, `failed_task` |\n| `*`: subscription to every event type, including future ones | — |\n\nOn a failed event, branch on `error_code`; [Operations and limits](/api/operations.html) lists the values. `error_message` is a readable explanation, and `failed_task` is always `processing`. For progress and page counts, call `GET /documents/{id}/processing/latest`.\n\nEvery event uses this envelope:\n\n```json\n{\n  \"id\": \"72c46026-d682-4325-ae72-e2c09db7d525\",\n  \"type\": \"document.processing.completed\",\n  \"created_at\": \"2026-10-05T17:42:00.000000+00:00\",\n  \"data\": {\n    \"document_id\": \"6650c0ffee0000000000a001\",\n    \"document_name\": \"8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf\",\n    \"processing_id\": \"6650c0ffee0000000000a002\",\n    \"status\": \"completed\"\n  }\n}\n```\n\n- `id` is the event ID. It matches the `X-Webhook-Id` header and stays the same across every attempt and retry, so use it to deduplicate.\n- `created_at` is when the event was created. It stays the same on retries.\n- `document_name` is the stored name. It is generated unless the upload sent `strip_file_name=false`, so correlate by ID rather than by name.\n\nIgnore fields you do not recognize, and accept event types you do not handle without failing.\n\n## Delivery headers\n\n| Header | Meaning |\n|---|---|\n| `Content-Type: application/json` | UTF-8 JSON event body |\n| `X-Webhook-Id` | The event ID, the same as the body's `id` |\n| `X-Webhook-Timestamp` | Unix time in seconds at which this attempt was signed |\n| `X-Webhook-Signature` | `sha256=` followed by the 64 lowercase hexadecimal characters of the HMAC-SHA256 digest |\n| `User-Agent: LexSelect-Webhooks/1.0` | Identifies the sender; authenticate deliveries by their signature |\n\nHeader names are case-insensitive.\n\n## Verify signatures\n\nUse the **entire signing secret as UTF-8 text**, including `whsec_`; don't strip the prefix or decode the secret. The signed message is:\n\n`UTF8(X-Webhook-Timestamp + \".\") || exact raw request body bytes`\n\nCompute HMAC-SHA256 and compare the digest bytes in constant time before parsing the JSON: re-serialized JSON does not match the signature. This Node.js 20+ module uses only the standard library:\n\n```js\n// file: verify-webhook.mjs\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\n/** Verify one delivery without parsing or changing its signed body bytes. */\nexport function verifyWebhook(\n  rawBody,\n  headers,\n  signingSecret,\n  nowSeconds = Math.floor(Date.now() / 1000),\n  toleranceSeconds = 300\n) {\n  const timestamp = headers['x-webhook-timestamp'];\n  const signature = headers['x-webhook-signature'];\n  if (\n    !Buffer.isBuffer(rawBody) ||\n    typeof signingSecret !== 'string' ||\n    !signingSecret.startsWith('whsec_') ||\n    typeof timestamp !== 'string' ||\n    !/^\\d{1,12}$/.test(timestamp) ||\n    typeof signature !== 'string' ||\n    !/^sha256=[0-9a-f]{64}$/.test(signature) ||\n    !Number.isSafeInteger(nowSeconds) ||\n    !Number.isSafeInteger(toleranceSeconds) ||\n    toleranceSeconds < 0\n  ) return false;\n  const signedAt = Number(timestamp);\n  if (Math.abs(nowSeconds - signedAt) > toleranceSeconds) return false;\n  const expected = createHmac('sha256', Buffer.from(signingSecret, 'utf8'))\n    .update(timestamp + '.', 'utf8')\n    .update(rawBody)\n    .digest();\n  const received = Buffer.from(signature.slice('sha256='.length), 'hex');\n  return received.length === expected.length && timingSafeEqual(received, expected);\n}\n```\n\nPass the unmodified `Buffer` read from the request stream. In Express, mount `express.raw({ type: 'application/json' })` on the webhook route before any JSON body parser. After verifying, parse the JSON and check that `payload.id` matches `X-Webhook-Id`.\n\nThe example accepts timestamps up to five minutes either side of your clock. Keep your server clock synchronized, and adjust the tolerance if you need to. Check the attempt timestamp, not `created_at`: every retry is signed again with a fresh timestamp but keeps the original body.\n\n## Acknowledge and deduplicate\n\nStore or enqueue the event, then return `2xx` within 10 seconds, and do slow work such as `/parse` in your own worker. A `3xx` redirect is not followed and counts as a failed attempt. If you could not store the event, return an error so that the delivery is retried.\n\nDelivery is at-least-once and unordered. Record each event `id` under a unique constraint, and acknowledge duplicates without repeating their effects.\n\n## Retries and endpoint health\n\n- A delivery is attempted up to five times: the first send, then retries after about 30 seconds, 2 minutes, 15 minutes and 1 hour. A `Retry-After` header from your receiver does not change this schedule.\n- Every non-`2xx` response, timeout, connection failure and DNS failure is retried. Deliveries to unsafe or private addresses are refused rather than retried.\n- Retries normally finish within about 80 minutes of the event. If delivery is delayed, for example during an outage, automatic delivery stops 24 hours after the event was created.\n- After 10 consecutive deliveries fail their final attempt, the endpoint is disabled. A successful delivery resets the count.\n- **Pause** in Studio stops real deliveries to an endpoint until you choose **Resume**. A disabled endpoint stays disabled until you choose **Re-enable**, which keeps the signing secret and resets the failure count. Events created while an endpoint is paused or disabled are not sent later; the reconciliation sweep picks up those documents.\n\n**Retry delivery** in Studio resends one failed delivery from the last 30 days, including after its automatic window has closed. It sends the original body and event ID to the endpoint's current URL, signed with the current secret and a fresh timestamp. The endpoint must be active and verified, and a delivery whose record has expired cannot be retried. The document it names may already have been deleted by retention, so check `/processing/latest` before you rely on it.\n\n**Test URL**, **Send Test** and **Retry delivery** are each limited to five uses per account per minute.\n\n## Send Test and Test URL\n\n**Send Test** sends a signed test event to a saved endpoint, including before domain verification and while the endpoint is paused or disabled. It is sent once, is not retried, and does not change the endpoint's status or failure count. A test event has the real envelope and the same `data` keys as the real event of its type, with the document and processing ID `000000000000000000000000`, `document_name: \"test-document.pdf\"` and an extra `message: \"This is a test webhook event\"`. Have your handler recognize the zero document ID and skip fetching results for it. Besides the real event types, Send Test can send two test-only types, `document.processing.started` and `document.processing.page_completed`, which real processing does not send.\n\n**Test URL** checks connectivity before you save an endpoint. It sends this unsigned request:\n\n```json\n{\"type\":\"lexselect.webhook.probe\",\"data\":{\"message\":\"Unsigned connectivity test; no webhook has been saved.\"}}\n```\n\nThe request carries `X-LexSelect-Test: true` and `User-Agent: LexSelect-Webhook-Probe/1.0`, and has no signature, timestamp or event ID. A receiver that enforces signatures answers it with `401`, which is expected; use **Send Test** to check signatures. Test URL requires public HTTPS on port 443 with no credentials or fragment in the URL, and does not follow redirects.\n\n## Troubleshooting\n\n| Symptom | What to check |\n|---|---|\n| Receiver returns `401` or `403` | The saved secret, raw-body handling, your server clock, and any proxy or firewall in front of the receiver. A `401` to **Test URL** is expected. |\n| Receiver returns `404` | The hostname, `POST` route, path prefix and tunnel routing. This status comes from your receiver. |\n| `/parse` returns `404` | Use `data.document_id` with an API key from the same account, and check whether the document was deleted or reached its retention deadline. |\n| `/parse` returns no pages for an email | Parse the child documents listed by `GET /documents?parent_id={id}`. |\n| No real deliveries | Account ownership, the event subscription, the endpoint status and domain verification. |\n| DNS, TLS or timeout failures | Public DNS, the HTTPS certificate, reachability and a prompt `2xx`. |\n| Endpoint disabled | Fix the receiver, choose **Re-enable**, then use **Retry delivery** for the failures you need. |\n| Duplicate deliveries | Deduplicate by event ID; retries keep the same ID. |\n\n**Webhooks → View in Logs**, or **Logs**, in Studio shows each delivery's status, latency and error. Payload previews can be truncated or redacted. Workspace owners can also open **Workspace logs** to see other members' delivery metadata, without payloads. When you contact [support@lexselect.io](mailto:support@lexselect.io), include event IDs and approximate times, and never share your signing secret.\n"
    }
  ],
  "paths": {
    "/folders/{folder_id}/sub-documents": {
      "get": {
        "operationId": "listFolderSubDocuments",
        "summary": "List sub-documents across a folder",
        "description": "Lists the sub-documents detected in the files directly inside a folder or project, as one paginated list. Filter by `type`, for example every invoice in the folder, then read each result in its file's `/parse` output using its page range and anchors. Sub-documents and their IDs are rebuilt when a file is processed again. `kind` is currently always `subdocument`.\n",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "folder_id",
            "in": "path",
            "required": true,
            "description": "ID of the folder or project. Only files directly inside it are included.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Case-insensitive exact match on the open-vocabulary `document_type` (e.g. `Notice`, `Invoice`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Return only entries of this kind.",
            "schema": {
              "type": "string",
              "enum": [
                "subdocument",
                "section"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per response, from 1 to 200 (default 50). Other values return `400`.",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Rows to skip (default 0).",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sub-document rows in per-document reading order.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "folder_id",
                    "total",
                    "limit",
                    "offset",
                    "sub_documents"
                  ],
                  "properties": {
                    "folder_id": {
                      "type": "string"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Number of matching sub-documents in the folder, across all responses."
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "sub_documents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FolderSubDocumentRow"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID, filter or pagination parameter, or an unsupported `X-API-Version`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The folder does not exist or belongs to another account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "422": {
            "description": "The folder contains more than 5,000 files.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "description": "The service is temporarily unavailable. Retry with backoff.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/folders/$FOLDER_ID/sub-documents\""
          }
        ]
      }
    },
    "/documents": {
      "get": {
        "operationId": "listDocuments",
        "summary": "List files",
        "description": "Lists the items directly inside a project, folder or uploaded email. Without `parent_id`, it lists your projects. For an EML or MSG upload, the children are its email body and attachments. A new upload can take a few seconds to appear, so retry before concluding that a document is missing.\n",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "parent_id",
            "in": "query",
            "required": false,
            "description": "ID of the project, folder or uploaded email whose children to list. Omit it to list your projects.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Results per response, from 1 to 100 (default 25). Other values return `400`.",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous response. If the item it points to has since been deleted, the listing restarts from the beginning.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "Field to sort by.",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "size",
                "created_at",
                "modified_at"
              ],
              "default": "name"
            }
          },
          {
            "name": "sort_direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "description": "Filter by item type.",
            "schema": {
              "type": "string",
              "enum": [
                "file",
                "folder",
                "project"
              ]
            }
          },
          {
            "name": "include_deleted",
            "in": "query",
            "required": false,
            "description": "Also return items that have been deleted but not yet removed. They disappear once removal finishes and cannot be restored.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of documents.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter or an unsupported `X-API-Version`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The parent does not exist or belongs to another account.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents\""
          }
        ]
      }
    },
    "/documents/upload": {
      "post": {
        "operationId": "uploadDocument",
        "summary": "Upload file",
        "description": "Uploads one file as `multipart/form-data` and returns the created document. Processing then runs asynchronously: wait for `completed` on `GET /documents/{id}/processing/latest` or a webhook before you read results.\n\n- **Formats and size:** PDF, DOC, DOCX, ODT, RTF, EML and MSG, up to 250 MiB (262,144,000 bytes).\n- **Fields:** `name`, `file`, `parent_id`, `strip_file_name`, `content_type`, `size`, and for accounts with OCR import enabled, `ocr` and `ocr_format`. Send one `file` part; other fields are ignored.\n- **Field order:** send all fields before the `file` part. With `size` (streaming) this is required, and fields after `file` are ignored.\n- **Location:** without `parent_id`, the file goes to your Default Project. The response's `parent_id` tells you which folder that is.\n- **Name:** by default the stored name is generated, as `<uuid>.<extension>`. Send `strip_file_name=false` to keep `name`.\n- **Streaming:** with the exact byte count in `size`, the file is stored while it is being received, so large uploads finish sooner.\n- **Your own OCR (PDF only, enabled per account):** send the OCR you already have as an `ocr` part (JSON, or gzip-compressed JSON, up to 128 MiB) with `ocr_format`: `lexselect` for the `LexSelectOcrV1` schema below, or `aws_textract` for AWS Textract output (`DetectDocumentText` / `AnalyzeDocument` responses, one per page in a list, or the `GetDocumentTextDetection` / `GetDocumentAnalysis` result pages). Pages it covers are read from it instead of by our OCR, so text quality on those pages follows your OCR engine; a page it misses, or that holds no words, is read by our OCR. An invalid file returns `400` with the reason and no document is created. The response's `ocr` object reports the import, and after processing, how many pages came from your file.\n\nAn accepted upload can still fail processing, for example when the document exceeds the page limit. After a timeout, `500`, `503` or `504`, the document may exist and be processing, so look it up before you upload again; an `Idempotency-Key` doesn't replay these responses. [Operations and limits](/api/operations.html) gives the lookup.",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "file"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "File name including its extension. It is used to check the extension and infer `content_type`, and is stored as the document name only when `strip_file_name=false`.\n",
                    "example": "report.pdf",
                    "maxLength": 255
                  },
                  "size": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 262144000,
                    "example": 0,
                    "description": "Exact file size in bytes. Optional; a positive value streams the upload, and a size that does not match the file returns `400`. Omit it or send `0` to buffer the upload."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The file, up to 250 MiB. Send it after the other fields."
                  },
                  "content_type": {
                    "type": "string",
                    "description": "MIME type of the file. Inferred from `name` when omitted."
                  },
                  "parent_id": {
                    "type": "string",
                    "description": "ID of the project or folder to upload into. Defaults to your Default Project."
                  },
                  "strip_file_name": {
                    "type": "boolean",
                    "default": true,
                    "description": "`true` (default): store the document under a generated name, a UUID plus the original extension, such as `<uuid>.pdf`. `false`: store it as `name`; if that name already exists in the folder, a suffix such as ` (1)` is added. Accepts `true` or `false`, `1` or `0`, and `yes` or `no`; other values return `400`.\n"
                  },
                  "ocr_format": {
                    "type": "string",
                    "enum": [
                      "lexselect",
                      "aws_textract"
                    ],
                    "description": "Format of the `ocr` part. Required with `ocr`; with `size` (streaming), send it before the `file` part. Available when OCR import is enabled for your account; otherwise `403`."
                  },
                  "ocr": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/LexSelectOcrV1"
                      },
                      {
                        "type": "object",
                        "description": "AWS Textract JSON as returned (`ocr_format=aws_textract`)."
                      },
                      {
                        "type": "string",
                        "format": "binary",
                        "description": "Either of the above, gzip-compressed."
                      }
                    ],
                    "description": "Your own OCR for the PDF, as JSON or gzip-compressed JSON (up to 128 MiB; gzip a large document's OCR). With `ocr_format=lexselect` it is a `LexSelectOcrV1` document; with `aws_textract`, Textract's JSON as returned. May come before or after the `file` part.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "File uploaded and processing queued.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid `multipart/form-data`, a required field is missing, a field or the parent ID is invalid, `size` does not match the file, the `ocr` part is invalid (`detail` says why) or sent without `ocr_format` (or the other way round), `ocr_format` is not supported or the file is not a PDF, or `X-API-Version` is unsupported.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "Not enough prepaid credit (`code: PAYG_INSUFFICIENT_FUNDS`). Add funds in Studio, then retry.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "The upload was refused: the API key can't use this endpoint (replace it with a new key from Studio), the upload carries `ocr` but OCR import isn't enabled for the account, the account's trial limit has been reached, Enterprise billing isn't accepting new documents, or prepaid processing is paused (`code: PAYG_PAYMENT_PAUSED`: update the payment method in Studio; `code: PAYG_WALLET_FROZEN`: contact support).",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Billing Paused",
                  "status": 403,
                  "detail": "Processing is paused. Review your billing settings.",
                  "code": "PAYG_PAYMENT_PAUSED"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Billing is temporarily unavailable for the account; `code` names the cause. No document was created, so upload again later. With `code: WORKSPACE_CONTEXT_CHANGED`, the account's workspace membership changed during the request: the document was created but not processed or charged, it's deleted automatically, and you can upload again straight away.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/ContentTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "502": {
            "description": "The file could not be stored, and no document was created. Retry the upload.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "UPLOAD_OPERATION_ID=\"$(uuidgen)\"   # keep it: reuse it only to retry this upload\ncurl --fail-with-body --silent --show-error -X POST \\\n  -H \"Authorization: Bearer $LEXSELECT_API_KEY\" \\\n  -H \"X-API-Version: 2026-11-05\" \\\n  -H \"Idempotency-Key: $UPLOAD_OPERATION_ID\" \\\n  -F \"name=upload-$UPLOAD_OPERATION_ID.pdf\" \\\n  -F \"strip_file_name=false\" \\\n  -F \"file=@./example.pdf\" \\\n  \"https://api.lexselect.io/api/documents/upload\""
          }
        ]
      }
    },
    "/documents/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Document ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getDocument",
        "summary": "Get file",
        "description": "Returns one document, folder or project.",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Document details.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Document"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID\""
          }
        ]
      },
      "delete": {
        "operationId": "deleteDocument",
        "summary": "Delete file",
        "description": "Permanently deletes a document or folder and its processing results. The response confirms that deletion has been accepted, and removal finishes shortly afterwards; until then, repeating the request succeeds again, and afterwards it returns `404`. Deleted documents cannot be restored. Usage and billing records are kept. See [Operations and limits](/api/operations.html).",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Document deleted.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DocumentDeleteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -X DELETE -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID\""
          }
        ]
      }
    },
    "/documents/{id}/parse": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Document ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getDocumentParse",
        "summary": "Get parsed document structure",
        "description": "Returns the latest processing run's output, page by page, as the slices you name in `include`. The default is `tree,key_values` (version `2026-11-05` and later; `tree,kvps` on earlier versions).\n\n**Slices:**\n\n- `tree`: the page's layout tree. Versions `2026-08-01` and later return the current node shape; versions `2026-06-07` and earlier return the legacy shape. Word-level `token` nodes are included only with `tokens=true`.\n- `key_values` (version `2026-11-05` and later): every key-value pair detected on the page, such as a form field and its value, as `{key, value, unvalued, score, key_box, value_box, node_id, source}`. Detection runs on every document with no setup, and covers pairs in tables and inside sentences, which the tree holds as `meta.key_values` on the table or paragraph rather than as `key_value` nodes.\n- `kvps`: fields from an extraction workflow set up for your account by LexSelect; otherwise `null` on every page. From version `2026-11-05`, requesting it adds `kvps_status` (`configured` or `not_configured`) to the response.\n- `text`: plain text in reading order, one line per `line` node, joined with `\\n`.\n- `tables`: every table as `{id, headers, rows}`, with one header entry per column.\n- `blocks`: the page's `header`, `footer`, `section`, `line`, `table` and `sub_document` nodes as a flat list of `{id, class, text}`.\n- `paragraphs`: logical paragraphs in reading order, with their section and heading. A paragraph that continues onto the next page is flagged on both pages.\n- `clauses`: one nested outline of the document's headings and numbered clauses, returned once at the top level rather than per page.\n\n**Filtering:**\n\n- `pages` selects the pages to return, by one-based page number.\n- `exclude_node_types` removes the subtrees of the listed classes before the slices are built. It applies to `tree`, `key_values`, `text`, `tables`, `blocks`, `paragraphs` and `clauses`; `key_values` ignores `token`, `kv_key` and `kv_value`, the parts each pair is built from. Use `tokens`, not `exclude_node_types`, to control word nodes.\n- `strip_geometry=true` removes node `coordinates`, the page `width` and `height`, and the `key_box` and `value_box` of key-value pairs.\n- `tokens=true` adds `token` nodes, with their coordinates and `meta.styling`, to `tree`.\n\nWhile the run is `in_progress`, and after it stops as `unknown`, the response contains only the requested pages that completed. Request large documents in batches with `pages=`, for example 100 pages at a time.\n\nFor an uploaded email, the upload itself has no pages: parse each child document listed by `GET /documents?parent_id={id}`.\n\nSee [Understand output](/api/understanding-output.html) for IDs, word coordinates, tables across pages, clean text, clauses and key-value fields.",
        "tags": [
          "Results"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "pages",
            "in": "query",
            "required": false,
            "description": "Pages to return, as one-based page numbers and inclusive ranges, such as `1-10`, `1,3,5` or `1,3-5,10`. Omit it to return every page.\n\nPage numbers beyond the document's last page are ignored, so `pages=1-1000` on a 6-page document returns its 6 pages; `total_pages` in the response gives the page count. While the run is `in_progress`, and after it stops as `unknown`, only the requested pages that completed are returned.\n\nA list covering more than 100,000 distinct pages returns `400`, as does invalid syntax such as a non-number, zero, a negative number or a reversed range.\n\nThis filter only changes what is returned; the whole document is processed and billed.",
            "schema": {
              "type": "string",
              "example": "1-10"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Comma-separated slices to return: `tree`, `key_values` (version `2026-11-05` and later), `kvps`, `text`, `tables`, `blocks`, `paragraphs` and `clauses`. Defaults to `tree,key_values` from version `2026-11-05`, and to `tree,kvps` on earlier versions. Unknown values return `400`.\n",
            "schema": {
              "type": "string",
              "example": "text,tables"
            }
          },
          {
            "name": "exclude_node_types",
            "in": "query",
            "required": false,
            "description": "Comma-separated node class names whose subtrees are removed before the slices are built, such as `header,footer`. It applies to `tree`, `key_values`, `text`, `tables`, `blocks`, `paragraphs` and `clauses`. Use `tokens`, not `exclude_node_types`, to control word nodes: `token` is ignored for `tree` from version `2026-08-01`, and it removes the words the other slices are built from, leaving their text empty. `key_values` ignores `token`, `kv_key` and `kv_value`, the parts each pair is built from; `key_value` removes the pairs held as nodes. See the node-class table in [Understand output](/api/understanding-output.html). Unknown names return `400`.\n",
            "schema": {
              "type": "string",
              "example": "header,footer"
            }
          },
          {
            "name": "strip_geometry",
            "in": "query",
            "required": false,
            "description": "`true` removes node `coordinates` from `tree`, the page `width` and `height`, and the `key_box` and `value_box` of key-value pairs in `key_values` and `meta.key_values`; the rest of `meta` and the other slices are unchanged. On versions `2026-06-07` and earlier it also removes `engine_meta` and `tokens`.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "tokens",
            "in": "query",
            "required": false,
            "description": "**Version `2026-08-01` and later.** `true` adds `token` nodes, one per word with its coordinates and `meta.styling`, to `tree`. Without it, each `line` node carries its text in `value.text`, and responses are about five times smaller. Versions `2026-06-07` and earlier always include word data.\n",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Parsed document structure.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParseResponse"
                },
                "examples": {
                  "singlePage": {
                    "summary": "Synthetic completed one-page parse, selected text/paragraphs",
                    "value": {
                      "document_id": "6650c0ffee0000000000a001",
                      "processing_id": "6650c0ffee0000000000a002",
                      "document_type": null,
                      "language": {
                        "code": "en"
                      },
                      "direction": "ltr",
                      "transcript": {
                        "line_numbered": false
                      },
                      "sub_documents": [],
                      "status": "completed",
                      "page_count": 1,
                      "total_pages": 1,
                      "pages": [
                        {
                          "page_index": 0,
                          "width": 612,
                          "height": 792,
                          "language": {
                            "code": "en",
                            "confidence": 0.98
                          },
                          "direction": "ltr",
                          "transcript": {
                            "line_numbered": false
                          },
                          "text": "Example paragraph.",
                          "paragraphs": [
                            {
                              "ids": [
                                "paragraph_0"
                              ],
                              "class": "paragraph",
                              "text": "Example paragraph.",
                              "heading": null
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID, `include`, `pages` or `exclude_node_types` value, or an unsupported `X-API-Version`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The document does not exist, belongs to another account or has been deleted; or, with `code: PROCESSING_NOT_FOUND`, it has no processing run yet. Retry `PROCESSING_NOT_FOUND` with backoff until your application's deadline. If it persists, contact support with the document ID and `X-Request-Id` before uploading another copy. Branch on `code`, not on the message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID/parse\""
          }
        ]
      }
    },
    "/documents/{id}/render": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Document ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getDocumentRender",
        "summary": "Render document to Markdown or HTML (experimental)",
        "description": "**Experimental.** Renders the document's layout tree as one Markdown or HTML string and returns it as the raw response body, not JSON. Use it to preview the output in a Markdown viewer or a browser; for stable structured output, use `/parse`, because the rendering rules can change.\n\n**Rendering rules:**\n\n- `title` and `heading` become `#` and `##` headings in Markdown, or `<h1>` and `<h2>` in HTML.\n- `paragraph` and `abstract` become one block each, and a run of `list_item` nodes becomes one list.\n- `section` and `sub_document` become `<section>`. Adjacent columns become a `lexselect-band` of `lexselect-column` elements, shown side by side only with `style=full` or your own CSS. Elements are always in reading order.\n- `transcript_line`, `question` and `answer` become a line-numbered transcript grid with tagged question and answer rows.\n- Right-to-left pages, from version `2026-09-03`: with `style=full`, `<html>` carries the document's main `lang` and `dir`, and each `<section class=\"lexselect-page\">` its own. In the default fragment, each right-to-left page is wrapped in `<div lang=\"…\" dir=\"rtl\">`.\n- `image` and `chart` become a `<figure>` placeholder, and a standalone `caption` becomes `<p class=\"lexselect-caption\">`.\n- `table` becomes a Markdown table, or `<table>` with `<thead>` and `<tbody>`, with one header cell per column.\n- `header` and `footer` are included; use `exclude_node_types=header,footer` to leave them out.\n- A standalone `line` becomes one line of text in Markdown or a `<p>` in HTML.\n\nPages are separated by `\\n\\n---\\n\\n` in Markdown and by `<hr class=\"lexselect-page-break\"/>` in the HTML fragment. With `style=full`, each page becomes a `<section class=\"lexselect-page\">` in a styled standalone document. Use `pages=` to keep responses for large documents small.\n\n**Security:** the HTML output escapes all document text. The Markdown output passes document text through unchanged, and Markdown allows inline HTML, so an HTML `script` element in a source document reaches your Markdown renderer. Sanitize the HTML you produce from it, for example with DOMPurify or `rehype-sanitize`; Markdown renderers such as `marked` do not sanitize their output.\n",
        "tags": [
          "Results"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "description": "`markdown` or `html`.",
            "schema": {
              "type": "string",
              "enum": [
                "markdown",
                "html"
              ],
              "example": "markdown"
            }
          },
          {
            "name": "pages",
            "in": "query",
            "required": false,
            "description": "Pages to render, with the same syntax and limits as on `/parse`, such as `1-10` or `1,3,5`. Omit it to render every page.\n\nThis filter only changes what is returned; the whole document is processed and billed.",
            "schema": {
              "type": "string",
              "example": "1-5"
            }
          },
          {
            "name": "exclude_node_types",
            "in": "query",
            "required": false,
            "description": "Comma-separated node class names whose subtrees are left out, such as `header,footer`. Unknown names return `400`.\n",
            "schema": {
              "type": "string",
              "example": "header,footer"
            }
          },
          {
            "name": "style",
            "in": "query",
            "required": false,
            "description": "**HTML only.** `full`, or its alias `document`, returns a self-contained styled HTML document with framed pages, side-by-side columns and a line-numbered transcript grid, ready to open in a browser. Without it, the response is an HTML fragment with pages separated by `<hr class=\"lexselect-page-break\"/>`. Ignored for Markdown.\n",
            "schema": {
              "type": "string",
              "enum": [
                "full",
                "document"
              ],
              "example": "full"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rendered document. Content-Type is `text/markdown; charset=utf-8` or `text/html; charset=utf-8` depending on format.\n",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "`inline`, with a file name for the rendered document."
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "example": "# Section heading\n\nBody text line one.\nBody text line two.\n\n| Name | Age |\n| --- | --- |\n| Alice | 30 |\n"
                }
              },
              "text/html": {
                "schema": {
                  "type": "string",
                  "example": "<section><p>Hello world</p></section>\n<table><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody><tr><td>Alice</td><td>30</td></tr></tbody></table>\n"
                }
              }
            }
          },
          "400": {
            "description": "Missing or unknown `format` or `style`, an invalid `pages` or `exclude_node_types` value, or an unsupported `X-API-Version`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The document does not exist, belongs to another account or has been deleted; or, with `code: PROCESSING_NOT_FOUND`, it has no processing run yet. Retry `PROCESSING_NOT_FOUND` with backoff until your application's deadline. If it persists, contact support with the document ID and `X-Request-Id` before uploading another copy. Branch on `code`, not on the message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID/render?format=markdown\""
          }
        ]
      }
    },
    "/documents/{id}/processing/latest": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Document ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getProcessingLatest",
        "summary": "Get processing status",
        "description": "Returns the status and progress of the document's latest processing run. On version `2026-06-07` and later, progress is `pages_done` of `pages_total`, with `total_known` and `stage`; version `2026-03-06` returns the deprecated `processing_progress` and `page_count` instead.\n\nRight after an upload this can return `404` until the run starts, so retry with backoff. For an uploaded email, each child document reports its own pages under the same `processing_id`.\n",
        "tags": [
          "Processing"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "include_provenance",
            "in": "query",
            "description": "`true` adds `processing_provenance`: the processing and workflow versions used for the run.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Latest processing status.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessingLatestResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The document does not exist, belongs to another account or has been deleted; or, with `code: PROCESSING_NOT_FOUND`, it has no processing run yet. Retry `PROCESSING_NOT_FOUND` with backoff until your application's deadline. If it persists, contact support with the document ID and `X-Request-Id` before uploading another copy. Branch on `code`, not on the message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID/processing/latest\""
          }
        ]
      }
    },
    "/documents/{id}/processing/latest/pages": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Document ID.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "operationId": "getProcessingPages",
        "summary": "List pages",
        "description": "Returns page metadata, such as index, dimensions and type, for the latest processing run, without extracted content. While the run is `in_progress`, and after it stops as `unknown`, only completed pages are listed.\n",
        "tags": [
          "Results"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Pages per response, from 1 to 100 (default 50). Other values return `400`.",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` from the previous response, a non-negative integer; omit it or leave it empty to start from the first page. Other values return `400`.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page metadata list.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PageListResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "The document does not exist, belongs to another account or has been deleted; or, with `code: PROCESSING_NOT_FOUND`, it has no processing run yet. Retry `PROCESSING_NOT_FOUND` with backoff until your application's deadline. If it persists, contact support with the document ID and `X-Request-Id` before uploading another copy. Branch on `code`, not on the message.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/documents/$DOCUMENT_ID/processing/latest/pages\""
          }
        ]
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Get current usage and spend",
        "description": "Returns usage for the account behind this API key: pages processed (in the current billing period for Enterprise, over the lifetime for PAYG), the remaining allowance, the number of documents stored and, for the billing owner, spend so far with the price per page. Use it for monitoring and budget alerts.\n\nUsage counts processing of documents uploaded through the API or Studio. `spend` is `null` for PAYG accounts, whose credit and price are in `wallet` and `page_rate_micros`, for custom invoiced terms, when no price applies to your plan, and for workspace members who are not the billing owner; `spend_note` says which. See [Operations and limits](/api/operations.html) for how allowances and credit work.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage and spend.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSummary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "405": {
            "description": "Only `GET` is supported.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/usage\""
          }
        ]
      }
    },
    "/usage/daily": {
      "get": {
        "operationId": "getUsageDaily",
        "summary": "Get daily usage",
        "description": "Returns the pages processed and processing runs completed on each UTC day, for forecasting and for alerts when a batch uses more pages than expected. Every day in the range is present, including days with no activity.\n\nThe range is inclusive and defaults to the 30 days ending today (UTC). A range longer than 366 days, a malformed date or a `start` after `end` returns `400`.\n\nAn Enterprise workspace's billing owner sees the whole workspace; other members see their own runs, and a personal account sees its own. Usage counts processing of documents uploaded through the API or Studio, and remains after the documents are deleted; document content is not kept in usage history.\n\nRuns are counted on the UTC day they completed, so totals over a range can differ from `GET /usage`, which counts over the billing period for Enterprise and the lifetime for PAYG.\n",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XApiVersion"
          },
          {
            "name": "start",
            "in": "query",
            "required": false,
            "description": "First day of the range, `YYYY-MM-DD` (inclusive). Defaults to 29 days before `end`.",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-08-24"
            }
          },
          {
            "name": "end",
            "in": "query",
            "required": false,
            "description": "Last day of the range, `YYYY-MM-DD` (inclusive). Defaults to today (UTC).",
            "schema": {
              "type": "string",
              "format": "date",
              "example": "2026-09-22"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Zero-filled per-day usage series.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageDaily"
                }
              }
            }
          },
          "400": {
            "description": "A malformed date, a `start` after `end`, a range longer than 366 days, or an unsupported `X-API-Version`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "405": {
            "description": "Only `GET` is supported.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "x-codeSamples": [
          {
            "lang": "Shell",
            "label": "curl",
            "source": "curl --fail-with-body --silent --show-error -H \"Authorization: Bearer $LEXSELECT_API_KEY\" -H \"X-API-Version: 2026-11-05\" \"https://api.lexselect.io/api/usage/daily\""
          }
        ]
      }
    }
  },
  "webhooks": {
    "document.processing.queued": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveDocumentProcessingQueued",
        "summary": "Document processing queued",
        "description": "An outbound HMAC-signed POST to your configured receiver. This is not a LexSelect REST endpoint. Emitted when an API upload is accepted into the processing queue.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookUserAgent"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookQueuedEvent"
              },
              "example": {
                "id": "72c46026-d682-4325-ae72-e2c09db7d525",
                "type": "document.processing.queued",
                "created_at": "2026-10-05T17:42:00.000000+00:00",
                "data": {
                  "document_id": "6650c0ffee0000000000a001",
                  "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
                  "status": "queued"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Your receiver accepted the event durably (for example 200 or 202)."
          },
          "default": {
            "description": "Not acknowledged. Eligible real-event failures follow the documented automatic retry schedule."
          }
        }
      }
    },
    "document.processing.completed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveDocumentProcessingCompleted",
        "summary": "Document processing completed",
        "description": "An outbound HMAC-signed POST to your configured receiver. This is not a LexSelect REST endpoint. Can also be emitted for documents processed through Studio by the endpoint-owning account.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookUserAgent"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCompletedEvent"
              },
              "example": {
                "id": "72c46026-d682-4325-ae72-e2c09db7d525",
                "type": "document.processing.completed",
                "created_at": "2026-10-05T17:42:00.000000+00:00",
                "data": {
                  "document_id": "6650c0ffee0000000000a001",
                  "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
                  "processing_id": "6650c0ffee0000000000a002",
                  "status": "completed"
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Your receiver accepted the event durably (for example 200 or 202)."
          },
          "default": {
            "description": "Not acknowledged. Eligible real-event failures follow the documented automatic retry schedule."
          }
        }
      }
    },
    "document.processing.failed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveDocumentProcessingFailed",
        "summary": "Document processing failed",
        "description": "An outbound HMAC-signed POST to your configured receiver. This is not a LexSelect REST endpoint. Can also be emitted for documents processed through Studio by the endpoint-owning account.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookUserAgent"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookFailedEvent"
              },
              "example": {
                "id": "72c46026-d682-4325-ae72-e2c09db7d525",
                "type": "document.processing.failed",
                "created_at": "2026-10-05T17:42:00.000000+00:00",
                "data": {
                  "document_id": "6650c0ffee0000000000a001",
                  "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
                  "processing_id": "6650c0ffee0000000000a002",
                  "status": "failed",
                  "failed_task": "processing",
                  "error_code": "PROCESSING_FAILED",
                  "error_message": "Document processing failed. Contact support with the document ID."
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Your receiver accepted the event durably (for example 200 or 202)."
          },
          "default": {
            "description": "Not acknowledged. Eligible real-event failures follow the documented automatic retry schedule."
          }
        }
      }
    },
    "signedTest": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveSignedWebhookTest",
        "summary": "Send Test (signed synthetic event)",
        "description": "One signed test sent from Studio to a saved endpoint, including unverified, paused or disabled endpoints. It neither activates the endpoint nor enters the automatic retry sequence. Started/page-completed examples here do not mean those real events are emitted.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookId"
          },
          {
            "$ref": "#/components/parameters/WebhookTimestamp"
          },
          {
            "$ref": "#/components/parameters/WebhookSignature"
          },
          {
            "$ref": "#/components/parameters/WebhookUserAgent"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSignedTestEvent"
              },
              "examples": {
                "queued": {
                  "summary": "Queued test",
                  "value": {
                    "id": "bbf397ac-3173-40ce-9f04-12d3b29cfecc",
                    "type": "document.processing.queued",
                    "created_at": "2026-10-05T17:42:00.000000+00:00",
                    "data": {
                      "message": "This is a test webhook event",
                      "document_id": "000000000000000000000000",
                      "document_name": "test-document.pdf",
                      "status": "queued"
                    }
                  }
                },
                "started": {
                  "summary": "Started test",
                  "value": {
                    "id": "bbf397ac-3173-40ce-9f04-12d3b29cfecc",
                    "type": "document.processing.started",
                    "created_at": "2026-10-05T17:42:00.000000+00:00",
                    "data": {
                      "message": "This is a test webhook event",
                      "document_id": "000000000000000000000000",
                      "document_name": "test-document.pdf",
                      "status": "started"
                    }
                  }
                },
                "page_completed": {
                  "summary": "Page Completed test",
                  "value": {
                    "id": "bbf397ac-3173-40ce-9f04-12d3b29cfecc",
                    "type": "document.processing.page_completed",
                    "created_at": "2026-10-05T17:42:00.000000+00:00",
                    "data": {
                      "message": "This is a test webhook event",
                      "document_id": "000000000000000000000000",
                      "document_name": "test-document.pdf",
                      "status": "processing",
                      "page_number": 1,
                      "total_pages": 5
                    }
                  }
                },
                "completed": {
                  "summary": "Completed test",
                  "value": {
                    "id": "bbf397ac-3173-40ce-9f04-12d3b29cfecc",
                    "type": "document.processing.completed",
                    "created_at": "2026-10-05T17:42:00.000000+00:00",
                    "data": {
                      "message": "This is a test webhook event",
                      "document_id": "000000000000000000000000",
                      "document_name": "test-document.pdf",
                      "processing_id": "000000000000000000000000",
                      "status": "completed"
                    }
                  }
                },
                "failed": {
                  "summary": "Failed test",
                  "value": {
                    "id": "bbf397ac-3173-40ce-9f04-12d3b29cfecc",
                    "type": "document.processing.failed",
                    "created_at": "2026-10-05T17:42:00.000000+00:00",
                    "data": {
                      "message": "This is a test webhook event",
                      "document_id": "000000000000000000000000",
                      "document_name": "test-document.pdf",
                      "processing_id": "000000000000000000000000",
                      "status": "failed",
                      "failed_task": "processing",
                      "error_code": "PROCESSING_FAILED",
                      "error_message": "Document processing failed. Contact support with the document ID."
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Your test receiver accepted the signed test."
          },
          "default": {
            "description": "Studio displays the test result; no automatic retry is scheduled."
          }
        }
      }
    },
    "connectivityProbe": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "operationId": "receiveWebhookConnectivityProbe",
        "summary": "Test URL (unsigned connectivity probe)",
        "description": "An unsigned outbound POST before saving a webhook. It is not a domain-verification challenge and must not be processed as an authenticated event. A signature-only receiver can legitimately return 401; use Send Test to verify signatures.",
        "security": [],
        "parameters": [
          {
            "name": "X-LexSelect-Test",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "const": "true"
            }
          },
          {
            "name": "User-Agent",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "LexSelect-Webhook-Probe/1.0"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookConnectivityProbe"
              },
              "example": {
                "type": "lexselect.webhook.probe",
                "data": {
                  "message": "Unsigned connectivity test; no webhook has been saved."
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "The URL accepted the unsigned probe."
          },
          "default": {
            "description": "Studio reports the connectivity result; no webhook was saved or verified."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API-Key",
        "description": "Bearer API key issued by LexSelect. Keys are prefixed `lxs_`. Pass as `Authorization: Bearer lxs_your_key`. Requires `ProcessingAPI` scope for document and processing endpoints.\n"
      }
    },
    "schemas": {
      "WebhookQueuedEvent": {
        "type": "object",
        "description": "Real queued processing notification. Additional fields may be added.",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable event ID; matches X-Webhook-Id and survives automatic/manual retries.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "const": "document.processing.queued"
          },
          "created_at": {
            "type": "string",
            "description": "Original event envelope time, unchanged on retry. Verify freshness using the signature header timestamp.",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "required": [
              "document_id",
              "document_name",
              "status"
            ],
            "properties": {
              "document_id": {
                "type": "string",
                "description": "Document ID to use with the account-owned document API.",
                "pattern": "^[0-9a-f]{24}$"
              },
              "document_name": {
                "type": "string",
                "description": "Stored filename; may be generated by strip_file_name=true."
              },
              "status": {
                "type": "string",
                "const": "queued"
              }
            }
          }
        },
        "example": {
          "id": "72c46026-d682-4325-ae72-e2c09db7d525",
          "type": "document.processing.queued",
          "created_at": "2026-10-05T17:42:00.000000+00:00",
          "data": {
            "document_id": "6650c0ffee0000000000a001",
            "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
            "status": "queued"
          }
        }
      },
      "WebhookCompletedEvent": {
        "type": "object",
        "description": "Real completed processing notification. Additional fields may be added.",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable event ID; matches X-Webhook-Id and survives automatic/manual retries.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "const": "document.processing.completed"
          },
          "created_at": {
            "type": "string",
            "description": "Original event envelope time, unchanged on retry. Verify freshness using the signature header timestamp.",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "required": [
              "document_id",
              "document_name",
              "processing_id",
              "status"
            ],
            "properties": {
              "document_id": {
                "type": "string",
                "description": "Document ID to use with the account-owned document API.",
                "pattern": "^[0-9a-f]{24}$"
              },
              "document_name": {
                "type": "string",
                "description": "Stored filename; may be generated by strip_file_name=true."
              },
              "processing_id": {
                "type": "string",
                "description": "Processing run identifier; use it to distinguish reprocessing runs."
              },
              "status": {
                "type": "string",
                "const": "completed"
              }
            }
          }
        },
        "example": {
          "id": "72c46026-d682-4325-ae72-e2c09db7d525",
          "type": "document.processing.completed",
          "created_at": "2026-10-05T17:42:00.000000+00:00",
          "data": {
            "document_id": "6650c0ffee0000000000a001",
            "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
            "processing_id": "6650c0ffee0000000000a002",
            "status": "completed"
          }
        }
      },
      "WebhookFailedEvent": {
        "type": "object",
        "description": "Real failed processing notification. Additional fields may be added.",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable event ID; matches X-Webhook-Id and survives automatic/manual retries.",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "const": "document.processing.failed"
          },
          "created_at": {
            "type": "string",
            "description": "Original event envelope time, unchanged on retry. Verify freshness using the signature header timestamp.",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "required": [
              "document_id",
              "document_name",
              "processing_id",
              "status",
              "failed_task",
              "error_code",
              "error_message"
            ],
            "properties": {
              "document_id": {
                "type": "string",
                "description": "Document ID to use with the account-owned document API.",
                "pattern": "^[0-9a-f]{24}$"
              },
              "document_name": {
                "type": "string",
                "description": "Stored filename; may be generated by strip_file_name=true."
              },
              "processing_id": {
                "type": "string",
                "description": "Processing run identifier; use it to distinguish reprocessing runs."
              },
              "status": {
                "type": "string",
                "const": "failed"
              },
              "failed_task": {
                "type": "string",
                "description": "Public stage placeholder (processing); internal task names are not forwarded."
              },
              "error_code": {
                "type": "string",
                "description": "Safe cause matching the public processing-status error code."
              },
              "error_message": {
                "type": "string",
                "description": "Failure description; do not depend on exact wording."
              }
            }
          }
        },
        "example": {
          "id": "72c46026-d682-4325-ae72-e2c09db7d525",
          "type": "document.processing.failed",
          "created_at": "2026-10-05T17:42:00.000000+00:00",
          "data": {
            "document_id": "6650c0ffee0000000000a001",
            "document_name": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf",
            "processing_id": "6650c0ffee0000000000a002",
            "status": "failed",
            "failed_task": "processing",
            "error_code": "PROCESSING_FAILED",
            "error_message": "Document processing failed. Contact support with the document ID."
          }
        }
      },
      "WebhookSignedTestEvent": {
        "description": "Synthetic Send Test payload. It intentionally differs from real processing data and has no real processing_id.",
        "oneOf": [
          {
            "type": "object",
            "title": "Queued test",
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Synthetic event ID matching X-Webhook-Id.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "const": "document.processing.queued"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "data": {
                "type": "object",
                "required": [
                  "document_id",
                  "document_name",
                  "message",
                  "status"
                ],
                "properties": {
                  "document_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "document_name": {
                    "type": "string",
                    "const": "test-document.pdf"
                  },
                  "message": {
                    "type": "string",
                    "const": "This is a test webhook event"
                  },
                  "status": {
                    "type": "string",
                    "const": "queued"
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Started test",
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Synthetic event ID matching X-Webhook-Id.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "const": "document.processing.started"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "data": {
                "type": "object",
                "required": [
                  "document_id",
                  "document_name",
                  "message",
                  "status"
                ],
                "properties": {
                  "document_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "document_name": {
                    "type": "string",
                    "const": "test-document.pdf"
                  },
                  "message": {
                    "type": "string",
                    "const": "This is a test webhook event"
                  },
                  "status": {
                    "type": "string",
                    "const": "started"
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Page-completed test",
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Synthetic event ID matching X-Webhook-Id.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "const": "document.processing.page_completed"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "data": {
                "type": "object",
                "required": [
                  "document_id",
                  "document_name",
                  "message",
                  "status",
                  "page_number",
                  "total_pages"
                ],
                "properties": {
                  "document_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "document_name": {
                    "type": "string",
                    "const": "test-document.pdf"
                  },
                  "message": {
                    "type": "string",
                    "const": "This is a test webhook event"
                  },
                  "status": {
                    "type": "string",
                    "const": "processing"
                  },
                  "page_number": {
                    "type": "integer",
                    "const": 1
                  },
                  "total_pages": {
                    "type": "integer",
                    "const": 5
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Completed test",
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Synthetic event ID matching X-Webhook-Id.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "const": "document.processing.completed"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "data": {
                "type": "object",
                "required": [
                  "document_id",
                  "document_name",
                  "processing_id",
                  "message",
                  "status"
                ],
                "properties": {
                  "document_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "document_name": {
                    "type": "string",
                    "const": "test-document.pdf"
                  },
                  "processing_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "message": {
                    "type": "string",
                    "const": "This is a test webhook event"
                  },
                  "status": {
                    "type": "string",
                    "const": "completed"
                  }
                }
              }
            }
          },
          {
            "type": "object",
            "title": "Failed test",
            "required": [
              "id",
              "type",
              "created_at",
              "data"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Synthetic event ID matching X-Webhook-Id.",
                "format": "uuid"
              },
              "type": {
                "type": "string",
                "const": "document.processing.failed"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "data": {
                "type": "object",
                "required": [
                  "document_id",
                  "document_name",
                  "processing_id",
                  "message",
                  "status",
                  "failed_task",
                  "error_code",
                  "error_message"
                ],
                "properties": {
                  "document_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "document_name": {
                    "type": "string",
                    "const": "test-document.pdf"
                  },
                  "processing_id": {
                    "type": "string",
                    "const": "000000000000000000000000"
                  },
                  "message": {
                    "type": "string",
                    "const": "This is a test webhook event"
                  },
                  "status": {
                    "type": "string",
                    "const": "failed"
                  },
                  "failed_task": {
                    "type": "string",
                    "const": "processing"
                  },
                  "error_code": {
                    "type": "string",
                    "const": "PROCESSING_FAILED"
                  },
                  "error_message": {
                    "type": "string",
                    "const": "Document processing failed. Contact support with the document ID."
                  }
                }
              }
            }
          }
        ]
      },
      "WebhookConnectivityProbe": {
        "type": "object",
        "description": "Unsigned pre-save Test URL probe; no signature, timestamp, event ID or real document.",
        "required": [
          "type",
          "data"
        ],
        "properties": {
          "type": {
            "type": "string",
            "const": "lexselect.webhook.probe"
          },
          "data": {
            "type": "object",
            "required": [
              "message"
            ],
            "properties": {
              "message": {
                "type": "string",
                "const": "Unsigned connectivity test; no webhook has been saved."
              }
            }
          }
        }
      },
      "ProblemDetails": {
        "type": "object",
        "description": "RFC 9457 Problem Details error response.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "URI reference identifying the problem type.",
            "example": "about:blank"
          },
          "title": {
            "type": "string",
            "description": "Short, human-readable summary of the problem type.",
            "example": "Unauthorized"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code.",
            "example": 401
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation specific to this occurrence.",
            "example": "Missing or invalid Authorization header."
          },
          "code": {
            "type": "string",
            "description": "Optional machine-readable cause, including PAYG_INSUFFICIENT_FUNDS for billing rejection."
          }
        }
      },
      "DocumentOcr": {
        "type": "object",
        "description": "Present only on documents uploaded with your own OCR (`ocr` part). The page counts appear once processing has written its pages.",
        "properties": {
          "source": {
            "type": "string",
            "enum": [
              "client"
            ]
          },
          "format": {
            "type": "string",
            "description": "The `ocr_format` sent.",
            "example": "aws_textract"
          },
          "engine": {
            "type": "string",
            "description": "The OCR engine named by the file (`source.engine` in `LexSelectOcrV1`; `aws_textract` for Textract output).",
            "example": "aws_textract"
          },
          "pages": {
            "type": "integer",
            "description": "Pages in your OCR file.",
            "example": 12
          },
          "pages_with_words": {
            "type": "integer",
            "description": "Pages in your OCR file that hold at least one word.",
            "example": 12
          },
          "pages_read_from_client": {
            "type": "integer",
            "description": "Pages of the latest processing read from your OCR.",
            "example": 11
          },
          "pages_read_by_us": {
            "type": "integer",
            "description": "Pages of the latest processing that your OCR did not cover (missing, empty, or text running down the page) and that our OCR read.",
            "example": 1
          }
        }
      },
      "LexSelectOcrV1": {
        "type": "object",
        "description": "Vendor-neutral page-word OCR, for `ocr_format=lexselect`. Convert any OCR engine's output to it. Boxes are `[x0, y0, x1, y1]` (or a `polygon` of `[x, y]` points) in the page's `unit`, measured from the `origin`; we normalise them onto the upright page. Every word is kept, whatever its confidence. Unknown fields are rejected.",
        "required": [
          "schema",
          "source",
          "pages"
        ],
        "additionalProperties": false,
        "properties": {
          "schema": {
            "type": "string",
            "enum": [
              "lexselect.ocr.v1"
            ]
          },
          "source": {
            "type": "object",
            "required": [
              "engine"
            ],
            "additionalProperties": false,
            "properties": {
              "engine": {
                "type": "string",
                "maxLength": 64,
                "example": "tesseract"
              },
              "version": {
                "type": "string",
                "maxLength": 64
              }
            }
          },
          "pages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5000,
            "items": {
              "type": "object",
              "required": [
                "page"
              ],
              "additionalProperties": false,
              "properties": {
                "page": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "1-based page number in the uploaded PDF. Each page appears once."
                },
                "unit": {
                  "type": "string",
                  "enum": [
                    "normalized",
                    "pixel",
                    "point",
                    "inch"
                  ],
                  "default": "normalized",
                  "description": "`normalized` = fractions of the page (0-1). Other units need `width` and `height`."
                },
                "width": {
                  "type": "number",
                  "exclusiveMinimum": 0,
                  "description": "Width of the measured page or image, in `unit`."
                },
                "height": {
                  "type": "number",
                  "exclusiveMinimum": 0,
                  "description": "Height of the measured page or image, in `unit`."
                },
                "origin": {
                  "type": "string",
                  "enum": [
                    "top-left",
                    "bottom-left"
                  ],
                  "default": "top-left"
                },
                "rotation": {
                  "type": "integer",
                  "enum": [
                    0,
                    90,
                    180,
                    270
                  ],
                  "default": 0,
                  "description": "Degrees clockwise the measured image was turned from the upright page."
                },
                "languages": {
                  "type": "array",
                  "maxItems": 20,
                  "items": {
                    "type": "object",
                    "required": [
                      "code"
                    ],
                    "properties": {
                      "code": {
                        "type": "string",
                        "description": "BCP-47 language code.",
                        "example": "en"
                      },
                      "confidence": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 1
                      }
                    }
                  }
                },
                "words": {
                  "type": "array",
                  "maxItems": 20000,
                  "items": {
                    "$ref": "#/components/schemas/LexSelectOcrText"
                  }
                },
                "lines": {
                  "type": "array",
                  "maxItems": 5000,
                  "description": "Used only when `words` is empty; each line is split into words.",
                  "items": {
                    "$ref": "#/components/schemas/LexSelectOcrText"
                  }
                }
              }
            }
          }
        },
        "example": {
          "schema": "lexselect.ocr.v1",
          "source": {
            "engine": "tesseract"
          },
          "pages": [
            {
              "page": 1,
              "unit": "point",
              "width": 612,
              "height": 792,
              "words": [
                {
                  "text": "AGREEMENT",
                  "bbox": [
                    72,
                    90,
                    160,
                    102
                  ],
                  "confidence": 0.98
                }
              ]
            }
          ]
        }
      },
      "LexSelectOcrText": {
        "type": "object",
        "description": "A word or line with exactly one of `bbox` or `polygon`.",
        "required": [
          "text"
        ],
        "additionalProperties": false,
        "properties": {
          "text": {
            "type": "string",
            "maxLength": 2000
          },
          "bbox": {
            "type": "array",
            "minItems": 4,
            "maxItems": 4,
            "items": {
              "type": "number"
            },
            "description": "[x0, y0, x1, y1] with x1 >= x0 and y1 >= y0."
          },
          "polygon": {
            "type": "array",
            "minItems": 3,
            "items": {
              "type": "array",
              "minItems": 2,
              "maxItems": 2,
              "items": {
                "type": "number"
              }
            }
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "text_type": {
            "type": "string",
            "enum": [
              "printed",
              "handwriting"
            ]
          }
        }
      },
      "Document": {
        "type": "object",
        "description": "A document, folder or project.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Document ID (24 hexadecimal characters).",
            "example": "665f1a2b3c4d5e6f7a8b9c0d"
          },
          "name": {
            "type": "string",
            "description": "Stored name. For uploads it is generated unless the upload sent `strip_file_name=false`.",
            "example": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf"
          },
          "type": {
            "type": "string",
            "description": "`file` for documents (including email attachments and bodies), `folder` or `project`.",
            "enum": [
              "file",
              "folder",
              "project"
            ],
            "example": "file"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the containing project, folder or, for an email body or attachment, the uploaded email. `null` for projects.",
            "example": "665f1a2b3c4d5e6f7a8b9c0d"
          },
          "file_extension": {
            "type": [
              "string",
              "null"
            ],
            "description": "Lowercase file extension, or `null` for folders and projects.",
            "example": "pdf"
          },
          "size": {
            "type": [
              "integer",
              "null"
            ],
            "description": "File size in bytes, or `null` for folders and projects.",
            "example": 204800
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "modified_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted": {
            "type": "boolean",
            "description": "`true` for an item that has been deleted and is still being removed.",
            "example": false
          },
          "ocr": {
            "$ref": "#/components/schemas/DocumentOcr"
          },
          "processing_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status on file listings when enriched: CREATED, PENDING, QUEUED, IN_PROGRESS, SUCCESS, FAILED, CANCELED, TIMEOUT or another internal status string. This uppercase vocabulary differs from /processing/latest; absent/unavailable enrichment is null.",
            "example": "SUCCESS"
          },
          "processing_progress": {
            "type": [
              "number",
              "null"
            ],
            "description": "Processing progress (0.0 to 1.0). **Pinned version `2026-03-06` only** — omitted on `2026-06-07` and later, where progress is served by `GET /documents/{id}/processing/latest` (the document object is metadata, not a progress surface). Present on files when enrichment is available.\n",
            "example": 1
          },
          "is_preview_ready": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether a preview is available. Present on files in list responses when enrichment is available."
          },
          "document_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Detected document type (e.g., \"Transcript\"). Present on files in list responses when enrichment is available.",
            "example": "Transcript"
          }
        }
      },
      "DocumentListResponse": {
        "type": "object",
        "required": [
          "items",
          "total"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Document"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cursor for the next page. Null if no more results."
          },
          "total": {
            "type": "integer",
            "description": "Number of items in this page.",
            "example": 25
          }
        }
      },
      "ProgressDetail": {
        "type": "object",
        "description": "**Version `2026-06-07` and later.** Progress for one scope: `done` of `total` units are complete. While `total_known` is `false`, `total` can still grow as more pages are found, so show counts rather than a percentage.\n",
        "required": [
          "scope",
          "kind",
          "done",
          "total",
          "total_known"
        ],
        "properties": {
          "scope": {
            "type": "string",
            "description": "Scope identifier (currently `page`).",
            "example": "page"
          },
          "kind": {
            "type": "string",
            "description": "Unit of work counted (currently `page`).",
            "example": "page"
          },
          "done": {
            "type": "integer",
            "description": "Completed units in this scope.",
            "example": 7
          },
          "total": {
            "type": "integer",
            "description": "Discovered units in this scope so far.",
            "example": 12
          },
          "total_known": {
            "type": "boolean",
            "description": "`true` once `total` is final or the run has finished. Follows the same rule as the response's `total_known`.",
            "example": true
          }
        }
      },
      "ProcessingLatestResponse": {
        "type": "object",
        "description": "The response projection is selected by X-API-Version. Latest/default is 2026-11-05; versions from 2026-06-07 use progress facts, and 2026-03-06 uses legacy progress fields. See the versioning guide.",
        "properties": {
          "document_id": {
            "type": "string",
            "example": "665f1a2b3c4d5e6f7a8b9c0d"
          },
          "document_name": {
            "type": "string",
            "example": "8f0c2d4e-5b7a-4e1f-9c3d-2a6b8e0f1c47.pdf"
          },
          "processing_id": {
            "type": [
              "string",
              "null"
            ],
            "example": "665f1a2b3c4d5e6f7a8b9c0e"
          },
          "status": {
            "type": "string",
            "enum": [
              "in_progress",
              "completed",
              "failed",
              "unknown"
            ],
            "description": "`in_progress` while the run is queued, waiting or processing; `completed` when it finished successfully; `failed` when it failed (see `error_code`); `unknown` when the run was canceled. No webhook event is sent for `unknown`, and the run does not continue; upload the file again, as a new operation, if you still need results.",
            "example": "completed"
          },
          "language": {
            "description": "**Version `2026-09-03` and later.** The language of most of the document's pages, or `null` when none was detected or the run has not completed.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LanguageTag"
              },
              {
                "type": "null"
              }
            ]
          },
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "rtl",
              "ltr",
              null
            ],
            "description": "**Version `2026-09-03` and later.** Reading direction of the document's majority-language pages, or `null` when not detected.\n",
            "example": "rtl"
          },
          "progress_details": {
            "type": "array",
            "description": "Per-scope progress facts. **Version `2026-06-07` and later only.**\n",
            "items": {
              "$ref": "#/components/schemas/ProgressDetail"
            }
          },
          "pages_done": {
            "type": "integer",
            "description": "Successfully processed pages. **Version `2026-06-07` and later only.**\n",
            "example": 7
          },
          "pages_total": {
            "type": "integer",
            "description": "Pages discovered so far (in progress + done). **Version `2026-06-07` and later only.**\n",
            "example": 12
          },
          "total_known": {
            "type": "boolean",
            "description": "**Version `2026-06-07` and later.** `true` once `pages_total` is final or the run has finished. Before any pages are found it reads `true` at `0` of `0`, because nothing is outstanding yet, so compute a percentage only when `pages_total` is greater than `0`; while `false`, show counts.",
            "example": true
          },
          "stage": {
            "type": "string",
            "description": "Coarse stage label, including preparing, page-processing, post-processing and done. It is not a terminal-status substitute. Present from 2026-06-07.",
            "example": "done"
          },
          "processing_progress": {
            "type": "number",
            "format": "float",
            "description": "Legacy progress fraction (0.0 to 1.0). **Pinned version `2026-03-06` only.**\n",
            "example": 1
          },
          "engine_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Version of the processing software that produced this run, or `null` when unknown.",
            "example": 3
          },
          "processing_provenance": {
            "type": "object",
            "description": "Present only when include_provenance=true is requested. Version facts from this owned processing record, not the serving API or test runner. Missing facts are null; internal source revision identifiers are not exposed.\n",
            "properties": {
              "engine_version": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "engine_commit": {
                "type": [
                  "string",
                  "null"
                ],
                "deprecated": true,
                "description": "Always null. Retained for compatibility; internal source revisions are not exposed."
              },
              "workflow": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "The resolved workflow version pinned by this processing.",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Name of the processing workflow configured for your account."
                  },
                  "version": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                }
              }
            }
          },
          "page_count": {
            "type": "integer",
            "description": "**Version `2026-03-06` only.** Number of pages in the run; later versions use `pages_total`. Not related to `page_count` on `/parse`.",
            "example": 12
          },
          "preview_available": {
            "type": "boolean",
            "description": "Reserved for Studio; ignore it.",
            "example": true
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error_message": {
            "type": "string",
            "description": "Present only for failed processings. Customer page-cap exhaustion includes its safe member/key limit reason and billing-period or lifetime window. Private structured errors and stack traces are not exposed by this projection.\n"
          },
          "error_code": {
            "type": "string",
            "description": "Safe failed-processing cause such as DOCUMENT_PAGE_LIMIT_EXCEEDED, CUSTOMER_PAGE_LIMIT_REACHED, a PAYG admission code, or PROCESSING_FAILED."
          }
        }
      },
      "LanguageTag": {
        "type": "object",
        "description": "**Version `2026-09-03` and later.** A detected language. On a page it is the page's main language, with a confidence score when available. On a document or sub-document it is the most common page language, without a confidence score.\n",
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Language code, for example `en`, `ar` or `zh`. Hebrew is reported as `iw`.",
            "example": "ar"
          },
          "confidence": {
            "type": "number",
            "format": "float",
            "minimum": 0,
            "maximum": 1,
            "description": "Detection confidence from 0 to 1.",
            "example": 0.97
          }
        }
      },
      "PageMetadata": {
        "type": "object",
        "properties": {
          "page_index": {
            "type": "integer",
            "description": "Zero-based page index.",
            "example": 0
          },
          "width": {
            "type": "number",
            "format": "float",
            "description": "Page width in points (1/72 inch), the unit of node coordinates for every input format.",
            "example": 612
          },
          "height": {
            "type": "number",
            "format": "float",
            "description": "Page height in the same units as `width`.",
            "example": 792
          },
          "page_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Page content type, for example `text`.",
            "example": "text"
          },
          "language": {
            "description": "**API version `2026-09-03` and later.** The page's primary OCR-detected language; `null` when the OCR reported none (or the page was processed before this field existed).\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LanguageTag"
              },
              {
                "type": "null"
              }
            ]
          },
          "direction": {
            "description": "**API version `2026-09-03` and later.** The direction the page's rows read in — the same gate the parser used to order words and columns: `rtl` for right-to-left scripts (Arabic, Hebrew, Persian, Urdu, …), `ltr` otherwise; `null` for rows written before the field.\n",
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "rtl",
              "ltr",
              null
            ],
            "example": "rtl"
          }
        }
      },
      "PageListResponse": {
        "type": "object",
        "required": [
          "pages",
          "total"
        ],
        "properties": {
          "pages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PageMetadata"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cursor for the next page. Null if no more results."
          },
          "total": {
            "type": "integer",
            "description": "Total number of pages for this processing.",
            "example": 12
          }
        }
      },
      "DocumentDeleteResponse": {
        "type": "object",
        "required": [
          "id",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "665f1a2b3c4d5e6f7a8b9c0d"
          },
          "deleted": {
            "type": "boolean",
            "description": "Always `true`; deletion has been accepted and finishes shortly after the response.",
            "example": true
          }
        }
      },
      "ParseResponse": {
        "type": "object",
        "required": [
          "document_id",
          "processing_id",
          "status",
          "document_type",
          "sub_documents",
          "total_pages",
          "page_count",
          "pages"
        ],
        "properties": {
          "document_id": {
            "type": "string",
            "example": "665f1a2b3c4d5e6f7a8b9c0d"
          },
          "processing_id": {
            "type": [
              "string",
              "null"
            ],
            "example": "665f1a2b3c4d5e6f7a8b9c0e"
          },
          "status": {
            "type": "string",
            "enum": [
              "completed",
              "in_progress",
              "failed",
              "unknown"
            ],
            "description": "Status of the latest processing run, as on `GET /documents/{id}/processing/latest`. While the run is `in_progress`, and after it stops as `unknown`, `pages` contains only pages that completed.\n"
          },
          "document_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Broad document category, named after the lead document, for example `Deposition Transcript`, `Affidavit`, `Motion`, `Order` or `Petition`. Exhibits and attachments do not change it: a motion with exhibits is `Motion`. `Other` when no category fits, and `null` when the document was not classified. The list of values is open, so treat the value as a display string.\n",
            "examples": [
              "Deposition Transcript",
              "Affidavit",
              "Motion",
              "Other",
              null
            ]
          },
          "language": {
            "description": "**Version `2026-09-03` and later.** The language of most of the document's pages, or `null` when none was detected or the run has not completed.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LanguageTag"
              },
              {
                "type": "null"
              }
            ]
          },
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "rtl",
              "ltr",
              null
            ],
            "description": "**Version `2026-09-03` and later.** Reading direction of the document's majority-language pages, or `null` when not detected.\n",
            "example": "rtl"
          },
          "transcript": {
            "$ref": "#/components/schemas/TranscriptFlag",
            "description": "**Version `2026-09-12` and later.** `line_numbered` is `true` when at least one page has a printed column of transcript line numbers.\n"
          },
          "kvps_status": {
            "type": "string",
            "enum": [
              "configured",
              "not_configured"
            ],
            "description": "**Version `2026-11-05` and later**, present when `include` contains `kvps`. Says why `kvps` is what it is. `not_configured`: no extraction workflow is set up for this run, so `kvps` is `null` on every page. This is the usual case; the form fields detected on any document are in `key_values`. `configured`: the run's workflow extracts `kvps`; a page it skipped, or has not reached yet, is `null`. For a run made with a workflow set up by name, the answer reflects the workflow's currently active version. Absent when the run's workflow could not be read at request time, rather than a guessed `not_configured`.\n",
            "example": "not_configured"
          },
          "sub_documents": {
            "type": "array",
            "description": "Outline of the distinct documents detected inside this one, such as an exhibit, an attached affidavit or a motion within a brief. Each entry covers a contiguous page range and can contain nested `children`. Empty when none were detected.\n",
            "items": {
              "$ref": "#/components/schemas/SubDocument"
            }
          },
          "total_pages": {
            "type": "integer",
            "description": "Number of pages in the run before the `pages=` filter is applied. While the run is `in_progress` it counts only completed pages and can grow; use `status` to detect completion.",
            "example": 6
          },
          "page_count": {
            "type": "integer",
            "description": "Number of pages returned in this response, after the `pages=` filter. Not related to the deprecated `page_count` on `/processing/latest`.\n",
            "example": 5
          },
          "truncated": {
            "type": "boolean",
            "description": "Always `false`. An omitted `pages=` returns every page — the former 50-page default cap is retired. Retained for backward compatibility with clients written against the capped behavior.\n",
            "example": false
          },
          "default_page_limit": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Always `null` — no default page cap is applied. Retained for backward compatibility with clients written against the former 50-page cap.\n",
            "example": null
          },
          "pages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ParsedPage"
            }
          },
          "clauses": {
            "type": "array",
            "description": "Document-wide nested clause and heading outline for the selected output, with source page/node references. Present when clauses is included.",
            "items": {
              "$ref": "#/components/schemas/ParsedClause"
            }
          }
        }
      },
      "SubDocument": {
        "type": "object",
        "description": "A distinct document detected inside the uploaded one, covering a contiguous page range. When two sub-documents share a page, as in newspapers and gazettes, `start_anchor` and `end_anchor` mark where each begins and ends on that page, and adjacent sub-documents overlap on exactly that page.\n",
        "required": [
          "id",
          "label",
          "page_from",
          "page_to",
          "children"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Sub-document ID, unique within this processing run. A new run assigns new IDs.",
            "example": "2d07ae8a-cd0b-43ba-ae68-7cb21cecc2b0"
          },
          "label": {
            "type": "string",
            "description": "Human-readable category label. Matches the `starter_category` field for single-start sub-documents; derived from the dominant category for aggregates.\n",
            "example": "Affidavit First Page"
          },
          "page_from": {
            "type": "integer",
            "description": "Zero-based index of the first page in this sub-document (inclusive). Matches `ParsedPage.page_index` in the same response.\n",
            "example": 0
          },
          "page_to": {
            "type": "integer",
            "description": "Zero-based index of the last page in this sub-document (inclusive).\n",
            "example": 2
          },
          "document_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Detected document type, for example `Corporate Resolution`. The list of values is open; `null` when no type was detected.\n",
            "example": "Corporate Resolution"
          },
          "language": {
            "description": "**API version `2026-09-03` and later (`/parse` only).** The majority page language over this sub-document's page span (`{code}`), or `null`; nested `children` carry their own.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LanguageTag"
              },
              {
                "type": "null"
              }
            ]
          },
          "children": {
            "type": "array",
            "description": "Nested sub-documents contained within this one.",
            "items": {
              "$ref": "#/components/schemas/SubDocument"
            }
          },
          "start_anchor": {
            "$ref": "#/components/schemas/SubDocumentAnchor"
          },
          "end_anchor": {
            "$ref": "#/components/schemas/SubDocumentAnchor"
          },
          "starter_category": {
            "type": "string",
            "description": "**Versions `2026-06-07` and earlier.** Deprecated; use `label`.",
            "example": "Affidavit First Page"
          }
        }
      },
      "FolderSubDocumentRow": {
        "type": "object",
        "description": "One sub-document found in a file in the folder, with the page range and anchors needed to locate it in that file's `/parse` output.\n",
        "required": [
          "id",
          "document_id",
          "kind",
          "label",
          "page_from",
          "page_to"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Sub-document ID, unique within this processing run. A new run assigns new IDs."
          },
          "document_id": {
            "type": "string",
            "description": "Parent document — pass to `/documents/{id}/parse`."
          },
          "kind": {
            "type": "string",
            "description": "Currently always `subdocument`.",
            "enum": [
              "subdocument",
              "section"
            ]
          },
          "label": {
            "type": "string",
            "description": "Readable label for the sub-document's category."
          },
          "document_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Open-vocabulary type (e.g. `Notice`)."
          },
          "page_from": {
            "type": "integer",
            "description": "Zero-based index of the first page (inclusive)."
          },
          "page_to": {
            "type": "integer",
            "description": "Zero-based index of the last page (inclusive)."
          },
          "start_anchor": {
            "$ref": "#/components/schemas/SubDocumentAnchor"
          },
          "end_anchor": {
            "$ref": "#/components/schemas/SubDocumentAnchor"
          }
        }
      },
      "SubDocumentAnchor": {
        "type": [
          "object",
          "null"
        ],
        "description": "**API version `2026-08-05` and later.** Sub-page boundary position: where on the shared page this sub-document starts (`start_anchor`) or ends (`end_anchor`). `null` means the boundary is page-granular on that side (top of `page_from` / bottom of `page_to`).\n",
        "properties": {
          "page_index": {
            "type": "integer",
            "description": "Zero-based page carrying the boundary block.",
            "example": 143
          },
          "bbox": {
            "type": [
              "array",
              "null"
            ],
            "description": "`[x0, y0, x1, y1]` of the boundary block in page coordinates (same space as `ParsedTreeNode.coordinates`); `null` when the detector could not ground the boundary to a block.\n",
            "items": {
              "type": "number"
            },
            "minItems": 4,
            "maxItems": 4,
            "example": [
              63,
              88.5,
              240.4,
              130.2
            ]
          }
        }
      },
      "TranscriptFlag": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "line_numbered"
        ],
        "description": "**API version `2026-09-12` and later.** Whether a printed transcript line-number gutter was read.\n",
        "properties": {
          "line_numbered": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "ParsedPage": {
        "type": "object",
        "description": "A single page of parsed output. Which fields are present depends on the `include` query param. Fields for slices that were not requested are omitted entirely. `width` and `height` are omitted when `strip_geometry=true`.\n",
        "required": [
          "page_index"
        ],
        "properties": {
          "page_index": {
            "type": "integer",
            "description": "Zero-based page index within the processing.",
            "example": 0
          },
          "width": {
            "type": "number",
            "format": "float",
            "description": "Page width in points. Omitted when `strip_geometry=true`.",
            "example": 612
          },
          "height": {
            "type": "number",
            "format": "float",
            "description": "Page height in points. Omitted when `strip_geometry=true`.",
            "example": 792
          },
          "language": {
            "description": "**API version `2026-09-03` and later.** The page's primary OCR-detected language, or `null`.\n",
            "oneOf": [
              {
                "$ref": "#/components/schemas/LanguageTag"
              },
              {
                "type": "null"
              }
            ]
          },
          "direction": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "rtl",
              "ltr",
              null
            ],
            "description": "**API version `2026-09-03` and later.** The direction the page's text reads in; words, columns and table cells in `tree`, `text` and `blocks` are already in that reading order. Render right-to-left pages with `dir=\"rtl\"`.\n",
            "example": "rtl"
          },
          "transcript": {
            "$ref": "#/components/schemas/TranscriptFlag",
            "description": "**Version `2026-09-12` and later.** `line_numbered` is `true` when this page has printed transcript line numbers. Each number is then given once, on the `transcript_line` node's `value.transcript_line_number`, and is removed from the text.\n"
          },
          "tree": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ParsedTree"
              },
              {
                "type": "null"
              }
            ],
            "description": "The page's layout tree. Present when `include` contains `tree`, which it does by default. `null` when the page has no layout result yet. Honors `exclude_node_types`, `tokens` and `strip_geometry`.\n"
          },
          "kvps": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                },
                "example": {
                  "Plaintiff Name": "Jane Doe",
                  "Claim Number": "CLM-12345"
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Fields from an extraction workflow set up for your account, as `{field name: value}`. `null` when no such workflow ran, which is the usual case: the form fields detected on any document are in `key_values` (version `2026-11-05` and later) and in the tree as `key_value` nodes. From version `2026-11-05`, `kvps` is returned only when `include` names it, and the response's `kvps_status` says whether a workflow is set up. No query option enables an extraction workflow.\n"
          },
          "key_values": {
            "type": "array",
            "description": "**Version `2026-11-05` and later**, present when `include` contains `key_values` (the default). Every key-value pair detected on the page, such as a form field and its filled-in value, in reading order and with no setup. A pair can sit in three places in the tree, and this list has all of them: a `key_value` node (`source: field`), a table (`source: table`, a node in one cell or a pair whose key and value are in different cells) or a sentence (`source: inline`, whose words stay in the paragraph's text). Honors `exclude_node_types` and `strip_geometry`.\n",
            "items": {
              "$ref": "#/components/schemas/ParsedKeyValue"
            }
          },
          "text": {
            "type": "string",
            "description": "Plain text in reading order, one line per LINE node joined with `\\n`. Present only when `include` contains `text`. Respects `exclude_node_types`.\n",
            "example": "Hello world\nSecond line of text"
          },
          "tables": {
            "type": "array",
            "description": "All tables on the page, each projected to `{headers, rows}`. Present only when `include` contains `tables`.\n",
            "items": {
              "$ref": "#/components/schemas/ParsedTable"
            }
          },
          "blocks": {
            "type": "array",
            "description": "Flat pre-order list of content blocks for the page. Present only when `include` contains `blocks`.\n",
            "items": {
              "$ref": "#/components/schemas/ParsedBlock"
            }
          },
          "paragraphs": {
            "type": "array",
            "description": "Reading-order logical paragraphs with section attribution. Present only when `include` contains `paragraphs`. Each entry merges a paragraph split across a column break into one block and carries the governing section number forward. Projected **per page**: a paragraph split across a *page* break stays two entries joined by the seam flags (`continues_next` on the last entry of page N ∧ `continues_prev` on the first of page N+1) — concatenate their `text` to reconstruct the full paragraph. The `section` number is carried across the page seam so both entries cite the same section.\n",
            "items": {
              "$ref": "#/components/schemas/ParsedParagraph"
            }
          }
        }
      },
      "KeyValuePair": {
        "type": "object",
        "description": "A detected key-value pair: a key (a field label) and its value. Detection is English-only and finds labelled fields, not terms written in running prose.\n",
        "required": [
          "key",
          "value",
          "unvalued",
          "score"
        ],
        "additionalProperties": false,
        "properties": {
          "key": {
            "type": "string",
            "description": "The key's text.",
            "example": "EIN"
          },
          "value": {
            "type": "string",
            "description": "The value's text; empty when `unvalued`.",
            "example": "81-3829306"
          },
          "unvalued": {
            "type": "boolean",
            "description": "`true` for a blank field: a key with no value.",
            "example": false
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "How confident the model is that the value belongs to the key, from 0 to 1. `null` for a blank field.",
            "example": 0.9731
          },
          "key_box": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "array",
              "items": {
                "type": "number"
              }
            },
            "description": "Four `[x, y]` corners of the key's words. Omitted when `strip_geometry=true`; absent on `meta.key_value`, where the node's `kv_key` child has the box."
          },
          "value_box": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "array",
              "items": {
                "type": "number"
              }
            },
            "description": "Four `[x, y]` corners of the value's words; `null` for a blank field. Omitted when `strip_geometry=true`; absent on `meta.key_value`."
          }
        }
      },
      "ParsedKeyValue": {
        "type": "object",
        "description": "One entry of the `key_values` slice; a `KeyValuePair` plus where it sits in the tree.",
        "required": [
          "key",
          "value",
          "unvalued",
          "score",
          "node_id",
          "source"
        ],
        "additionalProperties": false,
        "properties": {
          "key": {
            "$ref": "#/components/schemas/KeyValuePair/properties/key"
          },
          "value": {
            "$ref": "#/components/schemas/KeyValuePair/properties/value"
          },
          "unvalued": {
            "$ref": "#/components/schemas/KeyValuePair/properties/unvalued"
          },
          "score": {
            "$ref": "#/components/schemas/KeyValuePair/properties/score"
          },
          "key_box": {
            "$ref": "#/components/schemas/KeyValuePair/properties/key_box"
          },
          "value_box": {
            "$ref": "#/components/schemas/KeyValuePair/properties/value_box"
          },
          "node_id": {
            "type": "string",
            "description": "The tree node the pair belongs to — the `key_value` node, or the `table` or paragraph that lists it in `meta.key_values`.",
            "example": "key_value_3"
          },
          "source": {
            "type": "string",
            "enum": [
              "field",
              "table",
              "inline"
            ],
            "description": "`field`: a `key_value` node. `table`: inside a table, as a `key_value` node in one cell or listed in the table's `meta.key_values` when the key and the value are in different cells. `inline`: inside a sentence; the words stay in the paragraph's text and the paragraph lists the pair in `meta.key_values`.\n"
          }
        }
      },
      "ParsedTable": {
        "type": "object",
        "description": "A table on one page.",
        "required": [
          "headers",
          "rows"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ID of the `table` node on this page. To join a table across pages, use `meta.table.id` in the tree.",
            "example": "tbl_0"
          },
          "headers": {
            "type": "array",
            "description": "Header text, one entry per detected header node. A header node containing several cells becomes one joined string. Use the tree to read individual header cells.",
            "items": {
              "type": "string"
            },
            "example": [
              "Name",
              "Age"
            ]
          },
          "rows": {
            "type": "array",
            "description": "Body rows, each an array of cell text in column order.",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "example": [
              [
                "Alice",
                "30"
              ],
              [
                "Bob",
                "42"
              ]
            ]
          }
        }
      },
      "ParsedBlock": {
        "type": "object",
        "description": "One entry of the `blocks` slice: a `header`, `footer`, `section`, `line`, `table` or `sub_document` node in reading order. Text inside other classes, such as paragraphs and headings, appears through their `line` blocks. A `section` block's `text` covers its whole section, whose lines, tables and nested sections also appear as their own blocks.\n",
        "required": [
          "id",
          "class",
          "text"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "ID of the source tree node. Node IDs belong to one page of one document's processing run, and some can repeat within a page; see [Understand output](/api/understanding-output.html) for which, and for a safe citation key.",
            "example": "section_0"
          },
          "class": {
            "type": "string",
            "description": "Node class of the block.",
            "enum": [
              "header",
              "footer",
              "section",
              "line",
              "table",
              "sub_document"
            ],
            "x-enum-descriptions": [
              "Page header region",
              "Page footer region",
              "Logical section, such as a numbered paragraph; its text covers the whole section",
              "One text line",
              "A whole table as text; use the `tables` slice for headers and rows",
              "Detected sub-document boundary, such as the start of an exhibit"
            ],
            "example": "section"
          },
          "text": {
            "type": "string",
            "description": "Text of the block.",
            "example": "Hello world"
          }
        }
      },
      "ParsedParagraph": {
        "type": "object",
        "description": "A reading-order logical paragraph with section attribution, as emitted in the `paragraphs` slice. Projected per page — see `ParsedPage.paragraphs` for the cross-page stitching contract.\n",
        "required": [
          "ids",
          "text"
        ],
        "properties": {
          "ids": {
            "type": "array",
            "description": "Page/run-local source node IDs merged into this paragraph, in reading order. Pair with processing_id and page_index for a reference.",
            "items": {
              "type": "string"
            },
            "example": [
              "paragraph_3",
              "paragraph_4"
            ]
          },
          "class": {
            "type": "string",
            "description": "Lowercase public source class, such as paragraph, list_item, heading or footnote; see the node-class reference.",
            "example": "paragraph"
          },
          "section": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Governing section/paragraph number (e.g. `\"31\"`, `\"1.2\"`) carried forward from the nearest preceding marker, or `null` when no section governs this paragraph. Always a string when present.\n",
            "example": "31"
          },
          "text": {
            "type": "string",
            "description": "Concatenated text of the merged paragraph.",
            "example": "The disclosing party shall provide the information within ten days."
          },
          "continues_prev": {
            "type": "boolean",
            "description": "True when this paragraph continues one whose start is on the previous page — stitch with that page's trailing entry.\n",
            "example": false
          },
          "continues_next": {
            "type": "boolean",
            "description": "True when this paragraph continues onto the next page — stitch with that page's leading entry.\n",
            "example": false
          },
          "heading": {
            "type": [
              "string",
              "null"
            ],
            "description": "Governing heading text when detected, not necessarily text inside this paragraph."
          }
        }
      },
      "ParsedTree": {
        "type": "object",
        "description": "The hierarchical layout tree of one page. From API version `2026-08-01` `root` contains `ParsedTreeNode` nodes (the closed public shape). Requests pinning `2026-06-07` or earlier receive `ParsedTreeNodeLegacy` nodes instead — the shape is selected by the `X-API-Version` request header, which OpenAPI cannot express structurally.\n",
        "required": [
          "root"
        ],
        "properties": {
          "root": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/ParsedTreeNode"
                },
                {
                  "$ref": "#/components/schemas/ParsedTreeNodeLegacy"
                }
              ]
            }
          }
        }
      },
      "ParsedTreeNode": {
        "type": "object",
        "additionalProperties": false,
        "description": "**Version `2026-08-01` and later.** A node of the page tree. Nodes have exactly the keys `id`, `class`, `class_id`, `coordinates`, `value`, `meta` and `content`; `value` and `meta` are omitted when empty, and only the keys documented here appear in them. `token` nodes, one per word, are included only with `tokens=true`. Without them, each `line` node carries its text in `value.text`, so the tree always holds the full text.\n",
        "properties": {
          "id": {
            "type": "string",
            "example": "line_0"
          },
          "class": {
            "type": "string",
            "description": "Lowercase `NodeClass` name (`paragraph`, `line`, `table`, …) — the primary discriminator. Same vocabulary as the `class_id` table below, the `exclude_node_types` parameter, and the `blocks` slice. Not present on `ParsedTreeNodeLegacy`.\n",
            "example": "paragraph"
          },
          "class_id": {
            "type": "integer",
            "description": "Numeric node class, equivalent to `class`; you can use either.\nClass IDs are never renumbered, and new classes can be added.\nThe class name, such as `page_number` for 37, is what\n`exclude_node_types` accepts and what the `blocks` slice returns.\n\n| ID | Class | Category | Meaning |\n|---:|-------|----------|---------|\n| 0 | `root` | Structural | The root of a page tree; one per page |\n| 1 | `main_page` | Structural | The page container directly under the root |\n| 2 | `subpage` | Structural | The content region inside the page margins |\n| 3 | `header` | Header and footer | Running page header |\n| 4 | `footer` | Header and footer | Running page footer |\n| 5 | `content` | Structural | Generic body-content container |\n| 6 | `column` | Structural | One vertical reading column |\n| 7 | `section` | Structural | Logical section, such as a numbered paragraph in a legal filing; carries `value.paragraph_number`; sections can nest |\n| 8 | `transcript_line` | Transcript / Q&A | One numbered transcript line |\n| 9 | `question` | Transcript / Q&A | A question in a transcript or deposition |\n| 10 | `answer` | Transcript / Q&A | An answer in a transcript or deposition |\n| 11 | `question_mark_token` | Transcript / Q&A | The `Q.` or `Q:` marker that opens a question |\n| 12 | `answer_mark_token` | Transcript / Q&A | The `A.` or `A:` marker that opens an answer |\n| 13 | `speaker_turn` | Transcript / Q&A | A named speaker's or examiner's turn (`value.speaker`, `value.turn_number`, `value.role`; `meta.transcript.turn`); children are transcript paragraphs |\n| 14 | `vision_object` | Content block | A content block with no more specific class |\n| 15 | `token` | Text | One word; can carry `meta.styling` |\n| 16 | `line` | Text | One text line; the `text` slice is these lines in reading order |\n| 17 | `sub_document` | Structural | A detected sub-document boundary, such as an exhibit inside a combined PDF |\n| 18 | `horizontal_band` | Structural | A full-width horizontal band; columns sit inside bands |\n| 19 | `table` | Table | A table; the `tables` slice is built from these |\n| 20 | `table_row` | Table | One table row; children are table cells |\n| 21 | `table_column_header` | Table | A header row containing its column cells |\n| 22 | `table_cell` | Table | One table cell |\n| 23 | `title` | Content block | Document title |\n| 24 | `heading` | Content block | Section heading |\n| 25 | `paragraph` | Content block | Body paragraph |\n| 26 | `list_item` | Content block | One item of a bulleted or numbered list |\n| 27 | `chart` | Content block | Chart or graph |\n| 28 | `image` | Content block | Image or figure in the body |\n| 29 | `formula` | Content block | Display formula |\n| 30 | `inline_formula` | Content block | Formula inside a text line |\n| 31 | `formula_number` | Content block | Equation number next to a formula |\n| 32 | `caption` | Content block | Figure or table caption |\n| 33 | `aside` | Content block | Side or marginal text |\n| 34 | `footnote` | Content block | Footnote |\n| 35 | `reference` | Content block | Bibliography or reference entry |\n| 36 | `seal` | Content block | Stamp or seal |\n| 37 | `page_number` | Header and footer | Page number |\n| 38 | `code_block` | Content block | Monospaced code block |\n| 39 | `abstract` | Content block | Abstract |\n| 40 | `header_image` | Header and footer | Image in the header, such as a letterhead logo |\n| 41 | `footer_image` | Header and footer | Image in the footer |\n| 42 | `key_value` | Form field | A form field: a key and its value, or a blank field with no value |\n| 43 | `kv_key` | Form field | The key (label) half of a `key_value` |\n| 44 | `kv_value` | Form field | The value half of a `key_value` |\n"
          },
          "coordinates": {
            "type": "array",
            "description": "Four-point polygon in returned page width/height units (PDF points for PDFs), with top-left origin, x right and y down. Current word rectangles are [xmin,ymax], [xmax,ymax], [xmax,ymin], [xmin,ymin]. Absent when strip_geometry=true. See the word-geometry guide.",
            "items": {
              "type": "array",
              "items": {
                "type": "number"
              }
            }
          },
          "content": {
            "type": "array",
            "description": "Child nodes in reading order.",
            "items": {
              "$ref": "#/components/schemas/ParsedTreeNode"
            }
          },
          "value": {
            "type": "object",
            "additionalProperties": false,
            "description": "Class-specific values; only the keys below appear, and the object is omitted when none apply. A `line` node's `text` is filled in from its words when `token` nodes are omitted, which is the default.\n",
            "properties": {
              "text": {
                "type": "string",
                "example": "Annual Consent of Directors"
              },
              "paragraph_number": {
                "description": "Governing section/clause number string.",
                "example": "32.1(b)"
              },
              "transcript_line_number": {
                "type": "integer"
              },
              "question_number": {
                "description": "Printed question number, on `question` nodes."
              },
              "answer_number": {
                "description": "Printed answer number, on `answer` nodes."
              },
              "speaker": {
                "type": "string",
                "description": "**API version `2026-09-12` and later**, `speaker_turn` nodes only. The speaker as printed, without the colon (`MR. TAIT`, `THE DEPONENT`, `JUSTICE`).\n",
                "example": "MR. TAIT"
              },
              "turn_number": {
                "type": "string",
                "description": "**API version `2026-09-12` and later**, `speaker_turn` nodes only. The printed turn number when the transcript numbers its turns (`18` in `18. MR. TAIT: …`).\n",
                "example": "18"
              },
              "role": {
                "type": "string",
                "enum": [
                  "examiner",
                  "witness",
                  "bench",
                  "reporter"
                ],
                "description": "**API version `2026-09-12` and later**, `speaker_turn` nodes only. The speaker's courtroom role when the printed head implies one (`BY MR. TAIT:` → `examiner`, `THE DEPONENT` → `witness`, `JUSTICE` → `bench`); absent for plain names.\n"
              }
            }
          },
          "meta": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ParsedNodeMeta"
              }
            ],
            "description": "Curated node metadata. A closed object — every possible key is documented on `ParsedNodeMeta`; omitted when the node carries none.\n"
          }
        }
      },
      "ParsedTreeNodeLegacy": {
        "type": "object",
        "additionalProperties": true,
        "description": "**Versions `2026-06-07` and earlier.** The legacy tree node. It is an open object: `value` and `engine_meta` can contain other keys, so ignore keys you don't recognize. Nodes have a numeric `class_id` (same table as `ParsedTreeNode`) but no `class` name, and word data is always included. `strip_geometry=true` removes `coordinates`, `engine_meta` and `tokens`. New integrations should use the latest version and `ParsedTreeNode`.\n",
        "properties": {
          "id": {
            "type": "string",
            "example": "line_0"
          },
          "class_id": {
            "type": "integer",
            "description": "Node class identifier — see the table on `ParsedTreeNode.class_id` (same vocabulary, never renumbered).\n"
          },
          "coordinates": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "number"
              }
            }
          },
          "content": {
            "type": "array",
            "description": "Child nodes (recursive), in reading order.",
            "items": {
              "$ref": "#/components/schemas/ParsedTreeNodeLegacy"
            }
          },
          "value": {
            "type": "object",
            "additionalProperties": true,
            "description": "Class-specific values such as `text`, `paragraph_number` and `transcript_line_number`. Other keys can appear.\n"
          },
          "engine_meta": {
            "type": "object",
            "additionalProperties": true,
            "description": "Legacy layout metadata, replaced by `meta` in version `2026-08-01`. The `block`, `section`, `clause`, `heading`, `continuation`, styling and table keys carry over to `meta`; other keys do not.\n"
          },
          "tokens": {
            "type": "array",
            "description": "Raw internal token payload. On the default version word-level data is exposed as TOKEN child nodes via `tokens=true` instead.\n",
            "items": {}
          },
          "order_id": {
            "type": "integer",
            "description": "Internal ordering index — `content` order carries reading order on all versions.\n"
          }
        }
      },
      "ParsedNodeMeta": {
        "type": "object",
        "additionalProperties": false,
        "description": "**Version `2026-08-01` and later.** Node metadata. Only the keys listed here appear, and each one only on nodes where it applies.\n",
        "properties": {
          "block": {
            "type": "string",
            "description": "Content-block class name, from the same vocabulary as `class`, such as `paragraph`, `heading` or `table`.\n",
            "example": "paragraph"
          },
          "key_value": {
            "$ref": "#/components/schemas/KeyValuePair",
            "description": "**Version `2026-11-05` and later**, on `key_value` nodes: the pair's key, value, `unvalued` and `score`, without boxes. The node's `kv_key` and `kv_value` children hold the words and their coordinates.\n"
          },
          "key_values": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KeyValuePair"
            },
            "description": "**Version `2026-11-05` and later**, on a `table` whose pairs have the key and the value in different cells, and on a paragraph that has a pair inside a sentence. No node can hold such a pair, so the cells and lines keep their words and this list names each pair, with the boxes of its key's and value's words (omitted when `strip_geometry=true`). The `key_values` slice lists the same pairs.\n"
          },
          "sub_document": {
            "type": "object",
            "additionalProperties": false,
            "description": "**Version `2026-08-05` and later.** The sub-document this node belongs to, present on pages covered by a detected sub-document. When sub-documents nest, it is the innermost one; find its parents in the response's `sub_documents`. On a page shared by two sub-documents, ownership changes at the second one's `start_anchor` block, and containers that span the boundary keep the first owner.\n",
            "properties": {
              "id": {
                "type": "string",
                "description": "Matches a sub-document `id` in the same response.",
                "example": "2d07ae8a-cd0b-43ba-ae68-7cb21cecc2b0"
              }
            }
          },
          "section": {
            "type": "object",
            "additionalProperties": false,
            "description": "The section this node belongs to; use `number` for citations.",
            "properties": {
              "number": {
                "type": "string",
                "description": "Printed section or clause number, such as `32.1(b)`.",
                "example": "32.1(b)"
              },
              "is_marker": {
                "type": "boolean",
                "description": "`true` on the line that carries the printed section number."
              }
            }
          },
          "clause": {
            "type": "object",
            "additionalProperties": false,
            "description": "Clause-hierarchy metadata on marker nodes and clause containers. `parent` links to the nearest ancestor clause's `number`, letting clients rebuild the full hierarchy (or use the `clauses` slice for the stitched document outline).\n",
            "properties": {
              "number": {
                "type": "string",
                "example": "32.1(b)"
              },
              "level": {
                "type": "integer",
                "description": "1-based structural depth."
              },
              "parent": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Full number of the nearest ancestor clause.",
                "example": "32.1"
              },
              "ordinal": {
                "type": "integer",
                "description": "Position within its sibling run."
              },
              "scheme": {
                "type": "string",
                "enum": [
                  "decimal",
                  "paren_num",
                  "alpha",
                  "roman",
                  "unknown"
                ]
              },
              "kind": {
                "type": "string",
                "enum": [
                  "clause",
                  "list_item"
                ]
              }
            }
          },
          "heading": {
            "type": "object",
            "additionalProperties": false,
            "description": "Heading style measurements on `heading` and `title` nodes, relative to the same page. A missing key means it was not measured. Document-wide heading levels are in the `clauses` outline.\n",
            "properties": {
              "size_ratio": {
                "type": "number",
                "description": "Median token height vs page body median."
              },
              "bold_ratio": {
                "type": "number",
                "description": "Median stroke-width ratio."
              },
              "caps": {
                "type": "number",
                "description": "Uppercase-word fraction (0-1)."
              },
              "centered": {
                "type": "boolean",
                "description": "Whether the heading is centered on the page."
              },
              "keyword_rank": {
                "type": "integer",
                "description": "PART(1) > ARTICLE/SCHEDULE/…(2) > SECTION/…(3)."
              },
              "marker_level": {
                "type": "integer",
                "description": "Clause level when the heading carries a marker."
              }
            }
          },
          "heading_scope": {
            "type": "boolean",
            "description": "True on a SECTION container synthesized as a heading's scope (the heading plus the siblings it governs).\n"
          },
          "continuation": {
            "type": "object",
            "additionalProperties": false,
            "description": "Cross-page/column paragraph flow. Stitch `continues_next` on one node with `continues_prev` on the next page's leading node to reassemble a split paragraph (the `paragraphs` slice does the within-page merge for you).\n",
            "properties": {
              "continues_prev": {
                "type": "boolean"
              },
              "continues_next": {
                "type": "boolean"
              }
            }
          },
          "transcript": {
            "type": "object",
            "additionalProperties": false,
            "description": "**Version `2026-09-12` and later.** Transcript details, each present only on the node class noted.\n",
            "properties": {
              "line_number_source": {
                "type": "string",
                "enum": [
                  "read",
                  "interpolated"
                ],
                "description": "On `transcript_line` nodes: `read` when the number was read from the page, `interpolated` when it was inferred from the neighboring numbers because the printed number was missing or unreadable.\n"
              },
              "paragraph_break": {
                "type": "string",
                "enum": [
                  "start",
                  "indent",
                  "blank",
                  "turn"
                ],
                "description": "On transcript `paragraph` nodes: what starts the paragraph, which is the top of the column, an indented first line, a blank line above it, or a speaker, question or answer label.\n"
              },
              "turn": {
                "type": "string",
                "enum": [
                  "speaker",
                  "examiner",
                  "interjection",
                  "continued"
                ],
                "description": "On `speaker_turn` nodes: how the turn starts, which is a named speaker, an examiner label such as `BY MR. TAIT:`, an interjection within a question or answer, or a continuation of the previous page's last turn (with `continuation.continues_prev` set).\n"
              },
              "side_column": {
                "type": "boolean",
                "description": "On `transcript_line` nodes, and the nodes that group them, in a column beside the numbered lines: `true` when the line takes its `value.transcript_line_number` from the numbered row it sits beside. Cite it by that number; it is not a separate numbered line.\n"
              }
            }
          },
          "footnote_number": {
            "type": "string",
            "description": "Footnote marker text on FOOTNOTE nodes.",
            "example": "1."
          },
          "ignore_selection": {
            "type": "boolean",
            "description": "`true` on nodes that are not body text: printed transcript line numbers, `Q.` and `A.` markers, footnote markers, and text repeated on every page such as stamps. Skip them when you rebuild clean body text.\n"
          },
          "page_chrome": {
            "type": "boolean",
            "description": "`true` on `token` nodes that belong to text repeated across pages, such as filing stamps, running captions and court-reporter footers. These tokens also have `ignore_selection: true`; `page_chrome` tells them apart from other non-body tokens such as line numbers. Visible with `tokens=true`.\n"
          },
          "ocr_confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "**Version `2026-11-04` and later.** On `token` nodes: how confident OCR is in the word's text, from 0 to 1. Low values often mean the text on the page is damaged, faint or garbled, so use them to warn readers about words or pages that may be misread. Absent when OCR gave no score, which is not the same as a low score. Visible with `tokens=true`. Documents processed before this version's release carry no scores.\n",
            "example": 0.98
          },
          "table": {
            "type": "object",
            "additionalProperties": false,
            "description": "Links the fragments of a table across pages, on `table` nodes. Every fragment of one logical table has the same `id`, and a continuing fragment's `continuation_table_id` equals that `id`, so join the fragments on `id` within one document. A continuation is the first table on a page that continues the last table of the previous page.\n",
            "properties": {
              "id": {
                "type": "string",
                "example": "tbl_1"
              },
              "is_table_start": {
                "type": "boolean"
              },
              "continues_from_previous": {
                "type": "boolean"
              },
              "continuation_table_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "page_range": {
            "type": "object",
            "additionalProperties": false,
            "description": "Page span of a SUB_DOCUMENT node (zero-based, inclusive).\n",
            "properties": {
              "from": {
                "type": "integer"
              },
              "to": {
                "type": "integer"
              }
            }
          },
          "styling": {
            "type": "object",
            "additionalProperties": false,
            "description": "Visual text styling. On TOKEN nodes (visible with `tokens=true`) only detected attributes appear. From API version `2026-08-10`, nodes with TOKEN children (LINEs) also carry the handwriting rollup of their words — `text_type` `handwriting` (every word handwritten) or `mixed` (some are, listed in `handwritten_tokens`). From API version `2026-10-20` they carry the line-mark rollup too — `struck_tokens` / `underlined_tokens` — so the default token-dropped tree reports both.\n",
            "properties": {
              "strikethrough": {
                "type": "boolean",
                "const": true,
                "description": "**API version `2026-10-20` and later**, TOKEN nodes: the word is struck through. Absent when it is not.\n"
              },
              "underline": {
                "type": "boolean",
                "const": true,
                "description": "**API version `2026-10-20` and later**, TOKEN nodes: the word is underlined. Absent when it is not.\n"
              },
              "struck_tokens": {
                "type": "array",
                "description": "**API version `2026-10-20` and later.** On a node with TOKEN children: the struck-through words. `index` is the word's position in the line's word sequence — the same order `value.text` is synthesized from — so a caller can drop struck words from the text.\n",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              },
              "underlined_tokens": {
                "type": "array",
                "description": "**API version `2026-10-20` and later.** On a node with TOKEN children: the underlined words, indexed like `struck_tokens`.\n",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              },
              "text_type": {
                "type": "string",
                "enum": [
                  "handwriting",
                  "mixed"
                ],
                "description": "On a TOKEN: present when OCR classified the word as handwritten; printed tokens omit the key (absent means printed). On a node with TOKEN children (API version `2026-08-10` and later): `handwriting` when every word is handwritten, `mixed` when only some are (see `handwritten_tokens`).\n"
              },
              "handwritten_tokens": {
                "type": "array",
                "description": "**API version `2026-08-10` and later.** Only on `mixed` lines: the handwritten words. `index` is the word's position in the line's word sequence — the same order the line's `value.text` is synthesized from — so entries map back into the text unambiguously even when a word repeats.\n",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "text": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "UsageAccount": {
        "type": "object",
        "description": "The account this API key bills against. Pricing-sensitive fields are withheld from workspace members who are not the billing owner.\n",
        "additionalProperties": false,
        "required": [
          "kind",
          "plan",
          "tier",
          "status"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "personal",
              "workspace"
            ],
            "description": "`personal` — a standalone developer account, metered from its own processing runs. `workspace` — an Enterprise workspace whose page usage is pooled across its members.\n",
            "example": "personal"
          },
          "plan": {
            "type": "string",
            "enum": [
              "trial",
              "personal",
              "enterprise",
              "payg"
            ],
            "description": "payg: canonical personal free allowance and prepaid wallet. enterprise: workspace entitlement. trial/personal are older or workspace projections; inspect explicit unlimited and wallet fields rather than inferring price from plan alone."
          },
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "description": "Enterprise price tier — `tier_1`, `tier_2`, `tier_3`, or `custom` for individually negotiated commercial terms. `null` on personal accounts and for workspace members who are not the billing owner.\n",
            "example": "tier_2"
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "past_due",
              "suspended",
              null
            ],
            "description": "Subscription state where applicable; null for personal PAYG and members whose billing status is withheld.",
            "example": "active"
          }
        }
      },
      "UsagePeriod": {
        "type": "object",
        "description": "Page counters. For Enterprise they cover the current billing period. For PAYG they cover the account's lifetime, so ignore `start` and `end`; `wallet.free_window` describes the free page allowance.",
        "additionalProperties": false,
        "required": [
          "start",
          "end",
          "resets_at",
          "included_pages",
          "unlimited",
          "pages_processed",
          "remaining_pages",
          "overage_pages"
        ],
        "properties": {
          "start": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Start of the current period (inclusive)."
          },
          "end": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "End of the current period (exclusive)."
          },
          "resets_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When `pages_processed` next resets to zero. `null` when the allowance never resets (a lifetime personal allowance).\n"
          },
          "included_pages": {
            "type": [
              "integer",
              "null"
            ],
            "description": "PAYG: the free page allowance, `0` when the account has none and `null` for Unlimited. Enterprise: pages included in the period, or `null` when not available. Use `unlimited` to detect Unlimited.\n",
            "example": 100000
          },
          "unlimited": {
            "type": "boolean",
            "description": "`true` only when the account has no page limit.\n",
            "example": false
          },
          "pages_processed": {
            "type": "integer",
            "description": "Pages processed successfully, over the account's lifetime for PAYG and in the current billing period for Enterprise. Free pages used are in `wallet.free_used_pages`.",
            "example": 12345
          },
          "remaining_pages": {
            "type": [
              "integer",
              "null"
            ],
            "description": "PAYG: free pages still available after used and reserved pages, or `null` for Unlimited. Enterprise: pages left in the period when you can see them, otherwise `null`. Paid credit is in `wallet.available_cents`.",
            "example": 87655
          },
          "overage_pages": {
            "type": "integer",
            "description": "Pages processed beyond the included allowance, over the lifetime for PAYG and in the billing period for Enterprise.",
            "example": 0
          }
        }
      },
      "UsageLimits": {
        "type": "object",
        "description": "Trial limits, or `null` for PAYG and paid accounts. Per-document and upload limits apply to every account; see [Operations and limits](/api/operations.html).",
        "additionalProperties": false,
        "required": [
          "total_pages",
          "document_pages",
          "file_size_bytes"
        ],
        "properties": {
          "total_pages": {
            "type": "integer",
            "description": "Total pages the trial allowance permits.",
            "example": 10000
          },
          "document_pages": {
            "type": "integer",
            "description": "Maximum pages in any single document.",
            "example": 2000
          },
          "file_size_bytes": {
            "type": "integer",
            "description": "Maximum upload size in bytes.",
            "example": 262144000
          }
        }
      },
      "UsageSpend": {
        "type": "object",
        "description": "Enterprise spend estimate for the current period, in minor units of `currency`, visible to the billing owner. `spend` is `null` for PAYG accounts, which use `wallet` and `page_rate_micros`, for custom invoiced terms, when no price applies to your plan, and for members who cannot see billing; `spend_note` says which.\n",
        "additionalProperties": false,
        "required": [
          "currency",
          "subscription_cents",
          "overage_rate_micros",
          "overage_cents",
          "estimated_total_cents"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "Lowercase ISO 4217 code of the currency of every amount below, such as `usd`.",
            "example": "usd"
          },
          "subscription_cents": {
            "type": "integer",
            "description": "The period's fixed subscription amount.",
            "example": 500000
          },
          "overage_rate_micros": {
            "type": "integer",
            "description": "Price of one page beyond the included allowance, in millionths of the currency unit (1,000,000 is 1.00). `0` when the plan has no per-page overage.\n",
            "example": 30000
          },
          "overage_cents": {
            "type": "integer",
            "description": "Cost of `period.overage_pages` so far.",
            "example": 0
          },
          "estimated_total_cents": {
            "type": "integer",
            "description": "`subscription_cents + overage_cents`, the estimated spend so far this period. The final amount is on your invoice.\n",
            "example": 500000
          }
        }
      },
      "UsageSummary": {
        "type": "object",
        "description": "Usage and spend for the account behind this API key. Processing of documents uploaded through the API or Studio is included. Pay-as-you-go accounts share one free page allowance and prepaid wallet across both.\n",
        "additionalProperties": false,
        "required": [
          "as_of",
          "account",
          "period",
          "limits",
          "documents_stored",
          "spend",
          "spend_note"
        ],
        "properties": {
          "as_of": {
            "type": "string",
            "format": "date-time",
            "description": "When this snapshot was taken."
          },
          "account": {
            "$ref": "#/components/schemas/UsageAccount"
          },
          "period": {
            "$ref": "#/components/schemas/UsagePeriod"
          },
          "limits": {
            "description": "Trial ceilings, or `null` when no trial allowance is in force.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/UsageLimits"
              },
              {
                "type": "null"
              }
            ]
          },
          "documents_stored": {
            "type": "integer",
            "description": "Files your account currently stores, from every upload source, including email bodies and attachments extracted from uploaded emails. Deleted files are not counted.\n",
            "example": 123
          },
          "spend": {
            "description": "Spend for the current period, or `null`; `spend_note` explains why.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/UsageSpend"
              },
              {
                "type": "null"
              }
            ]
          },
          "spend_note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why `spend` is `null`, in one sentence; `null` when `spend` is present.\n",
            "example": "Prepaid USD credit is charged at the stated page rate after free pages."
          },
          "wallet": {
            "$ref": "#/components/schemas/PaygWallet",
            "description": "Prepaid credit and free page allowance. Present for PAYG accounts, and only to the account owner."
          },
          "page_rate_micros": {
            "type": "integer",
            "minimum": 0,
            "description": "Price per page for PAYG accounts, in millionths of a US dollar: `30000` is $0.03. Present together with `wallet`.",
            "example": 30000
          }
        }
      },
      "UsageDay": {
        "type": "object",
        "description": "One UTC calendar day of usage.",
        "additionalProperties": false,
        "required": [
          "date",
          "pages_processed",
          "documents_processed"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "description": "The UTC calendar day, `YYYY-MM-DD`.",
            "example": "2026-08-24"
          },
          "pages_processed": {
            "type": "integer",
            "description": "Successful pages processed on this day.",
            "example": 120
          },
          "documents_processed": {
            "type": "integer",
            "description": "Successful processing runs completed on this day.",
            "example": 7
          }
        }
      },
      "UsageDaily": {
        "type": "object",
        "description": "A zero-filled per-day usage series: every UTC calendar day in the requested range is present, in ascending order, including days with no activity.\n",
        "additionalProperties": false,
        "required": [
          "start",
          "end",
          "days",
          "total_pages",
          "total_documents"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date",
            "description": "First day of the range (inclusive).",
            "example": "2026-08-24"
          },
          "end": {
            "type": "string",
            "format": "date",
            "description": "Last day of the range (inclusive).",
            "example": "2026-09-22"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageDay"
            }
          },
          "total_pages": {
            "type": "integer",
            "description": "Sum of `pages_processed` across the range.",
            "example": 3400
          },
          "total_documents": {
            "type": "integer",
            "description": "Sum of `documents_processed` across the range.",
            "example": 210
          }
        }
      },
      "ParsedClause": {
        "type": "object",
        "description": "One entry of the `clauses` outline, a clause, list item or heading. Fields are present or `null` only when detected; `children` are in document order.",
        "properties": {
          "kind": {
            "type": "string",
            "description": "`clause` or `list_item` for numbered entries, `heading` for headings.",
            "enum": [
              "clause",
              "heading",
              "list_item"
            ]
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Printed clause number, such as `32.1(b)`. Clauses and list items only."
          },
          "level": {
            "type": [
              "integer",
              "null"
            ],
            "description": "One-based nesting depth."
          },
          "parent": {
            "type": [
              "string",
              "null"
            ],
            "description": "Number of the enclosing clause. Clauses and list items only."
          },
          "scheme": {
            "type": [
              "string",
              "null"
            ],
            "description": "Numbering style, such as `decimal`, `paren_num`, `alpha` or `roman`. Clauses and list items only."
          },
          "page_index": {
            "type": "integer",
            "minimum": 0,
            "description": "Zero-based index of the page the entry starts on."
          },
          "node_id": {
            "type": "string",
            "description": "ID of the tree node the entry comes from, on that page."
          },
          "text": {
            "type": "string",
            "description": "Text of the clause or heading."
          },
          "style": {
            "type": [
              "string",
              "object",
              "null"
            ],
            "description": "Heading entries only. The heading's style measurements, with the same meaning as on the tree's `meta.heading`; a value is `null` when it was not measured.",
            "properties": {
              "size_ratio": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "bold_ratio": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "caps": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "keyword_rank": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "children": {
            "type": "array",
            "description": "Nested entries, in document order.",
            "items": {
              "$ref": "#/components/schemas/ParsedClause"
            }
          }
        }
      },
      "PaygWallet": {
        "description": "Prepaid US-dollar credit and free page allowance of a PAYG account, visible to the account owner. Amounts are integer US cents. Free pages are used before credit, and pages are reserved while a document processes.",
        "properties": {
          "initialized": {
            "default": true,
            "title": "Initialized",
            "description": "Whether the account's wallet has been set up.",
            "type": "boolean"
          },
          "has_funded": {
            "default": false,
            "title": "Has Funded",
            "description": "Whether credit has ever been added to the wallet.",
            "type": "boolean"
          },
          "migration_pending": {
            "default": false,
            "title": "Migration Pending",
            "description": "`true` while the account's billing setup is being completed. New processing waits until it finishes (`code: PAYG_MIGRATION_PENDING`).",
            "type": "boolean"
          },
          "currency": {
            "const": "USD",
            "description": "Always `USD`, in uppercase.",
            "default": "USD",
            "title": "Currency",
            "type": "string"
          },
          "free_total_pages": {
            "minimum": 0,
            "description": "Free pages granted to the account in the current free window.",
            "title": "Free Total Pages",
            "type": "integer"
          },
          "free_used_pages": {
            "minimum": 0,
            "description": "Free pages used by completed processing.",
            "title": "Free Used Pages",
            "type": "integer"
          },
          "free_reserved_pages": {
            "minimum": 0,
            "description": "Free pages reserved by processing that is still running.",
            "title": "Free Reserved Pages",
            "type": "integer"
          },
          "free_unlimited": {
            "default": false,
            "title": "Free Unlimited",
            "description": "`true` when the account has unlimited free pages.",
            "type": "boolean"
          },
          "free_window": {
            "default": "lifetime",
            "enum": [
              "lifetime",
              "rolling_30d"
            ],
            "description": "`lifetime` when the free allowance is granted once, `rolling_30d` when it applies to the last 30 days.",
            "title": "Free Window",
            "type": "string"
          },
          "free_available_pages": {
            "anyOf": [
              {
                "minimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Free pages still available after used and reserved pages; `0` when the allowance is used up and `null` for Unlimited.",
            "title": "Free Available Pages"
          },
          "balance_cents": {
            "minimum": 0,
            "description": "Credit balance in cents, including reserved credit.",
            "title": "Balance Cents",
            "type": "integer"
          },
          "reserved_cents": {
            "minimum": 0,
            "description": "Credit reserved by processing that is still running, in cents.",
            "title": "Reserved Cents",
            "type": "integer"
          },
          "available_cents": {
            "minimum": 0,
            "description": "Credit available for new processing, in cents (`balance_cents` minus `reserved_cents`).",
            "title": "Available Cents",
            "type": "integer"
          },
          "frozen": {
            "title": "Frozen",
            "description": "`true` when the wallet is frozen. New processing is paused (`code: PAYG_WALLET_FROZEN`); contact support.",
            "type": "boolean"
          },
          "payment_failure_paused": {
            "title": "Payment Failure Paused",
            "description": "`true` when an automatic top-up payment failed. New processing that needs credit is paused (`code: PAYG_PAYMENT_PAUSED`) until you update the payment method in Studio.",
            "type": "boolean"
          },
          "processing_paused": {
            "title": "Processing Paused",
            "description": "`true` when new processing cannot start: the wallet is frozen, paused after a payment failure or still being set up, or there are no free pages left and not enough credit for one page.",
            "type": "boolean"
          },
          "saved_card": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SavedCard"
              },
              {
                "type": "null"
              }
            ],
            "description": "The card saved for top-ups, or `null`.",
            "default": null
          },
          "auto_topup": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "revision": {
                "type": "integer",
                "minimum": 0,
                "description": "Settings version; increases each time the settings change."
              },
              "threshold_cents": {
                "type": "integer",
                "minimum": 0,
                "description": "A top-up starts when available credit falls below this amount, in cents."
              },
              "amount_cents": {
                "type": "integer",
                "minimum": 0,
                "description": "Amount added by each top-up, in cents."
              },
              "retry_attempt_count": {
                "type": "integer",
                "minimum": 0,
                "description": "Retries made for the current failed top-up."
              },
              "monthly_limit_cents": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Most that top-ups can add per month, in cents; `null` for no limit."
              },
              "monthly_spent_cents": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Amount added by top-ups this month, in cents, or `null` when not available."
              },
              "monthly_committed_cents": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 0,
                "description": "Amount added this month plus top-ups in progress, in cents, or `null` when not available."
              },
              "next_retry_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "When the failed top-up is retried next."
              },
              "last_decline_code": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Reason the card issuer gave for the last declined top-up."
              },
              "first_failure_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "When the current run of failed top-ups began."
              },
              "last_failure_at": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "When the last top-up failed."
              },
              "cap_window_start": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Start of the month that `monthly_limit_cents` applies to."
              },
              "cap_window_end": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "End of the month that `monthly_limit_cents` applies to."
              },
              "enabled": {
                "type": "boolean",
                "description": "Whether automatic top-up is on."
              },
              "status": {
                "type": "string",
                "description": "Current state of automatic top-up, for example `disabled`, or `card_required` when a new card is needed."
              }
            },
            "description": "Automatic top-up settings and status. Monthly amounts can be `null` when not available."
          }
        },
        "required": [
          "free_total_pages",
          "free_used_pages",
          "free_reserved_pages",
          "free_available_pages",
          "balance_cents",
          "reserved_cents",
          "available_cents",
          "frozen",
          "payment_failure_paused",
          "processing_paused",
          "auto_topup"
        ],
        "title": "WalletProjection",
        "type": "object",
        "additionalProperties": false
      },
      "SavedCard": {
        "description": "Display details of the saved card.",
        "properties": {
          "available": {
            "const": true,
            "description": "Always `true`.",
            "default": true,
            "title": "Available",
            "type": "boolean"
          },
          "brand": {
            "title": "Brand",
            "description": "Card brand, such as `visa`.",
            "type": "string"
          },
          "last4": {
            "pattern": "^[0-9]{4}$",
            "title": "Last4",
            "type": "string"
          },
          "exp_month": {
            "maximum": 12,
            "minimum": 1,
            "title": "Exp Month",
            "type": "integer"
          },
          "exp_year": {
            "minimum": 2000,
            "title": "Exp Year",
            "type": "integer"
          }
        },
        "required": [
          "brand",
          "last4",
          "exp_month",
          "exp_year"
        ],
        "title": "SavedCard",
        "type": "object"
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "ID of this request; quote it when you contact support. Send your own UUID v4 in `X-Request-Id` to have it echoed back; otherwise one is generated. A response replayed for an `Idempotency-Key` carries the original request's ID. Present on every response.",
        "schema": {
          "type": "string",
          "format": "uuid",
          "example": "550e8400-e29b-41d4-a716-446655440000"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests per minute allowed by the limit that applied to this request.",
        "schema": {
          "type": "integer",
          "example": 60
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Number of requests remaining in the current rate-limit window.",
        "schema": {
          "type": "integer",
          "example": 59
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix timestamp (seconds) at which the current rate-limit window resets.",
        "schema": {
          "type": "integer",
          "example": 1741802460
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying. Sent on rate-limit `429` responses; concurrency-limit responses omit it.",
        "schema": {
          "type": "integer",
          "example": 30
        }
      }
    },
    "parameters": {
      "WebhookId": {
        "name": "X-Webhook-Id",
        "in": "header",
        "required": true,
        "description": "Event ID, the same as the body's `id`. It stays the same on every attempt and retry.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "WebhookTimestamp": {
        "name": "X-Webhook-Timestamp",
        "in": "header",
        "required": true,
        "description": "Unix time in seconds when this attempt was signed. It is the part before the dot in the signed message.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]+$",
          "example": "1791222120"
        }
      },
      "WebhookSignature": {
        "name": "X-Webhook-Signature",
        "in": "header",
        "required": true,
        "description": "`sha256=` followed by the hexadecimal HMAC-SHA256 of `X-Webhook-Timestamp`, a dot and the exact raw body bytes, keyed with the full `whsec_` secret as UTF-8 text. Compare in constant time; see the [Webhooks guide](/api/webhooks.html).",
        "schema": {
          "type": "string",
          "pattern": "^sha256=[0-9a-f]{64}$"
        }
      },
      "WebhookUserAgent": {
        "name": "User-Agent",
        "in": "header",
        "required": true,
        "description": "Identifies the sender. Authenticate deliveries by their signature.",
        "schema": {
          "type": "string",
          "example": "LexSelect-Webhooks/1.0"
        }
      },
      "XApiVersion": {
        "name": "X-API-Version",
        "in": "header",
        "required": false,
        "description": "API version to use. Without it, requests use the latest version, `2026-11-05`; send it on every request to keep that version's response shape. Unsupported values return `400`. [Versions and changelog](/api/versioning.html) lists what changed in each version.\n",
        "schema": {
          "type": "string",
          "example": "2026-11-05"
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "One value per operation, reused when you retry that operation. Repeating a request with the same key within 24 hours returns the stored response of the first request. Only successful responses and `400`, `413` and `415` are stored, so after a timeout or `5xx` the same key runs the request again; look the upload up first, as described in [Operations and limits](/api/operations.html). Keys are scoped to your API key, not to an endpoint or body, so use a new value for every new operation, including an upload you send again after correcting an error. Retry with the same key only after the earlier attempt has returned or timed out.",
        "schema": {
          "type": "string",
          "example": "upload-7b07818a-72ad-4b48-9cba-e09a9fb3d581"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Unauthorized",
              "status": 401,
              "detail": "Missing or invalid Authorization header."
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key is valid but does not have permission for this operation.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "examples": {
              "insufficientPermissions": {
                "value": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "detail": "Insufficient permissions for this operation."
                }
              },
              "trialLimitReached": {
                "value": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "detail": "Your free trial limit has been reached. Contact your account administrator or reach out to LexSelect to upgrade your plan."
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Too many requests, or a configured limit stops this request. Check `code` first:\n\n- **`REQUESTS_DISABLED` or `PROCESSING_PAUSED`:** a limit for this API key, member or account is set to `0`. Retrying does not help until the account owner, or for a workspace its owner, changes the limit in Studio, so do not retry automatically, even when `Retry-After` is present.\n- **No `code`, with `Retry-After`:** a rate limit, including the per-IP limit. Wait that many seconds, then retry.\n- **No `code`, without `Retry-After`:** a concurrency limit. Reduce the number of parallel requests and retry with exponential backoff and jitter.\n\nResponses from the per-IP limit carry `Retry-After` but no `X-RateLimit-*` headers. See [Operations and limits](/api/operations.html).\n",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "examples": {
              "rateLimited": {
                "summary": "Rate limit",
                "value": {
                  "type": "about:blank",
                  "title": "Too Many Requests",
                  "status": 429,
                  "detail": "Rate limit exceeded. Retry after 30 seconds."
                }
              },
              "processingPaused": {
                "summary": "Processing paused by a limit of 0",
                "value": {
                  "type": "about:blank",
                  "title": "Processing paused",
                  "status": 429,
                  "detail": "Document processing is paused by an API usage limit. Ask your workspace owner to update the limit.",
                  "code": "PROCESSING_PAUSED"
                }
              }
            }
          }
        }
      },
      "InternalServerError": {
        "description": "An unexpected server error occurred.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Internal Server Error",
              "status": 500,
              "detail": "An unexpected error occurred."
            }
          }
        }
      },
      "GatewayTimeout": {
        "description": "The request took too long to complete. Retry reads with backoff; before retrying an upload, check whether the document was created.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Gateway Timeout",
              "status": 504,
              "detail": "The request timed out. Retry reads; check whether an upload created a document before retrying it."
            }
          }
        }
      },
      "ContentTooLarge": {
        "description": "The uploaded file exceeds the 250 MB limit.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Content Too Large",
              "status": 413,
              "detail": "File exceeds the maximum allowed size of 250 MiB."
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "The uploaded file extension is not supported.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Unsupported Media Type",
              "status": 415,
              "detail": "Unsupported file type. Allowed formats: doc, docx, eml, msg, odt, pdf, rtf."
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid identifier, parameter or request body.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "NotFound": {
        "description": "The owned resource does not exist, belongs to another account or has been purged.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "The service is temporarily unavailable. Retry with backoff.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/ProblemDetails"
            },
            "example": {
              "type": "about:blank",
              "title": "Service Unavailable",
              "status": 503,
              "detail": "The service is temporarily unavailable. Retry the request shortly."
            }
          }
        }
      }
    }
  }
}